From f505d4b8579a977221845935784c1d098d747a13 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Fri, 7 Jan 2022 11:55:41 +0000 Subject: [PATCH] Add the carbon api (#351) --- carbon/.gitignore | 2 + carbon/Dockerfile | 3 + carbon/Makefile | 28 +++ carbon/README.md | 9 + carbon/domain/domain.go | 21 +++ carbon/examples.json | 24 +++ carbon/generate.go | 3 + carbon/handler/carbon.go | 78 ++++++++ carbon/main.go | 24 +++ carbon/micro.mu | 1 + carbon/proto/carbon.pb.go | 321 ++++++++++++++++++++++++++++++++ carbon/proto/carbon.pb.micro.go | 93 +++++++++ carbon/proto/carbon.proto | 32 ++++ carbon/publicapi.json | 9 + pkg/api/api.go | 4 +- 15 files changed, 650 insertions(+), 2 deletions(-) create mode 100644 carbon/.gitignore create mode 100644 carbon/Dockerfile create mode 100644 carbon/Makefile create mode 100644 carbon/README.md create mode 100644 carbon/domain/domain.go create mode 100644 carbon/examples.json create mode 100644 carbon/generate.go create mode 100644 carbon/handler/carbon.go create mode 100644 carbon/main.go create mode 100644 carbon/micro.mu create mode 100644 carbon/proto/carbon.pb.go create mode 100644 carbon/proto/carbon.pb.micro.go create mode 100644 carbon/proto/carbon.proto create mode 100644 carbon/publicapi.json diff --git a/carbon/.gitignore b/carbon/.gitignore new file mode 100644 index 0000000..ba1d4a6 --- /dev/null +++ b/carbon/.gitignore @@ -0,0 +1,2 @@ + +carbon diff --git a/carbon/Dockerfile b/carbon/Dockerfile new file mode 100644 index 0000000..0fa3c6d --- /dev/null +++ b/carbon/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine +ADD carbon /carbon +ENTRYPOINT [ "/carbon" ] diff --git a/carbon/Makefile b/carbon/Makefile new file mode 100644 index 0000000..1417564 --- /dev/null +++ b/carbon/Makefile @@ -0,0 +1,28 @@ + +GOPATH:=$(shell go env GOPATH) +.PHONY: init +init: + go get -u github.com/golang/protobuf/proto + go get -u github.com/golang/protobuf/protoc-gen-go + go get github.com/micro/micro/v3/cmd/protoc-gen-micro + go get github.com/micro/micro/v3/cmd/protoc-gen-openapi + +.PHONY: api +api: + protoc --openapi_out=. --proto_path=. proto/carbon.proto + +.PHONY: proto +proto: + protoc --proto_path=. --micro_out=. --go_out=:. proto/carbon.proto + +.PHONY: build +build: + go build -o carbon *.go + +.PHONY: test +test: + go test -v ./... -cover + +.PHONY: docker +docker: + docker build . -t carbon:latest diff --git a/carbon/README.md b/carbon/README.md new file mode 100644 index 0000000..3c9bf57 --- /dev/null +++ b/carbon/README.md @@ -0,0 +1,9 @@ +Purchase carbon offsets + +# Carbon Service + +Purchase carbon offsets to help climate change and achieve carbon neutrality. +Offset purchases are currently limited to 1kg (or 0.001 tonnes) per request. +Make multiple requests to purchase the required amount. + +Powered by [Ecologi](https://ecologi.com/) diff --git a/carbon/domain/domain.go b/carbon/domain/domain.go new file mode 100644 index 0000000..d8a75c7 --- /dev/null +++ b/carbon/domain/domain.go @@ -0,0 +1,21 @@ +package domain + +type Project struct { + Name string `json:"name"` + Percentage float64 `json:"splitPercentage"` + Tonnes float64 `json:"splitAmountTonnes"` +} + +type OffsetRequest struct { + Number int32 `json:"number"` + Units string `json:"units"` +} + +type OffsetResponse struct { + Number int32 `json:"number"` + Units string `json:"units"` + Tonnes float64 `json:"numberInTonnes"` + Amount float64 `json:"amount"` + Currency string `json:"currency"` + Projects []Project `json:"projectDetails"` +} diff --git a/carbon/examples.json b/carbon/examples.json new file mode 100644 index 0000000..d6a80d2 --- /dev/null +++ b/carbon/examples.json @@ -0,0 +1,24 @@ +{ + "offset": [{ + "title": "Offset Carbon", + "run_check": false, + "request": {}, + "response": { + "units": 1, + "metric": "KG", + "tonnes": 0.001, + "projects": [ + { + "name": "Producing electricity from wind power in Northeast Thailand", + "percentage": 98, + "tonnes": 0.001 + }, + { + "name": "Wind power generation in Bac Lieu Province, Vietnam", + "percentage": 2, + "tonnes": 0 + } + ] + } + }] +} diff --git a/carbon/generate.go b/carbon/generate.go new file mode 100644 index 0000000..7d9db91 --- /dev/null +++ b/carbon/generate.go @@ -0,0 +1,3 @@ +package main + +//go:generate make proto diff --git a/carbon/handler/carbon.go b/carbon/handler/carbon.go new file mode 100644 index 0000000..973f623 --- /dev/null +++ b/carbon/handler/carbon.go @@ -0,0 +1,78 @@ +package handler + +import ( + "context" + + "github.com/micro/micro/v3/service/config" + "github.com/micro/micro/v3/service/errors" + "github.com/micro/micro/v3/service/logger" + "github.com/micro/services/carbon/domain" + "github.com/micro/services/pkg/api" + + pb "github.com/micro/services/carbon/proto" +) + +type Carbon struct { + apiKey string + apiAddress string +} + +func New() *Carbon { + v, err := config.Get("carbon.api_key") + if err != nil { + logger.Fatalf("carbon.api_key config not found: %v", err) + } + apiKey := v.String("") + if len(apiKey) == 0 { + logger.Fatal("carbon.api_key config not found") + } + + api.SetKey("Authorization", "Bearer "+apiKey) + api.SetKey("Content-Type", "application/json") + + v, err = config.Get("carbon.api_address") + if err != nil { + logger.Fatalf("carbon.api_address config not found: %v", err) + } + apiAddress := v.String("") + if len(apiKey) == 0 { + logger.Fatal("carbon.api_address config not found") + } + + return &Carbon{ + apiKey: apiKey, + apiAddress: apiAddress, + } +} + +func (c *Carbon) Offset(ctx context.Context, req *pb.OffsetRequest, rsp *pb.OffsetResponse) error { + var resp domain.OffsetResponse + + // currently do not support options + r := &domain.OffsetRequest{ + Number: 1, + Units: "KG", + } + + if err := api.Post(c.apiAddress+"/impact/carbon", r, &resp); err != nil { + logger.Error("Failed to purchase offsets: ", err.Error()) + return errors.InternalServerError("carbon.offset", "failed to purchase offsets") + } + + logger.Infof("Purchased %d %s: %v\n", r.Number, r.Units, resp) + + rsp.Units = resp.Number + rsp.Metric = resp.Units + rsp.Tonnes = resp.Tonnes + //rsp.Cost = resp.Amount + //rsp.Currency = resp.Currency + for _, p := range resp.Projects { + rsp.Projects = append(rsp.Projects, &pb.Project{ + Name: p.Name, + Percentage: p.Percentage, + Tonnes: p.Tonnes, + }) + } + + return nil +} diff --git a/carbon/main.go b/carbon/main.go new file mode 100644 index 0000000..bf485b0 --- /dev/null +++ b/carbon/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "github.com/micro/micro/v3/service" + "github.com/micro/micro/v3/service/logger" + "github.com/micro/services/carbon/handler" + pb "github.com/micro/services/carbon/proto" +) + +func main() { + // Create service + srv := service.New( + service.Name("carbon"), + service.Version("latest"), + ) + + // Register handler + pb.RegisterCarbonHandler(srv.Server(), handler.New()) + + // Run service + if err := srv.Run(); err != nil { + logger.Fatal(err) + } +} diff --git a/carbon/micro.mu b/carbon/micro.mu new file mode 100644 index 0000000..b19a450 --- /dev/null +++ b/carbon/micro.mu @@ -0,0 +1 @@ +service carbon diff --git a/carbon/proto/carbon.pb.go b/carbon/proto/carbon.pb.go new file mode 100644 index 0000000..1799254 --- /dev/null +++ b/carbon/proto/carbon.pb.go @@ -0,0 +1,321 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.15.6 +// source: proto/carbon.proto + +package carbon + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Project struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // name of the project + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // percentage that went to this + Percentage float64 `protobuf:"fixed64,2,opt,name=percentage,proto3" json:"percentage,omitempty"` + // amount in tonnes + Tonnes float64 `protobuf:"fixed64,3,opt,name=tonnes,proto3" json:"tonnes,omitempty"` +} + +func (x *Project) Reset() { + *x = Project{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_carbon_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Project) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Project) ProtoMessage() {} + +func (x *Project) ProtoReflect() protoreflect.Message { + mi := &file_proto_carbon_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Project.ProtoReflect.Descriptor instead. +func (*Project) Descriptor() ([]byte, []int) { + return file_proto_carbon_proto_rawDescGZIP(), []int{0} +} + +func (x *Project) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Project) GetPercentage() float64 { + if x != nil { + return x.Percentage + } + return 0 +} + +func (x *Project) GetTonnes() float64 { + if x != nil { + return x.Tonnes + } + return 0 +} + +// Purchase 1kg (0.001 tonnes) of carbon offsets in a single request +type OffsetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *OffsetRequest) Reset() { + *x = OffsetRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_carbon_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OffsetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OffsetRequest) ProtoMessage() {} + +func (x *OffsetRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_carbon_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OffsetRequest.ProtoReflect.Descriptor instead. +func (*OffsetRequest) Descriptor() ([]byte, []int) { + return file_proto_carbon_proto_rawDescGZIP(), []int{1} +} + +type OffsetResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // number of units purchased + Units int32 `protobuf:"varint,1,opt,name=units,proto3" json:"units,omitempty"` + // the metric used e.g KG or Tonnes + Metric string `protobuf:"bytes,2,opt,name=metric,proto3" json:"metric,omitempty"` + // number of tonnes + Tonnes float64 `protobuf:"fixed64,3,opt,name=tonnes,proto3" json:"tonnes,omitempty"` + // projects it was allocated to + Projects []*Project `protobuf:"bytes,4,rep,name=projects,proto3" json:"projects,omitempty"` +} + +func (x *OffsetResponse) Reset() { + *x = OffsetResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_carbon_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OffsetResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OffsetResponse) ProtoMessage() {} + +func (x *OffsetResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_carbon_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OffsetResponse.ProtoReflect.Descriptor instead. +func (*OffsetResponse) Descriptor() ([]byte, []int) { + return file_proto_carbon_proto_rawDescGZIP(), []int{2} +} + +func (x *OffsetResponse) GetUnits() int32 { + if x != nil { + return x.Units + } + return 0 +} + +func (x *OffsetResponse) GetMetric() string { + if x != nil { + return x.Metric + } + return "" +} + +func (x *OffsetResponse) GetTonnes() float64 { + if x != nil { + return x.Tonnes + } + return 0 +} + +func (x *OffsetResponse) GetProjects() []*Project { + if x != nil { + return x.Projects + } + return nil +} + +var File_proto_carbon_proto protoreflect.FileDescriptor + +var file_proto_carbon_proto_rawDesc = []byte{ + 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x63, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x22, 0x55, 0x0a, 0x07, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, + 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, + 0x6f, 0x6e, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x74, 0x6f, 0x6e, + 0x6e, 0x65, 0x73, 0x22, 0x0f, 0x0a, 0x0d, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x83, 0x01, 0x0a, 0x0e, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x12, 0x16, 0x0a, + 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x6f, 0x6e, 0x6e, 0x65, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x74, 0x6f, 0x6e, 0x6e, 0x65, 0x73, 0x12, 0x2b, 0x0a, + 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0f, 0x2e, 0x63, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x52, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x32, 0x43, 0x0a, 0x06, 0x43, 0x61, + 0x72, 0x62, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x06, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x15, + 0x2e, 0x63, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x2e, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x63, 0x61, 0x72, 0x62, 0x6f, 0x6e, 0x2e, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, + 0x10, 0x5a, 0x0e, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x63, 0x61, 0x72, 0x62, 0x6f, + 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proto_carbon_proto_rawDescOnce sync.Once + file_proto_carbon_proto_rawDescData = file_proto_carbon_proto_rawDesc +) + +func file_proto_carbon_proto_rawDescGZIP() []byte { + file_proto_carbon_proto_rawDescOnce.Do(func() { + file_proto_carbon_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_carbon_proto_rawDescData) + }) + return file_proto_carbon_proto_rawDescData +} + +var file_proto_carbon_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_proto_carbon_proto_goTypes = []interface{}{ + (*Project)(nil), // 0: carbon.Project + (*OffsetRequest)(nil), // 1: carbon.OffsetRequest + (*OffsetResponse)(nil), // 2: carbon.OffsetResponse +} +var file_proto_carbon_proto_depIdxs = []int32{ + 0, // 0: carbon.OffsetResponse.projects:type_name -> carbon.Project + 1, // 1: carbon.Carbon.Offset:input_type -> carbon.OffsetRequest + 2, // 2: carbon.Carbon.Offset:output_type -> carbon.OffsetResponse + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_proto_carbon_proto_init() } +func file_proto_carbon_proto_init() { + if File_proto_carbon_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_carbon_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Project); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_carbon_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OffsetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_carbon_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OffsetResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_carbon_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_carbon_proto_goTypes, + DependencyIndexes: file_proto_carbon_proto_depIdxs, + MessageInfos: file_proto_carbon_proto_msgTypes, + }.Build() + File_proto_carbon_proto = out.File + file_proto_carbon_proto_rawDesc = nil + file_proto_carbon_proto_goTypes = nil + file_proto_carbon_proto_depIdxs = nil +} diff --git a/carbon/proto/carbon.pb.micro.go b/carbon/proto/carbon.pb.micro.go new file mode 100644 index 0000000..12a7652 --- /dev/null +++ b/carbon/proto/carbon.pb.micro.go @@ -0,0 +1,93 @@ +// Code generated by protoc-gen-micro. DO NOT EDIT. +// source: proto/carbon.proto + +package carbon + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +import ( + context "context" + api "github.com/micro/micro/v3/service/api" + client "github.com/micro/micro/v3/service/client" + server "github.com/micro/micro/v3/service/server" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +// Reference imports to suppress errors if they are not otherwise used. +var _ api.Endpoint +var _ context.Context +var _ client.Option +var _ server.Option + +// Api Endpoints for Carbon service + +func NewCarbonEndpoints() []*api.Endpoint { + return []*api.Endpoint{} +} + +// Client API for Carbon service + +type CarbonService interface { + Offset(ctx context.Context, in *OffsetRequest, opts ...client.CallOption) (*OffsetResponse, error) +} + +type carbonService struct { + c client.Client + name string +} + +func NewCarbonService(name string, c client.Client) CarbonService { + return &carbonService{ + c: c, + name: name, + } +} + +func (c *carbonService) Offset(ctx context.Context, in *OffsetRequest, opts ...client.CallOption) (*OffsetResponse, error) { + req := c.c.NewRequest(c.name, "Carbon.Offset", in) + out := new(OffsetResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Carbon service + +type CarbonHandler interface { + Offset(context.Context, *OffsetRequest, *OffsetResponse) error +} + +func RegisterCarbonHandler(s server.Server, hdlr CarbonHandler, opts ...server.HandlerOption) error { + type carbon interface { + Offset(ctx context.Context, in *OffsetRequest, out *OffsetResponse) error + } + type Carbon struct { + carbon + } + h := &carbonHandler{hdlr} + return s.Handle(s.NewHandler(&Carbon{h}, opts...)) +} + +type carbonHandler struct { + CarbonHandler +} + +func (h *carbonHandler) Offset(ctx context.Context, in *OffsetRequest, out *OffsetResponse) error { + return h.CarbonHandler.Offset(ctx, in, out) +} diff --git a/carbon/proto/carbon.proto b/carbon/proto/carbon.proto new file mode 100644 index 0000000..d12e088 --- /dev/null +++ b/carbon/proto/carbon.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package carbon; + +option go_package = "./proto;carbon"; + +service Carbon { + rpc Offset(OffsetRequest) returns (OffsetResponse) {} +} + +message Project { + // name of the project + string name = 1; + // percentage that went to this + double percentage = 2; + // amount in tonnes + double tonnes = 3; +} + +// Purchase 1kg (0.001 tonnes) of carbon offsets in a single request +message OffsetRequest {} + +message OffsetResponse { + // number of units purchased + int32 units = 1; + // the metric used e.g KG or Tonnes + string metric = 2; + // number of tonnes + double tonnes = 3; + // projects it was allocated to + repeated Project projects = 4; +} diff --git a/carbon/publicapi.json b/carbon/publicapi.json new file mode 100644 index 0000000..1448753 --- /dev/null +++ b/carbon/publicapi.json @@ -0,0 +1,9 @@ +{ + "name": "carbon", + "icon": "🌋", + "category": "climate", + "display_name": "Carbon", + "pricing": { + "Carbon.Offset": 5000000 + } +} diff --git a/pkg/api/api.go b/pkg/api/api.go index ee021f0..1c7ae66 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -40,7 +40,7 @@ func Get(url string, rsp interface{}) error { return err } - if resp.StatusCode != 200 { + if resp.StatusCode >= 400 { return fmt.Errorf("Non 200 response %v: %v", resp.StatusCode, string(b)) } @@ -78,7 +78,7 @@ func Post(url string, ureq, rsp interface{}) error { return err } - if resp.StatusCode != 200 { + if resp.StatusCode >= 400 { return fmt.Errorf("Non 200 response %v: %v", resp.StatusCode, string(b)) }