private/apigen: Panic types defined in main package

The API generator was generating invalid code when types were defined in
a main package because the generated Go code was defining in import from
it.

This commit update the Go generator to panic with a explicit error
message if that situation happens.

The commit also add a new endpoint to the example with a named types
(i.e. no anonymous) to show that the Generator works fine with them.

Change-Id: Ieddd89c67048de50516f7ac7787d602660dc4a54
This commit is contained in:
Ivan Fraixedes 2023-09-25 14:08:33 +02:00
parent 7ab7ac49c8
commit 48d7be7eab
No known key found for this signature in database
GPG Key ID: FB6101AFB5CB5AD5
7 changed files with 123 additions and 1 deletions

View File

@ -16,6 +16,7 @@ import (
"storj.io/common/uuid"
"storj.io/storj/private/api"
"storj.io/storj/private/apigen/example/myapi"
)
const dateLayout = "2006-01-02T15:04:05.999Z"
@ -23,6 +24,7 @@ const dateLayout = "2006-01-02T15:04:05.999Z"
var ErrDocsAPI = errs.Class("example docs api")
type DocumentsService interface {
GetOne(ctx context.Context, path string) (*myapi.Document, api.HTTPError)
UpdateContent(ctx context.Context, path string, id uuid.UUID, date time.Time, request struct {
Content string "json:\"content\""
}) (*struct {
@ -50,11 +52,44 @@ func NewDocuments(log *zap.Logger, mon *monkit.Scope, service DocumentsService,
}
docsRouter := router.PathPrefix("/api/v0/docs").Subrouter()
docsRouter.HandleFunc("/{path}", handler.handleGetOne).Methods("GET")
docsRouter.HandleFunc("/{path}", handler.handleUpdateContent).Methods("POST")
return handler
}
func (h *DocumentsHandler) handleGetOne(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer h.mon.Task()(&ctx)(&err)
w.Header().Set("Content-Type", "application/json")
path, ok := mux.Vars(r)["path"]
if !ok {
api.ServeError(h.log, w, http.StatusBadRequest, errs.New("missing path route param"))
return
}
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
if err != nil {
h.auth.RemoveAuthCookie(w)
api.ServeError(h.log, w, http.StatusUnauthorized, err)
return
}
retVal, httpErr := h.service.GetOne(ctx, path)
if httpErr.Err != nil {
api.ServeError(h.log, w, httpErr.Status, httpErr.Err)
return
}
err = json.NewEncoder(w).Encode(retVal)
if err != nil {
h.log.Debug("failed to write json GetOne response", zap.Error(ErrDocsAPI.Wrap(err)))
}
}
func (h *DocumentsHandler) handleUpdateContent(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error

View File

@ -7,8 +7,34 @@
<h2 id='list-of-endpoints'>List of Endpoints</h2>
* Documents
* [Get One](#documents-get-one)
* [Update Content](#documents-update-content)
<h3 id='documents-get-one'>Get One (<a href='#list-of-endpoints'>go to full list</a>)</h3>
Get one document with the specified version
`GET /api/v0/docs/{path}`
**Path Params:**
| name | type | elaboration |
|---|---|---|
| `path` | `string` | |
**Response body:**
```typescript
{
id: string // UUID formatted as `00000000-0000-0000-0000-000000000000`
date: string // Date timestamp formatted as `2006-01-02T15:00:00Z`
pathParam: string
body: string
version: number
}
```
<h3 id='documents-update-content'>Update Content (<a href='#list-of-endpoints'>go to full list</a>)</h3>
Update the content of the document with the specified path and ID if the last update is before the indicated date

View File

@ -4,6 +4,14 @@
import { HttpClient } from '@/utils/httpClient';
import { Time, UUID } from '@/types/common';
export class Document {
id: UUID;
date: Time;
pathParam: string;
body: string;
version: number;
}
export class UpdateContentRequest {
content: string;
}
@ -19,6 +27,16 @@ export class docsHttpApiV0 {
private readonly http: HttpClient = new HttpClient();
private readonly ROOT_PATH: string = '/api/v0/docs';
public async GetOne(path: string): Promise<Document> {
const fullPath = `${this.ROOT_PATH}/${path}`;
const response = await this.http.get(fullPath);
if (response.ok) {
return response.json().then((body) => body as Document);
}
const err = await response.json();
throw new Error(err.error);
}
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);

View File

@ -11,6 +11,7 @@ import (
"storj.io/common/uuid"
"storj.io/storj/private/apigen"
"storj.io/storj/private/apigen/example/myapi"
)
func main() {
@ -18,6 +19,16 @@ func main() {
g := a.Group("Documents", "docs")
g.Get("/{path}", &apigen.Endpoint{
Name: "Get One",
Description: "Get one document with the specified version",
MethodName: "GetOne",
Response: myapi.Document{},
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",

View File

@ -0,0 +1,19 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
package myapi
import (
"time"
"storj.io/common/uuid"
)
// Document is a retrieved document.
type Document struct {
ID uuid.UUID `json:"id"`
Date time.Time `json:"date"`
PathParam string `json:"pathParam"`
Body string `json:"body"`
Version uint `json:"version"`
}

View File

@ -42,7 +42,12 @@ func (a *API) generateGo() ([]byte, error) {
getPackageName := func(path string) string {
pathPackages := strings.Split(path, "/")
return pathPackages[len(pathPackages)-1]
name := pathPackages[len(pathPackages)-1]
if name == "main" {
panic(errs.New(`invalid package name. Your types cannot be defined in a package named "main"`))
}
return name
}
imports := struct {

View File

@ -25,6 +25,7 @@ import (
"storj.io/storj/private/api"
"storj.io/storj/private/apigen"
"storj.io/storj/private/apigen/example"
"storj.io/storj/private/apigen/example/myapi"
)
type (
@ -44,6 +45,13 @@ func (a auth) IsAuthenticated(ctx context.Context, r *http.Request, isCookieAuth
func (a auth) RemoveAuthCookie(w http.ResponseWriter) {}
func (s service) GetOne(
ctx context.Context,
pathParam string,
) (*myapi.Document, api.HTTPError) {
return &myapi.Document{}, api.HTTPError{}
}
func (s service) UpdateContent(
ctx context.Context,
pathParam string,