diff --git a/Makefile b/Makefile index 82a1bc3..7efcf11 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,11 @@ define HELP_TEXT Welcome! Targets: - help provides help text - deps resolve dependencies - lint lint source for errors or breaking changes - legal prepends license to source code + help provides help text + deps resolve dependencies + lint lint source for errors or breaking changes + legal prepends license to source code + generate generates swagger docs endef export HELP_TEXT @@ -23,3 +24,10 @@ lint: legal: .legal .legal: addlicense -f ./legal/header.txt -skip yaml -skip yml -skip xml . + +generate: + buf generate . + cd scripts && go run ./merge-swagger \ + -output ../swagger.json \ + ../**/*.swagger.json \ + ../swagger/metadata.json diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000..75bb6af --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,10 @@ +version: v1 + +managed: + enabled: true + go_package_prefix: + default: code.pitz.tech/licensing/api-go + +plugins: + - plugin: buf.build/grpc-ecosystem/openapiv2 + out: . diff --git a/contributors/v1/service.proto b/contributors/v1/service.proto index 6ab72f1..8f53ba9 100644 --- a/contributors/v1/service.proto +++ b/contributors/v1/service.proto @@ -14,16 +14,30 @@ package contributors.v1; import "google/api/annotations.proto"; -message ListRequest {} +message ListRequest { + string product_id = 1; +} + message ListResponse {} -message CreateRequest {} +message CreateRequest { + string product_id = 1; +} + message CreateResponse {} -message UpdateRequest {} +message UpdateRequest { + string product_id = 1; + string contributor_id = 2; +} + message UpdateResponse {} -message DeleteRequest {} +message DeleteRequest { + string product_id = 1; + string contributor_id = 2; +} + message DeleteResponse {} service ContributorService { diff --git a/licenses/v1/service.proto b/licenses/v1/service.proto index 73d807b..f5bb5f2 100644 --- a/licenses/v1/service.proto +++ b/licenses/v1/service.proto @@ -15,12 +15,17 @@ package licenses.v1; import "google/api/annotations.proto"; message ListRequest {} + message ListResponse {} message PurchaseRequest {} + message PurchaseResponse {} -message CancelRequest {} +message CancelRequest { + string license_id = 1; +} + message CancelResponse {} service LicenseService { @@ -39,8 +44,7 @@ service LicenseService { rpc Cancel(CancelRequest) returns (CancelResponse) { option (google.api.http) = { - post: "/v1/licenses/{license_id}" - body: "*" + delete: "/v1/licenses/{license_id}" }; }; } diff --git a/packages/v1/service.proto b/packages/v1/service.proto index adfd7e0..3ce0b19 100644 --- a/packages/v1/service.proto +++ b/packages/v1/service.proto @@ -14,19 +14,37 @@ package packages.v1; import "google/api/annotations.proto"; -message ListRequest {} +message ListRequest { + string product_id = 1; +} + message ListResponse {} -message CreateRequest {} +message CreateRequest { + string product_id = 1; +} + message CreateResponse {} -message ReadRequest {} +message ReadRequest { + string product_id = 1; + string package_name = 2; +} + message ReadResponse {} -message UpdateRequest {} +message UpdateRequest { + string product_id = 1; + string package_name = 2; +} + message UpdateResponse {} -message DeleteRequest {} +message DeleteRequest { + string product_id = 1; + string package_name = 2; +} + message DeleteResponse {} service PackageService { diff --git a/products/v1/service.proto b/products/v1/service.proto index a1fdd4c..aa5c123 100644 --- a/products/v1/service.proto +++ b/products/v1/service.proto @@ -15,18 +15,29 @@ package products.v1; import "google/api/annotations.proto"; message ListRequest {} + message ListResponse {} message CreateRequest {} + message CreateResponse {} -message ReadRequest {} +message ReadRequest { + string product_id = 1; +} + message ReadResponse {} -message UpdateRequest {} +message UpdateRequest { + string product_id = 1; +} + message UpdateResponse {} -message DeleteRequest {} +message DeleteRequest { + string product_id = 1; +} + message DeleteResponse {} service ProductService { diff --git a/scripts/go.mod b/scripts/go.mod new file mode 100644 index 0000000..9ce0f38 --- /dev/null +++ b/scripts/go.mod @@ -0,0 +1,5 @@ +module code.pitz.tech/licensing/proto + +go 1.20 + +require dario.cat/mergo v1.0.0 diff --git a/scripts/go.sum b/scripts/go.sum new file mode 100644 index 0000000..930289f --- /dev/null +++ b/scripts/go.sum @@ -0,0 +1,5 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/scripts/merge-swagger/main.go b/scripts/merge-swagger/main.go new file mode 100644 index 0000000..b8fcf51 --- /dev/null +++ b/scripts/merge-swagger/main.go @@ -0,0 +1,107 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "os" + "path/filepath" + + "dario.cat/mergo" +) + +func newCLI(name, usage string) *CLI { + cli := &CLI{} + cli.FlagSet = flag.NewFlagSet(name, flag.ExitOnError) + + cli.FlagSet.Usage = func() { + cli.Printf("Usage:\n") + cli.Printf(" %s %s\n\n", name, usage) + cli.FlagSet.PrintDefaults() + cli.Printf("\n") + } + + return cli +} + +type CLI struct { + *flag.FlagSet +} + +func (cli *CLI) Printf(format string, args ...any) { + _, _ = fmt.Fprintf(cli.FlagSet.Output(), format, args...) +} + +func readFile(match string, dest any) error { + data, err := os.ReadFile(match) + if err != nil { + return err + } + + return json.Unmarshal(data, dest) +} + +func main() { + cli := newCLI("merge-swagger", "[opts] ") + + output := cli.String("output", "-", `Specify where the merged file should be written. "-" for stdout.`) + + err := cli.Parse(os.Args[1:]) + if err != nil { + panic(err) + } + + if cli.NArg() < 1 { + cli.Usage() + return + } + + var all []string + + for _, arg := range cli.Args() { + matches, err := filepath.Glob(arg) + if err != nil { + panic(err) + } + + all = append(all, matches...) + } + + full := map[string]interface{}{} + + for _, match := range all { + data := map[string]interface{}{} + + err = readFile(match, &data) + if err != nil { + panic(err) + } + + err = mergo.Merge(&full, data, mergo.WithOverride, mergo.WithAppendSlice) + if err != nil { + panic(err) + } + } + + full["consumes"] = []string{ + "application/json", + } + + full["produces"] = []string{ + "application/json", + } + + out := os.Stdout + if output != nil && *output != "-" && *output != "" { + out, err = os.Create(*output) + if err != nil { + panic(err) + } + + defer out.Close() + } + + enc := json.NewEncoder(out) + enc.SetIndent("", " ") + _ = enc.Encode(full) +} diff --git a/swagger.json b/swagger.json new file mode 100644 index 0000000..f0c9abc --- /dev/null +++ b/swagger.json @@ -0,0 +1,867 @@ +{ + "consumes": [ + "application/json" + ], + "definitions": { + "contributorsv1CreateResponse": { + "type": "object" + }, + "contributorsv1DeleteResponse": { + "type": "object" + }, + "contributorsv1ListResponse": { + "type": "object" + }, + "contributorsv1UpdateResponse": { + "type": "object" + }, + "licensesv1ListResponse": { + "type": "object" + }, + "packagesv1CreateResponse": { + "type": "object" + }, + "packagesv1DeleteResponse": { + "type": "object" + }, + "packagesv1ListResponse": { + "type": "object" + }, + "packagesv1ReadResponse": { + "type": "object" + }, + "packagesv1UpdateResponse": { + "type": "object" + }, + "productsv1CreateRequest": { + "type": "object" + }, + "productsv1CreateResponse": { + "type": "object" + }, + "productsv1DeleteResponse": { + "type": "object" + }, + "productsv1ListResponse": { + "type": "object" + }, + "productsv1ReadResponse": { + "type": "object" + }, + "productsv1UpdateResponse": { + "type": "object" + }, + "protobufAny": { + "additionalProperties": {}, + "properties": { + "@type": { + "type": "string" + } + }, + "type": "object" + }, + "rpcStatus": { + "properties": { + "code": { + "format": "int32", + "type": "integer" + }, + "details": { + "items": { + "$ref": "#/definitions/protobufAny", + "type": "object" + }, + "type": "array" + }, + "message": { + "type": "string" + } + }, + "type": "object" + }, + "tokensv1CreateRequest": { + "type": "object" + }, + "tokensv1CreateResponse": { + "type": "object" + }, + "tokensv1DeleteResponse": { + "type": "object" + }, + "tokensv1ListResponse": { + "type": "object" + }, + "v1AuthenticateRequest": { + "type": "object" + }, + "v1AuthenticateResponse": { + "type": "object" + }, + "v1CancelResponse": { + "type": "object" + }, + "v1CurrentResponse": { + "type": "object" + }, + "v1PurchaseRequest": { + "type": "object" + }, + "v1PurchaseResponse": { + "type": "object" + }, + "v1SignupRequest": { + "type": "object" + }, + "v1SignupResponse": { + "type": "object" + } + }, + "info": { + "title": "The Licensing API", + "version": "v1.0" + }, + "paths": { + "/v1/licenses": { + "get": { + "operationId": "LicenseService_List", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/licensesv1ListResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "LicenseService" + ] + }, + "post": { + "operationId": "LicenseService_Purchase", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1PurchaseRequest" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1PurchaseResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "LicenseService" + ] + } + }, + "/v1/licenses/{licenseId}": { + "delete": { + "operationId": "LicenseService_Cancel", + "parameters": [ + { + "in": "path", + "name": "licenseId", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1CancelResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "LicenseService" + ] + } + }, + "/v1/products": { + "get": { + "operationId": "ProductService_List", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/productsv1ListResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "ProductService" + ] + }, + "post": { + "operationId": "ProductService_Create", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/productsv1CreateRequest" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/productsv1CreateResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "ProductService" + ] + } + }, + "/v1/products/{productId}": { + "delete": { + "operationId": "ProductService_Delete", + "parameters": [ + { + "in": "path", + "name": "productId", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/productsv1DeleteResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "ProductService" + ] + }, + "get": { + "operationId": "ProductService_Read", + "parameters": [ + { + "in": "path", + "name": "productId", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/productsv1ReadResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "ProductService" + ] + }, + "post": { + "operationId": "ProductService_Update", + "parameters": [ + { + "in": "path", + "name": "productId", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/productsv1UpdateResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "ProductService" + ] + } + }, + "/v1/products/{productId}/contributors": { + "get": { + "operationId": "ContributorService_List", + "parameters": [ + { + "in": "path", + "name": "productId", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/contributorsv1ListResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "ContributorService" + ] + }, + "post": { + "operationId": "ContributorService_Create", + "parameters": [ + { + "in": "path", + "name": "productId", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/contributorsv1CreateResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "ContributorService" + ] + } + }, + "/v1/products/{productId}/contributors/{contributorId}": { + "delete": { + "operationId": "ContributorService_Delete", + "parameters": [ + { + "in": "path", + "name": "productId", + "required": true, + "type": "string" + }, + { + "in": "path", + "name": "contributorId", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/contributorsv1DeleteResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "ContributorService" + ] + }, + "post": { + "operationId": "ContributorService_Update", + "parameters": [ + { + "in": "path", + "name": "productId", + "required": true, + "type": "string" + }, + { + "in": "path", + "name": "contributorId", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/contributorsv1UpdateResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "ContributorService" + ] + } + }, + "/v1/products/{productId}/packages": { + "get": { + "operationId": "PackageService_List", + "parameters": [ + { + "in": "path", + "name": "productId", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/packagesv1ListResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "PackageService" + ] + }, + "post": { + "operationId": "PackageService_Create", + "parameters": [ + { + "in": "path", + "name": "productId", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/packagesv1CreateResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "PackageService" + ] + } + }, + "/v1/products/{productId}/packages/{packageName}": { + "delete": { + "operationId": "PackageService_Delete", + "parameters": [ + { + "in": "path", + "name": "productId", + "required": true, + "type": "string" + }, + { + "in": "path", + "name": "packageName", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/packagesv1DeleteResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "PackageService" + ] + }, + "get": { + "operationId": "PackageService_Read", + "parameters": [ + { + "in": "path", + "name": "productId", + "required": true, + "type": "string" + }, + { + "in": "path", + "name": "packageName", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/packagesv1ReadResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "PackageService" + ] + }, + "post": { + "operationId": "PackageService_Update", + "parameters": [ + { + "in": "path", + "name": "productId", + "required": true, + "type": "string" + }, + { + "in": "path", + "name": "packageName", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/packagesv1UpdateResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "PackageService" + ] + } + }, + "/v1/token": { + "post": { + "operationId": "TokenService_Authenticate", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1AuthenticateRequest" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1AuthenticateResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "TokenService" + ] + } + }, + "/v1/tokens": { + "get": { + "operationId": "TokenService_List", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/tokensv1ListResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "TokenService" + ] + }, + "post": { + "operationId": "TokenService_Create", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/tokensv1CreateRequest" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/tokensv1CreateResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "TokenService" + ] + } + }, + "/v1/tokens/{tokenId}": { + "delete": { + "operationId": "TokenService_Delete", + "parameters": [ + { + "in": "path", + "name": "tokenId", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/tokensv1DeleteResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "TokenService" + ] + } + }, + "/v1/users": { + "post": { + "operationId": "UserService_Signup", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1SignupRequest" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1SignupResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "UserService" + ] + } + }, + "/v1/users/current": { + "get": { + "operationId": "UserService_Current", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1CurrentResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "UserService" + ] + } + } + }, + "produces": [ + "application/json" + ], + "swagger": "2.0", + "tags": [ + { + "name": "ContributorService" + }, + { + "name": "LicenseService" + }, + { + "name": "PackageService" + }, + { + "name": "ProductService" + }, + { + "name": "TokenService" + }, + { + "name": "UserService" + } + ] +} diff --git a/swagger/metadata.json b/swagger/metadata.json new file mode 100644 index 0000000..866ad0c --- /dev/null +++ b/swagger/metadata.json @@ -0,0 +1,6 @@ +{ + "info": { + "title": "The Licensing API", + "version": "v1.0" + } +} diff --git a/tokens/v1/service.proto b/tokens/v1/service.proto index 39b5ad3..a7cbd60 100644 --- a/tokens/v1/service.proto +++ b/tokens/v1/service.proto @@ -15,15 +15,21 @@ package tokens.v1; import "google/api/annotations.proto"; message AuthenticateRequest {} + message AuthenticateResponse {} message ListRequest {} + message ListResponse {} message CreateRequest {} + message CreateResponse {} -message DeleteRequest {} +message DeleteRequest { + string token_id = 1; +} + message DeleteResponse {} service TokenService {