diff --git a/cmd/internal/client/client.go b/cmd/internal/client/client.go index ec9a1c21..2d94270d 100644 --- a/cmd/internal/client/client.go +++ b/cmd/internal/client/client.go @@ -348,9 +348,10 @@ func (c *Client) assignedTasks(ctx context.Context, statuses []openapi.GetApiDow } func (c *Client) RequestDeviceCode(ctx context.Context) (DeviceCode, error) { - res, err := c.api.PostApiAuthDeviceCodeWithResponse(ctx, openapi.DeviceCodeRequest{ + scope := "downloader:register" + res, err := c.api.PostApiAuthDeviceCodeWithResponse(ctx, openapi.PostApiAuthDeviceCodeJSONRequestBody{ ClientId: "zpan-cli", - Scope: "downloader:register", + Scope: &scope, }) if err != nil { return DeviceCode{}, err @@ -362,17 +363,17 @@ func (c *Client) RequestDeviceCode(ctx context.Context) (DeviceCode, error) { return DeviceCode{}, fmt.Errorf("POST /api/auth/device/code failed: empty response") } return DeviceCode{ - DeviceCode: res.JSON200.DeviceCode, - UserCode: res.JSON200.UserCode, - VerificationURI: res.JSON200.VerificationUri, - VerificationURIComplete: res.JSON200.VerificationUriComplete, - ExpiresIn: res.JSON200.ExpiresIn, - Interval: res.JSON200.Interval, + DeviceCode: derefString(res.JSON200.DeviceCode), + UserCode: derefString(res.JSON200.UserCode), + VerificationURI: derefString(res.JSON200.VerificationUri), + VerificationURIComplete: derefString(res.JSON200.VerificationUriComplete), + ExpiresIn: derefFloatToInt(res.JSON200.ExpiresIn), + Interval: derefFloatToInt(res.JSON200.Interval), }, nil } func (c *Client) PollDeviceToken(ctx context.Context, deviceCode string) (DeviceToken, error) { - res, err := c.api.PostApiAuthDeviceTokenWithResponse(ctx, openapi.DeviceTokenRequest{ + res, err := c.api.PostApiAuthDeviceTokenWithResponse(ctx, openapi.PostApiAuthDeviceTokenJSONRequestBody{ GrantType: "urn:ietf:params:oauth:grant-type:device_code", DeviceCode: deviceCode, ClientId: "zpan-cli", @@ -390,7 +391,7 @@ func (c *Client) PollDeviceToken(ctx context.Context, deviceCode string) (Device AccessToken: res.JSON200.AccessToken, TokenType: res.JSON200.TokenType, ExpiresIn: res.JSON200.ExpiresIn, - Scope: res.JSON200.Scope, + Scope: derefString(res.JSON200.Scope), }, nil } @@ -536,14 +537,6 @@ func (c *Client) createMatter( if err := expectStatus("POST", "/api/objects", res.StatusCode(), res.Body, http.StatusOK, http.StatusCreated); err != nil { return ObjectDraft{}, err } - if res.JSON200 != nil { - return ObjectDraft{ - ID: res.JSON200.Id, - Name: res.JSON200.Name, - UploadURL: derefString(res.JSON200.UploadUrl), - ContentDisposition: derefString(res.JSON200.ContentDisposition), - }, nil - } if res.JSON201 != nil { return ObjectDraft{ ID: res.JSON201.Id, @@ -646,6 +639,16 @@ func derefString(value *string) string { return *value } +// better-auth's device/code schema types expires_in/interval as `number` and +// leaves them optional, so the generated client surfaces them as *float32. +// They are always whole-second integers at runtime. +func derefFloatToInt(value *float32) int { + if value == nil { + return 0 + } + return int(*value) +} + func bearer(token string) openapi.RequestEditorFn { return func(ctx context.Context, req *http.Request) error { if token != "" { diff --git a/cmd/internal/client/client_test.go b/cmd/internal/client/client_test.go index 4ad2aa51..ad1ba17e 100644 --- a/cmd/internal/client/client_test.go +++ b/cmd/internal/client/client_test.go @@ -42,6 +42,8 @@ func TestCreateObjectUsesRenameConflictStrategy(t *testing.T) { t.Fatal(err) } w.Header().Set("Content-Type", "application/json") + // POST /api/objects always returns 201 Created. + w.WriteHeader(http.StatusCreated) _ = json.NewEncoder(w).Encode(ObjectDraft{ID: "object-1", Name: "movie (1).mkv"}) })) defer server.Close() diff --git a/cmd/internal/openapi/client.gen.go b/cmd/internal/openapi/client.gen.go index a7343c79..79e449f6 100644 --- a/cmd/internal/openapi/client.gen.go +++ b/cmd/internal/openapi/client.gen.go @@ -16,6 +16,54 @@ import ( "github.com/oapi-codegen/runtime" ) +// Defines values for PostApiAuthDeviceCode400JSONResponseBodyError. +const ( + PostApiAuthDeviceCode400JSONResponseBodyErrorInvalidClient PostApiAuthDeviceCode400JSONResponseBodyError = "invalid_client" + PostApiAuthDeviceCode400JSONResponseBodyErrorInvalidRequest PostApiAuthDeviceCode400JSONResponseBodyError = "invalid_request" +) + +// Valid indicates whether the value is a known member of the PostApiAuthDeviceCode400JSONResponseBodyError enum. +func (e PostApiAuthDeviceCode400JSONResponseBodyError) Valid() bool { + switch e { + case PostApiAuthDeviceCode400JSONResponseBodyErrorInvalidClient: + return true + case PostApiAuthDeviceCode400JSONResponseBodyErrorInvalidRequest: + return true + default: + return false + } +} + +// Defines values for PostApiAuthDeviceToken400JSONResponseBodyError. +const ( + PostApiAuthDeviceToken400JSONResponseBodyErrorAccessDenied PostApiAuthDeviceToken400JSONResponseBodyError = "access_denied" + PostApiAuthDeviceToken400JSONResponseBodyErrorAuthorizationPending PostApiAuthDeviceToken400JSONResponseBodyError = "authorization_pending" + PostApiAuthDeviceToken400JSONResponseBodyErrorExpiredToken PostApiAuthDeviceToken400JSONResponseBodyError = "expired_token" + PostApiAuthDeviceToken400JSONResponseBodyErrorInvalidGrant PostApiAuthDeviceToken400JSONResponseBodyError = "invalid_grant" + PostApiAuthDeviceToken400JSONResponseBodyErrorInvalidRequest PostApiAuthDeviceToken400JSONResponseBodyError = "invalid_request" + PostApiAuthDeviceToken400JSONResponseBodyErrorSlowDown PostApiAuthDeviceToken400JSONResponseBodyError = "slow_down" +) + +// Valid indicates whether the value is a known member of the PostApiAuthDeviceToken400JSONResponseBodyError enum. +func (e PostApiAuthDeviceToken400JSONResponseBodyError) Valid() bool { + switch e { + case PostApiAuthDeviceToken400JSONResponseBodyErrorAccessDenied: + return true + case PostApiAuthDeviceToken400JSONResponseBodyErrorAuthorizationPending: + return true + case PostApiAuthDeviceToken400JSONResponseBodyErrorExpiredToken: + return true + case PostApiAuthDeviceToken400JSONResponseBodyErrorInvalidGrant: + return true + case PostApiAuthDeviceToken400JSONResponseBodyErrorInvalidRequest: + return true + case PostApiAuthDeviceToken400JSONResponseBodyErrorSlowDown: + return true + default: + return false + } +} + // Defines values for GetApiDownloadsDownloaders200JSONResponseBodyItemsHeartbeatEngine. const ( GetApiDownloadsDownloaders200JSONResponseBodyItemsHeartbeatEngineAria2 GetApiDownloadsDownloaders200JSONResponseBodyItemsHeartbeatEngine = "aria2" @@ -645,13 +693,13 @@ func (e PostApiDownloadsTasks201JSONResponseBodyStatusState) Valid() bool { // Defines values for DeleteApiDownloadsTasksId200JSONResponseBodyDeleted. const ( - True DeleteApiDownloadsTasksId200JSONResponseBodyDeleted = true + DeleteApiDownloadsTasksId200JSONResponseBodyDeletedTrue DeleteApiDownloadsTasksId200JSONResponseBodyDeleted = true ) // Valid indicates whether the value is a known member of the DeleteApiDownloadsTasksId200JSONResponseBodyDeleted enum. func (e DeleteApiDownloadsTasksId200JSONResponseBodyDeleted) Valid() bool { switch e { - case True: + case DeleteApiDownloadsTasksId200JSONResponseBodyDeletedTrue: return true default: return false @@ -1363,6 +1411,63 @@ func (e PostApiObjectsJSONBodyOnConflict) Valid() bool { } } +// Defines values for DeleteApiObjectsId200JSONResponseBodyDeleted. +const ( + DeleteApiObjectsId200JSONResponseBodyDeletedTrue DeleteApiObjectsId200JSONResponseBodyDeleted = true +) + +// Valid indicates whether the value is a known member of the DeleteApiObjectsId200JSONResponseBodyDeleted enum. +func (e DeleteApiObjectsId200JSONResponseBodyDeleted) Valid() bool { + switch e { + case DeleteApiObjectsId200JSONResponseBodyDeletedTrue: + return true + default: + return false + } +} + +// Defines values for PatchApiObjectsIdJSONBodyOnConflict. +const ( + PatchApiObjectsIdJSONBodyOnConflictFail PatchApiObjectsIdJSONBodyOnConflict = "fail" + PatchApiObjectsIdJSONBodyOnConflictRename PatchApiObjectsIdJSONBodyOnConflict = "rename" + PatchApiObjectsIdJSONBodyOnConflictReplace PatchApiObjectsIdJSONBodyOnConflict = "replace" +) + +// Valid indicates whether the value is a known member of the PatchApiObjectsIdJSONBodyOnConflict enum. +func (e PatchApiObjectsIdJSONBodyOnConflict) Valid() bool { + switch e { + case PatchApiObjectsIdJSONBodyOnConflictFail: + return true + case PatchApiObjectsIdJSONBodyOnConflictRename: + return true + case PatchApiObjectsIdJSONBodyOnConflictReplace: + return true + default: + return false + } +} + +// Defines values for PostApiObjectsIdCopiesJSONBodyOnConflict. +const ( + PostApiObjectsIdCopiesJSONBodyOnConflictFail PostApiObjectsIdCopiesJSONBodyOnConflict = "fail" + PostApiObjectsIdCopiesJSONBodyOnConflictRename PostApiObjectsIdCopiesJSONBodyOnConflict = "rename" + PostApiObjectsIdCopiesJSONBodyOnConflictReplace PostApiObjectsIdCopiesJSONBodyOnConflict = "replace" +) + +// Valid indicates whether the value is a known member of the PostApiObjectsIdCopiesJSONBodyOnConflict enum. +func (e PostApiObjectsIdCopiesJSONBodyOnConflict) Valid() bool { + switch e { + case PostApiObjectsIdCopiesJSONBodyOnConflictFail: + return true + case PostApiObjectsIdCopiesJSONBodyOnConflictRename: + return true + case PostApiObjectsIdCopiesJSONBodyOnConflictReplace: + return true + default: + return false + } +} + // Defines values for PutApiObjectsIdStatusJSONBodyOnConflict. const ( PutApiObjectsIdStatusJSONBodyOnConflictFail PutApiObjectsIdStatusJSONBodyOnConflict = "fail" @@ -1402,6 +1507,24 @@ func (e PutApiObjectsIdStatusJSONBodyStatus) Valid() bool { } } +// Defines values for PostApiObjectsIdTransfersJSONBodyMode. +const ( + Copy PostApiObjectsIdTransfersJSONBodyMode = "copy" + Move PostApiObjectsIdTransfersJSONBodyMode = "move" +) + +// Valid indicates whether the value is a known member of the PostApiObjectsIdTransfersJSONBodyMode enum. +func (e PostApiObjectsIdTransfersJSONBodyMode) Valid() bool { + switch e { + case Copy: + return true + case Move: + return true + default: + return false + } +} + // Defines values for PostApiObjectsIdUploads201JSONResponseBodyStatus. const ( PostApiObjectsIdUploads201JSONResponseBodyStatusAborted PostApiObjectsIdUploads201JSONResponseBodyStatus = "aborted" @@ -1480,41 +1603,75 @@ func (e PutApiObjectsIdUploadsUploadSessionIdStatus200JSONResponseBodyStatus) Va } } -// DeviceCode defines model for DeviceCode. -type DeviceCode struct { - DeviceCode string `json:"device_code"` - ExpiresIn int `json:"expires_in"` - Interval int `json:"interval"` - UserCode string `json:"user_code"` - VerificationUri string `json:"verification_uri"` - VerificationUriComplete string `json:"verification_uri_complete"` +// Matter defines model for Matter. +type Matter struct { + Alias string `json:"alias"` + CreatedAt string `json:"createdAt"` + Dirtype int `json:"dirtype"` + Id string `json:"id"` + Name string `json:"name"` + Object string `json:"object"` + OrgId string `json:"orgId"` + Parent string `json:"parent"` + Size int `json:"size"` + Status string `json:"status"` + StorageId string `json:"storageId"` + Type string `json:"type"` + UpdatedAt string `json:"updatedAt"` } -// DeviceCodeRequest defines model for DeviceCodeRequest. -type DeviceCodeRequest struct { +// ObjectPage defines model for ObjectPage. +type ObjectPage struct { + Items []Matter `json:"items"` + Page int `json:"page"` + PageSize int `json:"pageSize"` + Total int `json:"total"` +} + +// TransferResult defines model for TransferResult. +type TransferResult struct { + Id string `json:"id"` + SourceDeleted bool `json:"sourceDeleted"` +} + +// PostApiAuthDeviceApproveJSONBody defines parameters for PostApiAuthDeviceApprove. +type PostApiAuthDeviceApproveJSONBody struct { + // UserCode The user code to approve + UserCode string `json:"userCode"` +} + +// PostApiAuthDeviceCodeJSONBody defines parameters for PostApiAuthDeviceCode. +type PostApiAuthDeviceCodeJSONBody struct { + // ClientId The client ID of the application ClientId string `json:"client_id"` - Scope string `json:"scope"` + + // Scope Space-separated list of scopes + Scope *string `json:"scope,omitempty"` } -// DeviceToken defines model for DeviceToken. -type DeviceToken struct { - AccessToken string `json:"access_token"` - ExpiresIn int `json:"expires_in"` - Scope string `json:"scope"` - TokenType string `json:"token_type"` +// PostApiAuthDeviceCode400JSONResponseBodyError defines parameters for PostApiAuthDeviceCode. +type PostApiAuthDeviceCode400JSONResponseBodyError string + +// PostApiAuthDeviceDenyJSONBody defines parameters for PostApiAuthDeviceDeny. +type PostApiAuthDeviceDenyJSONBody struct { + // UserCode The user code to deny + UserCode string `json:"userCode"` } -// DeviceTokenRequest defines model for DeviceTokenRequest. -type DeviceTokenRequest struct { - ClientId string `json:"client_id"` +// PostApiAuthDeviceTokenJSONBody defines parameters for PostApiAuthDeviceToken. +type PostApiAuthDeviceTokenJSONBody struct { + // ClientId The client ID of the application + ClientId string `json:"client_id"` + + // DeviceCode The device verification code DeviceCode string `json:"device_code"` - GrantType string `json:"grant_type"` + + // GrantType The grant type for device flow + GrantType string `json:"grant_type"` } -// ErrorResponse defines model for ErrorResponse. -type ErrorResponse struct { - Error string `json:"error"` -} +// PostApiAuthDeviceToken400JSONResponseBodyError defines parameters for PostApiAuthDeviceToken. +type PostApiAuthDeviceToken400JSONResponseBodyError string // GetApiDownloadsDownloaders200JSONResponseBodyItemsHeartbeatEngine defines parameters for GetApiDownloadsDownloaders. type GetApiDownloadsDownloaders200JSONResponseBodyItemsHeartbeatEngine string @@ -1821,6 +1978,18 @@ type PutApiDownloadsTasksIdStatus200JSONResponseBodyStatusRuntimePhase string // PutApiDownloadsTasksIdStatus200JSONResponseBodyStatusState defines parameters for PutApiDownloadsTasksIdStatus. type PutApiDownloadsTasksIdStatus200JSONResponseBodyStatusState string +// GetApiObjectsParams defines parameters for GetApiObjects. +type GetApiObjectsParams struct { + Parent *string `form:"parent,omitempty" json:"parent,omitempty"` + Path *string `form:"path,omitempty" json:"path,omitempty"` + Status *string `form:"status,omitempty" json:"status,omitempty"` + Type *string `form:"type,omitempty" json:"type,omitempty"` + Search *string `form:"search,omitempty" json:"search,omitempty"` + Page *string `form:"page,omitempty" json:"page,omitempty"` + PageSize *string `form:"pageSize,omitempty" json:"pageSize,omitempty"` + OrgId *string `form:"orgId,omitempty" json:"orgId,omitempty"` +} + // PostApiObjectsJSONBody defines parameters for PostApiObjects. type PostApiObjectsJSONBody struct { Dirtype *int `json:"dirtype,omitempty"` @@ -1834,6 +2003,28 @@ type PostApiObjectsJSONBody struct { // PostApiObjectsJSONBodyOnConflict defines parameters for PostApiObjects. type PostApiObjectsJSONBodyOnConflict string +// DeleteApiObjectsId200JSONResponseBodyDeleted defines parameters for DeleteApiObjectsId. +type DeleteApiObjectsId200JSONResponseBodyDeleted bool + +// PatchApiObjectsIdJSONBody defines parameters for PatchApiObjectsId. +type PatchApiObjectsIdJSONBody struct { + Name *string `json:"name,omitempty"` + OnConflict *PatchApiObjectsIdJSONBodyOnConflict `json:"onConflict,omitempty"` + Parent *string `json:"parent,omitempty"` +} + +// PatchApiObjectsIdJSONBodyOnConflict defines parameters for PatchApiObjectsId. +type PatchApiObjectsIdJSONBodyOnConflict string + +// PostApiObjectsIdCopiesJSONBody defines parameters for PostApiObjectsIdCopies. +type PostApiObjectsIdCopiesJSONBody struct { + OnConflict *PostApiObjectsIdCopiesJSONBodyOnConflict `json:"onConflict,omitempty"` + Parent *string `json:"parent,omitempty"` +} + +// PostApiObjectsIdCopiesJSONBodyOnConflict defines parameters for PostApiObjectsIdCopies. +type PostApiObjectsIdCopiesJSONBodyOnConflict string + // PutApiObjectsIdStatusJSONBody defines parameters for PutApiObjectsIdStatus. type PutApiObjectsIdStatusJSONBody struct { OnConflict *PutApiObjectsIdStatusJSONBodyOnConflict `json:"onConflict,omitempty"` @@ -1846,6 +2037,16 @@ type PutApiObjectsIdStatusJSONBodyOnConflict string // PutApiObjectsIdStatusJSONBodyStatus defines parameters for PutApiObjectsIdStatus. type PutApiObjectsIdStatusJSONBodyStatus string +// PostApiObjectsIdTransfersJSONBody defines parameters for PostApiObjectsIdTransfers. +type PostApiObjectsIdTransfersJSONBody struct { + Mode PostApiObjectsIdTransfersJSONBodyMode `json:"mode"` + TargetOrgId string `json:"targetOrgId"` + TargetParent *string `json:"targetParent,omitempty"` +} + +// PostApiObjectsIdTransfersJSONBodyMode defines parameters for PostApiObjectsIdTransfers. +type PostApiObjectsIdTransfersJSONBodyMode string + // PostApiObjectsIdUploadsJSONBody defines parameters for PostApiObjectsIdUploads. type PostApiObjectsIdUploadsJSONBody struct { PartSize *int `json:"partSize,omitempty"` @@ -1877,11 +2078,17 @@ type PutApiObjectsIdUploadsUploadSessionIdStatusJSONBodyStatus string // PutApiObjectsIdUploadsUploadSessionIdStatus200JSONResponseBodyStatus defines parameters for PutApiObjectsIdUploadsUploadSessionIdStatus. type PutApiObjectsIdUploadsUploadSessionIdStatus200JSONResponseBodyStatus string +// PostApiAuthDeviceApproveJSONRequestBody defines body for PostApiAuthDeviceApprove for application/json ContentType. +type PostApiAuthDeviceApproveJSONRequestBody PostApiAuthDeviceApproveJSONBody + // PostApiAuthDeviceCodeJSONRequestBody defines body for PostApiAuthDeviceCode for application/json ContentType. -type PostApiAuthDeviceCodeJSONRequestBody = DeviceCodeRequest +type PostApiAuthDeviceCodeJSONRequestBody PostApiAuthDeviceCodeJSONBody + +// PostApiAuthDeviceDenyJSONRequestBody defines body for PostApiAuthDeviceDeny for application/json ContentType. +type PostApiAuthDeviceDenyJSONRequestBody PostApiAuthDeviceDenyJSONBody // PostApiAuthDeviceTokenJSONRequestBody defines body for PostApiAuthDeviceToken for application/json ContentType. -type PostApiAuthDeviceTokenJSONRequestBody = DeviceTokenRequest +type PostApiAuthDeviceTokenJSONRequestBody PostApiAuthDeviceTokenJSONBody // PostApiDownloadsDownloadersJSONRequestBody defines body for PostApiDownloadsDownloaders for application/json ContentType. type PostApiDownloadsDownloadersJSONRequestBody PostApiDownloadsDownloadersJSONBody @@ -1907,9 +2114,18 @@ type PutApiDownloadsTasksIdStatusJSONRequestBody PutApiDownloadsTasksIdStatusJSO // PostApiObjectsJSONRequestBody defines body for PostApiObjects for application/json ContentType. type PostApiObjectsJSONRequestBody PostApiObjectsJSONBody +// PatchApiObjectsIdJSONRequestBody defines body for PatchApiObjectsId for application/json ContentType. +type PatchApiObjectsIdJSONRequestBody PatchApiObjectsIdJSONBody + +// PostApiObjectsIdCopiesJSONRequestBody defines body for PostApiObjectsIdCopies for application/json ContentType. +type PostApiObjectsIdCopiesJSONRequestBody PostApiObjectsIdCopiesJSONBody + // PutApiObjectsIdStatusJSONRequestBody defines body for PutApiObjectsIdStatus for application/json ContentType. type PutApiObjectsIdStatusJSONRequestBody PutApiObjectsIdStatusJSONBody +// PostApiObjectsIdTransfersJSONRequestBody defines body for PostApiObjectsIdTransfers for application/json ContentType. +type PostApiObjectsIdTransfersJSONRequestBody PostApiObjectsIdTransfersJSONBody + // PostApiObjectsIdUploadsJSONRequestBody defines body for PostApiObjectsIdUploads for application/json ContentType. type PostApiObjectsIdUploadsJSONRequestBody PostApiObjectsIdUploadsJSONBody @@ -1992,11 +2208,21 @@ func WithRequestEditorFn(fn RequestEditorFn) ClientOption { // The interface specification for the client above. type ClientInterface interface { + // PostApiAuthDeviceApproveWithBody request with any body + PostApiAuthDeviceApproveWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostApiAuthDeviceApprove(ctx context.Context, body PostApiAuthDeviceApproveJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostApiAuthDeviceCodeWithBody request with any body PostApiAuthDeviceCodeWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) PostApiAuthDeviceCode(ctx context.Context, body PostApiAuthDeviceCodeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostApiAuthDeviceDenyWithBody request with any body + PostApiAuthDeviceDenyWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostApiAuthDeviceDeny(ctx context.Context, body PostApiAuthDeviceDenyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostApiAuthDeviceTokenWithBody request with any body PostApiAuthDeviceTokenWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -2052,16 +2278,40 @@ type ClientInterface interface { PutApiDownloadsTasksIdStatus(ctx context.Context, id string, body PutApiDownloadsTasksIdStatusJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetApiObjects request + GetApiObjects(ctx context.Context, params *GetApiObjectsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostApiObjectsWithBody request with any body PostApiObjectsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) PostApiObjects(ctx context.Context, body PostApiObjectsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // DeleteApiObjectsId request + DeleteApiObjectsId(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetApiObjectsId request + GetApiObjectsId(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PatchApiObjectsIdWithBody request with any body + PatchApiObjectsIdWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PatchApiObjectsId(ctx context.Context, id string, body PatchApiObjectsIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostApiObjectsIdCopiesWithBody request with any body + PostApiObjectsIdCopiesWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostApiObjectsIdCopies(ctx context.Context, id string, body PostApiObjectsIdCopiesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // PutApiObjectsIdStatusWithBody request with any body PutApiObjectsIdStatusWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) PutApiObjectsIdStatus(ctx context.Context, id string, body PutApiObjectsIdStatusJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostApiObjectsIdTransfersWithBody request with any body + PostApiObjectsIdTransfersWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostApiObjectsIdTransfers(ctx context.Context, id string, body PostApiObjectsIdTransfersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostApiObjectsIdUploadsWithBody request with any body PostApiObjectsIdUploadsWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -2081,6 +2331,30 @@ type ClientInterface interface { PutApiObjectsIdUploadsUploadSessionIdStatus(ctx context.Context, id string, uploadSessionId string, body PutApiObjectsIdUploadsUploadSessionIdStatusJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) } +func (c *Client) PostApiAuthDeviceApproveWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostApiAuthDeviceApproveRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostApiAuthDeviceApprove(ctx context.Context, body PostApiAuthDeviceApproveJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostApiAuthDeviceApproveRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) PostApiAuthDeviceCodeWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewPostApiAuthDeviceCodeRequestWithBody(c.Server, contentType, body) if err != nil { @@ -2105,6 +2379,30 @@ func (c *Client) PostApiAuthDeviceCode(ctx context.Context, body PostApiAuthDevi return c.Client.Do(req) } +func (c *Client) PostApiAuthDeviceDenyWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostApiAuthDeviceDenyRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostApiAuthDeviceDeny(ctx context.Context, body PostApiAuthDeviceDenyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostApiAuthDeviceDenyRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) PostApiAuthDeviceTokenWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewPostApiAuthDeviceTokenRequestWithBody(c.Server, contentType, body) if err != nil { @@ -2357,6 +2655,18 @@ func (c *Client) PutApiDownloadsTasksIdStatus(ctx context.Context, id string, bo return c.Client.Do(req) } +func (c *Client) GetApiObjects(ctx context.Context, params *GetApiObjectsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetApiObjectsRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) PostApiObjectsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewPostApiObjectsRequestWithBody(c.Server, contentType, body) if err != nil { @@ -2381,6 +2691,78 @@ func (c *Client) PostApiObjects(ctx context.Context, body PostApiObjectsJSONRequ return c.Client.Do(req) } +func (c *Client) DeleteApiObjectsId(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteApiObjectsIdRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetApiObjectsId(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetApiObjectsIdRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PatchApiObjectsIdWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchApiObjectsIdRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PatchApiObjectsId(ctx context.Context, id string, body PatchApiObjectsIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchApiObjectsIdRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostApiObjectsIdCopiesWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostApiObjectsIdCopiesRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostApiObjectsIdCopies(ctx context.Context, id string, body PostApiObjectsIdCopiesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostApiObjectsIdCopiesRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) PutApiObjectsIdStatusWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewPutApiObjectsIdStatusRequestWithBody(c.Server, id, contentType, body) if err != nil { @@ -2405,6 +2787,30 @@ func (c *Client) PutApiObjectsIdStatus(ctx context.Context, id string, body PutA return c.Client.Do(req) } +func (c *Client) PostApiObjectsIdTransfersWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostApiObjectsIdTransfersRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostApiObjectsIdTransfers(ctx context.Context, id string, body PostApiObjectsIdTransfersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostApiObjectsIdTransfersRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) PostApiObjectsIdUploadsWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewPostApiObjectsIdUploadsRequestWithBody(c.Server, id, contentType, body) if err != nil { @@ -2489,6 +2895,46 @@ func (c *Client) PutApiObjectsIdUploadsUploadSessionIdStatus(ctx context.Context return c.Client.Do(req) } +// NewPostApiAuthDeviceApproveRequest calls the generic PostApiAuthDeviceApprove builder with application/json body +func NewPostApiAuthDeviceApproveRequest(server string, body PostApiAuthDeviceApproveJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostApiAuthDeviceApproveRequestWithBody(server, "application/json", bodyReader) +} + +// NewPostApiAuthDeviceApproveRequestWithBody generates requests for PostApiAuthDeviceApprove with any type of body +func NewPostApiAuthDeviceApproveRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/auth/device/approve") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewPostApiAuthDeviceCodeRequest calls the generic PostApiAuthDeviceCode builder with application/json body func NewPostApiAuthDeviceCodeRequest(server string, body PostApiAuthDeviceCodeJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -2529,6 +2975,46 @@ func NewPostApiAuthDeviceCodeRequestWithBody(server string, contentType string, return req, nil } +// NewPostApiAuthDeviceDenyRequest calls the generic PostApiAuthDeviceDeny builder with application/json body +func NewPostApiAuthDeviceDenyRequest(server string, body PostApiAuthDeviceDenyJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostApiAuthDeviceDenyRequestWithBody(server, "application/json", bodyReader) +} + +// NewPostApiAuthDeviceDenyRequestWithBody generates requests for PostApiAuthDeviceDeny with any type of body +func NewPostApiAuthDeviceDenyRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/auth/device/deny") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewPostApiAuthDeviceTokenRequest calls the generic PostApiAuthDeviceToken builder with application/json body func NewPostApiAuthDeviceTokenRequest(server string, body PostApiAuthDeviceTokenJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -3144,6 +3630,144 @@ func NewPutApiDownloadsTasksIdStatusRequestWithBody(server string, id string, co return req, nil } +// NewGetApiObjectsRequest generates requests for GetApiObjects +func NewGetApiObjectsRequest(server string, params *GetApiObjectsParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/objects") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Parent != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "parent", *params.Parent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Path != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "path", *params.Path, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Status != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "status", *params.Status, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Type != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "type", *params.Type, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Search != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "search", *params.Search, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Page != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "page", *params.Page, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.PageSize != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "pageSize", *params.PageSize, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.OrgId != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "orgId", *params.OrgId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewPostApiObjectsRequest calls the generic PostApiObjects builder with application/json body func NewPostApiObjectsRequest(server string, body PostApiObjectsJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -3184,6 +3808,168 @@ func NewPostApiObjectsRequestWithBody(server string, contentType string, body io return req, nil } +// NewDeleteApiObjectsIdRequest generates requests for DeleteApiObjectsId +func NewDeleteApiObjectsIdRequest(server string, id string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/objects/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetApiObjectsIdRequest generates requests for GetApiObjectsId +func NewGetApiObjectsIdRequest(server string, id string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/objects/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewPatchApiObjectsIdRequest calls the generic PatchApiObjectsId builder with application/json body +func NewPatchApiObjectsIdRequest(server string, id string, body PatchApiObjectsIdJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPatchApiObjectsIdRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewPatchApiObjectsIdRequestWithBody generates requests for PatchApiObjectsId with any type of body +func NewPatchApiObjectsIdRequestWithBody(server string, id string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/objects/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPostApiObjectsIdCopiesRequest calls the generic PostApiObjectsIdCopies builder with application/json body +func NewPostApiObjectsIdCopiesRequest(server string, id string, body PostApiObjectsIdCopiesJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostApiObjectsIdCopiesRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewPostApiObjectsIdCopiesRequestWithBody generates requests for PostApiObjectsIdCopies with any type of body +func NewPostApiObjectsIdCopiesRequestWithBody(server string, id string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/objects/%s/copies", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewPutApiObjectsIdStatusRequest calls the generic PutApiObjectsIdStatus builder with application/json body func NewPutApiObjectsIdStatusRequest(server string, id string, body PutApiObjectsIdStatusJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -3231,6 +4017,53 @@ func NewPutApiObjectsIdStatusRequestWithBody(server string, id string, contentTy return req, nil } +// NewPostApiObjectsIdTransfersRequest calls the generic PostApiObjectsIdTransfers builder with application/json body +func NewPostApiObjectsIdTransfersRequest(server string, id string, body PostApiObjectsIdTransfersJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostApiObjectsIdTransfersRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewPostApiObjectsIdTransfersRequestWithBody generates requests for PostApiObjectsIdTransfers with any type of body +func NewPostApiObjectsIdTransfersRequestWithBody(server string, id string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/objects/%s/transfers", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewPostApiObjectsIdUploadsRequest calls the generic PostApiObjectsIdUploads builder with application/json body func NewPostApiObjectsIdUploadsRequest(server string, id string, body PostApiObjectsIdUploadsJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -3470,11 +4303,21 @@ func WithBaseURL(baseURL string) ClientOption { // ClientWithResponsesInterface is the interface specification for the client with responses above. type ClientWithResponsesInterface interface { + // PostApiAuthDeviceApproveWithBodyWithResponse request with any body + PostApiAuthDeviceApproveWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiAuthDeviceApproveResponse, error) + + PostApiAuthDeviceApproveWithResponse(ctx context.Context, body PostApiAuthDeviceApproveJSONRequestBody, reqEditors ...RequestEditorFn) (*PostApiAuthDeviceApproveResponse, error) + // PostApiAuthDeviceCodeWithBodyWithResponse request with any body PostApiAuthDeviceCodeWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiAuthDeviceCodeResponse, error) PostApiAuthDeviceCodeWithResponse(ctx context.Context, body PostApiAuthDeviceCodeJSONRequestBody, reqEditors ...RequestEditorFn) (*PostApiAuthDeviceCodeResponse, error) + // PostApiAuthDeviceDenyWithBodyWithResponse request with any body + PostApiAuthDeviceDenyWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiAuthDeviceDenyResponse, error) + + PostApiAuthDeviceDenyWithResponse(ctx context.Context, body PostApiAuthDeviceDenyJSONRequestBody, reqEditors ...RequestEditorFn) (*PostApiAuthDeviceDenyResponse, error) + // PostApiAuthDeviceTokenWithBodyWithResponse request with any body PostApiAuthDeviceTokenWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiAuthDeviceTokenResponse, error) @@ -3530,16 +4373,40 @@ type ClientWithResponsesInterface interface { PutApiDownloadsTasksIdStatusWithResponse(ctx context.Context, id string, body PutApiDownloadsTasksIdStatusJSONRequestBody, reqEditors ...RequestEditorFn) (*PutApiDownloadsTasksIdStatusResponse, error) + // GetApiObjectsWithResponse request + GetApiObjectsWithResponse(ctx context.Context, params *GetApiObjectsParams, reqEditors ...RequestEditorFn) (*GetApiObjectsResponse, error) + // PostApiObjectsWithBodyWithResponse request with any body PostApiObjectsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiObjectsResponse, error) PostApiObjectsWithResponse(ctx context.Context, body PostApiObjectsJSONRequestBody, reqEditors ...RequestEditorFn) (*PostApiObjectsResponse, error) + // DeleteApiObjectsIdWithResponse request + DeleteApiObjectsIdWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*DeleteApiObjectsIdResponse, error) + + // GetApiObjectsIdWithResponse request + GetApiObjectsIdWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*GetApiObjectsIdResponse, error) + + // PatchApiObjectsIdWithBodyWithResponse request with any body + PatchApiObjectsIdWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchApiObjectsIdResponse, error) + + PatchApiObjectsIdWithResponse(ctx context.Context, id string, body PatchApiObjectsIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchApiObjectsIdResponse, error) + + // PostApiObjectsIdCopiesWithBodyWithResponse request with any body + PostApiObjectsIdCopiesWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiObjectsIdCopiesResponse, error) + + PostApiObjectsIdCopiesWithResponse(ctx context.Context, id string, body PostApiObjectsIdCopiesJSONRequestBody, reqEditors ...RequestEditorFn) (*PostApiObjectsIdCopiesResponse, error) + // PutApiObjectsIdStatusWithBodyWithResponse request with any body PutApiObjectsIdStatusWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutApiObjectsIdStatusResponse, error) PutApiObjectsIdStatusWithResponse(ctx context.Context, id string, body PutApiObjectsIdStatusJSONRequestBody, reqEditors ...RequestEditorFn) (*PutApiObjectsIdStatusResponse, error) + // PostApiObjectsIdTransfersWithBodyWithResponse request with any body + PostApiObjectsIdTransfersWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiObjectsIdTransfersResponse, error) + + PostApiObjectsIdTransfersWithResponse(ctx context.Context, id string, body PostApiObjectsIdTransfersJSONRequestBody, reqEditors ...RequestEditorFn) (*PostApiObjectsIdTransfersResponse, error) + // PostApiObjectsIdUploadsWithBodyWithResponse request with any body PostApiObjectsIdUploadsWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiObjectsIdUploadsResponse, error) @@ -3559,10 +4426,97 @@ type ClientWithResponsesInterface interface { PutApiObjectsIdUploadsUploadSessionIdStatusWithResponse(ctx context.Context, id string, uploadSessionId string, body PutApiObjectsIdUploadsUploadSessionIdStatusJSONRequestBody, reqEditors ...RequestEditorFn) (*PutApiObjectsIdUploadsUploadSessionIdStatusResponse, error) } +type PostApiAuthDeviceApproveResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *struct { + Success *bool `json:"success,omitempty"` + } + JSON400 *struct { + Message string `json:"message"` + } + JSON401 *struct { + Message string `json:"message"` + } + JSON403 *struct { + Message *string `json:"message,omitempty"` + } + JSON404 *struct { + Message *string `json:"message,omitempty"` + } + JSON429 *struct { + Message *string `json:"message,omitempty"` + } + JSON500 *struct { + Message *string `json:"message,omitempty"` + } +} + +// Status returns HTTPResponse.Status +func (r PostApiAuthDeviceApproveResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostApiAuthDeviceApproveResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostApiAuthDeviceApproveResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type PostApiAuthDeviceCodeResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *DeviceCode + JSON200 *struct { + // DeviceCode The device verification code + DeviceCode *string `json:"device_code,omitempty"` + + // ExpiresIn Lifetime in seconds of the device code + ExpiresIn *float32 `json:"expires_in,omitempty"` + + // Interval Minimum polling interval in seconds + Interval *float32 `json:"interval,omitempty"` + + // UserCode The user code to display + UserCode *string `json:"user_code,omitempty"` + + // VerificationUri The URL for user verification. Defaults to /device if not configured. + VerificationUri *string `json:"verification_uri,omitempty"` + + // VerificationUriComplete The complete URL with user code as query parameter. + VerificationUriComplete *string `json:"verification_uri_complete,omitempty"` + } + JSON400 *struct { + Error *PostApiAuthDeviceCode400JSONResponseBodyError `json:"error,omitempty"` + ErrorDescription *string `json:"error_description,omitempty"` + } + JSON401 *struct { + Message string `json:"message"` + } + JSON403 *struct { + Message *string `json:"message,omitempty"` + } + JSON404 *struct { + Message *string `json:"message,omitempty"` + } + JSON429 *struct { + Message *string `json:"message,omitempty"` + } + JSON500 *struct { + Message *string `json:"message,omitempty"` + } } // Status returns HTTPResponse.Status @@ -3589,11 +4543,84 @@ func (r PostApiAuthDeviceCodeResponse) ContentType() string { return "" } +type PostApiAuthDeviceDenyResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *struct { + Success *bool `json:"success,omitempty"` + } + JSON400 *struct { + Message string `json:"message"` + } + JSON401 *struct { + Message string `json:"message"` + } + JSON403 *struct { + Message *string `json:"message,omitempty"` + } + JSON404 *struct { + Message *string `json:"message,omitempty"` + } + JSON429 *struct { + Message *string `json:"message,omitempty"` + } + JSON500 *struct { + Message *string `json:"message,omitempty"` + } +} + +// Status returns HTTPResponse.Status +func (r PostApiAuthDeviceDenyResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostApiAuthDeviceDenyResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostApiAuthDeviceDenyResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type PostApiAuthDeviceTokenResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *DeviceToken - JSON400 *ErrorResponse + JSON200 *struct { + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + Scope *string `json:"scope,omitempty"` + TokenType string `json:"token_type"` + } + JSON400 *struct { + Error *PostApiAuthDeviceToken400JSONResponseBodyError `json:"error,omitempty"` + ErrorDescription *string `json:"error_description,omitempty"` + } + JSON401 *struct { + Message string `json:"message"` + } + JSON403 *struct { + Message *string `json:"message,omitempty"` + } + JSON404 *struct { + Message *string `json:"message,omitempty"` + } + JSON429 *struct { + Message *string `json:"message,omitempty"` + } + JSON500 *struct { + Message *string `json:"message,omitempty"` + } } // Status returns HTTPResponse.Status @@ -4846,23 +5873,63 @@ func (r PutApiDownloadsTasksIdStatusResponse) ContentType() string { return "" } +type GetApiObjectsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *ObjectPage + JSON400 *struct { + Error string `json:"error"` + } + JSON403 *struct { + Error string `json:"error"` + } +} + +// Status returns HTTPResponse.Status +func (r GetApiObjectsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetApiObjectsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetApiObjectsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type PostApiObjectsResponse struct { Body []byte HTTPResponse *http.Response - JSON200 *struct { + JSON201 *struct { ContentDisposition *string `json:"contentDisposition,omitempty"` Id string `json:"id"` Name string `json:"name"` UploadUrl *string `json:"uploadUrl,omitempty"` } - JSON201 *struct { - ContentDisposition *string `json:"contentDisposition,omitempty"` - Id string `json:"id"` - Name string `json:"name"` - UploadUrl *string `json:"uploadUrl,omitempty"` + JSON400 *struct { + Error string `json:"error"` + } + JSON403 *struct { + Error string `json:"error"` + } + JSON409 *struct { + Error string `json:"error"` + } + JSON500 *struct { + Error string `json:"error"` } - JSON403 *ErrorResponse - JSON409 *ErrorResponse } // Status returns HTTPResponse.Status @@ -4889,17 +5956,194 @@ func (r PostApiObjectsResponse) ContentType() string { return "" } -type PutApiObjectsIdStatusResponse struct { +type DeleteApiObjectsIdResponse struct { Body []byte HTTPResponse *http.Response JSON200 *struct { - ContentDisposition *string `json:"contentDisposition,omitempty"` - Id string `json:"id"` - Name string `json:"name"` - UploadUrl *string `json:"uploadUrl,omitempty"` + Deleted DeleteApiObjectsId200JSONResponseBodyDeleted `json:"deleted"` + Id string `json:"id"` + Purged *int `json:"purged,omitempty"` + } + JSON400 *struct { + Error string `json:"error"` + } + JSON404 *struct { + Error string `json:"error"` + } + JSON409 *struct { + Error string `json:"error"` + } +} + +// Status returns HTTPResponse.Status +func (r DeleteApiObjectsIdResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteApiObjectsIdResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteApiObjectsIdResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetApiObjectsIdResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *struct { + Alias string `json:"alias"` + CreatedAt string `json:"createdAt"` + Dirtype int `json:"dirtype"` + DownloadUrl *string `json:"downloadUrl,omitempty"` + Id string `json:"id"` + Name string `json:"name"` + Object string `json:"object"` + OrgId string `json:"orgId"` + Parent string `json:"parent"` + Size int `json:"size"` + Status string `json:"status"` + StorageId string `json:"storageId"` + Type string `json:"type"` + UpdatedAt string `json:"updatedAt"` + } + JSON400 *struct { + Error string `json:"error"` + } + JSON402 *struct { + Error string `json:"error"` + } + JSON404 *struct { + Error string `json:"error"` + } + JSON422 *struct { + Error string `json:"error"` + } +} + +// Status returns HTTPResponse.Status +func (r GetApiObjectsIdResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetApiObjectsIdResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetApiObjectsIdResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PatchApiObjectsIdResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Matter + JSON400 *struct { + Error string `json:"error"` + } + JSON404 *struct { + Error string `json:"error"` + } +} + +// Status returns HTTPResponse.Status +func (r PatchApiObjectsIdResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PatchApiObjectsIdResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PatchApiObjectsIdResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostApiObjectsIdCopiesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON201 *Matter + JSON400 *struct { + Error string `json:"error"` + } + JSON404 *struct { + Error string `json:"error"` + } +} + +// Status returns HTTPResponse.Status +func (r PostApiObjectsIdCopiesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostApiObjectsIdCopiesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostApiObjectsIdCopiesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PutApiObjectsIdStatusResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Matter + JSON400 *struct { + Error string `json:"error"` + } + JSON403 *struct { + Error string `json:"error"` + } + JSON404 *struct { + Error string `json:"error"` + } + JSON422 *struct { + Error string `json:"error"` } - JSON403 *ErrorResponse - JSON404 *ErrorResponse } // Status returns HTTPResponse.Status @@ -4926,6 +6170,48 @@ func (r PutApiObjectsIdStatusResponse) ContentType() string { return "" } +type PostApiObjectsIdTransfersResponse struct { + Body []byte + HTTPResponse *http.Response + JSON201 *TransferResult + JSON400 *struct { + Error string `json:"error"` + } + JSON403 *struct { + Error string `json:"error"` + } + JSON404 *struct { + Error string `json:"error"` + } + JSON422 *struct { + Error string `json:"error"` + } +} + +// Status returns HTTPResponse.Status +func (r PostApiObjectsIdTransfersResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostApiObjectsIdTransfersResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostApiObjectsIdTransfersResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type PostApiObjectsIdUploadsResponse struct { Body []byte HTTPResponse *http.Response @@ -4939,9 +6225,18 @@ type PostApiObjectsIdUploadsResponse struct { UpdatedAt string `json:"updatedAt"` UploadId string `json:"uploadId"` } - JSON400 *ErrorResponse - JSON403 *ErrorResponse - JSON404 *ErrorResponse + JSON400 *struct { + Error string `json:"error"` + } + JSON403 *struct { + Error string `json:"error"` + } + JSON404 *struct { + Error string `json:"error"` + } + JSON502 *struct { + Error string `json:"error"` + } } // Status returns HTTPResponse.Status @@ -4981,9 +6276,15 @@ type DeleteApiObjectsIdUploadsUploadSessionIdResponse struct { UpdatedAt string `json:"updatedAt"` UploadId string `json:"uploadId"` } - JSON400 *ErrorResponse - JSON403 *ErrorResponse - JSON404 *ErrorResponse + JSON400 *struct { + Error string `json:"error"` + } + JSON403 *struct { + Error string `json:"error"` + } + JSON404 *struct { + Error string `json:"error"` + } } // Status returns HTTPResponse.Status @@ -5021,9 +6322,18 @@ type PostApiObjectsIdUploadsUploadSessionIdPartsResponse struct { } `json:"parts"` UploadId string `json:"uploadId"` } - JSON400 *ErrorResponse - JSON403 *ErrorResponse - JSON404 *ErrorResponse + JSON400 *struct { + Error string `json:"error"` + } + JSON403 *struct { + Error string `json:"error"` + } + JSON404 *struct { + Error string `json:"error"` + } + JSON502 *struct { + Error string `json:"error"` + } } // Status returns HTTPResponse.Status @@ -5063,9 +6373,18 @@ type PutApiObjectsIdUploadsUploadSessionIdStatusResponse struct { UpdatedAt string `json:"updatedAt"` UploadId string `json:"uploadId"` } - JSON400 *ErrorResponse - JSON403 *ErrorResponse - JSON404 *ErrorResponse + JSON400 *struct { + Error string `json:"error"` + } + JSON403 *struct { + Error string `json:"error"` + } + JSON404 *struct { + Error string `json:"error"` + } + JSON502 *struct { + Error string `json:"error"` + } } // Status returns HTTPResponse.Status @@ -5092,6 +6411,23 @@ func (r PutApiObjectsIdUploadsUploadSessionIdStatusResponse) ContentType() strin return "" } +// PostApiAuthDeviceApproveWithBodyWithResponse request with arbitrary body returning *PostApiAuthDeviceApproveResponse +func (c *ClientWithResponses) PostApiAuthDeviceApproveWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiAuthDeviceApproveResponse, error) { + rsp, err := c.PostApiAuthDeviceApproveWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostApiAuthDeviceApproveResponse(rsp) +} + +func (c *ClientWithResponses) PostApiAuthDeviceApproveWithResponse(ctx context.Context, body PostApiAuthDeviceApproveJSONRequestBody, reqEditors ...RequestEditorFn) (*PostApiAuthDeviceApproveResponse, error) { + rsp, err := c.PostApiAuthDeviceApprove(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostApiAuthDeviceApproveResponse(rsp) +} + // PostApiAuthDeviceCodeWithBodyWithResponse request with arbitrary body returning *PostApiAuthDeviceCodeResponse func (c *ClientWithResponses) PostApiAuthDeviceCodeWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiAuthDeviceCodeResponse, error) { rsp, err := c.PostApiAuthDeviceCodeWithBody(ctx, contentType, body, reqEditors...) @@ -5109,6 +6445,23 @@ func (c *ClientWithResponses) PostApiAuthDeviceCodeWithResponse(ctx context.Cont return ParsePostApiAuthDeviceCodeResponse(rsp) } +// PostApiAuthDeviceDenyWithBodyWithResponse request with arbitrary body returning *PostApiAuthDeviceDenyResponse +func (c *ClientWithResponses) PostApiAuthDeviceDenyWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiAuthDeviceDenyResponse, error) { + rsp, err := c.PostApiAuthDeviceDenyWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostApiAuthDeviceDenyResponse(rsp) +} + +func (c *ClientWithResponses) PostApiAuthDeviceDenyWithResponse(ctx context.Context, body PostApiAuthDeviceDenyJSONRequestBody, reqEditors ...RequestEditorFn) (*PostApiAuthDeviceDenyResponse, error) { + rsp, err := c.PostApiAuthDeviceDeny(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostApiAuthDeviceDenyResponse(rsp) +} + // PostApiAuthDeviceTokenWithBodyWithResponse request with arbitrary body returning *PostApiAuthDeviceTokenResponse func (c *ClientWithResponses) PostApiAuthDeviceTokenWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiAuthDeviceTokenResponse, error) { rsp, err := c.PostApiAuthDeviceTokenWithBody(ctx, contentType, body, reqEditors...) @@ -5290,6 +6643,15 @@ func (c *ClientWithResponses) PutApiDownloadsTasksIdStatusWithResponse(ctx conte return ParsePutApiDownloadsTasksIdStatusResponse(rsp) } +// GetApiObjectsWithResponse request returning *GetApiObjectsResponse +func (c *ClientWithResponses) GetApiObjectsWithResponse(ctx context.Context, params *GetApiObjectsParams, reqEditors ...RequestEditorFn) (*GetApiObjectsResponse, error) { + rsp, err := c.GetApiObjects(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetApiObjectsResponse(rsp) +} + // PostApiObjectsWithBodyWithResponse request with arbitrary body returning *PostApiObjectsResponse func (c *ClientWithResponses) PostApiObjectsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiObjectsResponse, error) { rsp, err := c.PostApiObjectsWithBody(ctx, contentType, body, reqEditors...) @@ -5307,6 +6669,58 @@ func (c *ClientWithResponses) PostApiObjectsWithResponse(ctx context.Context, bo return ParsePostApiObjectsResponse(rsp) } +// DeleteApiObjectsIdWithResponse request returning *DeleteApiObjectsIdResponse +func (c *ClientWithResponses) DeleteApiObjectsIdWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*DeleteApiObjectsIdResponse, error) { + rsp, err := c.DeleteApiObjectsId(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteApiObjectsIdResponse(rsp) +} + +// GetApiObjectsIdWithResponse request returning *GetApiObjectsIdResponse +func (c *ClientWithResponses) GetApiObjectsIdWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*GetApiObjectsIdResponse, error) { + rsp, err := c.GetApiObjectsId(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetApiObjectsIdResponse(rsp) +} + +// PatchApiObjectsIdWithBodyWithResponse request with arbitrary body returning *PatchApiObjectsIdResponse +func (c *ClientWithResponses) PatchApiObjectsIdWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchApiObjectsIdResponse, error) { + rsp, err := c.PatchApiObjectsIdWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePatchApiObjectsIdResponse(rsp) +} + +func (c *ClientWithResponses) PatchApiObjectsIdWithResponse(ctx context.Context, id string, body PatchApiObjectsIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchApiObjectsIdResponse, error) { + rsp, err := c.PatchApiObjectsId(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePatchApiObjectsIdResponse(rsp) +} + +// PostApiObjectsIdCopiesWithBodyWithResponse request with arbitrary body returning *PostApiObjectsIdCopiesResponse +func (c *ClientWithResponses) PostApiObjectsIdCopiesWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiObjectsIdCopiesResponse, error) { + rsp, err := c.PostApiObjectsIdCopiesWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostApiObjectsIdCopiesResponse(rsp) +} + +func (c *ClientWithResponses) PostApiObjectsIdCopiesWithResponse(ctx context.Context, id string, body PostApiObjectsIdCopiesJSONRequestBody, reqEditors ...RequestEditorFn) (*PostApiObjectsIdCopiesResponse, error) { + rsp, err := c.PostApiObjectsIdCopies(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostApiObjectsIdCopiesResponse(rsp) +} + // PutApiObjectsIdStatusWithBodyWithResponse request with arbitrary body returning *PutApiObjectsIdStatusResponse func (c *ClientWithResponses) PutApiObjectsIdStatusWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutApiObjectsIdStatusResponse, error) { rsp, err := c.PutApiObjectsIdStatusWithBody(ctx, id, contentType, body, reqEditors...) @@ -5324,6 +6738,23 @@ func (c *ClientWithResponses) PutApiObjectsIdStatusWithResponse(ctx context.Cont return ParsePutApiObjectsIdStatusResponse(rsp) } +// PostApiObjectsIdTransfersWithBodyWithResponse request with arbitrary body returning *PostApiObjectsIdTransfersResponse +func (c *ClientWithResponses) PostApiObjectsIdTransfersWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiObjectsIdTransfersResponse, error) { + rsp, err := c.PostApiObjectsIdTransfersWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostApiObjectsIdTransfersResponse(rsp) +} + +func (c *ClientWithResponses) PostApiObjectsIdTransfersWithResponse(ctx context.Context, id string, body PostApiObjectsIdTransfersJSONRequestBody, reqEditors ...RequestEditorFn) (*PostApiObjectsIdTransfersResponse, error) { + rsp, err := c.PostApiObjectsIdTransfers(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostApiObjectsIdTransfersResponse(rsp) +} + // PostApiObjectsIdUploadsWithBodyWithResponse request with arbitrary body returning *PostApiObjectsIdUploadsResponse func (c *ClientWithResponses) PostApiObjectsIdUploadsWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiObjectsIdUploadsResponse, error) { rsp, err := c.PostApiObjectsIdUploadsWithBody(ctx, id, contentType, body, reqEditors...) @@ -5384,6 +6815,88 @@ func (c *ClientWithResponses) PutApiObjectsIdUploadsUploadSessionIdStatusWithRes return ParsePutApiObjectsIdUploadsUploadSessionIdStatusResponse(rsp) } +// ParsePostApiAuthDeviceApproveResponse parses an HTTP response from a PostApiAuthDeviceApproveWithResponse call +func ParsePostApiAuthDeviceApproveResponse(rsp *http.Response) (*PostApiAuthDeviceApproveResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostApiAuthDeviceApproveResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest struct { + Success *bool `json:"success,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest struct { + Message string `json:"message"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest struct { + Message string `json:"message"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + // ParsePostApiAuthDeviceCodeResponse parses an HTTP response from a PostApiAuthDeviceCodeWithResponse call func ParsePostApiAuthDeviceCodeResponse(rsp *http.Response) (*PostApiAuthDeviceCodeResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -5399,12 +6912,167 @@ func ParsePostApiAuthDeviceCodeResponse(rsp *http.Response) (*PostApiAuthDeviceC switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest DeviceCode + var dest struct { + // DeviceCode The device verification code + DeviceCode *string `json:"device_code,omitempty"` + + // ExpiresIn Lifetime in seconds of the device code + ExpiresIn *float32 `json:"expires_in,omitempty"` + + // Interval Minimum polling interval in seconds + Interval *float32 `json:"interval,omitempty"` + + // UserCode The user code to display + UserCode *string `json:"user_code,omitempty"` + + // VerificationUri The URL for user verification. Defaults to /device if not configured. + VerificationUri *string `json:"verification_uri,omitempty"` + + // VerificationUriComplete The complete URL with user code as query parameter. + VerificationUriComplete *string `json:"verification_uri_complete,omitempty"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON200 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest struct { + Error *PostApiAuthDeviceCode400JSONResponseBodyError `json:"error,omitempty"` + ErrorDescription *string `json:"error_description,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest struct { + Message string `json:"message"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePostApiAuthDeviceDenyResponse parses an HTTP response from a PostApiAuthDeviceDenyWithResponse call +func ParsePostApiAuthDeviceDenyResponse(rsp *http.Response) (*PostApiAuthDeviceDenyResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostApiAuthDeviceDenyResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest struct { + Success *bool `json:"success,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest struct { + Message string `json:"message"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest struct { + Message string `json:"message"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + } return response, nil @@ -5425,19 +7093,72 @@ func ParsePostApiAuthDeviceTokenResponse(rsp *http.Response) (*PostApiAuthDevice switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest DeviceToken + var dest struct { + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + Scope *string `json:"scope,omitempty"` + TokenType string `json:"token_type"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest ErrorResponse + var dest struct { + Error *PostApiAuthDeviceToken400JSONResponseBodyError `json:"error,omitempty"` + ErrorDescription *string `json:"error_description,omitempty"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest struct { + Message string `json:"message"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest struct { + Message *string `json:"message,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + } return response, nil @@ -6783,6 +8504,50 @@ func ParsePutApiDownloadsTasksIdStatusResponse(rsp *http.Response) (*PutApiDownl return response, nil } +// ParseGetApiObjectsResponse parses an HTTP response from a GetApiObjectsWithResponse call +func ParseGetApiObjectsResponse(rsp *http.Response) (*GetApiObjectsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetApiObjectsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ObjectPage + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + } + + return response, nil +} + // ParsePostApiObjectsResponse parses an HTTP response from a PostApiObjectsWithResponse call func ParsePostApiObjectsResponse(rsp *http.Response) (*PostApiObjectsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -6797,18 +8562,6 @@ func ParsePostApiObjectsResponse(rsp *http.Response) (*PostApiObjectsResponse, e } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest struct { - ContentDisposition *string `json:"contentDisposition,omitempty"` - Id string `json:"id"` - Name string `json:"name"` - UploadUrl *string `json:"uploadUrl,omitempty"` - } - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: var dest struct { ContentDisposition *string `json:"contentDisposition,omitempty"` @@ -6821,20 +8574,264 @@ func ParsePostApiObjectsResponse(rsp *http.Response) (*PostApiObjectsResponse, e } response.JSON201 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON403 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON409 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseDeleteApiObjectsIdResponse parses an HTTP response from a DeleteApiObjectsIdWithResponse call +func ParseDeleteApiObjectsIdResponse(rsp *http.Response) (*DeleteApiObjectsIdResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteApiObjectsIdResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest struct { + Deleted DeleteApiObjectsId200JSONResponseBodyDeleted `json:"deleted"` + Id string `json:"id"` + Purged *int `json:"purged,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON409 = &dest + + } + + return response, nil +} + +// ParseGetApiObjectsIdResponse parses an HTTP response from a GetApiObjectsIdWithResponse call +func ParseGetApiObjectsIdResponse(rsp *http.Response) (*GetApiObjectsIdResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetApiObjectsIdResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest struct { + Alias string `json:"alias"` + CreatedAt string `json:"createdAt"` + Dirtype int `json:"dirtype"` + DownloadUrl *string `json:"downloadUrl,omitempty"` + Id string `json:"id"` + Name string `json:"name"` + Object string `json:"object"` + OrgId string `json:"orgId"` + Parent string `json:"parent"` + Size int `json:"size"` + Status string `json:"status"` + StorageId string `json:"storageId"` + Type string `json:"type"` + UpdatedAt string `json:"updatedAt"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 402: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON402 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + } + + return response, nil +} + +// ParsePatchApiObjectsIdResponse parses an HTTP response from a PatchApiObjectsIdWithResponse call +func ParsePatchApiObjectsIdResponse(rsp *http.Response) (*PatchApiObjectsIdResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PatchApiObjectsIdResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Matter + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + } + + return response, nil +} + +// ParsePostApiObjectsIdCopiesResponse parses an HTTP response from a PostApiObjectsIdCopiesWithResponse call +func ParsePostApiObjectsIdCopiesResponse(rsp *http.Response) (*PostApiObjectsIdCopiesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostApiObjectsIdCopiesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest Matter + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + } return response, nil @@ -6855,31 +8852,110 @@ func ParsePutApiObjectsIdStatusResponse(rsp *http.Response) (*PutApiObjectsIdSta switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest struct { - ContentDisposition *string `json:"contentDisposition,omitempty"` - Id string `json:"id"` - Name string `json:"name"` - UploadUrl *string `json:"uploadUrl,omitempty"` - } + var dest Matter if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON200 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON403 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + } + + return response, nil +} + +// ParsePostApiObjectsIdTransfersResponse parses an HTTP response from a PostApiObjectsIdTransfersWithResponse call +func ParsePostApiObjectsIdTransfersResponse(rsp *http.Response) (*PostApiObjectsIdTransfersResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostApiObjectsIdTransfersResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest TransferResult + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + } return response, nil @@ -6916,26 +8992,41 @@ func ParsePostApiObjectsIdUploadsResponse(rsp *http.Response) (*PostApiObjectsId response.JSON201 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON403 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 502: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON502 = &dest + } return response, nil @@ -6972,21 +9063,27 @@ func ParseDeleteApiObjectsIdUploadsUploadSessionIdResponse(rsp *http.Response) ( response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON403 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -7026,26 +9123,41 @@ func ParsePostApiObjectsIdUploadsUploadSessionIdPartsResponse(rsp *http.Response response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON403 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 502: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON502 = &dest + } return response, nil @@ -7082,26 +9194,41 @@ func ParsePutApiObjectsIdUploadsUploadSessionIdStatusResponse(rsp *http.Response response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON403 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest ErrorResponse + var dest struct { + Error string `json:"error"` + } if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 502: + var dest struct { + Error string `json:"error"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON502 = &dest + } return response, nil diff --git a/docs/openapi/downloader.json b/docs/openapi/downloader.json index c51b9951..c3f683de 100644 --- a/docs/openapi/downloader.json +++ b/docs/openapi/downloader.json @@ -1,133 +1,398 @@ { - "openapi": "3.0.0", + "openapi": "3.0.3", "info": { - "title": "ZPan Downloader API", + "title": "ZPan API", "version": "0.1.0" }, + "tags": [ + { + "name": "Objects", + "description": "Files and folders, including S3 multipart upload sessions" + }, + { + "name": "Events", + "description": "Multiplexed server-sent event stream" + }, + { + "name": "Download Tasks", + "description": "Remote download tasks" + }, + { + "name": "Downloaders", + "description": "Download agents and their heartbeats" + } + ], "components": { "schemas": { - "DeviceCode": { + "ObjectPage": { "type": "object", "properties": { - "device_code": { - "type": "string" + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Matter" + } }, - "user_code": { - "type": "string" - }, - "verification_uri": { - "type": "string" - }, - "verification_uri_complete": { - "type": "string" - }, - "expires_in": { + "total": { "type": "integer" }, - "interval": { + "page": { + "type": "integer" + }, + "pageSize": { "type": "integer" } }, "required": [ - "device_code", - "user_code", - "verification_uri", - "verification_uri_complete", - "expires_in", - "interval" + "items", + "total", + "page", + "pageSize" ] }, - "DeviceCodeRequest": { + "Matter": { "type": "object", "properties": { - "client_id": { + "id": { "type": "string" }, - "scope": { - "type": "string" - } - }, - "required": [ - "client_id", - "scope" - ] - }, - "DeviceToken": { - "type": "object", - "properties": { - "access_token": { + "orgId": { "type": "string" }, - "token_type": { + "alias": { "type": "string" }, - "expires_in": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "size": { "type": "integer" }, - "scope": { - "type": "string" - } - }, - "required": [ - "access_token", - "token_type", - "expires_in", - "scope" - ] - }, - "ErrorResponse": { - "type": "object", - "properties": { - "error": { - "type": "string" - } - }, - "required": [ - "error" - ] - }, - "DeviceTokenRequest": { - "type": "object", - "properties": { - "grant_type": { + "dirtype": { + "type": "integer" + }, + "parent": { "type": "string" }, - "device_code": { + "object": { "type": "string" }, - "client_id": { + "storageId": { + "type": "string" + }, + "status": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { "type": "string" } }, "required": [ - "grant_type", - "device_code", - "client_id" + "id", + "orgId", + "alias", + "name", + "type", + "size", + "dirtype", + "parent", + "object", + "storageId", + "status", + "createdAt", + "updatedAt" + ] + }, + "TransferResult": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sourceDeleted": { + "type": "boolean" + } + }, + "required": [ + "id", + "sourceDeleted" ] } }, "parameters": {} }, "paths": { - "/api/auth/device/code": { + "/api/objects": { + "get": { + "tags": [ + "Objects" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": false, + "name": "parent", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "path", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "status", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "search", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "page", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "pageSize", + "in": "query" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "orgId", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Objects", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjectPage" + } + } + } + }, + "400": { + "description": "No active organization", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + } + }, "post": { + "tags": [ + "Objects" + ], "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceCodeRequest" + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "minLength": 1 + }, + "size": { + "type": "integer", + "minimum": 0 + }, + "parent": { + "type": "string", + "default": "" + }, + "dirtype": { + "type": "integer", + "default": 0 + }, + "onConflict": { + "type": "string", + "enum": [ + "fail", + "rename", + "replace" + ] + } + }, + "required": [ + "name", + "type" + ] } } } }, "responses": { - "200": { - "description": "Device authorization code", + "201": { + "description": "Created object draft with upload URL", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceCode" + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "uploadUrl": { + "type": "string" + }, + "contentDisposition": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ] + } + } + } + }, + "400": { + "description": "No active organization", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "Name conflict", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "500": { + "description": "Storage not configured", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] } } } @@ -135,35 +400,1303 @@ } } }, - "/api/auth/device/token": { + "/api/objects/{id}/uploads": { "post": { + "tags": [ + "Objects" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + } + ], "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceTokenRequest" + "type": "object", + "properties": { + "partSize": { + "type": "integer", + "minimum": 5242880, + "maximum": 536870912 + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Object multipart upload session", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "objectId": { + "type": "string" + }, + "uploadId": { + "type": "string" + }, + "partSize": { + "type": "integer" + }, + "status": { + "type": "string", + "enum": [ + "active", + "completed", + "aborted" + ] + }, + "expiresAt": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "objectId", + "uploadId", + "partSize", + "status", + "expiresAt", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "Invalid upload session", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "502": { + "description": "Storage failure", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + } + } + }, + "/api/objects/{id}/uploads/{uploadSessionId}/parts": { + "post": { + "tags": [ + "Objects" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "uploadSessionId", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "partNumbers": { + "type": "array", + "items": { + "type": "integer", + "minimum": 1, + "maximum": 10000 + }, + "minItems": 1, + "maxItems": 100 + } + }, + "required": [ + "partNumbers" + ] } } } }, "responses": { "200": { - "description": "Device access token", + "description": "Presigned multipart upload parts", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceToken" + "type": "object", + "properties": { + "uploadId": { + "type": "string" + }, + "partSize": { + "type": "integer" + }, + "parts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "partNumber": { + "type": "integer" + }, + "url": { + "type": "string" + } + }, + "required": [ + "partNumber", + "url" + ] + } + } + }, + "required": [ + "uploadId", + "partSize", + "parts" + ] } } } }, "400": { - "description": "Device login error", + "description": "Invalid upload session", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ErrorResponse" + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "502": { + "description": "Storage failure", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + } + } + }, + "/api/objects/{id}/uploads/{uploadSessionId}/status": { + "put": { + "tags": [ + "Objects" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "uploadSessionId", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "completed" + ] + }, + "parts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "partNumber": { + "type": "integer", + "minimum": 1, + "maximum": 10000 + }, + "etag": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "partNumber", + "etag" + ] + }, + "minItems": 1 + } + }, + "required": [ + "status", + "parts" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Completed object multipart upload session", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "objectId": { + "type": "string" + }, + "uploadId": { + "type": "string" + }, + "partSize": { + "type": "integer" + }, + "status": { + "type": "string", + "enum": [ + "active", + "completed", + "aborted" + ] + }, + "expiresAt": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "objectId", + "uploadId", + "partSize", + "status", + "expiresAt", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "Invalid upload session", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "502": { + "description": "Storage failure", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + } + } + }, + "/api/objects/{id}/uploads/{uploadSessionId}": { + "delete": { + "tags": [ + "Objects" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "uploadSessionId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Aborted object multipart upload session", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "objectId": { + "type": "string" + }, + "uploadId": { + "type": "string" + }, + "partSize": { + "type": "integer" + }, + "status": { + "type": "string", + "enum": [ + "active", + "completed", + "aborted" + ] + }, + "expiresAt": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "objectId", + "uploadId", + "partSize", + "status", + "expiresAt", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "Invalid upload session", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + } + } + }, + "/api/objects/{id}": { + "get": { + "tags": [ + "Objects" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Object", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Matter" + }, + { + "type": "object", + "properties": { + "downloadUrl": { + "type": "string" + } + } + } + ] + } + } + } + }, + "400": { + "description": "No active organization", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "402": { + "description": "Insufficient credits", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "Traffic quota exceeded", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + } + }, + "patch": { + "tags": [ + "Objects" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "parent": { + "type": "string" + }, + "onConflict": { + "type": "string", + "enum": [ + "fail", + "rename", + "replace" + ] + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Updated object", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Matter" + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + } + }, + "delete": { + "tags": [ + "Objects" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Deleted object", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "deleted": { + "type": "boolean", + "enum": [ + true + ] + }, + "purged": { + "type": "integer" + } + }, + "required": [ + "id", + "deleted" + ] + } + } + } + }, + "400": { + "description": "No active organization", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "Object must be trashed before permanent deletion", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + } + } + }, + "/api/objects/{id}/status": { + "put": { + "tags": [ + "Objects" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "active", + "trashed" + ] + }, + "onConflict": { + "type": "string", + "enum": [ + "fail", + "rename", + "replace" + ] + } + }, + "required": [ + "status" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Updated object", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Matter" + } + } + } + }, + "400": { + "description": "No active organization", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "Quota exceeded", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + } + } + }, + "/api/objects/{id}/copies": { + "post": { + "tags": [ + "Objects" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "parent": { + "type": "string", + "default": "" + }, + "onConflict": { + "type": "string", + "enum": [ + "fail", + "rename", + "replace" + ] + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Copied object", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Matter" + } + } + } + }, + "400": { + "description": "No active organization", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + } + } + }, + "/api/objects/{id}/transfers": { + "post": { + "tags": [ + "Objects" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "targetOrgId": { + "type": "string", + "minLength": 1 + }, + "targetParent": { + "type": "string", + "default": "" + }, + "mode": { + "type": "string", + "enum": [ + "copy", + "move" + ] + } + }, + "required": [ + "targetOrgId", + "mode" + ] + } + } + } + }, + "responses": { + "201": { + "description": "Transferred object", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransferResult" + } + } + } + }, + "400": { + "description": "Invalid transfer target", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "Quota exceeded", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] } } } @@ -173,6 +1706,9 @@ }, "/api/downloads/tasks": { "get": { + "tags": [ + "Download Tasks" + ], "parameters": [ { "schema": { @@ -865,6 +2401,9 @@ } }, "post": { + "tags": [ + "Download Tasks" + ], "requestBody": { "required": true, "content": { @@ -1526,6 +3065,9 @@ }, "/api/downloads/tasks/{id}": { "get": { + "tags": [ + "Download Tasks" + ], "parameters": [ { "schema": { @@ -2098,6 +3640,9 @@ } }, "delete": { + "tags": [ + "Download Tasks" + ], "parameters": [ { "schema": { @@ -2209,6 +3754,9 @@ } }, "patch": { + "tags": [ + "Download Tasks" + ], "parameters": [ { "schema": { @@ -3186,6 +4734,9 @@ }, "/api/downloads/tasks/{id}/status": { "put": { + "tags": [ + "Download Tasks" + ], "parameters": [ { "schema": { @@ -3837,6 +5388,9 @@ }, "/api/downloads/tasks/{id}/attempts": { "post": { + "tags": [ + "Download Tasks" + ], "parameters": [ { "schema": { @@ -4480,6 +6034,9 @@ }, "/api/downloads/downloaders/me/heartbeats": { "post": { + "tags": [ + "Downloaders" + ], "requestBody": { "required": true, "content": { @@ -4679,6 +6236,9 @@ }, "/api/downloads/downloaders": { "get": { + "tags": [ + "Downloaders" + ], "responses": { "200": { "description": "Downloaders", @@ -4810,6 +6370,9 @@ } }, "post": { + "tags": [ + "Downloaders" + ], "requestBody": { "required": true, "content": { @@ -5053,6 +6616,9 @@ }, "/api/downloads/downloaders/{id}": { "patch": { + "tags": [ + "Downloaders" + ], "parameters": [ { "schema": { @@ -5212,6 +6778,9 @@ } }, "delete": { + "tags": [ + "Downloaders" + ], "parameters": [ { "schema": { @@ -5266,8 +6835,13 @@ } } }, - "/api/objects": { + "/api/auth/device/code": { "post": { + "tags": [ + "Device-authorization" + ], + "description": "Request a device and user code\n\nFollow [rfc8628#section-3.2](https://datatracker.ietf.org/doc/html/rfc8628#section-3.2)", + "parameters": [], "requestBody": { "required": true, "content": { @@ -5275,38 +6849,18 @@ "schema": { "type": "object", "properties": { - "name": { + "client_id": { "type": "string", - "minLength": 1 + "description": "The client ID of the application" }, - "type": { + "scope": { "type": "string", - "minLength": 1 - }, - "size": { - "type": "integer", - "minimum": 0 - }, - "parent": { - "type": "string", - "default": "" - }, - "dirtype": { - "type": "integer", - "default": 0 - }, - "onConflict": { - "type": "string", - "enum": [ - "fail", - "rename", - "replace" - ] + "description": "Space-separated list of scopes", + "nullable": true } }, "required": [ - "name", - "type" + "client_id" ] } } @@ -5314,312 +6868,153 @@ }, "responses": { "200": { - "description": "Object draft with upload URL", + "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { - "id": { - "type": "string" + "device_code": { + "type": "string", + "description": "The device verification code" }, - "name": { - "type": "string" + "user_code": { + "type": "string", + "description": "The user code to display" }, - "uploadUrl": { - "type": "string" + "verification_uri": { + "type": "string", + "format": "uri", + "description": "The URL for user verification. Defaults to /device if not configured." }, - "contentDisposition": { - "type": "string" + "verification_uri_complete": { + "type": "string", + "format": "uri", + "description": "The complete URL with user code as query parameter." + }, + "expires_in": { + "type": "number", + "description": "Lifetime in seconds of the device code" + }, + "interval": { + "type": "number", + "description": "Minimum polling interval in seconds" } - }, - "required": [ - "id", - "name" - ] - } - } - } - }, - "201": { - "description": "Object draft with upload URL", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "uploadUrl": { - "type": "string" - }, - "contentDisposition": { - "type": "string" - } - }, - "required": [ - "id", - "name" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "409": { - "description": "Name conflict", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - } - }, - "/api/objects/{id}/status": { - "put": { - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "active", - "trashed" - ] - }, - "onConflict": { - "type": "string", - "enum": [ - "fail", - "rename", - "replace" - ] - } - }, - "required": [ - "status" - ] - } - } - } - }, - "responses": { - "200": { - "description": "Confirmed object", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "uploadUrl": { - "type": "string" - }, - "contentDisposition": { - "type": "string" - } - }, - "required": [ - "id", - "name" - ] - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "404": { - "description": "Not found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - } - }, - "/api/objects/{id}/uploads": { - "post": { - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "partSize": { - "type": "integer", - "minimum": 5242880, - "maximum": 536870912 } } } } - } - }, - "responses": { - "201": { - "description": "Object multipart upload session", + }, + "400": { + "description": "Error response", "content": { "application/json": { "schema": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "objectId": { - "type": "string" - }, - "uploadId": { - "type": "string" - }, - "partSize": { - "type": "integer" - }, - "status": { + "error": { "type": "string", "enum": [ - "active", - "completed", - "aborted" + "invalid_request", + "invalid_client" ] }, - "expiresAt": { + "error_description": { "type": "string" - }, - "createdAt": { - "type": "string" - }, - "updatedAt": { + } + } + } + } + } + }, + "401": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { "type": "string" } }, "required": [ - "id", - "objectId", - "uploadId", - "partSize", - "status", - "expiresAt", - "createdAt", - "updatedAt" + "message" ] } } - } - }, - "400": { - "description": "Invalid upload session", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } + }, + "description": "Unauthorized. Due to missing or invalid authentication." }, "403": { - "description": "Forbidden", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ErrorResponse" + "type": "object", + "properties": { + "message": { + "type": "string" + } + } } } - } + }, + "description": "Forbidden. You do not have permission to access this resource or to perform this action." }, "404": { - "description": "Not found", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ErrorResponse" + "type": "object", + "properties": { + "message": { + "type": "string" + } + } } } - } + }, + "description": "Not Found. The requested resource was not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + }, + "description": "Too Many Requests. You have exceeded the rate limit. Try again later." + }, + "500": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + }, + "description": "Internal Server Error. This is a problem with the server that you cannot fix." } } } }, - "/api/objects/{id}/uploads/{uploadSessionId}/parts": { + "/api/auth/device/token": { "post": { - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "id", - "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": true, - "name": "uploadSessionId", - "in": "path" - } + "tags": [ + "Device-authorization" ], + "description": "Exchange device code for access token\n\nFollow [rfc8628#section-3.4](https://datatracker.ietf.org/doc/html/rfc8628#section-3.4)", + "parameters": [], "requestBody": { "required": true, "content": { @@ -5627,158 +7022,23 @@ "schema": { "type": "object", "properties": { - "partNumbers": { - "type": "array", - "items": { - "type": "integer", - "minimum": 1, - "maximum": 10000 - }, - "minItems": 1, - "maxItems": 100 - } - }, - "required": [ - "partNumbers" - ] - } - } - } - }, - "responses": { - "200": { - "description": "Presigned multipart upload parts", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "uploadId": { - "type": "string" - }, - "partSize": { - "type": "integer" - }, - "parts": { - "type": "array", - "items": { - "type": "object", - "properties": { - "partNumber": { - "type": "integer" - }, - "url": { - "type": "string" - } - }, - "required": [ - "partNumber", - "url" - ] - } - } - }, - "required": [ - "uploadId", - "partSize", - "parts" - ] - } - } - } - }, - "400": { - "description": "Invalid upload session", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "404": { - "description": "Not found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - } - }, - "/api/objects/{id}/uploads/{uploadSessionId}/status": { - "put": { - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "id", - "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": true, - "name": "uploadSessionId", - "in": "path" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "status": { + "grant_type": { "type": "string", - "enum": [ - "completed" - ] + "description": "The grant type for device flow" }, - "parts": { - "type": "array", - "items": { - "type": "object", - "properties": { - "partNumber": { - "type": "integer", - "minimum": 1, - "maximum": 10000 - }, - "etag": { - "type": "string", - "minLength": 1 - } - }, - "required": [ - "partNumber", - "etag" - ] - }, - "minItems": 1 + "device_code": { + "type": "string", + "description": "The device verification code" + }, + "client_id": { + "type": "string", + "description": "The client ID of the application" } }, "required": [ - "status", - "parts" + "grant_type", + "device_code", + "client_id" ] } } @@ -5786,190 +7046,419 @@ }, "responses": { "200": { - "description": "Completed object multipart upload session", + "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { - "id": { + "access_token": { "type": "string" }, - "objectId": { + "token_type": { "type": "string" }, - "uploadId": { - "type": "string" - }, - "partSize": { + "expires_in": { "type": "integer" }, - "status": { - "type": "string", - "enum": [ - "active", - "completed", - "aborted" - ] - }, - "expiresAt": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "updatedAt": { + "scope": { "type": "string" } }, "required": [ - "id", - "objectId", - "uploadId", - "partSize", - "status", - "expiresAt", - "createdAt", - "updatedAt" + "access_token", + "token_type", + "expires_in" ] } } } }, "400": { - "description": "Invalid upload session", + "description": "Error response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ErrorResponse" + "type": "object", + "properties": { + "error": { + "type": "string", + "enum": [ + "authorization_pending", + "slow_down", + "expired_token", + "access_denied", + "invalid_request", + "invalid_grant" + ] + }, + "error_description": { + "type": "string" + } + } } } } }, + "401": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ] + } + } + }, + "description": "Unauthorized. Due to missing or invalid authentication." + }, "403": { - "description": "Forbidden", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ErrorResponse" + "type": "object", + "properties": { + "message": { + "type": "string" + } + } } } - } + }, + "description": "Forbidden. You do not have permission to access this resource or to perform this action." }, "404": { - "description": "Not found", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ErrorResponse" + "type": "object", + "properties": { + "message": { + "type": "string" + } + } } } - } + }, + "description": "Not Found. The requested resource was not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + }, + "description": "Too Many Requests. You have exceeded the rate limit. Try again later." + }, + "500": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + }, + "description": "Internal Server Error. This is a problem with the server that you cannot fix." } } } }, - "/api/objects/{id}/uploads/{uploadSessionId}": { - "delete": { - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "id", - "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": true, - "name": "uploadSessionId", - "in": "path" - } + "/api/auth/device/approve": { + "post": { + "tags": [ + "Device-authorization" ], + "description": "Approve device authorization", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "userCode": { + "type": "string", + "description": "The user code to approve" + } + }, + "required": [ + "userCode" + ] + } + } + } + }, "responses": { "200": { - "description": "Aborted object multipart upload session", + "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "objectId": { - "type": "string" - }, - "uploadId": { - "type": "string" - }, - "partSize": { - "type": "integer" - }, - "status": { - "type": "string", - "enum": [ - "active", - "completed", - "aborted" - ] - }, - "expiresAt": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "updatedAt": { - "type": "string" + "success": { + "type": "boolean" } - }, - "required": [ - "id", - "objectId", - "uploadId", - "partSize", - "status", - "expiresAt", - "createdAt", - "updatedAt" - ] + } } } } }, "400": { - "description": "Invalid upload session", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ErrorResponse" + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ] } } - } + }, + "description": "Bad Request. Usually due to missing parameters, or invalid parameters." + }, + "401": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ] + } + } + }, + "description": "Unauthorized. Due to missing or invalid authentication." }, "403": { - "description": "Forbidden", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ErrorResponse" + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + }, + "description": "Forbidden. You do not have permission to access this resource or to perform this action." + }, + "404": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + }, + "description": "Not Found. The requested resource was not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + }, + "description": "Too Many Requests. You have exceeded the rate limit. Try again later." + }, + "500": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + }, + "description": "Internal Server Error. This is a problem with the server that you cannot fix." + } + } + } + }, + "/api/auth/device/deny": { + "post": { + "tags": [ + "Device-authorization" + ], + "description": "Deny device authorization", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "userCode": { + "type": "string", + "description": "The user code to deny" + } + }, + "required": [ + "userCode" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + } + } } } } }, - "404": { - "description": "Not found", + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ErrorResponse" + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ] } } - } + }, + "description": "Bad Request. Usually due to missing parameters, or invalid parameters." + }, + "401": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ] + } + } + }, + "description": "Unauthorized. Due to missing or invalid authentication." + }, + "403": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + }, + "description": "Forbidden. You do not have permission to access this resource or to perform this action." + }, + "404": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + }, + "description": "Not Found. The requested resource was not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + }, + "description": "Too Many Requests. You have exceeded the rate limit. Try again later." + }, + "500": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + }, + "description": "Internal Server Error. This is a problem with the server that you cannot fix." } } } diff --git a/package.json b/package.json index b8a8f484..cd924522 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@hono/zod-validator": "^0.7.6", "@hookform/resolvers": "^5.2.2", "@libsql/client": "^0.17.2", + "@scalar/hono-api-reference": "^0.11.3", "@tanstack/react-query": "^5.100.9", "@tanstack/react-router": "^1.93.0", "@tanstack/react-table": "^8.21.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c3086f4..96068534 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,6 +58,9 @@ importers: '@libsql/client': specifier: ^0.17.2 version: 0.17.2 + '@scalar/hono-api-reference': + specifier: ^0.11.3 + version: 0.11.3(hono@4.12.21) '@tanstack/react-query': specifier: ^5.100.9 version: 5.100.9(react@19.2.5) @@ -2817,6 +2820,32 @@ packages: cpu: [x64] os: [win32] + '@scalar/client-side-rendering@0.2.3': + resolution: {integrity: sha512-AazrKedEZ/JR/ze9UGrWCqpRp9T88QlgBZWVOYC8QAMISz5CGUDZKqpayz2iwGzzZAtKF0VW/Y0sbL7ERTvn6w==} + engines: {node: '>=22'} + + '@scalar/helpers@0.8.2': + resolution: {integrity: sha512-qNbqUjSB3S4Gr4A0oANcm5G1Ip+EqBxICYKhe9YzmnaBpbmW6shxqpiivApTvvuDf+uIhR3uMwWyVQbYcGLsxA==} + engines: {node: '>=22'} + + '@scalar/hono-api-reference@0.11.3': + resolution: {integrity: sha512-dgBIOKeQnlXzL2G27sPbvlA7RHfp+BuEiNEZBRihdIaohhWDYaE4uw2OkPzeaMWHov+QYxsdgWc3IMIEVdnusw==} + engines: {node: '>=22'} + peerDependencies: + hono: ^4.12.5 + + '@scalar/schemas@0.4.3': + resolution: {integrity: sha512-YZ/FcxM5ctZ65m07q5DOOUhuQA5SjZvnoyP6jCCfoiaQ/aVXTDF1vZ1+RMYhKqDwfV/QCW36k0H/38aG/8lNyA==} + engines: {node: '>=22'} + + '@scalar/types@0.13.3': + resolution: {integrity: sha512-+rtVPVC7UPDGaqvBZY8FjXI5cW2xJtJDVsOGuoevRUBkJb3feSiBkGIZxYy0JkJCl9wVUi/PqGEcut6Nb3RnJw==} + engines: {node: '>=22'} + + '@scalar/validation@0.6.0': + resolution: {integrity: sha512-tpmmG+/xRE2Kn9RpflU3AIyZv08v10+E1ZrJCx7z6+/91zHVxy0M73kC1LT4/8PbYNt85ywyC8+n+D99JdMcGA==} + engines: {node: '>=20'} + '@shikijs/core@4.0.2': resolution: {integrity: sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==} engines: {node: '>=20'} @@ -5344,6 +5373,10 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + tailwind-merge@3.5.0: resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} @@ -5468,6 +5501,10 @@ packages: tw-animate-css@1.4.0: resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + type-fest@5.7.0: + resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==} + engines: {node: '>=20'} + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -8056,6 +8093,33 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.62.0': optional: true + '@scalar/client-side-rendering@0.2.3': + dependencies: + '@scalar/schemas': 0.4.3 + '@scalar/types': 0.13.3 + '@scalar/validation': 0.6.0 + + '@scalar/helpers@0.8.2': {} + + '@scalar/hono-api-reference@0.11.3(hono@4.12.21)': + dependencies: + '@scalar/client-side-rendering': 0.2.3 + hono: 4.12.21 + + '@scalar/schemas@0.4.3': + dependencies: + '@scalar/helpers': 0.8.2 + '@scalar/validation': 0.6.0 + + '@scalar/types@0.13.3': + dependencies: + '@scalar/helpers': 0.8.2 + nanoid: 5.1.11 + type-fest: 5.7.0 + zod: 4.4.3 + + '@scalar/validation@0.6.0': {} + '@shikijs/core@4.0.2': dependencies: '@shikijs/primitive': 4.0.2 @@ -10968,6 +11032,8 @@ snapshots: symbol-tree@3.2.4: {} + tagged-tag@1.0.0: {} + tailwind-merge@3.5.0: {} tailwindcss@4.2.2: {} @@ -11099,6 +11165,10 @@ snapshots: tw-animate-css@1.4.0: {} + type-fest@5.7.0: + dependencies: + tagged-tag: 1.0.0 + typescript@5.9.3: {} ufo@1.6.4: {} diff --git a/scripts/build-client-spec.ts b/scripts/build-client-spec.ts new file mode 100644 index 00000000..869545e8 --- /dev/null +++ b/scripts/build-client-spec.ts @@ -0,0 +1,93 @@ +import { createTestApp } from '../server/test/setup' + +// The downloader Go client only talks to these resources. Selecting their paths +// from the (fully auto-generated) merged /api/openapi.json keeps the generated +// client lean and keeps codegen robust — feeding it all 80+ better-auth +// endpoints would bloat the client and risk oapi-codegen choking. This is a +// scope allowlist, not a hand-maintained spec: the path/schema *content* is +// still generated; we only choose which generated paths to emit a client for. +const KEEP_PREFIXES = ['/api/auth/device/', '/api/downloads/', '/api/objects'] + +type Doc = { + paths: Record + components?: { schemas?: Record } + [k: string]: unknown +} + +// oapi-codegen v2 only supports OpenAPI 3.0.x, but the served document (and +// better-auth's generated schema) are 3.1 — which expresses nullability as +// `type: ["string", "null"]`. Rewrite those unions to the 3.0 form +// `type: "string", nullable: true` in place so the generator accepts the spec. +// Only the codegen spec is downconverted; the served /api/openapi.json stays 3.1. +function downconvertTo30(node: unknown): void { + if (Array.isArray(node)) { + for (const v of node) downconvertTo30(v) + return + } + if (!node || typeof node !== 'object') return + const obj = node as Record + if (Array.isArray(obj.type) && obj.type.includes('null')) { + const nonNull = (obj.type as string[]).filter((t) => t !== 'null') + obj.type = nonNull.length === 1 ? nonNull[0] : nonNull + obj.nullable = true + } + for (const v of Object.values(obj)) downconvertTo30(v) +} + +// The downloader client attaches its bearer token manually via a RequestEditorFn, +// so it needs no security metadata. Strip it: better-auth's bearerAuth scheme +// otherwise makes oapi-codegen (client-only) emit a `BearerAuthScopes` const whose +// context-key type is only generated in server mode → undefined symbol. +function stripSecurity(spec: Doc): void { + delete (spec as Record).security + if (spec.components) delete (spec.components as Record).securitySchemes + for (const item of Object.values(spec.paths)) { + if (!item || typeof item !== 'object') continue + for (const op of Object.values(item as Record)) { + if (op && typeof op === 'object') delete (op as Record).security + } + } +} + +// Collect every `#/components/schemas/X` reachable from `node`, transitively. +function collectRefs(node: unknown, schemas: Record, used: Set): void { + if (Array.isArray(node)) { + for (const v of node) collectRefs(v, schemas, used) + return + } + if (!node || typeof node !== 'object') return + for (const [k, v] of Object.entries(node)) { + if (k === '$ref' && typeof v === 'string') { + const name = v.match(/^#\/components\/schemas\/(.+)$/)?.[1] + if (name && !used.has(name)) { + used.add(name) + collectRefs(schemas[name], schemas, used) + } + } else { + collectRefs(v, schemas, used) + } + } +} + +// Builds the downloader client OpenAPI spec by reading the real merged document +// from a throwaway in-memory app, then scoping it to the downloader's paths. +export async function buildClientSpec(): Promise { + const { app } = await createTestApp({ DOWNLOAD_TOKEN_SECRET: 'codegen' }) + const res = await app.request('/api/openapi.json') + if (res.status !== 200) throw new Error(`/api/openapi.json returned ${res.status}`) + const doc = (await res.json()) as Doc + + const paths = Object.fromEntries( + Object.entries(doc.paths).filter(([p]) => KEEP_PREFIXES.some((prefix) => p.startsWith(prefix))), + ) + + const allSchemas = doc.components?.schemas ?? {} + const used = new Set() + collectRefs(paths, allSchemas, used) + const schemas = Object.fromEntries(Object.entries(allSchemas).filter(([name]) => used.has(name))) + + const spec = { ...doc, openapi: '3.0.3', paths, components: { ...doc.components, schemas } } + stripSecurity(spec) + downconvertTo30(spec) + return spec +} diff --git a/scripts/check-downloader-openapi.ts b/scripts/check-downloader-openapi.ts index fe0aaba1..afa4ce25 100644 --- a/scripts/check-downloader-openapi.ts +++ b/scripts/check-downloader-openapi.ts @@ -3,7 +3,7 @@ import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { promisify } from 'node:util' -import { downloaderOpenAPIDocument } from '../server/openapi/downloader' +import { buildClientSpec } from './build-client-spec' const execFile = promisify(execFileCallback) const root = process.cwd() @@ -16,11 +16,7 @@ async function main() { const configPath = join(tempDir, 'oapi-codegen.yaml') await mkdir(join(root, 'docs/openapi'), { recursive: true }) - await writeFile( - generatedDocPath, - `${JSON.stringify(downloaderOpenAPIDocument(), null, 2)}\n`, - 'utf8', - ) + await writeFile(generatedDocPath, `${JSON.stringify(await buildClientSpec(), null, 2)}\n`, 'utf8') await writeFile( configPath, [ diff --git a/scripts/generate-downloader-openapi.ts b/scripts/generate-downloader-openapi.ts index 359ecef8..01c0763c 100644 --- a/scripts/generate-downloader-openapi.ts +++ b/scripts/generate-downloader-openapi.ts @@ -1,7 +1,10 @@ import { mkdir, writeFile } from 'node:fs/promises' import { dirname, resolve } from 'node:path' -import { downloaderOpenAPIDocument } from '../server/openapi/downloader' +import { buildClientSpec } from './build-client-spec' const output = resolve('docs/openapi/downloader.json') await mkdir(dirname(output), { recursive: true }) -await writeFile(output, `${JSON.stringify(downloaderOpenAPIDocument(), null, 2)}\n`) +await writeFile(output, `${JSON.stringify(await buildClientSpec(), null, 2)}\n`) +// The in-memory app keeps no open handles, but exit explicitly so the script +// never hangs on a stray timer from a transitively-imported module. +process.exit(0) diff --git a/server/app.ts b/server/app.ts index b9a5d636..21497ab1 100644 --- a/server/app.ts +++ b/server/app.ts @@ -1,6 +1,7 @@ import { release as osRelease } from 'node:os' +import { OpenAPIHono } from '@hono/zod-openapi' +import { Scalar } from '@scalar/hono-api-reference' import type { Context } from 'hono' -import { Hono } from 'hono' import { cors } from 'hono/cors' import type { Auth } from './auth' import { createDeps } from './composition' @@ -37,7 +38,6 @@ import { imageHostingDomain } from './middleware/image-hosting-domain' import { accessLog } from './middleware/logger' import type { Env } from './middleware/platform' import { platformMiddleware } from './middleware/platform' -import { downloaderOpenAPIDocument } from './openapi/downloader' import type { Platform } from './platform/interface' import { getDeployPlatform } from './runtime-platform' import type { Deps } from './usecases/deps' @@ -45,7 +45,7 @@ import { INSTANCE_TELEMETRY_CRON, reportInstanceTelemetry } from './usecases/sit import { ensureSitePublicOrigin } from './usecases/site/public-origin' export function createApp(platform: Platform, auth: Auth, deps: Deps = createDeps(platform)) { - const app = new Hono() + const app = new OpenAPIHono() const corsOrigins = getCorsOrigins(platform) app.use('/*', platformMiddleware(platform, auth)) @@ -96,7 +96,70 @@ export function createApp(platform: Platform, auth: Auth, deps: Deps = createDep return a.handler(c.req.raw) }) - app.get('/api/openapi/downloader.json', (c) => c.json(downloaderOpenAPIDocument())) + // Global OpenAPI document. Aggregates every route defined with `.openapi()` + // across all mounted sub-apps — a route appears here as soon as its resource is + // converted to OpenAPIHono, no curation needed. better-auth endpoints (incl. the + // device flow) document themselves separately at /api/auth/reference. + app.get('/api/openapi.json', async (c) => { + const doc = app.getOpenAPIDocument({ + openapi: '3.1.0', + info: { title: 'ZPan API', version: '0.1.0' }, + // Top-level tag order + descriptions; Scalar groups operations by these. + tags: [ + { name: 'Objects', description: 'Files and folders, including S3 multipart upload sessions' }, + { name: 'Events', description: 'Multiplexed server-sent event stream' }, + { name: 'Download Tasks', description: 'Remote download tasks' }, + { name: 'Downloaders', description: 'Download agents and their heartbeats' }, + ], + }) + + // Merge better-auth's own auto-generated schema (sign-in/up, organization, + // the device-authorization flow, …) into the same document. Both halves are + // generated — nothing here is a hand-maintained endpoint definition; new + // better-auth endpoints appear automatically. Its paths are relative to the + // /api/auth mount, so prefix them. + const authDoc = (await c.get('auth').api.generateOpenAPISchema()) as { + paths?: Record + components?: { schemas?: Record } + } + for (const [path, item] of Object.entries(authDoc.paths ?? {})) { + doc.paths[`/api/auth${path}`] = item as (typeof doc.paths)[string] + } + doc.components ??= {} + doc.components.schemas = { + ...(authDoc.components?.schemas as typeof doc.components.schemas), + ...doc.components.schemas, + } + + // better-auth's device-authorization plugin advertises POST /device/token as + // returning { session, user }, but its handler actually returns the OAuth + // device token { access_token, token_type, expires_in } (see better-auth's + // device-authorization/routes.mjs). Correct that one wrong response so the + // document — and the generated downloader client — match the real wire shape. + const deviceTokenJson = ( + doc.paths['/api/auth/device/token'] as + | { post?: { responses?: Record }> } } + | undefined + )?.post?.responses?.['200']?.content?.['application/json'] + if (deviceTokenJson) { + deviceTokenJson.schema = { + type: 'object', + properties: { + access_token: { type: 'string' }, + token_type: { type: 'string' }, + expires_in: { type: 'integer' }, + scope: { type: 'string' }, + }, + required: ['access_token', 'token_type', 'expires_in'], + } + } + + return c.json(doc) + }) + + // Scalar interactive API reference for the global document above. Our own + // resources live here; better-auth serves its own reference at /api/auth/reference. + app.get('/api/docs', Scalar({ url: '/api/openapi.json', title: 'ZPan API' })) app.all('/dav', (c) => c.redirect('/dav/', 308)) app.route('/dav', webdav) diff --git a/server/auth.ts b/server/auth.ts index 33f960aa..66282ff7 100644 --- a/server/auth.ts +++ b/server/auth.ts @@ -2,7 +2,7 @@ import { apiKey } from '@better-auth/api-key' import { APIError, type BetterAuthPlugin, betterAuth } from 'better-auth' import { drizzleAdapter } from 'better-auth/adapters/drizzle' import type { CaptchaOptions } from 'better-auth/plugins' -import { admin, bearer, captcha, deviceAuthorization, organization, username } from 'better-auth/plugins' +import { admin, bearer, captcha, deviceAuthorization, openAPI, organization, username } from 'better-auth/plugins' import { genericOAuth } from 'better-auth/plugins/generic-oauth' import { adminAc, memberAc, ownerAc } from 'better-auth/plugins/organization/access' import { count, eq, like } from 'drizzle-orm' @@ -249,6 +249,11 @@ export async function createAuth( ), plugins: [ admin(), + // Self-documents every better-auth endpoint (incl. the device-authorization + // flow) at GET /api/auth/reference (Scalar UI) and + // /api/auth/open-api/generate-schema. Replaces the old hand-written device + // route stubs; our own routes live in the global doc at /api/openapi.json. + openAPI(), organization({ roles: { owner: ownerAc, diff --git a/server/http/downloads/download-tasks.ts b/server/http/downloads/download-tasks.ts index cb000d02..f6c62d69 100644 --- a/server/http/downloads/download-tasks.ts +++ b/server/http/downloads/download-tasks.ts @@ -34,6 +34,7 @@ function jsonResponse(schema: z.ZodType, description: string) { } const listRoute = createRoute({ + tags: ['Download Tasks'], method: 'get', path: '/', middleware: [requirePermission('remoteDownload', 'read', { allowDownloader: true })] as const, @@ -45,6 +46,7 @@ const listRoute = createRoute({ }) const createRouteDoc = createRoute({ + tags: ['Download Tasks'], method: 'post', path: '/', middleware: [requirePermission('remoteDownload', 'create', { minTeamRole: 'editor' })] as const, @@ -58,6 +60,7 @@ const createRouteDoc = createRoute({ }) const getRoute = createRoute({ + tags: ['Download Tasks'], method: 'get', path: '/{id}', middleware: [requirePermission('remoteDownload', 'read')] as const, @@ -69,6 +72,7 @@ const getRoute = createRoute({ }) const updateRoute = createRoute({ + tags: ['Download Tasks'], method: 'patch', path: '/{id}', middleware: [requirePermission('remoteDownload', 'cancel', { allowDownloader: true })] as const, @@ -93,6 +97,7 @@ const taskErrorResponses = { } const statusRoute = createRoute({ + tags: ['Download Tasks'], method: 'put', path: '/{id}/status', middleware: [requirePermission('remoteDownload', 'cancel')] as const, @@ -107,6 +112,7 @@ const statusRoute = createRoute({ }) const attemptRoute = createRoute({ + tags: ['Download Tasks'], method: 'post', path: '/{id}/attempts', middleware: [requirePermission('remoteDownload', 'cancel')] as const, @@ -121,6 +127,7 @@ const attemptRoute = createRoute({ }) const deleteRoute = createRoute({ + tags: ['Download Tasks'], method: 'delete', path: '/{id}', middleware: [requirePermission('remoteDownload', 'cancel')] as const, diff --git a/server/http/downloads/downloaders.ts b/server/http/downloads/downloaders.ts index 35bd1ee7..46ea5645 100644 --- a/server/http/downloads/downloaders.ts +++ b/server/http/downloads/downloaders.ts @@ -37,6 +37,7 @@ function jsonResponse(schema: z.ZodType, description: string) { } const listRoute = createRoute({ + tags: ['Downloaders'], method: 'get', path: '/', middleware: [requireAdmin] as const, @@ -47,6 +48,7 @@ const listRoute = createRoute({ }) const createRouteDoc = createRoute({ + tags: ['Downloaders'], method: 'post', path: '/', middleware: [requireAdmin] as const, @@ -59,6 +61,7 @@ const createRouteDoc = createRoute({ }) const updateRoute = createRoute({ + tags: ['Downloaders'], method: 'patch', path: '/{id}', middleware: [requireAdmin] as const, @@ -73,6 +76,7 @@ const updateRoute = createRoute({ }) const deleteRoute = createRoute({ + tags: ['Downloaders'], method: 'delete', path: '/{id}', middleware: [requireAdmin] as const, @@ -84,6 +88,7 @@ const deleteRoute = createRoute({ }) const heartbeatRoute = createRoute({ + tags: ['Downloaders'], method: 'post', path: '/me/heartbeats', middleware: [requireDownloader] as const, diff --git a/server/http/events.ts b/server/http/events.ts index 57eff307..58d21e84 100644 --- a/server/http/events.ts +++ b/server/http/events.ts @@ -1,5 +1,5 @@ -import { Hono } from 'hono' -import { z } from 'zod' +import { createRoute, OpenAPIHono, z } from '@hono/zod-openapi' +import type { Context } from 'hono' import { requireAuth } from '../middleware/auth' import type { Env } from '../middleware/platform' import { type EventsMessage, streamEvents } from '../usecases/events' @@ -22,6 +22,50 @@ const eventsQuerySchema = z.object({ dtSortDir: z.enum(['asc', 'desc']).optional().catch(undefined), }) +// The wire/doc contract for the query string. Kept to lenient optional strings +// (no enum, no `.catch()`): the OpenAPI generator can't map a ZodCatch, and a +// malformed sort param must be silently ignored — never 400 an always-on stream. +// The strict coercion still happens in the handler via `eventsQuerySchema`. +const eventsQueryDocSchema = z.object({ + downloadTasks: z.string().optional().openapi({ description: 'Set to "1" to subscribe to download-task events.' }), + dtStatus: z.string().optional(), + dtCategory: z.string().optional(), + dtTag: z.string().optional(), + dtSortBy: z + .string() + .optional() + .openapi({ description: 'One of: createdAt | source | category | tags | status | progress | eta' }), + dtSortDir: z.string().optional().openapi({ description: 'asc | desc' }), +}) + +// The SSE body is a stream of text/event-stream frames, not JSON, so the schema +// is just a string. OpenAPI 3.x has no native way to type the named events of a +// single stream, so they're spelled out in the route description below. +const eventStreamRoute = createRoute({ + tags: ['Events'], + method: 'get', + path: '/', + middleware: [requireAuth] as const, + summary: 'Server-sent events stream', + description: [ + 'A single SSE connection multiplexing several domains via named events:', + '', + '- `jobs` → `{ activeCount }` — background-job set changed (always on)', + '- `notifications` → `{ unreadCount }` — unread count changed (always on)', + '- `download-tasks` → `{ items, total, page, pageSize }` — download tasks changed (opt-in via `?downloadTasks=1`)', + '- `heartbeat` → `{ at }` — keep-alive emitted when nothing changed for a while', + '- `error` → `{ message }` — a domain query failed this tick', + ].join('\n'), + request: { query: eventsQueryDocSchema }, + responses: { + 200: { + content: { 'text/event-stream': { schema: z.string() } }, + description: 'Open SSE stream of domain-change events', + }, + 401: { content: { 'application/json': { schema: z.object({ error: z.string() }) } }, description: 'Unauthorized' }, + }, +}) + // One SSE stream multiplexing several domains via named events: // event: jobs → { activeCount } background-job set changed (always on) // event: notifications → { unreadCount } unread count changed (always on) @@ -35,7 +79,7 @@ const eventsQuerySchema = z.object({ // This handler owns only the wire: it builds the ReadableStream, encodes each // domain event the usecase emits as an SSE frame, and returns the Response. All // polling / fingerprint / change-detection lives in streamEvents (usecases/events.ts). -export const events = new Hono().use(requireAuth).get('/', (c) => { +export const events = new OpenAPIHono().openapi(eventStreamRoute, ((c: Context) => { const deps = c.get('deps') const query = eventsQuerySchema.parse(c.req.query()) @@ -88,6 +132,6 @@ export const events = new Hono().use(requireAuth).get('/', (c) => { Connection: 'keep-alive', }, }) -}) +}) as never) export default events diff --git a/server/http/objects.ts b/server/http/objects.ts index bedb9f25..bb435c75 100644 --- a/server/http/objects.ts +++ b/server/http/objects.ts @@ -1,15 +1,17 @@ -import { zValidator } from '@hono/zod-validator' +import { createRoute, OpenAPIHono, z } from '@hono/zod-openapi' import type { Context } from 'hono' -import { Hono } from 'hono' import { createMiddleware } from 'hono/factory' import { ZPAN_CLOUD_URL_DEFAULT } from '../../shared/constants' import { copyObjectBodySchema, createMatterSchema, createObjectUploadSessionSchema, + objectDraftSchema, objectStatusSchema, + objectUploadSessionSchema, objectUploadStatusSchema, patchMatterSchema, + presignObjectUploadPartsResponseSchema, presignObjectUploadPartsSchema, transferMatterSchema, } from '../../shared/schemas' @@ -37,6 +39,68 @@ import { updateObject, } from '../usecases/object' +// `.openapi()` infers the handler context from the route's request schemas, but +// these handlers return many ad-hoc status/shape unions the response schemas +// don't enumerate, so each handler is cast `as never`. That erases the inferred +// context, so we re-assert the minimal surface the bodies use. Mirrors the +// pattern in http/downloads/download-tasks.ts. +type OpenAPIContext = Context & { + req: Context['req'] & { + valid(target: 'json'): unknown + } +} + +// Object output shape (StorageObject) for response docs. Response types never +// reach the frontend — callers go through unwrap() — so this only feeds the +// OpenAPI document. +const matterSchema = z + .object({ + id: z.string(), + orgId: z.string(), + alias: z.string(), + name: z.string(), + type: z.string(), + size: z.number().int(), + dirtype: z.number().int(), + parent: z.string(), + object: z.string(), + storageId: z.string(), + status: z.string(), + createdAt: z.string(), + updatedAt: z.string(), + }) + .openapi('Matter') + +const objectPageSchema = z + .object({ + items: z.array(matterSchema), + total: z.number().int(), + page: z.number().int(), + pageSize: z.number().int(), + }) + .openapi('ObjectPage') + +const errorSchema = z.object({ error: z.string() }) + +// List endpoint reads query params ad-hoc; declared here for docs + RPC typing. +// All optional so callers may send any subset. +const listObjectsQuerySchema = z.object({ + parent: z.string().optional(), + path: z.string().optional(), + status: z.string().optional(), + type: z.string().optional(), + search: z.string().optional(), + page: z.string().optional(), + pageSize: z.string().optional(), + orgId: z.string().optional(), +}) + +const json = (schema: z.ZodType, description: string) => ({ content: { 'application/json': { schema } }, description }) +const err = (description: string) => json(errorSchema, description) +const jsonBody = (schema: z.ZodType) => ({ body: { content: { 'application/json': { schema } }, required: true } }) +const idParam = z.object({ id: z.string() }) +const sessionParams = z.object({ id: z.string(), uploadSessionId: z.string() }) + // The caller acting on objects: a download-task-upload token acts on behalf of // the task creator; otherwise it is the authenticated user. function objectActor(c: Context): ObjectActor { @@ -91,270 +155,451 @@ async function objectUploadResponse(c: Context, action: () => Promise() - .use(async (c, next) => { - const principal = c.get('principal') - if (c.get('userId') || principal?.kind === 'download-task-upload') { - await next() - return - } - return c.json({ error: 'Unauthorized' }, 401) - }) - .get('/', requireTeamRole('viewer'), async (c) => { - const orgId = c.get('orgId') - if (!orgId) return c.json({ error: 'No active organization' }, 400) +const app = new OpenAPIHono() +// Blanket auth gate for every object route. Applied as a statement, not chained, +// because `.use()` returns the base Hono type and would strip `.openapi()`. +app.use(async (c, next) => { + const principal = c.get('principal') + if (c.get('userId') || principal?.kind === 'download-task-upload') { + await next() + return + } + return c.json({ error: 'Unauthorized' }, 401) +}) - const result = await listObjects(c.get('deps'), { - orgId, - userId: c.get('userId')!, - orgOverride: c.req.query('orgId'), - filters: { - parent: c.req.query('path') ?? c.req.query('parent') ?? '', - status: c.req.query('status') ?? 'active', - typeFilter: c.req.query('type'), - search: c.req.query('search'), - page: Number(c.req.query('page') ?? '1'), - pageSize: Number(c.req.query('pageSize') ?? '20'), - }, - }) - if (!result.ok) return c.json({ error: 'Forbidden' }, 403) - return c.json(result.result) - }) - .post('/', requireObjectWriteAccess, zValidator('json', createMatterSchema), async (c) => { - const orgId = c.get('orgId') - if (!orgId) return c.json({ error: 'No active organization' }, 400) +const objects = app + .openapi( + createRoute({ + tags: ['Objects'], + method: 'get', + path: '/', + middleware: [requireTeamRole('viewer')] as const, + request: { query: listObjectsQuerySchema }, + responses: { 200: json(objectPageSchema, 'Objects'), 400: err('No active organization'), 403: err('Forbidden') }, + }), + (async (c: OpenAPIContext) => { + const orgId = c.get('orgId') + if (!orgId) return c.json({ error: 'No active organization' }, 400) - try { - const result = await createObject(c.get('deps'), { orgId, actor: objectActor(c), input: c.req.valid('json') }) - if (!result.ok) { - if (result.reason === 'target_outside_authorization') - return c.json({ error: 'Target folder is outside task authorization' }, 403) - return c.json({ error: 'Storage not configured' }, 500) - } - if ('uploadUrl' in result) - return c.json( - { ...result.matter, uploadUrl: result.uploadUrl, contentDisposition: result.contentDisposition }, - 201, - ) - return c.json(result.matter, 201) - } catch (e) { - const mapped = mapDomainError(e) - if (mapped) return c.json(mapped.json, mapped.status) - throw e - } - }) - .post('/:id/uploads', requireObjectWriteAccess, zValidator('json', createObjectUploadSessionSchema), async (c) => - objectUploadResponse( - c, - () => { - const orgId = c.get('orgId') - if (!orgId) throw new ObjectUploadSessionError('not_found') - return createUploadSession(c.get('deps'), { - orgId, - objectId: c.req.param('id'), - actor: objectActor(c), - partSize: c.req.valid('json').partSize, - }) - }, - 201, - ), + const result = await listObjects(c.get('deps'), { + orgId, + userId: c.get('userId')!, + orgOverride: c.req.query('orgId'), + filters: { + parent: c.req.query('path') ?? c.req.query('parent') ?? '', + status: c.req.query('status') ?? 'active', + typeFilter: c.req.query('type'), + search: c.req.query('search'), + page: Number(c.req.query('page') ?? '1'), + pageSize: Number(c.req.query('pageSize') ?? '20'), + }, + }) + if (!result.ok) return c.json({ error: 'Forbidden' }, 403) + return c.json(result.result) + }) as never, ) - .post( - '/:id/uploads/:uploadSessionId/parts', - requireObjectWriteAccess, - zValidator('json', presignObjectUploadPartsSchema), - async (c) => + .openapi( + createRoute({ + tags: ['Objects'], + method: 'post', + path: '/', + middleware: [requireObjectWriteAccess] as const, + request: jsonBody(createMatterSchema), + responses: { + 201: json(objectDraftSchema, 'Created object draft with upload URL'), + 400: err('No active organization'), + 403: err('Forbidden'), + 409: err('Name conflict'), + 500: err('Storage not configured'), + }, + }), + (async (c: OpenAPIContext) => { + const orgId = c.get('orgId') + if (!orgId) return c.json({ error: 'No active organization' }, 400) + + try { + const result = await createObject(c.get('deps'), { + orgId, + actor: objectActor(c), + input: c.req.valid('json') as z.infer, + }) + if (!result.ok) { + if (result.reason === 'target_outside_authorization') + return c.json({ error: 'Target folder is outside task authorization' }, 403) + return c.json({ error: 'Storage not configured' }, 500) + } + if ('uploadUrl' in result) + return c.json( + { ...result.matter, uploadUrl: result.uploadUrl, contentDisposition: result.contentDisposition }, + 201, + ) + return c.json(result.matter, 201) + } catch (e) { + const mapped = mapDomainError(e) + if (mapped) return c.json(mapped.json, mapped.status) + throw e + } + }) as never, + ) + .openapi( + createRoute({ + tags: ['Objects'], + method: 'post', + path: '/{id}/uploads', + middleware: [requireObjectWriteAccess] as const, + request: { params: idParam, ...jsonBody(createObjectUploadSessionSchema) }, + responses: { + 201: json(objectUploadSessionSchema, 'Object multipart upload session'), + 400: err('Invalid upload session'), + 403: err('Forbidden'), + 404: err('Not found'), + 502: err('Storage failure'), + }, + }), + (async (c: OpenAPIContext) => + objectUploadResponse( + c, + () => { + const orgId = c.get('orgId') + if (!orgId) throw new ObjectUploadSessionError('not_found') + return createUploadSession(c.get('deps'), { + orgId, + objectId: c.req.param('id') as string, + actor: objectActor(c), + partSize: (c.req.valid('json') as z.infer).partSize, + }) + }, + 201, + )) as never, + ) + .openapi( + createRoute({ + tags: ['Objects'], + method: 'post', + path: '/{id}/uploads/{uploadSessionId}/parts', + middleware: [requireObjectWriteAccess] as const, + request: { params: sessionParams, ...jsonBody(presignObjectUploadPartsSchema) }, + responses: { + 200: json(presignObjectUploadPartsResponseSchema, 'Presigned multipart upload parts'), + 400: err('Invalid upload session'), + 403: err('Forbidden'), + 404: err('Not found'), + 502: err('Storage failure'), + }, + }), + (async (c: OpenAPIContext) => objectUploadResponse(c, () => { const orgId = c.get('orgId') if (!orgId) throw new ObjectUploadSessionError('not_found') return presignUploadSessionParts(c.get('deps'), { orgId, - objectId: c.req.param('id'), - sessionId: c.req.param('uploadSessionId'), - partNumbers: c.req.valid('json').partNumbers, + objectId: c.req.param('id') as string, + sessionId: c.req.param('uploadSessionId') as string, + partNumbers: (c.req.valid('json') as z.infer).partNumbers, }) - }), + })) as never, ) - .put( - '/:id/uploads/:uploadSessionId/status', - requireObjectWriteAccess, - zValidator('json', objectUploadStatusSchema), - async (c) => + .openapi( + createRoute({ + tags: ['Objects'], + method: 'put', + path: '/{id}/uploads/{uploadSessionId}/status', + middleware: [requireObjectWriteAccess] as const, + request: { params: sessionParams, ...jsonBody(objectUploadStatusSchema) }, + responses: { + 200: json(objectUploadSessionSchema, 'Completed object multipart upload session'), + 400: err('Invalid upload session'), + 403: err('Forbidden'), + 404: err('Not found'), + 502: err('Storage failure'), + }, + }), + (async (c: OpenAPIContext) => objectUploadResponse(c, () => { const orgId = c.get('orgId') if (!orgId) throw new ObjectUploadSessionError('not_found') return patchUploadSession(c.get('deps'), { orgId, - objectId: c.req.param('id'), - sessionId: c.req.param('uploadSessionId'), - input: { action: 'complete', parts: c.req.valid('json').parts }, + objectId: c.req.param('id') as string, + sessionId: c.req.param('uploadSessionId') as string, + input: { action: 'complete', parts: (c.req.valid('json') as z.infer).parts }, }) - }), + })) as never, ) - .delete('/:id/uploads/:uploadSessionId', requireObjectWriteAccess, async (c) => - objectUploadResponse(c, () => { - const orgId = c.get('orgId') - if (!orgId) throw new ObjectUploadSessionError('not_found') - return patchUploadSession(c.get('deps'), { - orgId, - objectId: c.req.param('id'), - sessionId: c.req.param('uploadSessionId'), - input: { action: 'abort' }, - }) + .openapi( + createRoute({ + tags: ['Objects'], + method: 'delete', + path: '/{id}/uploads/{uploadSessionId}', + middleware: [requireObjectWriteAccess] as const, + request: { params: sessionParams }, + responses: { + 200: json(objectUploadSessionSchema, 'Aborted object multipart upload session'), + 400: err('Invalid upload session'), + 403: err('Forbidden'), + 404: err('Not found'), + }, }), + (async (c: OpenAPIContext) => + objectUploadResponse(c, () => { + const orgId = c.get('orgId') + if (!orgId) throw new ObjectUploadSessionError('not_found') + return patchUploadSession(c.get('deps'), { + orgId, + objectId: c.req.param('id') as string, + sessionId: c.req.param('uploadSessionId') as string, + input: { action: 'abort' }, + }) + })) as never, ) - .get('/:id', requireTeamRole('viewer'), async (c) => { - const orgId = c.get('orgId') - if (!orgId) return c.json({ error: 'No active organization' }, 400) + .openapi( + createRoute({ + tags: ['Objects'], + method: 'get', + path: '/{id}', + middleware: [requireTeamRole('viewer')] as const, + request: { params: idParam }, + responses: { + 200: json(matterSchema.extend({ downloadUrl: z.string().optional() }), 'Object'), + 400: err('No active organization'), + 402: err('Insufficient credits'), + 404: err('Not found'), + 422: err('Traffic quota exceeded'), + }, + }), + (async (c: OpenAPIContext) => { + const orgId = c.get('orgId') + if (!orgId) return c.json({ error: 'No active organization' }, 400) - const result = await getObject(c.get('deps'), { - orgId, - objectId: c.req.param('id'), - cloudBaseUrl: cloudBaseUrl(c), - }) - if (result.ok) { - if ('downloadUrl' in result) return c.json({ ...result.matter, downloadUrl: result.downloadUrl }) - return c.json(result.matter) - } - switch (result.reason) { - case 'not_found': - return c.json({ error: 'Not found' }, 404) - case 'storage_not_found': - return c.json({ error: 'Storage not found' }, 404) - case 'quota_exceeded': - return c.json({ error: 'Traffic quota exceeded' }, 422) - case 'insufficient_credits': - return c.json({ error: 'insufficient_credits', code: 'insufficient_credits', resource: 'storage_egress' }, 402) - } - }) - .patch('/:id', requireObjectWriteAccess, zValidator('json', patchMatterSchema), async (c) => { - const orgId = c.get('orgId') - if (!orgId) return c.json({ error: 'No active organization' }, 400) - try { - const result = await updateObject(c.get('deps'), { + const result = await getObject(c.get('deps'), { orgId, - objectId: c.req.param('id'), - actorId: actorId(c), - input: c.req.valid('json'), + objectId: c.req.param('id') as string, + cloudBaseUrl: cloudBaseUrl(c), }) - if (!result.ok) return c.json({ error: 'Not found' }, 404) - return c.json(result.matter) - } catch (e) { - const mapped = mapDomainError(e) - if (mapped) return c.json(mapped.json, mapped.status) - return c.json({ error: (e as Error).message }, 400) - } - }) - // Lifecycle transitions: { status:'active' } confirms a draft or restores from - // trash (server picks by current state); { status:'trashed' } soft-deletes. - .put('/:id/status', requireObjectWriteAccess, zValidator('json', objectStatusSchema), async (c) => { - const orgId = c.get('orgId') - if (!orgId) return c.json({ error: 'No active organization' }, 400) - const objectId = c.req.param('id') - const { status, onConflict } = c.req.valid('json') - - const principal = c.get('principal') - if (principal?.kind === 'download-task-upload') { - // Upload tokens may only confirm their own draft. - if (status !== 'active') { - return c.json({ error: 'Download task upload token can only confirm uploads' }, 403) + if (result.ok) { + if ('downloadUrl' in result) return c.json({ ...result.matter, downloadUrl: result.downloadUrl }) + return c.json(result.matter) } - const authorized = await authorizeTaskUploadConfirm(c.get('deps'), { - orgId, - objectId, - taskId: principal.taskId, - downloaderId: principal.downloaderId, - targetFolder: principal.targetFolder, - }) - if (!authorized.ok) return c.json({ error: 'Forbidden' }, 403) - } - - if (status === 'trashed') { - const result = await trashObject(c.get('deps'), { orgId, objectId, actorId: actorId(c) }) - if (!result.ok) return c.json({ error: 'Not found' }, 404) - return c.json(result.matter) - } - - // status === 'active': confirm a draft, otherwise restore from trash. - try { - const confirmed = await confirmObject(c.get('deps'), { orgId, objectId, actorId: actorId(c), onConflict }) - if (confirmed.ok) return c.json(confirmed.matter) - if (confirmed.reason === 'quota_exceeded') return c.json({ error: 'Quota exceeded' }, 422) - } catch (e) { - const mapped = mapDomainError(e) - if (mapped) return c.json(mapped.json, mapped.status) - throw e - } - try { - const restored = await restoreObject(c.get('deps'), { orgId, objectId, actorId: actorId(c), onConflict }) - if (!restored.ok) return c.json({ error: 'Not found' }, 404) - return c.json(restored.matter) - } catch (e) { - const mapped = mapDomainError(e) - if (mapped) return c.json(mapped.json, mapped.status) - throw e - } - }) - .delete('/:id', requireTeamRole('editor'), async (c) => { - const orgId = c.get('orgId') - if (!orgId) return c.json({ error: 'No active organization' }, 400) - const objectId = c.req.param('id') - const result = await deleteObject(c.get('deps'), { orgId, objectId, userId: c.get('userId')! }) - if (result.ok) return c.json({ id: result.id, deleted: true, purged: result.purged }) - if (result.reason === 'not_trashed') { - // A draft (upload never confirmed) is discarded directly; a live object - // must be trashed before it can be permanently deleted. - const cancelled = await cancelObject(c.get('deps'), { orgId, objectId, actorId: actorId(c) }) - if (cancelled.ok) return c.json({ id: cancelled.id, deleted: true, purged: false }) - return c.json({ error: 'Object must be trashed before permanent deletion' }, 409) - } - return c.json({ error: 'Not found' }, 404) - }) - .post('/:id/copies', requireTeamRole('editor'), zValidator('json', copyObjectBodySchema), async (c) => { - const orgId = c.get('orgId') - if (!orgId) return c.json({ error: 'No active organization' }, 400) - - const body = c.req.valid('json') - try { - const result = await copyObject(c.get('deps'), { - orgId, - userId: c.get('userId')!, - input: { copyFrom: c.req.param('id'), parent: body.parent, onConflict: body.onConflict }, - }) - if (!result.ok) { - if (result.reason === 'storage_not_found') return c.json({ error: 'Storage not found' }, 404) - return c.json({ error: 'Not found' }, 404) - } - return c.json(result.matter, 201) - } catch (e) { - const mapped = mapDomainError(e) - if (mapped) return c.json(mapped.json, mapped.status) - throw e - } - }) - .post('/:id/transfers', requireTeamRole('viewer'), zValidator('json', transferMatterSchema), async (c) => { - const orgId = c.get('orgId') - if (!orgId) return c.json({ error: 'No active organization' }, 400) - - const result = await transferObject(c.get('deps'), { - orgId, - userId: c.get('userId')!, - objectId: c.req.param('id'), - input: c.req.valid('json'), - }) - if (!result.ok) { switch (result.reason) { - case 'same_org': - return c.json({ error: 'Target must be a different space', code: 'SAME_ORG' }, 400) case 'not_found': return c.json({ error: 'Not found' }, 404) - case 'forbidden': - return c.json({ error: 'Forbidden' }, 403) + case 'storage_not_found': + return c.json({ error: 'Storage not found' }, 404) case 'quota_exceeded': - return c.json({ error: 'Quota exceeded', code: 'QUOTA_EXCEEDED' }, 422) + return c.json({ error: 'Traffic quota exceeded' }, 422) + case 'insufficient_credits': + return c.json( + { error: 'insufficient_credits', code: 'insufficient_credits', resource: 'storage_egress' }, + 402, + ) } - } - return c.json(result.result, 201) - }) + }) as never, + ) + .openapi( + createRoute({ + tags: ['Objects'], + method: 'patch', + path: '/{id}', + middleware: [requireObjectWriteAccess] as const, + request: { params: idParam, ...jsonBody(patchMatterSchema) }, + responses: { 200: json(matterSchema, 'Updated object'), 400: err('Bad request'), 404: err('Not found') }, + }), + (async (c: OpenAPIContext) => { + const orgId = c.get('orgId') + if (!orgId) return c.json({ error: 'No active organization' }, 400) + try { + const result = await updateObject(c.get('deps'), { + orgId, + objectId: c.req.param('id') as string, + actorId: actorId(c), + input: c.req.valid('json') as z.infer, + }) + if (!result.ok) return c.json({ error: 'Not found' }, 404) + return c.json(result.matter) + } catch (e) { + const mapped = mapDomainError(e) + if (mapped) return c.json(mapped.json, mapped.status) + return c.json({ error: (e as Error).message }, 400) + } + }) as never, + ) + // Lifecycle transitions: { status:'active' } confirms a draft or restores from + // trash (server picks by current state); { status:'trashed' } soft-deletes. + .openapi( + createRoute({ + tags: ['Objects'], + method: 'put', + path: '/{id}/status', + middleware: [requireObjectWriteAccess] as const, + request: { params: idParam, ...jsonBody(objectStatusSchema) }, + responses: { + 200: json(matterSchema, 'Updated object'), + 400: err('No active organization'), + 403: err('Forbidden'), + 404: err('Not found'), + 422: err('Quota exceeded'), + }, + }), + (async (c: OpenAPIContext) => { + const orgId = c.get('orgId') + if (!orgId) return c.json({ error: 'No active organization' }, 400) + const objectId = c.req.param('id') as string + const { status, onConflict } = c.req.valid('json') as z.infer -export default app + const principal = c.get('principal') + if (principal?.kind === 'download-task-upload') { + // Upload tokens may only confirm their own draft. + if (status !== 'active') { + return c.json({ error: 'Download task upload token can only confirm uploads' }, 403) + } + const authorized = await authorizeTaskUploadConfirm(c.get('deps'), { + orgId, + objectId, + taskId: principal.taskId, + downloaderId: principal.downloaderId, + targetFolder: principal.targetFolder, + }) + if (!authorized.ok) return c.json({ error: 'Forbidden' }, 403) + } + + if (status === 'trashed') { + const result = await trashObject(c.get('deps'), { orgId, objectId, actorId: actorId(c) }) + if (!result.ok) return c.json({ error: 'Not found' }, 404) + return c.json(result.matter) + } + + // status === 'active': confirm a draft, otherwise restore from trash. + try { + const confirmed = await confirmObject(c.get('deps'), { orgId, objectId, actorId: actorId(c), onConflict }) + if (confirmed.ok) return c.json(confirmed.matter) + if (confirmed.reason === 'quota_exceeded') return c.json({ error: 'Quota exceeded' }, 422) + } catch (e) { + const mapped = mapDomainError(e) + if (mapped) return c.json(mapped.json, mapped.status) + throw e + } + try { + const restored = await restoreObject(c.get('deps'), { orgId, objectId, actorId: actorId(c), onConflict }) + if (!restored.ok) return c.json({ error: 'Not found' }, 404) + return c.json(restored.matter) + } catch (e) { + const mapped = mapDomainError(e) + if (mapped) return c.json(mapped.json, mapped.status) + throw e + } + }) as never, + ) + .openapi( + createRoute({ + tags: ['Objects'], + method: 'delete', + path: '/{id}', + middleware: [requireTeamRole('editor')] as const, + request: { params: idParam }, + responses: { + 200: json( + z.object({ id: z.string(), deleted: z.literal(true), purged: z.number().int().optional() }), + 'Deleted object', + ), + 400: err('No active organization'), + 404: err('Not found'), + 409: err('Object must be trashed before permanent deletion'), + }, + }), + (async (c: OpenAPIContext) => { + const orgId = c.get('orgId') + if (!orgId) return c.json({ error: 'No active organization' }, 400) + const objectId = c.req.param('id') as string + const result = await deleteObject(c.get('deps'), { orgId, objectId, userId: c.get('userId')! }) + if (result.ok) return c.json({ id: result.id, deleted: true, purged: result.purged }) + if (result.reason === 'not_trashed') { + // A draft (upload never confirmed) is discarded directly; a live object + // must be trashed before it can be permanently deleted. + const cancelled = await cancelObject(c.get('deps'), { orgId, objectId, actorId: actorId(c) }) + if (cancelled.ok) return c.json({ id: cancelled.id, deleted: true, purged: false }) + return c.json({ error: 'Object must be trashed before permanent deletion' }, 409) + } + return c.json({ error: 'Not found' }, 404) + }) as never, + ) + .openapi( + createRoute({ + tags: ['Objects'], + method: 'post', + path: '/{id}/copies', + middleware: [requireTeamRole('editor')] as const, + request: { params: idParam, ...jsonBody(copyObjectBodySchema) }, + responses: { + 201: json(matterSchema, 'Copied object'), + 400: err('No active organization'), + 404: err('Not found'), + }, + }), + (async (c: OpenAPIContext) => { + const orgId = c.get('orgId') + if (!orgId) return c.json({ error: 'No active organization' }, 400) + + const body = c.req.valid('json') as z.infer + try { + const result = await copyObject(c.get('deps'), { + orgId, + userId: c.get('userId')!, + input: { copyFrom: c.req.param('id') as string, parent: body.parent, onConflict: body.onConflict }, + }) + if (!result.ok) { + if (result.reason === 'storage_not_found') return c.json({ error: 'Storage not found' }, 404) + return c.json({ error: 'Not found' }, 404) + } + return c.json(result.matter, 201) + } catch (e) { + const mapped = mapDomainError(e) + if (mapped) return c.json(mapped.json, mapped.status) + throw e + } + }) as never, + ) + .openapi( + createRoute({ + tags: ['Objects'], + method: 'post', + path: '/{id}/transfers', + middleware: [requireTeamRole('viewer')] as const, + request: { params: idParam, ...jsonBody(transferMatterSchema) }, + responses: { + 201: json( + z.object({ id: z.string(), sourceDeleted: z.boolean() }).openapi('TransferResult'), + 'Transferred object', + ), + 400: err('Invalid transfer target'), + 403: err('Forbidden'), + 404: err('Not found'), + 422: err('Quota exceeded'), + }, + }), + (async (c: OpenAPIContext) => { + const orgId = c.get('orgId') + if (!orgId) return c.json({ error: 'No active organization' }, 400) + + const result = await transferObject(c.get('deps'), { + orgId, + userId: c.get('userId')!, + objectId: c.req.param('id') as string, + input: c.req.valid('json') as z.infer, + }) + if (!result.ok) { + switch (result.reason) { + case 'same_org': + return c.json({ error: 'Target must be a different space', code: 'SAME_ORG' }, 400) + case 'not_found': + return c.json({ error: 'Not found' }, 404) + case 'forbidden': + return c.json({ error: 'Forbidden' }, 403) + case 'quota_exceeded': + return c.json({ error: 'Quota exceeded', code: 'QUOTA_EXCEEDED' }, 422) + } + } + return c.json(result.result, 201) + }) as never, + ) + +export default objects diff --git a/server/openapi.test.ts b/server/openapi.test.ts new file mode 100644 index 00000000..3eed892c --- /dev/null +++ b/server/openapi.test.ts @@ -0,0 +1,60 @@ +import { describe, expect, it } from 'vitest' +import { createTestApp } from './test/setup' + +describe('global OpenAPI document', () => { + it('aggregates every OpenAPIHono route at /api/openapi.json', async () => { + const { app } = await createTestApp({ DOWNLOAD_TOKEN_SECRET: 'test-download-token-secret' }) + const res = await app.request('/api/openapi.json') + + expect(res.status).toBe(200) + const doc = (await res.json()) as { + openapi: string + paths: Record + tags?: { name: string }[] + } + expect(doc.openapi).toBe('3.1.0') + // Operations are tagged so Scalar groups them (not all under "default"). + expect(doc.paths['/api/objects']?.get?.tags).toContain('Objects') + expect(doc.paths['/api/events']?.get?.tags).toContain('Events') + expect((doc.tags ?? []).map((t) => t.name)).toEqual( + expect.arrayContaining(['Objects', 'Events', 'Download Tasks', 'Downloaders']), + ) + // Every resource already converted to `.openapi()` shows up automatically. + expect(Object.keys(doc.paths)).toEqual( + expect.arrayContaining([ + '/api/downloads/tasks', + '/api/downloads/tasks/{id}', + '/api/downloads/tasks/{id}/status', + '/api/downloads/tasks/{id}/attempts', + '/api/downloads/downloaders', + '/api/downloads/downloaders/{id}', + '/api/events', + '/api/objects', + '/api/objects/{id}', + '/api/objects/{id}/status', + '/api/objects/{id}/uploads/{uploadSessionId}/status', + ]), + ) + }) + + it('serves the Scalar reference UI at /api/docs pointing at the spec', async () => { + const { app } = await createTestApp({ DOWNLOAD_TOKEN_SECRET: 'test-download-token-secret' }) + const res = await app.request('/api/docs') + + expect(res.status).toBe(200) + expect(res.headers.get('content-type')).toContain('text/html') + const html = await res.text() + expect(html).toContain('/api/openapi.json') + }) + + it("merges better-auth's auto-generated schema (incl. the device flow) into the same doc", async () => { + const { app } = await createTestApp({ DOWNLOAD_TOKEN_SECRET: 'test-download-token-secret' }) + const res = await app.request('/api/openapi.json') + const doc = (await res.json()) as { paths: Record } + // better-auth's device-authorization endpoints come from its openAPI plugin, + // not hand-written stubs — prefixed under /api/auth. + const authPaths = Object.keys(doc.paths).filter((p) => p.startsWith('/api/auth/')) + expect(authPaths.length).toBeGreaterThan(0) + expect(authPaths.some((p) => p.includes('/device/'))).toBe(true) + }) +}) diff --git a/server/openapi/downloader.test.ts b/server/openapi/downloader.test.ts deleted file mode 100644 index 4bddf0f1..00000000 --- a/server/openapi/downloader.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { createTestApp } from '../test/setup' - -describe('downloader OpenAPI', () => { - it('serves downloader documentation from the real app route', async () => { - const { app } = await createTestApp({ DOWNLOAD_TOKEN_SECRET: 'test-download-token-secret' }) - const res = await app.request('/api/openapi/downloader.json') - - expect(res.status).toBe(200) - const doc = (await res.json()) as { paths: Record } - expect(Object.keys(doc.paths)).toEqual( - expect.arrayContaining([ - '/api/auth/device/code', - '/api/auth/device/token', - '/api/downloads/tasks', - '/api/downloads/tasks/{id}', - '/api/downloads/tasks/{id}/status', - '/api/downloads/tasks/{id}/attempts', - '/api/downloads/downloaders/me/heartbeats', - '/api/downloads/downloaders', - '/api/downloads/downloaders/{id}', - '/api/objects', - '/api/objects/{id}/status', - '/api/objects/{id}/uploads/{uploadSessionId}/status', - ]), - ) - }) -}) diff --git a/server/openapi/downloader.ts b/server/openapi/downloader.ts deleted file mode 100644 index 3103070d..00000000 --- a/server/openapi/downloader.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { createRoute, OpenAPIHono, z } from '@hono/zod-openapi' -import { - createMatterSchema, - createObjectUploadSessionSchema, - objectDraftSchema, - objectStatusSchema, - objectUploadSessionSchema, - objectUploadStatusSchema, - presignObjectUploadPartsResponseSchema, - presignObjectUploadPartsSchema, -} from '@shared/schemas' -import downloadTasks from '../http/downloads/download-tasks' -import downloaders, { downloaderSelfRoute } from '../http/downloads/downloaders' - -const errorSchema = z.object({ error: z.string() }).openapi('ErrorResponse') - -const deviceCodeRequestSchema = z - .object({ - client_id: z.string(), - scope: z.string(), - }) - .openapi('DeviceCodeRequest') - -const deviceCodeSchema = z - .object({ - device_code: z.string(), - user_code: z.string(), - verification_uri: z.string(), - verification_uri_complete: z.string(), - expires_in: z.number().int(), - interval: z.number().int(), - }) - .openapi('DeviceCode') - -const deviceTokenRequestSchema = z - .object({ - grant_type: z.string(), - device_code: z.string(), - client_id: z.string(), - }) - .openapi('DeviceTokenRequest') - -const deviceTokenSchema = z - .object({ - access_token: z.string(), - token_type: z.string(), - expires_in: z.number().int(), - scope: z.string(), - }) - .openapi('DeviceToken') - -function jsonResponse(schema: z.ZodType, description: string) { - return { - content: { - 'application/json': { - schema, - }, - }, - description, - } -} - -function mountBetterAuthDeviceRoutes(app: OpenAPIHono) { - app.openapi( - createRoute({ - method: 'post', - path: '/api/auth/device/code', - request: { - body: { - content: { 'application/json': { schema: deviceCodeRequestSchema } }, - required: true, - }, - }, - responses: { - 200: jsonResponse(deviceCodeSchema, 'Device authorization code'), - }, - }), - (c) => c.json({} as z.infer, 200), - ) - - app.openapi( - createRoute({ - method: 'post', - path: '/api/auth/device/token', - request: { - body: { - content: { 'application/json': { schema: deviceTokenRequestSchema } }, - required: true, - }, - }, - responses: { - 200: jsonResponse(deviceTokenSchema, 'Device access token'), - 400: jsonResponse(errorSchema, 'Device login error'), - }, - }), - (c) => c.json({} as z.infer, 200), - ) -} - -function mountObjectUploadRoutes(app: OpenAPIHono) { - app.openapi( - createRoute({ - method: 'post', - path: '/api/objects', - request: { - body: { - content: { 'application/json': { schema: createMatterSchema } }, - required: true, - }, - }, - responses: { - 200: jsonResponse(objectDraftSchema, 'Object draft with upload URL'), - 201: jsonResponse(objectDraftSchema, 'Object draft with upload URL'), - 403: jsonResponse(errorSchema, 'Forbidden'), - 409: jsonResponse(errorSchema, 'Name conflict'), - }, - }), - (c) => c.json({} as z.infer, 200), - ) - - app.openapi( - createRoute({ - method: 'put', - path: '/api/objects/{id}/status', - request: { - params: z.object({ id: z.string() }), - body: { - content: { 'application/json': { schema: objectStatusSchema } }, - required: true, - }, - }, - responses: { - 200: jsonResponse(objectDraftSchema, 'Confirmed object'), - 403: jsonResponse(errorSchema, 'Forbidden'), - 404: jsonResponse(errorSchema, 'Not found'), - }, - }), - (c) => c.json({} as z.infer, 200), - ) - - app.openapi( - createRoute({ - method: 'post', - path: '/api/objects/{id}/uploads', - request: { - params: z.object({ id: z.string() }), - body: { - content: { 'application/json': { schema: createObjectUploadSessionSchema } }, - required: true, - }, - }, - responses: { - 201: jsonResponse(objectUploadSessionSchema, 'Object multipart upload session'), - 400: jsonResponse(errorSchema, 'Invalid upload session'), - 403: jsonResponse(errorSchema, 'Forbidden'), - 404: jsonResponse(errorSchema, 'Not found'), - }, - }), - (c) => c.json({} as z.infer, 201), - ) - - app.openapi( - createRoute({ - method: 'post', - path: '/api/objects/{id}/uploads/{uploadSessionId}/parts', - request: { - params: z.object({ id: z.string(), uploadSessionId: z.string() }), - body: { - content: { 'application/json': { schema: presignObjectUploadPartsSchema } }, - required: true, - }, - }, - responses: { - 200: jsonResponse(presignObjectUploadPartsResponseSchema, 'Presigned multipart upload parts'), - 400: jsonResponse(errorSchema, 'Invalid upload session'), - 403: jsonResponse(errorSchema, 'Forbidden'), - 404: jsonResponse(errorSchema, 'Not found'), - }, - }), - (c) => c.json({} as z.infer, 200), - ) - - app.openapi( - createRoute({ - method: 'put', - path: '/api/objects/{id}/uploads/{uploadSessionId}/status', - request: { - params: z.object({ id: z.string(), uploadSessionId: z.string() }), - body: { - content: { 'application/json': { schema: objectUploadStatusSchema } }, - required: true, - }, - }, - responses: { - 200: jsonResponse(objectUploadSessionSchema, 'Completed object multipart upload session'), - 400: jsonResponse(errorSchema, 'Invalid upload session'), - 403: jsonResponse(errorSchema, 'Forbidden'), - 404: jsonResponse(errorSchema, 'Not found'), - }, - }), - (c) => c.json({} as z.infer, 200), - ) - - app.openapi( - createRoute({ - method: 'delete', - path: '/api/objects/{id}/uploads/{uploadSessionId}', - request: { - params: z.object({ id: z.string(), uploadSessionId: z.string() }), - }, - responses: { - 200: jsonResponse(objectUploadSessionSchema, 'Aborted object multipart upload session'), - 400: jsonResponse(errorSchema, 'Invalid upload session'), - 403: jsonResponse(errorSchema, 'Forbidden'), - 404: jsonResponse(errorSchema, 'Not found'), - }, - }), - (c) => c.json({} as z.infer, 200), - ) -} - -export function createDownloaderOpenAPIApp() { - const app = new OpenAPIHono() - mountBetterAuthDeviceRoutes(app) - app.route('/api/downloads/tasks', downloadTasks) - app.route('/api/downloads/downloaders', downloaderSelfRoute) - app.route('/api/downloads/downloaders', downloaders) - mountObjectUploadRoutes(app) - return app -} - -export function downloaderOpenAPIDocument() { - return createDownloaderOpenAPIApp().getOpenAPIDocument({ - openapi: '3.0.0', - info: { - title: 'ZPan Downloader API', - version: '0.1.0', - }, - }) -}