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:
parent
7ab7ac49c8
commit
48d7be7eab
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
19
private/apigen/example/myapi/types.go
Normal file
19
private/apigen/example/myapi/types.go
Normal 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"`
|
||||
}
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user