private/apigen: Rename Endpoint fields

Rename the Endpoint fields MethodName and RequestName because they were
confusing for what they are used.

This commit also adds some validations for these fields values and other
validations for Endpoint and EndpointGroup to avoid generating invalid
code.

It also include some tests for these new validations.

Closes https://github.com/storj/storj/issues/6333

Change-Id: Iaabfc33935517889e3729c8b37be51a55eea366c
This commit is contained in:
Ivan Fraixedes 2023-09-27 18:48:01 +02:00
parent ac86eb397a
commit 956109a097
No known key found for this signature in database
GPG Key ID: FB6101AFB5CB5AD5
8 changed files with 449 additions and 142 deletions

View File

@ -47,7 +47,8 @@ func (a *API) Group(name, prefix string) *EndpointGroup {
if !groupNameRegExp.MatchString(name) {
panic(
fmt.Sprintf(
"invalid name for API Endpoint Group. name must fulfill the regular expression `^([A-Z0-9]\\w*)?$``, got %q",
"invalid name for API Endpoint Group. name must fulfill the regular expression %q, got %q",
groupNameRegExp,
name,
),
)
@ -55,8 +56,9 @@ func (a *API) Group(name, prefix string) *EndpointGroup {
if !groupPrefixRegExp.MatchString(prefix) {
panic(
fmt.Sprintf(
"invalid prefix for API Endpoint Group %q. prefix must fulfill the regular expression `^\\w*$`, got %q",
"invalid prefix for API Endpoint Group %q. prefix must fulfill the regular expression %q, got %q",
name,
groupPrefixRegExp,
prefix,
),
)

View File

@ -7,7 +7,17 @@ import (
"fmt"
"net/http"
"reflect"
"regexp"
"strings"
"github.com/zeebo/errs"
)
var (
errsEndpoint = errs.Class("Endpoint")
goNameRegExp = regexp.MustCompile(`^[A-Z]\w*$`)
typeScriptNameRegExp = regexp.MustCompile(`^[a-z][a-zA-Z0-9_$]*$`)
)
// Endpoint represents endpoint's configuration.
@ -17,18 +27,21 @@ type Endpoint struct {
Name string
// Description is a free text to describe the endpoint for documentation purpose.
Description string
// MethodName is the name of method of the service interface which handles the business logic of
// this endpoint.
// It must fulfill the Go language specification for method names
// (https://go.dev/ref/spec#MethodName)
// TODO: Should we rename this field to be something like ServiceMethodName?
MethodName string
// RequestName is the name of the method used to name the method in the client side code. When not
// set, MethodName is used.
// TODO: Should we delete this field in favor of always using MethodName?
RequestName string
NoCookieAuth bool
NoAPIAuth bool
// GoName is an identifier used by the Go generator to generate specific server side code for this
// endpoint.
//
// It must start with an uppercase letter and fulfill the Go language specification for method
// names (https://go.dev/ref/spec#MethodName).
// It cannot be empty.
GoName string
// TypeScriptName is an identifier used by the TypeScript generator to generate specific client
// code for this endpoint
//
// It must start with a lowercase letter and can only contains letters, digits, _, and $.
// It cannot be empty.
TypeScriptName string
NoCookieAuth bool
NoAPIAuth bool
// Request is the type that defines the format of the request body.
Request interface{}
// Response is the type that defines the format of the response body.
@ -50,6 +63,27 @@ func (e *Endpoint) APIAuth() bool {
return !e.NoAPIAuth
}
// Validate validates the endpoint fields values are correct according to the documented constraints.
func (e *Endpoint) Validate() error {
if e.Name == "" {
return errsEndpoint.New("Name cannot be empty")
}
if e.Description == "" {
return errsEndpoint.New("Description cannot be empty")
}
if !goNameRegExp.MatchString(e.GoName) {
return errsEndpoint.New("GoName doesn't match the regular expression %q", goNameRegExp)
}
if !typeScriptNameRegExp.MatchString(e.TypeScriptName) {
return errsEndpoint.New("TypeScriptName doesn't match the regular expression %q", typeScriptNameRegExp)
}
return nil
}
// fullEndpoint represents endpoint with path and method.
type fullEndpoint struct {
Endpoint
@ -64,18 +98,13 @@ func (fe fullEndpoint) requestType() reflect.Type {
return t
}
name := fe.RequestName
if name == "" {
name = fe.MethodName
}
switch k := t.Kind(); k {
case reflect.Array, reflect.Slice:
if t.Elem().Name() == "" {
t = typeCustomName{Type: t, name: compoundTypeName(name, "Request")}
t = typeCustomName{Type: t, name: compoundTypeName(fe.TypeScriptName, "Request")}
}
case reflect.Struct:
t = typeCustomName{Type: t, name: compoundTypeName(name, "Request")}
t = typeCustomName{Type: t, name: compoundTypeName(fe.TypeScriptName, "Request")}
default:
panic(
fmt.Sprintf(
@ -98,10 +127,10 @@ func (fe fullEndpoint) responseType() reflect.Type {
switch k := t.Kind(); k {
case reflect.Array, reflect.Slice:
if t.Elem().Name() == "" {
t = typeCustomName{Type: t, name: compoundTypeName(fe.MethodName, "Response")}
t = typeCustomName{Type: t, name: compoundTypeName(fe.TypeScriptName, "Response")}
}
case reflect.Struct:
t = typeCustomName{Type: t, name: compoundTypeName(fe.MethodName, "Response")}
t = typeCustomName{Type: t, name: compoundTypeName(fe.TypeScriptName, "Response")}
default:
panic(
fmt.Sprintf(
@ -148,7 +177,10 @@ func (eg *EndpointGroup) Delete(path string, endpoint *Endpoint) {
}
// addEndpoint adds new endpoint to endpoints list.
// It panics if path doesn't begin with '/'.
// It panics if:
// - path doesn't begin with '/'.
// - endpoint.Validate() returns an error.
// - An Endpoint with the same path and method already exists.
func (eg *EndpointGroup) addEndpoint(path, method string, endpoint *Endpoint) {
if !strings.HasPrefix(path, "/") {
panic(
@ -161,11 +193,31 @@ func (eg *EndpointGroup) addEndpoint(path, method string, endpoint *Endpoint) {
)
}
if err := endpoint.Validate(); err != nil {
panic(err)
}
ep := &fullEndpoint{*endpoint, path, method}
for i, e := range eg.endpoints {
for _, e := range eg.endpoints {
if e.Path == path && e.Method == method {
eg.endpoints[i] = ep
return
panic(fmt.Sprintf("there is already an endpoint defined with path %q and method %q", path, method))
}
if e.GoName == ep.GoName {
panic(
fmt.Sprintf("GoName %q is already used by the endpoint with path %q and method %q", e.GoName, e.Path, e.Method),
)
}
if e.TypeScriptName == ep.TypeScriptName {
panic(
fmt.Sprintf(
"TypeScriptName %q is already used by the endpoint with path %q and method %q",
e.TypeScriptName,
e.Path,
e.Method,
),
)
}
}
eg.endpoints = append(eg.endpoints, ep)
@ -191,7 +243,7 @@ func (p Param) namedType(ep Endpoint, where string) reflect.Type {
if p.Type.Name() == "" {
return typeCustomName{
Type: p.Type,
name: compoundTypeName(ep.MethodName, where, "param", p.Name),
name: compoundTypeName(ep.GoName, where, "param", p.Name),
}
}

View File

@ -0,0 +1,253 @@
// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
package apigen
import (
"math/rand"
"net/http"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestEndpoint_Validate(t *testing.T) {
validEndpoint := Endpoint{
Name: "Test Endpoint",
Description: "This is an Endpoint purely for testing purposes",
GoName: "GenTest",
TypeScriptName: "genTest",
}
tcases := []struct {
testName string
endpointFn func() *Endpoint
errMsg string
}{
{
testName: "valid endpoint",
endpointFn: func() *Endpoint {
return &validEndpoint
},
},
{
testName: "empty name",
endpointFn: func() *Endpoint {
e := validEndpoint
e.Name = ""
return &e
},
errMsg: "Name cannot be empty",
},
{
testName: "empty description",
endpointFn: func() *Endpoint {
e := validEndpoint
e.Description = ""
return &e
},
errMsg: "Description cannot be empty",
},
{
testName: "empty Go name",
endpointFn: func() *Endpoint {
e := validEndpoint
e.GoName = ""
return &e
},
errMsg: "GoName doesn't match the regular expression",
},
{
testName: "no capitalized Go name ",
endpointFn: func() *Endpoint {
e := validEndpoint
e.GoName = "genTest"
return &e
},
errMsg: "GoName doesn't match the regular expression",
},
{
testName: "symbol in Go name",
endpointFn: func() *Endpoint {
e := validEndpoint
e.GoName = "GenTe$t"
return &e
},
errMsg: "GoName doesn't match the regular expression",
},
{
testName: "empty TypeScript name",
endpointFn: func() *Endpoint {
e := validEndpoint
e.TypeScriptName = ""
return &e
},
errMsg: "TypeScriptName doesn't match the regular expression",
},
{
testName: "capitalized TypeScript name ",
endpointFn: func() *Endpoint {
e := validEndpoint
e.TypeScriptName = "GenTest"
return &e
},
errMsg: "TypeScriptName doesn't match the regular expression",
},
{
testName: "dash in TypeScript name",
endpointFn: func() *Endpoint {
e := validEndpoint
e.TypeScriptName = "genTest-2"
return &e
},
errMsg: "TypeScriptName doesn't match the regular expression",
},
}
for _, tc := range tcases {
t.Run(tc.testName, func(t *testing.T) {
ep := tc.endpointFn()
err := ep.Validate()
if tc.errMsg == "" {
require.NoError(t, err)
return
}
require.Error(t, err)
require.ErrorContains(t, err, tc.errMsg)
})
}
}
func TestEndpointGroup(t *testing.T) {
t.Run("add endpoints", func(t *testing.T) {
endpointFn := func(postfix string) *Endpoint {
return &Endpoint{
Name: "Test Endpoint",
Description: "This is an Endpoint purely for testing purposes",
GoName: "GenTest" + postfix,
TypeScriptName: "genTest" + postfix,
}
}
path := "/" + strconv.Itoa(rand.Int())
eg := EndpointGroup{}
assert.NotPanics(t, func() { eg.Get(path, endpointFn(http.MethodGet)) }, "Get")
assert.NotPanics(t, func() { eg.Patch(path, endpointFn(http.MethodPatch)) }, "Patch")
assert.NotPanics(t, func() { eg.Post(path, endpointFn(http.MethodPost)) }, "Post")
assert.NotPanics(t, func() { eg.Delete(path, endpointFn(http.MethodDelete)) }, "Delete")
require.Len(t, eg.endpoints, 4, "Group endpoints count")
for i, m := range []string{http.MethodGet, http.MethodPatch, http.MethodPost, http.MethodDelete} {
ep := eg.endpoints[i]
assert.Equal(t, m, ep.Method)
assert.Equal(t, path, ep.Path)
assert.EqualValues(t, endpointFn(m), &ep.Endpoint)
}
})
t.Run("path does not begin with slash", func(t *testing.T) {
endpointFn := func(postfix string) *Endpoint {
return &Endpoint{
Name: "Test Endpoint",
Description: "This is an Endpoint purely for testing purposes",
GoName: "GenTest" + postfix,
TypeScriptName: "genTest" + postfix,
}
}
path := strconv.Itoa(rand.Int())
eg := EndpointGroup{}
assert.Panics(t, func() { eg.Get(path, endpointFn(http.MethodGet)) }, "Get")
assert.Panics(t, func() { eg.Patch(path, endpointFn(http.MethodPatch)) }, "Patch")
assert.Panics(t, func() { eg.Post(path, endpointFn(http.MethodPost)) }, "Post")
assert.Panics(t, func() { eg.Delete(path, endpointFn(http.MethodDelete)) }, "Delete")
})
t.Run("invalid endpoint", func(t *testing.T) {
endpointFn := func(postfix string) *Endpoint {
return &Endpoint{
Name: "",
Description: "This is an Endpoint purely for testing purposes",
GoName: "GenTest" + postfix,
TypeScriptName: "genTest" + postfix,
}
}
path := "/" + strconv.Itoa(rand.Int())
eg := EndpointGroup{}
assert.Panics(t, func() { eg.Get(path, endpointFn(http.MethodGet)) }, "Get")
assert.Panics(t, func() { eg.Patch(path, endpointFn(http.MethodPatch)) }, "Patch")
assert.Panics(t, func() { eg.Post(path, endpointFn(http.MethodPost)) }, "Post")
assert.Panics(t, func() { eg.Delete(path, endpointFn(http.MethodDelete)) }, "Delete")
})
t.Run("endpoint duplicate path method", func(t *testing.T) {
endpointFn := func(postfix string) *Endpoint {
return &Endpoint{
Name: "Test Endpoint",
Description: "This is an Endpoint purely for testing purposes",
GoName: "GenTest" + postfix,
TypeScriptName: "genTest" + postfix,
}
}
path := "/" + strconv.Itoa(rand.Int())
eg := EndpointGroup{}
assert.NotPanics(t, func() { eg.Get(path, endpointFn(http.MethodGet)) }, "Get")
assert.NotPanics(t, func() { eg.Patch(path, endpointFn(http.MethodPatch)) }, "Patch")
assert.NotPanics(t, func() { eg.Post(path, endpointFn(http.MethodPost)) }, "Post")
assert.NotPanics(t, func() { eg.Delete(path, endpointFn(http.MethodDelete)) }, "Delete")
assert.Panics(t, func() { eg.Get(path, endpointFn(http.MethodGet)) }, "Get")
assert.Panics(t, func() { eg.Patch(path, endpointFn(http.MethodPatch)) }, "Patch")
assert.Panics(t, func() { eg.Post(path, endpointFn(http.MethodPost)) }, "Post")
assert.Panics(t, func() { eg.Delete(path, endpointFn(http.MethodDelete)) }, "Delete")
})
t.Run("endpoint duplicate GoName", func(t *testing.T) {
endpointFn := func(postfix string) *Endpoint {
return &Endpoint{
Name: "Test Endpoint",
Description: "This is an Endpoint purely for testing purposes",
GoName: "GenTest",
TypeScriptName: "genTest" + postfix,
}
}
path := "/" + strconv.Itoa(rand.Int())
eg := EndpointGroup{}
assert.NotPanics(t, func() { eg.Get(path, endpointFn(http.MethodGet)) }, "Get")
assert.Panics(t, func() { eg.Patch(path, endpointFn(http.MethodPatch)) }, "Patch")
assert.Panics(t, func() { eg.Post(path, endpointFn(http.MethodPost)) }, "Post")
assert.Panics(t, func() { eg.Delete(path, endpointFn(http.MethodDelete)) }, "Delete")
})
t.Run("endpoint duplicate TypeScriptName", func(t *testing.T) {
endpointFn := func(postfix string) *Endpoint {
return &Endpoint{
Name: "Test Endpoint",
Description: "This is an Endpoint purely for testing purposes",
GoName: "GenTest" + postfix,
TypeScriptName: "genTest",
}
}
path := "/" + strconv.Itoa(rand.Int())
eg := EndpointGroup{}
assert.NotPanics(t, func() { eg.Patch(path, endpointFn(http.MethodPatch)) }, "Patch")
assert.Panics(t, func() { eg.Get(path, endpointFn(http.MethodGet)) }, "Get")
assert.Panics(t, func() { eg.Post(path, endpointFn(http.MethodPost)) }, "Post")
assert.Panics(t, func() { eg.Delete(path, endpointFn(http.MethodDelete)) }, "Delete")
})
}

View File

@ -12,59 +12,59 @@ export class Document {
version: Version;
}
export class GetResponseItem {
id: UUID;
path: string;
date: Time;
metadata: Metadata;
last_retrievals?: GetResponseItemLastretrievals;
}
export class GetResponseItemLastretrievalsItem {
user: string;
when: Time;
}
export class Metadata {
owner: string;
tags?: string[][];
}
export class UpdateContentRequest {
content: string;
}
export class UpdateContentResponse {
id: UUID;
date: Time;
pathParam: string;
body: string;
}
export class Version {
date: Time;
number: number;
}
export type GetResponse = Array<GetResponseItem>
export class getResponseItem {
id: UUID;
path: string;
date: Time;
metadata: Metadata;
last_retrievals?: getResponseItemLastretrievals;
}
export type GetResponseItemLastretrievals = Array<GetResponseItemLastretrievalsItem>
export class getResponseItemLastretrievalsItem {
user: string;
when: Time;
}
export class updateContentRequest {
content: string;
}
export class updateContentResponse {
id: UUID;
date: Time;
pathParam: string;
body: string;
}
export type getResponse = Array<getResponseItem>
export type getResponseItemLastretrievals = Array<getResponseItemLastretrievalsItem>
export class docsHttpApiV0 {
private readonly http: HttpClient = new HttpClient();
private readonly ROOT_PATH: string = '/api/v0/docs';
public async Get(): Promise<GetResponse> {
public async get(): Promise<getResponse> {
const fullPath = `${this.ROOT_PATH}/`;
const response = await this.http.get(fullPath);
if (response.ok) {
return response.json().then((body) => body as GetResponse);
return response.json().then((body) => body as getResponse);
}
const err = await response.json();
throw new Error(err.error);
}
public async GetOne(path: string): Promise<Document> {
public async getOne(path: string): Promise<Document> {
const fullPath = `${this.ROOT_PATH}/${path}`;
const response = await this.http.get(fullPath);
if (response.ok) {
@ -74,7 +74,7 @@ export class docsHttpApiV0 {
throw new Error(err.error);
}
public async GetTag(path: string, tagName: string): Promise<string[]> {
public async getTag(path: string, tagName: string): Promise<string[]> {
const fullPath = `${this.ROOT_PATH}/${path}/${tagName}`;
const response = await this.http.get(fullPath);
if (response.ok) {
@ -84,7 +84,7 @@ export class docsHttpApiV0 {
throw new Error(err.error);
}
public async GetVersions(path: string): Promise<Version[]> {
public async getVersions(path: string): Promise<Version[]> {
const fullPath = `${this.ROOT_PATH}/${path}`;
const response = await this.http.get(fullPath);
if (response.ok) {
@ -94,14 +94,14 @@ export class docsHttpApiV0 {
throw new Error(err.error);
}
public async UpdateContent(request: UpdateContentRequest, path: string, id: UUID, date: Time): Promise<UpdateContentResponse> {
public async updateContent(request: updateContentRequest, path: string, id: UUID, date: Time): Promise<updateContentResponse> {
const u = new URL(`${this.ROOT_PATH}/${path}`);
u.searchParams.set('id', id);
u.searchParams.set('date', date);
const fullPath = u.toString();
const response = await this.http.post(fullPath, JSON.stringify(request));
if (response.ok) {
return response.json().then((body) => body as UpdateContentResponse);
return response.json().then((body) => body as updateContentResponse);
}
const err = await response.json();
throw new Error(err.error);

View File

@ -21,9 +21,10 @@ func main() {
g := a.Group("Documents", "docs")
g.Get("/", &apigen.Endpoint{
Name: "Get Documents",
Description: "Get the paths to all the documents under the specified paths",
MethodName: "Get",
Name: "Get Documents",
Description: "Get the paths to all the documents under the specified paths",
GoName: "Get",
TypeScriptName: "get",
Response: []struct {
ID uuid.UUID `json:"id"`
Path string `json:"path"`
@ -37,20 +38,22 @@ func main() {
})
g.Get("/{path}", &apigen.Endpoint{
Name: "Get One",
Description: "Get the document in the specified path",
MethodName: "GetOne",
Response: myapi.Document{},
Name: "Get One",
Description: "Get the document in the specified path",
GoName: "GetOne",
TypeScriptName: "getOne",
Response: myapi.Document{},
PathParams: []apigen.Param{
apigen.NewParam("path", ""),
},
})
g.Get("/{path}/tag/{tagName}", &apigen.Endpoint{
Name: "Get a tag",
Description: "Get the tag of the document in the specified path and tag label ",
MethodName: "GetTag",
Response: [2]string{},
Name: "Get a tag",
Description: "Get the tag of the document in the specified path and tag label ",
GoName: "GetTag",
TypeScriptName: "getTag",
Response: [2]string{},
PathParams: []apigen.Param{
apigen.NewParam("path", ""),
apigen.NewParam("tagName", ""),
@ -58,19 +61,21 @@ func main() {
})
g.Get("/{path}/versions", &apigen.Endpoint{
Name: "Get Version",
Description: "Get all the version of the document in the specified path",
MethodName: "GetVersions",
Response: []myapi.Version{},
Name: "Get Version",
Description: "Get all the version of the document in the specified path",
GoName: "GetVersions",
TypeScriptName: "getVersions",
Response: []myapi.Version{},
PathParams: []apigen.Param{
apigen.NewParam("path", ""),
},
})
g.Post("/{path}", &apigen.Endpoint{
Name: "Update Content",
Description: "Update the content of the document with the specified path and ID if the last update is before the indicated date",
MethodName: "UpdateContent",
Name: "Update Content",
Description: "Update the content of the document with the specified path and ID if the last update is before the indicated date",
GoName: "UpdateContent",
TypeScriptName: "updateContent",
Response: struct {
ID uuid.UUID `json:"id"`
Date time.Time `json:"date"`

View File

@ -142,9 +142,9 @@ func (a *API) generateGo() ([]byte, error) {
if !isNillableType(responseType) {
returnParam = "*" + returnParam
}
pf("%s(ctx context.Context, "+paramStr+") (%s, api.HTTPError)", e.MethodName, returnParam)
pf("%s(ctx context.Context, "+paramStr+") (%s, api.HTTPError)", e.GoName, returnParam)
} else {
pf("%s(ctx context.Context, "+paramStr+") (api.HTTPError)", e.MethodName)
pf("%s(ctx context.Context, "+paramStr+") (api.HTTPError)", e.GoName)
}
}
pf("}")
@ -180,7 +180,7 @@ func (a *API) generateGo() ([]byte, error) {
pf("")
pf("%sRouter := router.PathPrefix(\"%s/%s\").Subrouter()", group.Prefix, a.endpointBasePath(), group.Prefix)
for _, endpoint := range group.endpoints {
handlerName := "handle" + endpoint.MethodName
handlerName := "handle" + endpoint.GoName
pf(
"%sRouter.HandleFunc(\"%s\", handler.%s).Methods(\"%s\")",
group.Prefix,
@ -199,7 +199,7 @@ func (a *API) generateGo() ([]byte, error) {
for _, endpoint := range group.endpoints {
i("net/http")
pf("")
handlerName := "handle" + endpoint.MethodName
handlerName := "handle" + endpoint.GoName
pf("func (h *%sHandler) %s(w http.ResponseWriter, r *http.Request) {", group.Name, handlerName)
pf("ctx := r.Context()")
pf("var err error")
@ -244,7 +244,7 @@ func (a *API) generateGo() ([]byte, error) {
}
methodFormat += ")"
pf(methodFormat, endpoint.MethodName)
pf(methodFormat, endpoint.GoName)
pf("if httpErr.Err != nil {")
pf("api.ServeError(h.log, w, httpErr.Status, httpErr.Err)")
if endpoint.Response == nil {
@ -261,7 +261,7 @@ func (a *API) generateGo() ([]byte, error) {
pf("if err != nil {")
pf(
"h.log.Debug(\"failed to write json %s response\", zap.Error(Err%sAPI.Wrap(err)))",
endpoint.MethodName,
endpoint.GoName,
cases.Title(language.Und).String(group.Prefix),
)
pf("}")

View File

@ -102,12 +102,7 @@ func (f *tsGenFile) createAPIClient(group *EndpointGroup) {
}
returnStmt += ";"
methodName := method.RequestName
if methodName == "" {
methodName = method.MethodName
}
f.pf("\tpublic async %s(%s): Promise<%s> {", methodName, funcArgs, returnType)
f.pf("\tpublic async %s(%s): Promise<%s> {", method.TypeScriptName, funcArgs, returnType)
if len(method.QueryParams) > 0 {
f.pf("\t\tconst u = new URL(`%s`);", path)
for _, p := range method.QueryParams {

View File

@ -31,50 +31,50 @@ func main() {
g := a.Group("ProjectManagement", "projects")
g.Post("/create", &apigen.Endpoint{
Name: "Create new Project",
Description: "Creates new Project with given info",
MethodName: "GenCreateProject",
RequestName: "createProject",
Response: console.Project{},
Request: console.UpsertProjectInfo{},
Name: "Create new Project",
Description: "Creates new Project with given info",
GoName: "GenCreateProject",
TypeScriptName: "createProject",
Response: console.Project{},
Request: console.UpsertProjectInfo{},
})
g.Patch("/update/{id}", &apigen.Endpoint{
Name: "Update Project",
Description: "Updates project with given info",
MethodName: "GenUpdateProject",
RequestName: "updateProject",
Response: console.Project{},
Request: console.UpsertProjectInfo{},
Name: "Update Project",
Description: "Updates project with given info",
GoName: "GenUpdateProject",
TypeScriptName: "updateProject",
Response: console.Project{},
Request: console.UpsertProjectInfo{},
PathParams: []apigen.Param{
apigen.NewParam("id", uuid.UUID{}),
},
})
g.Delete("/delete/{id}", &apigen.Endpoint{
Name: "Delete Project",
Description: "Deletes project by id",
MethodName: "GenDeleteProject",
RequestName: "deleteProject",
Name: "Delete Project",
Description: "Deletes project by id",
GoName: "GenDeleteProject",
TypeScriptName: "deleteProject",
PathParams: []apigen.Param{
apigen.NewParam("id", uuid.UUID{}),
},
})
g.Get("/", &apigen.Endpoint{
Name: "Get Projects",
Description: "Gets all projects user has",
MethodName: "GenGetUsersProjects",
RequestName: "getProjects",
Response: []console.Project{},
Name: "Get Projects",
Description: "Gets all projects user has",
GoName: "GenGetUsersProjects",
TypeScriptName: "getProjects",
Response: []console.Project{},
})
g.Get("/bucket-rollup", &apigen.Endpoint{
Name: "Get Project's Single Bucket Usage",
Description: "Gets project's single bucket usage by bucket ID",
MethodName: "GenGetSingleBucketUsageRollup",
RequestName: "getBucketRollup",
Response: accounting.BucketUsageRollup{},
Name: "Get Project's Single Bucket Usage",
Description: "Gets project's single bucket usage by bucket ID",
GoName: "GenGetSingleBucketUsageRollup",
TypeScriptName: "getBucketRollup",
Response: accounting.BucketUsageRollup{},
QueryParams: []apigen.Param{
apigen.NewParam("projectID", uuid.UUID{}),
apigen.NewParam("bucket", ""),
@ -84,11 +84,11 @@ func main() {
})
g.Get("/bucket-rollups", &apigen.Endpoint{
Name: "Get Project's All Buckets Usage",
Description: "Gets project's all buckets usage",
MethodName: "GenGetBucketUsageRollups",
RequestName: "getBucketRollups",
Response: []accounting.BucketUsageRollup{},
Name: "Get Project's All Buckets Usage",
Description: "Gets project's all buckets usage",
GoName: "GenGetBucketUsageRollups",
TypeScriptName: "getBucketRollups",
Response: []accounting.BucketUsageRollup{},
QueryParams: []apigen.Param{
apigen.NewParam("projectID", uuid.UUID{}),
apigen.NewParam("since", time.Time{}),
@ -97,11 +97,11 @@ func main() {
})
g.Get("/apikeys/{projectID}", &apigen.Endpoint{
Name: "Get Project's API Keys",
Description: "Gets API keys by project ID",
MethodName: "GenGetAPIKeys",
RequestName: "getAPIKeys",
Response: console.APIKeyPage{},
Name: "Get Project's API Keys",
Description: "Gets API keys by project ID",
GoName: "GenGetAPIKeys",
TypeScriptName: "getAPIKeys",
Response: console.APIKeyPage{},
PathParams: []apigen.Param{
apigen.NewParam("projectID", uuid.UUID{}),
},
@ -119,19 +119,19 @@ func main() {
g := a.Group("APIKeyManagement", "apikeys")
g.Post("/create", &apigen.Endpoint{
Name: "Create new macaroon API key",
Description: "Creates new macaroon API key with given info",
MethodName: "GenCreateAPIKey",
RequestName: "createAPIKey",
Response: console.CreateAPIKeyResponse{},
Request: console.CreateAPIKeyRequest{},
Name: "Create new macaroon API key",
Description: "Creates new macaroon API key with given info",
GoName: "GenCreateAPIKey",
TypeScriptName: "createAPIKey",
Response: console.CreateAPIKeyResponse{},
Request: console.CreateAPIKeyRequest{},
})
g.Delete("/delete/{id}", &apigen.Endpoint{
Name: "Delete API Key",
Description: "Deletes macaroon API key by id",
MethodName: "GenDeleteAPIKey",
RequestName: "deleteAPIKey",
Name: "Delete API Key",
Description: "Deletes macaroon API key by id",
GoName: "GenDeleteAPIKey",
TypeScriptName: "deleteAPIKey",
PathParams: []apigen.Param{
apigen.NewParam("id", uuid.UUID{}),
},
@ -142,11 +142,11 @@ func main() {
g := a.Group("UserManagement", "users")
g.Get("/", &apigen.Endpoint{
Name: "Get User",
Description: "Gets User by request context",
MethodName: "GenGetUser",
RequestName: "getUser",
Response: console.ResponseUser{},
Name: "Get User",
Description: "Gets User by request context",
GoName: "GenGetUser",
TypeScriptName: "getUser",
Response: console.ResponseUser{},
})
}