diff --git a/.readme/IMG_1375.jpeg b/.readme/IMG_1375.jpeg new file mode 100644 index 0000000..1c94900 Binary files /dev/null and b/.readme/IMG_1375.jpeg differ diff --git a/IMG_1375.jpeg b/IMG_1375.jpeg deleted file mode 100644 index 9e1b0b9..0000000 Binary files a/IMG_1375.jpeg and /dev/null differ diff --git a/README.md b/README.md index 7b38dfb..36b1ece 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,10 @@ simple object storage -![concept](./IMG_1375.jpeg) +![concept](./.readme/IMG_1375.jpeg) + +## Modes + + - Standalone + - Head + - StorageNode diff --git a/api/server.gen.go b/api/server.gen.go new file mode 100644 index 0000000..07368c0 --- /dev/null +++ b/api/server.gen.go @@ -0,0 +1,1040 @@ +// Package api provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT. +package api + +import ( + "bytes" + "compress/gzip" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "path" + "strings" + "time" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/gorilla/mux" + "github.com/oapi-codegen/runtime" + strictnethttp "github.com/oapi-codegen/runtime/strictmiddleware/nethttp" +) + +// Bucket defines model for Bucket. +type Bucket struct { + // CreatedAt Creation timestamp of the bucket + CreatedAt *time.Time `json:"created_at,omitempty"` + + // Name The name of the bucket + Name *string `json:"name,omitempty"` +} + +// Object defines model for Object. +type Object struct { + // Key The key (identifier) of the object + Key *string `json:"key,omitempty"` + + // LastModified The last modified timestamp of the object + LastModified *time.Time `json:"last_modified,omitempty"` + + // Size The size of the object in bytes + Size *int `json:"size,omitempty"` +} + +// GetBucketsParams defines parameters for GetBuckets. +type GetBucketsParams struct { + // Limit The maximum number of items to return + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` + + // Offset The number of items to skip before starting to collect the result set + Offset *int `form:"offset,omitempty" json:"offset,omitempty"` +} + +// PostBucketsJSONBody defines parameters for PostBuckets. +type PostBucketsJSONBody struct { + // Name Name of the bucket to be created + Name *string `json:"name,omitempty"` +} + +// GetBucketsBucketNameObjectsParams defines parameters for GetBucketsBucketNameObjects. +type GetBucketsBucketNameObjectsParams struct { + // Limit The maximum number of items to return + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` + + // Offset The number of items to skip before starting to collect the result set + Offset *int `form:"offset,omitempty" json:"offset,omitempty"` +} + +// PostBucketsJSONRequestBody defines body for PostBuckets for application/json ContentType. +type PostBucketsJSONRequestBody PostBucketsJSONBody + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // List all buckets + // (GET /buckets) + GetBuckets(w http.ResponseWriter, r *http.Request, params GetBucketsParams) + // Create a new bucket + // (POST /buckets) + PostBuckets(w http.ResponseWriter, r *http.Request) + // Delete a bucket + // (DELETE /buckets/{bucketName}) + DeleteBucketsBucketName(w http.ResponseWriter, r *http.Request, bucketName string) + // List objects in a bucket + // (GET /buckets/{bucketName}/objects) + GetBucketsBucketNameObjects(w http.ResponseWriter, r *http.Request, bucketName string, params GetBucketsBucketNameObjectsParams) + // Delete an object + // (DELETE /buckets/{bucketName}/objects/{objectKey}) + DeleteBucketsBucketNameObjectsObjectKey(w http.ResponseWriter, r *http.Request, bucketName string, objectKey string) + // Get object + // (GET /buckets/{bucketName}/objects/{objectKey}) + GetBucketsBucketNameObjectsObjectKey(w http.ResponseWriter, r *http.Request, bucketName string, objectKey string) + // Upload an object + // (PUT /buckets/{bucketName}/objects/{objectKey}) + PutBucketsBucketNameObjectsObjectKey(w http.ResponseWriter, r *http.Request, bucketName string, objectKey string) +} + +// ServerInterfaceWrapper converts contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface + HandlerMiddlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +type MiddlewareFunc func(http.Handler) http.Handler + +// GetBuckets operation middleware +func (siw *ServerInterfaceWrapper) GetBuckets(w http.ResponseWriter, r *http.Request) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params GetBucketsParams + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", r.URL.Query(), ¶ms.Limit) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "limit", Err: err}) + return + } + + // ------------- Optional query parameter "offset" ------------- + + err = runtime.BindQueryParameter("form", true, false, "offset", r.URL.Query(), ¶ms.Offset) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "offset", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetBuckets(w, r, params) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// PostBuckets operation middleware +func (siw *ServerInterfaceWrapper) PostBuckets(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.PostBuckets(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// DeleteBucketsBucketName operation middleware +func (siw *ServerInterfaceWrapper) DeleteBucketsBucketName(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "bucketName" ------------- + var bucketName string + + err = runtime.BindStyledParameterWithOptions("simple", "bucketName", mux.Vars(r)["bucketName"], &bucketName, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "bucketName", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.DeleteBucketsBucketName(w, r, bucketName) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// GetBucketsBucketNameObjects operation middleware +func (siw *ServerInterfaceWrapper) GetBucketsBucketNameObjects(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "bucketName" ------------- + var bucketName string + + err = runtime.BindStyledParameterWithOptions("simple", "bucketName", mux.Vars(r)["bucketName"], &bucketName, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "bucketName", Err: err}) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params GetBucketsBucketNameObjectsParams + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", r.URL.Query(), ¶ms.Limit) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "limit", Err: err}) + return + } + + // ------------- Optional query parameter "offset" ------------- + + err = runtime.BindQueryParameter("form", true, false, "offset", r.URL.Query(), ¶ms.Offset) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "offset", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetBucketsBucketNameObjects(w, r, bucketName, params) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// DeleteBucketsBucketNameObjectsObjectKey operation middleware +func (siw *ServerInterfaceWrapper) DeleteBucketsBucketNameObjectsObjectKey(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "bucketName" ------------- + var bucketName string + + err = runtime.BindStyledParameterWithOptions("simple", "bucketName", mux.Vars(r)["bucketName"], &bucketName, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "bucketName", Err: err}) + return + } + + // ------------- Path parameter "objectKey" ------------- + var objectKey string + + err = runtime.BindStyledParameterWithOptions("simple", "objectKey", mux.Vars(r)["objectKey"], &objectKey, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "objectKey", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.DeleteBucketsBucketNameObjectsObjectKey(w, r, bucketName, objectKey) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// GetBucketsBucketNameObjectsObjectKey operation middleware +func (siw *ServerInterfaceWrapper) GetBucketsBucketNameObjectsObjectKey(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "bucketName" ------------- + var bucketName string + + err = runtime.BindStyledParameterWithOptions("simple", "bucketName", mux.Vars(r)["bucketName"], &bucketName, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "bucketName", Err: err}) + return + } + + // ------------- Path parameter "objectKey" ------------- + var objectKey string + + err = runtime.BindStyledParameterWithOptions("simple", "objectKey", mux.Vars(r)["objectKey"], &objectKey, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "objectKey", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetBucketsBucketNameObjectsObjectKey(w, r, bucketName, objectKey) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// PutBucketsBucketNameObjectsObjectKey operation middleware +func (siw *ServerInterfaceWrapper) PutBucketsBucketNameObjectsObjectKey(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "bucketName" ------------- + var bucketName string + + err = runtime.BindStyledParameterWithOptions("simple", "bucketName", mux.Vars(r)["bucketName"], &bucketName, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "bucketName", Err: err}) + return + } + + // ------------- Path parameter "objectKey" ------------- + var objectKey string + + err = runtime.BindStyledParameterWithOptions("simple", "objectKey", mux.Vars(r)["objectKey"], &objectKey, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "objectKey", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.PutBucketsBucketNameObjectsObjectKey(w, r, bucketName, objectKey) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +type UnescapedCookieParamError struct { + ParamName string + Err error +} + +func (e *UnescapedCookieParamError) Error() string { + return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName) +} + +func (e *UnescapedCookieParamError) Unwrap() error { + return e.Err +} + +type UnmarshalingParamError struct { + ParamName string + Err error +} + +func (e *UnmarshalingParamError) Error() string { + return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error()) +} + +func (e *UnmarshalingParamError) Unwrap() error { + return e.Err +} + +type RequiredParamError struct { + ParamName string +} + +func (e *RequiredParamError) Error() string { + return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName) +} + +type RequiredHeaderError struct { + ParamName string + Err error +} + +func (e *RequiredHeaderError) Error() string { + return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName) +} + +func (e *RequiredHeaderError) Unwrap() error { + return e.Err +} + +type InvalidParamFormatError struct { + ParamName string + Err error +} + +func (e *InvalidParamFormatError) Error() string { + return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error()) +} + +func (e *InvalidParamFormatError) Unwrap() error { + return e.Err +} + +type TooManyValuesForParamError struct { + ParamName string + Count int +} + +func (e *TooManyValuesForParamError) Error() string { + return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count) +} + +// Handler creates http.Handler with routing matching OpenAPI spec. +func Handler(si ServerInterface) http.Handler { + return HandlerWithOptions(si, GorillaServerOptions{}) +} + +type GorillaServerOptions struct { + BaseURL string + BaseRouter *mux.Router + Middlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux. +func HandlerFromMux(si ServerInterface, r *mux.Router) http.Handler { + return HandlerWithOptions(si, GorillaServerOptions{ + BaseRouter: r, + }) +} + +func HandlerFromMuxWithBaseURL(si ServerInterface, r *mux.Router, baseURL string) http.Handler { + return HandlerWithOptions(si, GorillaServerOptions{ + BaseURL: baseURL, + BaseRouter: r, + }) +} + +// HandlerWithOptions creates http.Handler with additional options +func HandlerWithOptions(si ServerInterface, options GorillaServerOptions) http.Handler { + r := options.BaseRouter + + if r == nil { + r = mux.NewRouter() + } + if options.ErrorHandlerFunc == nil { + options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + } + } + wrapper := ServerInterfaceWrapper{ + Handler: si, + HandlerMiddlewares: options.Middlewares, + ErrorHandlerFunc: options.ErrorHandlerFunc, + } + + r.HandleFunc(options.BaseURL+"/buckets", wrapper.GetBuckets).Methods("GET") + + r.HandleFunc(options.BaseURL+"/buckets", wrapper.PostBuckets).Methods("POST") + + r.HandleFunc(options.BaseURL+"/buckets/{bucketName}", wrapper.DeleteBucketsBucketName).Methods("DELETE") + + r.HandleFunc(options.BaseURL+"/buckets/{bucketName}/objects", wrapper.GetBucketsBucketNameObjects).Methods("GET") + + r.HandleFunc(options.BaseURL+"/buckets/{bucketName}/objects/{objectKey}", wrapper.DeleteBucketsBucketNameObjectsObjectKey).Methods("DELETE") + + r.HandleFunc(options.BaseURL+"/buckets/{bucketName}/objects/{objectKey}", wrapper.GetBucketsBucketNameObjectsObjectKey).Methods("GET") + + r.HandleFunc(options.BaseURL+"/buckets/{bucketName}/objects/{objectKey}", wrapper.PutBucketsBucketNameObjectsObjectKey).Methods("PUT") + + return r +} + +type GetBucketsRequestObject struct { + Params GetBucketsParams +} + +type GetBucketsResponseObject interface { + VisitGetBucketsResponse(w http.ResponseWriter) error +} + +type GetBuckets200JSONResponse struct { + Items *[]Bucket `json:"items,omitempty"` + + // Limit Maximum number of items returned in the response + Limit *int `json:"limit,omitempty"` + + // Offset Number of items skipped before starting the response + Offset *int `json:"offset,omitempty"` + + // Total Total number of buckets available + Total *int `json:"total,omitempty"` +} + +func (response GetBuckets200JSONResponse) VisitGetBucketsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetBuckets500Response struct { +} + +func (response GetBuckets500Response) VisitGetBucketsResponse(w http.ResponseWriter) error { + w.WriteHeader(500) + return nil +} + +type PostBucketsRequestObject struct { + Body *PostBucketsJSONRequestBody +} + +type PostBucketsResponseObject interface { + VisitPostBucketsResponse(w http.ResponseWriter) error +} + +type PostBuckets201Response struct { +} + +func (response PostBuckets201Response) VisitPostBucketsResponse(w http.ResponseWriter) error { + w.WriteHeader(201) + return nil +} + +type PostBuckets400Response struct { +} + +func (response PostBuckets400Response) VisitPostBucketsResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type PostBuckets409Response struct { +} + +func (response PostBuckets409Response) VisitPostBucketsResponse(w http.ResponseWriter) error { + w.WriteHeader(409) + return nil +} + +type DeleteBucketsBucketNameRequestObject struct { + BucketName string `json:"bucketName"` +} + +type DeleteBucketsBucketNameResponseObject interface { + VisitDeleteBucketsBucketNameResponse(w http.ResponseWriter) error +} + +type DeleteBucketsBucketName204Response struct { +} + +func (response DeleteBucketsBucketName204Response) VisitDeleteBucketsBucketNameResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + +type DeleteBucketsBucketName404Response struct { +} + +func (response DeleteBucketsBucketName404Response) VisitDeleteBucketsBucketNameResponse(w http.ResponseWriter) error { + w.WriteHeader(404) + return nil +} + +type DeleteBucketsBucketName500Response struct { +} + +func (response DeleteBucketsBucketName500Response) VisitDeleteBucketsBucketNameResponse(w http.ResponseWriter) error { + w.WriteHeader(500) + return nil +} + +type GetBucketsBucketNameObjectsRequestObject struct { + BucketName string `json:"bucketName"` + Params GetBucketsBucketNameObjectsParams +} + +type GetBucketsBucketNameObjectsResponseObject interface { + VisitGetBucketsBucketNameObjectsResponse(w http.ResponseWriter) error +} + +type GetBucketsBucketNameObjects200JSONResponse struct { + Items *[]Object `json:"items,omitempty"` + + // Limit Maximum number of items returned in the response + Limit *int `json:"limit,omitempty"` + + // Offset Number of items skipped before starting the response + Offset *int `json:"offset,omitempty"` + + // Total Total number of objects available in the bucket + Total *int `json:"total,omitempty"` +} + +func (response GetBucketsBucketNameObjects200JSONResponse) VisitGetBucketsBucketNameObjectsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetBucketsBucketNameObjects404Response struct { +} + +func (response GetBucketsBucketNameObjects404Response) VisitGetBucketsBucketNameObjectsResponse(w http.ResponseWriter) error { + w.WriteHeader(404) + return nil +} + +type DeleteBucketsBucketNameObjectsObjectKeyRequestObject struct { + BucketName string `json:"bucketName"` + ObjectKey string `json:"objectKey"` +} + +type DeleteBucketsBucketNameObjectsObjectKeyResponseObject interface { + VisitDeleteBucketsBucketNameObjectsObjectKeyResponse(w http.ResponseWriter) error +} + +type DeleteBucketsBucketNameObjectsObjectKey204Response struct { +} + +func (response DeleteBucketsBucketNameObjectsObjectKey204Response) VisitDeleteBucketsBucketNameObjectsObjectKeyResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + +type DeleteBucketsBucketNameObjectsObjectKey404Response struct { +} + +func (response DeleteBucketsBucketNameObjectsObjectKey404Response) VisitDeleteBucketsBucketNameObjectsObjectKeyResponse(w http.ResponseWriter) error { + w.WriteHeader(404) + return nil +} + +type DeleteBucketsBucketNameObjectsObjectKey500Response struct { +} + +func (response DeleteBucketsBucketNameObjectsObjectKey500Response) VisitDeleteBucketsBucketNameObjectsObjectKeyResponse(w http.ResponseWriter) error { + w.WriteHeader(500) + return nil +} + +type GetBucketsBucketNameObjectsObjectKeyRequestObject struct { + BucketName string `json:"bucketName"` + ObjectKey string `json:"objectKey"` +} + +type GetBucketsBucketNameObjectsObjectKeyResponseObject interface { + VisitGetBucketsBucketNameObjectsObjectKeyResponse(w http.ResponseWriter) error +} + +type GetBucketsBucketNameObjectsObjectKey200ApplicationoctetStreamResponse struct { + Body io.Reader + ContentLength int64 +} + +func (response GetBucketsBucketNameObjectsObjectKey200ApplicationoctetStreamResponse) VisitGetBucketsBucketNameObjectsObjectKeyResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/octet-stream") + if response.ContentLength != 0 { + w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) + } + w.WriteHeader(200) + + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + _, err := io.Copy(w, response.Body) + return err +} + +type GetBucketsBucketNameObjectsObjectKey404Response struct { +} + +func (response GetBucketsBucketNameObjectsObjectKey404Response) VisitGetBucketsBucketNameObjectsObjectKeyResponse(w http.ResponseWriter) error { + w.WriteHeader(404) + return nil +} + +type PutBucketsBucketNameObjectsObjectKeyRequestObject struct { + BucketName string `json:"bucketName"` + ObjectKey string `json:"objectKey"` + Body io.Reader +} + +type PutBucketsBucketNameObjectsObjectKeyResponseObject interface { + VisitPutBucketsBucketNameObjectsObjectKeyResponse(w http.ResponseWriter) error +} + +type PutBucketsBucketNameObjectsObjectKey201Response struct { +} + +func (response PutBucketsBucketNameObjectsObjectKey201Response) VisitPutBucketsBucketNameObjectsObjectKeyResponse(w http.ResponseWriter) error { + w.WriteHeader(201) + return nil +} + +type PutBucketsBucketNameObjectsObjectKey400Response struct { +} + +func (response PutBucketsBucketNameObjectsObjectKey400Response) VisitPutBucketsBucketNameObjectsObjectKeyResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type PutBucketsBucketNameObjectsObjectKey500Response struct { +} + +func (response PutBucketsBucketNameObjectsObjectKey500Response) VisitPutBucketsBucketNameObjectsObjectKeyResponse(w http.ResponseWriter) error { + w.WriteHeader(500) + return nil +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { + // List all buckets + // (GET /buckets) + GetBuckets(ctx context.Context, request GetBucketsRequestObject) (GetBucketsResponseObject, error) + // Create a new bucket + // (POST /buckets) + PostBuckets(ctx context.Context, request PostBucketsRequestObject) (PostBucketsResponseObject, error) + // Delete a bucket + // (DELETE /buckets/{bucketName}) + DeleteBucketsBucketName(ctx context.Context, request DeleteBucketsBucketNameRequestObject) (DeleteBucketsBucketNameResponseObject, error) + // List objects in a bucket + // (GET /buckets/{bucketName}/objects) + GetBucketsBucketNameObjects(ctx context.Context, request GetBucketsBucketNameObjectsRequestObject) (GetBucketsBucketNameObjectsResponseObject, error) + // Delete an object + // (DELETE /buckets/{bucketName}/objects/{objectKey}) + DeleteBucketsBucketNameObjectsObjectKey(ctx context.Context, request DeleteBucketsBucketNameObjectsObjectKeyRequestObject) (DeleteBucketsBucketNameObjectsObjectKeyResponseObject, error) + // Get object + // (GET /buckets/{bucketName}/objects/{objectKey}) + GetBucketsBucketNameObjectsObjectKey(ctx context.Context, request GetBucketsBucketNameObjectsObjectKeyRequestObject) (GetBucketsBucketNameObjectsObjectKeyResponseObject, error) + // Upload an object + // (PUT /buckets/{bucketName}/objects/{objectKey}) + PutBucketsBucketNameObjectsObjectKey(ctx context.Context, request PutBucketsBucketNameObjectsObjectKeyRequestObject) (PutBucketsBucketNameObjectsObjectKeyResponseObject, error) +} + +type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc +type StrictMiddlewareFunc = strictnethttp.StrictHTTPMiddlewareFunc + +type StrictHTTPServerOptions struct { + RequestErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) + ResponseErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares, options: StrictHTTPServerOptions{ + RequestErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + }, + ResponseErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusInternalServerError) + }, + }} +} + +func NewStrictHandlerWithOptions(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc, options StrictHTTPServerOptions) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares, options: options} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc + options StrictHTTPServerOptions +} + +// GetBuckets operation middleware +func (sh *strictHandler) GetBuckets(w http.ResponseWriter, r *http.Request, params GetBucketsParams) { + var request GetBucketsRequestObject + + request.Params = params + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetBuckets(ctx, request.(GetBucketsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetBuckets") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetBucketsResponseObject); ok { + if err := validResponse.VisitGetBucketsResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PostBuckets operation middleware +func (sh *strictHandler) PostBuckets(w http.ResponseWriter, r *http.Request) { + var request PostBucketsRequestObject + + var body PostBucketsJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.PostBuckets(ctx, request.(PostBucketsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostBuckets") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(PostBucketsResponseObject); ok { + if err := validResponse.VisitPostBucketsResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// DeleteBucketsBucketName operation middleware +func (sh *strictHandler) DeleteBucketsBucketName(w http.ResponseWriter, r *http.Request, bucketName string) { + var request DeleteBucketsBucketNameRequestObject + + request.BucketName = bucketName + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.DeleteBucketsBucketName(ctx, request.(DeleteBucketsBucketNameRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeleteBucketsBucketName") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(DeleteBucketsBucketNameResponseObject); ok { + if err := validResponse.VisitDeleteBucketsBucketNameResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetBucketsBucketNameObjects operation middleware +func (sh *strictHandler) GetBucketsBucketNameObjects(w http.ResponseWriter, r *http.Request, bucketName string, params GetBucketsBucketNameObjectsParams) { + var request GetBucketsBucketNameObjectsRequestObject + + request.BucketName = bucketName + request.Params = params + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetBucketsBucketNameObjects(ctx, request.(GetBucketsBucketNameObjectsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetBucketsBucketNameObjects") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetBucketsBucketNameObjectsResponseObject); ok { + if err := validResponse.VisitGetBucketsBucketNameObjectsResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// DeleteBucketsBucketNameObjectsObjectKey operation middleware +func (sh *strictHandler) DeleteBucketsBucketNameObjectsObjectKey(w http.ResponseWriter, r *http.Request, bucketName string, objectKey string) { + var request DeleteBucketsBucketNameObjectsObjectKeyRequestObject + + request.BucketName = bucketName + request.ObjectKey = objectKey + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.DeleteBucketsBucketNameObjectsObjectKey(ctx, request.(DeleteBucketsBucketNameObjectsObjectKeyRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeleteBucketsBucketNameObjectsObjectKey") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(DeleteBucketsBucketNameObjectsObjectKeyResponseObject); ok { + if err := validResponse.VisitDeleteBucketsBucketNameObjectsObjectKeyResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetBucketsBucketNameObjectsObjectKey operation middleware +func (sh *strictHandler) GetBucketsBucketNameObjectsObjectKey(w http.ResponseWriter, r *http.Request, bucketName string, objectKey string) { + var request GetBucketsBucketNameObjectsObjectKeyRequestObject + + request.BucketName = bucketName + request.ObjectKey = objectKey + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetBucketsBucketNameObjectsObjectKey(ctx, request.(GetBucketsBucketNameObjectsObjectKeyRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetBucketsBucketNameObjectsObjectKey") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetBucketsBucketNameObjectsObjectKeyResponseObject); ok { + if err := validResponse.VisitGetBucketsBucketNameObjectsObjectKeyResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PutBucketsBucketNameObjectsObjectKey operation middleware +func (sh *strictHandler) PutBucketsBucketNameObjectsObjectKey(w http.ResponseWriter, r *http.Request, bucketName string, objectKey string) { + var request PutBucketsBucketNameObjectsObjectKeyRequestObject + + request.BucketName = bucketName + request.ObjectKey = objectKey + + request.Body = r.Body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.PutBucketsBucketNameObjectsObjectKey(ctx, request.(PutBucketsBucketNameObjectsObjectKeyRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PutBucketsBucketNameObjectsObjectKey") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(PutBucketsBucketNameObjectsObjectKeyResponseObject); ok { + if err := validResponse.VisitPutBucketsBucketNameObjectsObjectKeyResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "H4sIAAAAAAAC/+xYT28btxP9KgR/v0MLyJLcpIfuqXELBEbb2GjSUxEE1O6sxJj/Qs7KVg1992JIrrTS", + "rmxFdoGgzY1YkjOP897MkHvPS6udNWAw8OKeh3IBWsThRVPeANLIeevAo4T4vfQgEKoPIs5VEEovHUpr", + "eMF/ojlpDUOpIaDQjtma4QLYLFkb8dp6TVt5JRDOaB0fcVw54AUP6KWZ8/WIG6Ghb/7dAhjN9Izu7V9v", + "vtjZRyiRLF6lUe84N7AadnQDK/aNrMCgrCX4b1un2eQAaCUCftC2ovXVsFFawtol/ShtbB8XpSD/OhAl", + "mtk1yqRhsxVC2BqSBmEOfihe9Ema2kbCrUGRQgdaSEUQGuesxx/hTminYFxazVvS+KvrS/Y2LSCQu+Bo", + "sraeaWHEXJp5RhcInjAt1oDWizmwAH4pSxgTZomKrCce2du84tX1JR/xJfiQ7J+Pp+MpubUOjHCSF/zF", + "eDp+wUfcCVxEyidJN3E8hwEZ/w7oJSyBCeYIJemdKRmQQiqUysKLoDsRzqgJLSksZsJlxQv+GvAiuyQY", + "XmhA8IEXfw5xp8Wd1I1mptEz8ORSIujA0DIP2HjDiRte8E8N+NU27kpqSdpJOZxOVYtGIS/Op0OkD6ZX", + "32m4kY7NoLYeWEDhkWhDy0qrFB2bIuAhNApZiNk4BM7WdZocQDcE7v2IewjOmpDS9LvptNUimEiZcE7J", + "MgZ58jHQAe47xneTPJ5lZ/B/DzUv+P8m2/o3ycVvkivfNiuE92IVMzyGuKeX3w4wluiCqtVJe6KBFBy1", + "EeoZf7NnlOhwUPUZecwBWhRqoFzQ5w70VttiKaQSMwVHF4y9TB/InWx7THC+T4zubnoLfgmegfc2uQmN", + "1sKveMF/JRud5CMbzoZDXYiS18BtXh11jBSvXG/6SXptQydLPXxqIOCFrVZP0N1wG3vTa2EEbwYst1Y+", + "4rmw8oLr1ZmB27PjWx19IvTSUxNC38C6l0vnfVDp6C0EFpqyhBDqRqmo/JdDbF2IiuVApTU/HLQrlAdR", + "rRjcyUDc7VA7QFhc0RbqyX0aUODWyYUCHIjsz/E7E21YhamiZCSGw8SnTZn6i42jY0q1GWQyo8uFkNrO", + "tg7OuvZ3aerWxn2W++Xw5cFQJ/dDFB7eYyyy2jamOiUz96J+mLpJ5uC0xtveFG4lLui2wIKDMl2kko+H", + "Gu+W16uM4SR6n5XU0dfu/wV2/6vNk+G/2P3bJNt0/xb9fgN68l2gc/Hf2h9/RqHq3w66b4mji9HkPg1+", + "gdVRvWXzSKm91RH643XoQIvJpeiq9f+l1qRHn8ID/m3nUM/c5/IT8JQ+Z33L3rN0vFYLZOORhnaKbB5o", + "X18184BmHmoWtkTAs4AehN5tGpt/LjNpROxe+556de1drLrxAgxVS3AlUJykwx2JvQbsiMs1A+L6wykr", + "qnxtzubI8BL8rZdJn/G+Hf+zmE0pf1x4182/SHi19f+k8o55Jz5RdCc853KNbKJEjn3PXZqlULIn5M+t", + "jq0ut9UxzscNSSmNV7zgC0QXislEODnu/EmcLM/5+v367wAAAP//CmV4eJsWAAA=", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %w", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + res := make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.T, err error) { + resolvePath := PathToRawSpec("") + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { + pathToFile := url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadFromData(specData) + if err != nil { + return + } + return +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..96fd1a8 --- /dev/null +++ b/go.mod @@ -0,0 +1,22 @@ +module git.schreifuchs.ch/schreifuchs/warehouse + +go 1.23.2 + +require ( + github.com/getkin/kin-openapi v0.128.0 + github.com/gorilla/mux v1.8.0 + github.com/oapi-codegen/runtime v1.1.1 +) + +require ( + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/google/uuid v1.5.0 // indirect + github.com/invopop/yaml v0.3.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..246c992 --- /dev/null +++ b/go.sum @@ -0,0 +1,52 @@ +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4= +github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= +github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= +github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +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/openapi/openapi.yml b/openapi/openapi.yml new file mode 100644 index 0000000..2d98f96 --- /dev/null +++ b/openapi/openapi.yml @@ -0,0 +1,251 @@ + +openapi: 3.0.3 +info: + title: Object Storage API + description: API for managing objects in an object storage service. + version: 1.0.0 + contact: + name: API Support + email: support@example.com + +servers: + - url: https://api.example.com/v1 + +paths: + /buckets: + get: + summary: List all buckets + description: Retrieve a paginated list of all buckets in the object storage. + parameters: + - name: limit + in: query + schema: + type: integer + default: 10 + description: The maximum number of items to return + - name: offset + in: query + schema: + type: integer + default: 0 + description: The number of items to skip before starting to collect the result set + responses: + '200': + description: A paginated list of buckets. + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: Total number of buckets available + limit: + type: integer + description: Maximum number of items returned in the response + offset: + type: integer + description: Number of items skipped before starting the response + items: + type: array + items: + $ref: '#/components/schemas/Bucket' + '500': + description: Server error + + post: + summary: Create a new bucket + description: Create a new bucket to store objects. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: Name of the bucket to be created + example: my-new-bucket + responses: + '201': + description: Bucket created successfully + '400': + description: Bad request + '409': + description: Bucket already exists + + /buckets/{bucketName}: + delete: + summary: Delete a bucket + description: Delete a bucket and all its objects. + parameters: + - name: bucketName + in: path + required: true + schema: + type: string + description: The name of the bucket to delete + responses: + '204': + description: Bucket deleted successfully + '404': + description: Bucket not found + '500': + description: Server error + + /buckets/{bucketName}/objects: + get: + summary: List objects in a bucket + description: Retrieve a paginated list of objects within a specified bucket. + parameters: + - name: bucketName + in: path + required: true + schema: + type: string + description: The name of the bucket + - name: limit + in: query + schema: + type: integer + default: 10 + description: The maximum number of items to return + - name: offset + in: query + schema: + type: integer + default: 0 + description: The number of items to skip before starting to collect the result set + responses: + '200': + description: A paginated list of objects in the bucket. + content: + application/json: + schema: + type: object + properties: + total: + type: integer + description: Total number of objects available in the bucket + limit: + type: integer + description: Maximum number of items returned in the response + offset: + type: integer + description: Number of items skipped before starting the response + items: + type: array + items: + $ref: '#/components/schemas/Object' + '404': + description: Bucket not found + + /buckets/{bucketName}/objects/{objectKey}: + get: + summary: Get object + description: Retrieve an object from the specified bucket. + parameters: + - name: bucketName + in: path + required: true + schema: + type: string + description: The name of the bucket + - name: objectKey + in: path + required: true + schema: + type: string + description: The key (identifier) of the object + responses: + '200': + description: The requested object data + content: + application/octet-stream: + schema: + type: string + format: binary + '404': + description: Bucket or object not found + + put: + summary: Upload an object + description: Upload a new object or overwrite an existing one in the specified bucket. + parameters: + - name: bucketName + in: path + required: true + schema: + type: string + description: The name of the bucket + - name: objectKey + in: path + required: true + schema: + type: string + description: The key (identifier) for the object + requestBody: + required: true + content: + application/octet-stream: + schema: + type: string + format: binary + responses: + '201': + description: Object uploaded successfully + '400': + description: Invalid object data + '500': + description: Server error + + delete: + summary: Delete an object + description: Delete an object from the specified bucket. + parameters: + - name: bucketName + in: path + required: true + schema: + type: string + description: The name of the bucket + - name: objectKey + in: path + required: true + schema: + type: string + description: The key (identifier) of the object + responses: + '204': + description: Object deleted successfully + '404': + description: Bucket or object not found + '500': + description: Server error + +components: + schemas: + Bucket: + type: object + properties: + name: + type: string + description: The name of the bucket + created_at: + type: string + format: date-time + description: Creation timestamp of the bucket + Object: + type: object + properties: + key: + type: string + description: The key (identifier) of the object + size: + type: integer + description: The size of the object in bytes + last_modified: + type: string + format: date-time + description: The last modified timestamp of the object diff --git a/openapi/server.cfg.yml b/openapi/server.cfg.yml new file mode 100644 index 0000000..0e289ca --- /dev/null +++ b/openapi/server.cfg.yml @@ -0,0 +1,7 @@ +package: api +output: api/server.gen.go +generate: + embedded-spec: true + strict-server: true + models: true + gorilla-server: true