From 909a559e80e7c6950f71f7711e57012c17c66da2 Mon Sep 17 00:00:00 2001 From: Brian O'Connor Date: Fri, 1 Aug 2025 12:53:12 +0200 Subject: [PATCH] updating version, implementing DRS writeback using Claude Code --- CLAUDE.md | 90 +++++++++++++++++++ .../components/responses/400BadRequest.yaml | 18 +++- openapi/components/responses/409Conflict.yaml | 17 ++++ .../responses/422UnprocessableEntity.yaml | 17 ++++ .../schemas/AuthorizedLocation.yaml | 29 ++++++ .../schemas/CreateObjectRequest.yaml | 43 +++++++++ .../schemas/CreateObjectResponse.yaml | 26 ++++++ openapi/components/schemas/DrsService.yaml | 2 + .../schemas/FinalizeObjectRequest.yaml | 24 +++++ .../components/schemas/StorageLocation.yaml | 38 ++++++++ .../schemas/UpdateObjectRequest.yaml | 38 ++++++++ openapi/components/schemas/UploadURL.yaml | 39 ++++++++ .../components/schemas/UploadURLRequest.yaml | 8 ++ .../components/schemas/UploadURLResponse.yaml | 6 ++ .../schemas/WriteAuthorizations.yaml | 9 ++ .../components/schemas/WriteCapabilities.yaml | 28 ++++++ openapi/data_repository_service.openapi.yaml | 38 +++++++- openapi/paths/objects.yaml | 34 +++++++ openapi/paths/objects@{object_id}.yaml | 71 +++++++++++++++ .../paths/objects@{object_id}@finalize.yaml | 49 ++++++++++ .../objects@{object_id}@upload-urls.yaml | 43 +++++++++ openapi/paths/write@authorizations.yaml | 23 +++++ openapi/tags/WriteOperations.md | 69 ++++++++++++++ 23 files changed, 755 insertions(+), 4 deletions(-) create mode 100644 CLAUDE.md create mode 100644 openapi/components/responses/409Conflict.yaml create mode 100644 openapi/components/responses/422UnprocessableEntity.yaml create mode 100644 openapi/components/schemas/AuthorizedLocation.yaml create mode 100644 openapi/components/schemas/CreateObjectRequest.yaml create mode 100644 openapi/components/schemas/CreateObjectResponse.yaml create mode 100644 openapi/components/schemas/FinalizeObjectRequest.yaml create mode 100644 openapi/components/schemas/StorageLocation.yaml create mode 100644 openapi/components/schemas/UpdateObjectRequest.yaml create mode 100644 openapi/components/schemas/UploadURL.yaml create mode 100644 openapi/components/schemas/UploadURLRequest.yaml create mode 100644 openapi/components/schemas/UploadURLResponse.yaml create mode 100644 openapi/components/schemas/WriteAuthorizations.yaml create mode 100644 openapi/components/schemas/WriteCapabilities.yaml create mode 100644 openapi/paths/objects.yaml create mode 100644 openapi/paths/objects@{object_id}@finalize.yaml create mode 100644 openapi/paths/objects@{object_id}@upload-urls.yaml create mode 100644 openapi/paths/write@authorizations.yaml create mode 100644 openapi/tags/WriteOperations.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..0c8ca670 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,90 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This repository contains the GA4GH Data Repository Service (DRS) API schema definitions. DRS provides a generic interface to data repositories, allowing data consumers to access data in a standardized way regardless of where it's stored. The API defines a system for mapping logical IDs to means for physically retrieving data. + +## Development Commands + +### Documentation Building +```bash +# Install required tools +npm install -g @redocly/openapi-cli redoc-cli +npm install -g @ga4gh/gh-openapi-docs@0.2.2-rc3 + +# Build documentation (used in CI) +gh-openapi-docs +``` + +### OpenAPI Validation +```bash +# Validate OpenAPI specification +openapi-cli validate openapi/data_repository_service.openapi.yaml +``` + +## Architecture & Structure + +### Core Components + +- **`openapi/data_repository_service.openapi.yaml`**: Main OpenAPI 3.0.3 specification file +- **`openapi/components/`**: Reusable OpenAPI components organized by type: + - `schemas/`: Core data models (DrsObject, AccessMethod, AccessURL, etc.) + - `responses/`: Standard HTTP response definitions + - `parameters/`: Reusable path/query parameters + - `paths/`: API endpoint definitions +- **`openapi/tags/`**: Markdown documentation files for API sections + +### Key Data Models + +- **DrsObject**: Core entity representing a data object with metadata, checksums, and access methods +- **AccessMethod**: Defines how to access data (s3, gs, https, file, etc.) with URLs or access IDs +- **AccessURL**: Contains the actual URL and optional headers for data retrieval +- **Checksum**: Data integrity verification information + +### API Endpoints Structure + +- `/objects/{object_id}`: GET/POST/OPTIONS for object metadata +- `/objects/{object_id}/access/{access_id}`: GET/POST for access URLs +- `/bulkobjects/{object_id}`: Bulk operations for multiple objects +- `/service-info`: Service metadata endpoint + +## Development Workflow + +### Branch Strategy (HubFlow) + +- **`master`**: Production releases only +- **`develop`**: Stable development branch (all PRs target this) +- **Feature branches**: `feature/issue--` +- **Release branches**: `release/` or `release/drs-` + +### Making Changes + +1. Create issue in GitHub to track work +2. Create feature branch from `develop` following naming convention +3. Make changes to OpenAPI YAML files +4. Travis CI will automatically build and validate on push +5. Submit PR to `develop` branch +6. PRs require review and driver project voting for approval + +### File Organization + +- OpenAPI components are split into separate YAML files for maintainability +- Documentation is embedded in markdown files referenced from the main spec +- All schema changes must include proper documentation updates + +## CI/CD Pipeline + +- **Travis CI** builds documentation and validates OpenAPI spec +- **GitHub Pages** deployment for documentation hosting +- Preview builds available for all feature branches at: `ga4gh.github.io/data-repository-service-schemas/preview//` +- Production docs at: `ga4gh.github.io/data-repository-service-schemas/docs/` + +## Validation & Standards + +- OpenAPI 3.0.3 specification compliance required +- Two-space indentation, no tabs +- 80-character line limit +- All new schemas require normative documentation +- Security considerations must be documented for any auth-related changes \ No newline at end of file diff --git a/openapi/components/responses/400BadRequest.yaml b/openapi/components/responses/400BadRequest.yaml index 79dbd0db..0ddb5bb5 100644 --- a/openapi/components/responses/400BadRequest.yaml +++ b/openapi/components/responses/400BadRequest.yaml @@ -2,4 +2,20 @@ description: The request is malformed. content: application/json: schema: - $ref: '../schemas/Error.yaml' \ No newline at end of file + $ref: '../schemas/Error.yaml' + examples: + missing_required_field: + summary: Missing required field + value: + msg: "Missing required field: 'size'" + status_code: 400 + invalid_checksum: + summary: Invalid checksum format + value: + msg: "Invalid checksum format. Expected format: '{algorithm}:{checksum}'" + status_code: 400 + negative_size: + summary: Invalid size value + value: + msg: "Size must be a positive integer" + status_code: 400 \ No newline at end of file diff --git a/openapi/components/responses/409Conflict.yaml b/openapi/components/responses/409Conflict.yaml new file mode 100644 index 00000000..c78a0c2e --- /dev/null +++ b/openapi/components/responses/409Conflict.yaml @@ -0,0 +1,17 @@ +description: |- + The request conflicts with the current state of the resource +content: + application/json: + schema: + $ref: '../schemas/Error.yaml' + examples: + object_exists: + summary: Object already exists + value: + msg: "DRS object with ID 'abc123' already exists" + status_code: 409 + wrong_state: + summary: Object in wrong state for operation + value: + msg: "Cannot finalize object in state 'available'. Object must be in 'pending' state." + status_code: 409 \ No newline at end of file diff --git a/openapi/components/responses/422UnprocessableEntity.yaml b/openapi/components/responses/422UnprocessableEntity.yaml new file mode 100644 index 00000000..77b10712 --- /dev/null +++ b/openapi/components/responses/422UnprocessableEntity.yaml @@ -0,0 +1,17 @@ +description: |- + The request was well-formed but was unable to be followed due to semantic errors (e.g., checksum validation failed) +content: + application/json: + schema: + $ref: '../schemas/Error.yaml' + examples: + checksum_mismatch: + summary: Checksum validation failed + value: + msg: "Checksum validation failed: expected md5:d41d8cd98f00b204e9800998ecf8427e but got md5:5d41402abc4b2a76b9719d911017c592" + status_code: 422 + invalid_storage_location: + summary: Invalid storage location + value: + msg: "Storage location 'invalid-location' does not exist or is not accessible" + status_code: 422 \ No newline at end of file diff --git a/openapi/components/schemas/AuthorizedLocation.yaml b/openapi/components/schemas/AuthorizedLocation.yaml new file mode 100644 index 00000000..52d1da6b --- /dev/null +++ b/openapi/components/schemas/AuthorizedLocation.yaml @@ -0,0 +1,29 @@ +type: object +required: + - storage_location_id + - permissions +properties: + storage_location_id: + type: string + description: The storage location identifier this authorization applies to + example: "us-east-1-s3" + permissions: + type: array + items: + type: string + enum: + - create + - update + - delete + description: Write permissions granted for this storage location + example: ["create", "update", "delete"] + quota_remaining: + type: integer + format: int64 + description: Remaining storage quota in bytes for this location (null means no quota limit) + example: 1073741824000 + quota_total: + type: integer + format: int64 + description: Total storage quota in bytes for this location + example: 5497558138880 \ No newline at end of file diff --git a/openapi/components/schemas/CreateObjectRequest.yaml b/openapi/components/schemas/CreateObjectRequest.yaml new file mode 100644 index 00000000..6012ea9b --- /dev/null +++ b/openapi/components/schemas/CreateObjectRequest.yaml @@ -0,0 +1,43 @@ +type: object +required: + - size + - checksums + - target_storage_location +properties: + name: + type: string + description: |- + A string that can be used to name a `DrsObject`. + This string is made up of uppercase and lowercase letters, decimal digits, hyphen, period, and underscore [A-Za-z0-9.-_]. See http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282[portable filenames]. + example: "sample-data.bam" + size: + type: integer + format: int64 + description: Size of the object in bytes + example: 1073741824 + checksums: + type: array + items: + $ref: './Checksum.yaml' + minItems: 1 + description: The checksums of the object. At least one checksum must be provided. + target_storage_location: + type: string + description: The storage location where this object should be uploaded + example: "us-east-1-s3" + description: + type: string + description: A human readable description of the object + example: "Sample BAM file from analysis pipeline" + mime_type: + type: string + description: A string providing the mime-type of the object + example: "application/octet-stream" + aliases: + type: array + items: + type: string + description: A list of strings that can be used to find other metadata about this object from external metadata sources + version: + type: string + description: A string representing a version of this object \ No newline at end of file diff --git a/openapi/components/schemas/CreateObjectResponse.yaml b/openapi/components/schemas/CreateObjectResponse.yaml new file mode 100644 index 00000000..db50a913 --- /dev/null +++ b/openapi/components/schemas/CreateObjectResponse.yaml @@ -0,0 +1,26 @@ +type: object +required: + - object + - upload_url +properties: + object: + allOf: + - $ref: './DrsObject.yaml' + - type: object + properties: + state: + type: string + enum: + - pending + - uploading + - available + - failed + - deleted + description: Current state of the DRS object + example: "pending" + target_storage_location: + type: string + description: The target storage location for this object + example: "us-east-1-s3" + upload_url: + $ref: './UploadURL.yaml' \ No newline at end of file diff --git a/openapi/components/schemas/DrsService.yaml b/openapi/components/schemas/DrsService.yaml index cca41c35..7e0d4126 100644 --- a/openapi/components/schemas/DrsService.yaml +++ b/openapi/components/schemas/DrsService.yaml @@ -29,6 +29,8 @@ properties: totalObjectSize: type: integer description: The total size of all objects in this DRS service in bytes. As a general best practice, file bytes are counted for each unique file and not cloud mirrors or other redundant copies. + writeCapabilities: + $ref: './WriteCapabilities.yaml' diff --git a/openapi/components/schemas/FinalizeObjectRequest.yaml b/openapi/components/schemas/FinalizeObjectRequest.yaml new file mode 100644 index 00000000..37745932 --- /dev/null +++ b/openapi/components/schemas/FinalizeObjectRequest.yaml @@ -0,0 +1,24 @@ +type: object +required: + - uploaded_locations +properties: + uploaded_locations: + type: array + items: + type: object + required: + - storage_location_id + - checksums + properties: + storage_location_id: + type: string + description: The storage location where data was uploaded + example: "us-east-1-s3" + checksums: + type: array + items: + $ref: './Checksum.yaml' + minItems: 1 + description: Checksums computed after upload to verify integrity + minItems: 1 + description: List of storage locations where the object was successfully uploaded \ No newline at end of file diff --git a/openapi/components/schemas/StorageLocation.yaml b/openapi/components/schemas/StorageLocation.yaml new file mode 100644 index 00000000..f7f8ced7 --- /dev/null +++ b/openapi/components/schemas/StorageLocation.yaml @@ -0,0 +1,38 @@ +type: object +required: + - id + - type +properties: + id: + type: string + description: Unique identifier for this storage location + example: "us-east-1-s3" + type: + type: string + enum: + - s3 + - gs + - azure + - file + - ftp + - gsiftp + - globus + - https + description: Storage backend type + example: "s3" + region: + type: string + description: Geographic region or availability zone + example: "us-east-1" + description: + type: string + description: Human-readable description of this storage location + example: "AWS S3 US East 1" + public: + type: boolean + description: Whether this location allows public read access without authentication + example: false + endpoint: + type: string + description: Custom endpoint URL for this storage location (for non-standard cloud providers) + example: "https://s3.us-east-1.amazonaws.com" \ No newline at end of file diff --git a/openapi/components/schemas/UpdateObjectRequest.yaml b/openapi/components/schemas/UpdateObjectRequest.yaml new file mode 100644 index 00000000..956feb09 --- /dev/null +++ b/openapi/components/schemas/UpdateObjectRequest.yaml @@ -0,0 +1,38 @@ +type: object +properties: + name: + type: string + description: |- + A string that can be used to name a `DrsObject`. + This string is made up of uppercase and lowercase letters, decimal digits, hyphen, period, and underscore [A-Za-z0-9.-_]. See http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282[portable filenames]. + example: "updated-sample-data.bam" + size: + type: integer + format: int64 + description: New size of the object in bytes (triggers content update) + example: 2147483648 + checksums: + type: array + items: + $ref: './Checksum.yaml' + description: New checksums of the object (triggers content update) + target_storage_location: + type: string + description: New target storage location (triggers content update) + example: "europe-gcs" + description: + type: string + description: A human readable description of the object + example: "Updated sample BAM file from analysis pipeline" + mime_type: + type: string + description: A string providing the mime-type of the object + example: "application/octet-stream" + aliases: + type: array + items: + type: string + description: A list of strings that can be used to find other metadata about this object from external metadata sources + version: + type: string + description: A string representing a version of this object \ No newline at end of file diff --git a/openapi/components/schemas/UploadURL.yaml b/openapi/components/schemas/UploadURL.yaml new file mode 100644 index 00000000..b47c0d61 --- /dev/null +++ b/openapi/components/schemas/UploadURL.yaml @@ -0,0 +1,39 @@ +type: object +required: + - storage_location_id + - url + - method +properties: + storage_location_id: + type: string + description: The storage location this upload URL corresponds to + example: "us-east-1-s3" + url: + type: string + format: uri + description: Presigned URL for direct upload to storage backend + example: "https://s3.amazonaws.com/bucket/abc123?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=..." + method: + type: string + enum: + - PUT + - POST + description: HTTP method to use for upload + example: "PUT" + headers: + type: object + additionalProperties: + type: string + description: Required headers for the upload request + example: + Content-Type: "application/octet-stream" + expires: + type: string + format: date-time + description: When this upload URL expires + example: "2025-08-01T11:00:00Z" + form_data: + type: object + additionalProperties: + type: string + description: Form data fields required for POST uploads (used with some cloud storage multipart uploads) \ No newline at end of file diff --git a/openapi/components/schemas/UploadURLRequest.yaml b/openapi/components/schemas/UploadURLRequest.yaml new file mode 100644 index 00000000..8c4f48fb --- /dev/null +++ b/openapi/components/schemas/UploadURLRequest.yaml @@ -0,0 +1,8 @@ +type: object +required: + - storage_location_id +properties: + storage_location_id: + type: string + description: The storage location to request an upload URL for + example: "europe-gcs" \ No newline at end of file diff --git a/openapi/components/schemas/UploadURLResponse.yaml b/openapi/components/schemas/UploadURLResponse.yaml new file mode 100644 index 00000000..4939beb5 --- /dev/null +++ b/openapi/components/schemas/UploadURLResponse.yaml @@ -0,0 +1,6 @@ +type: object +required: + - upload_url +properties: + upload_url: + $ref: './UploadURL.yaml' \ No newline at end of file diff --git a/openapi/components/schemas/WriteAuthorizations.yaml b/openapi/components/schemas/WriteAuthorizations.yaml new file mode 100644 index 00000000..e693f3b9 --- /dev/null +++ b/openapi/components/schemas/WriteAuthorizations.yaml @@ -0,0 +1,9 @@ +type: object +required: + - authorized_locations +properties: + authorized_locations: + type: array + items: + $ref: './AuthorizedLocation.yaml' + description: List of storage locations the authenticated user can write to \ No newline at end of file diff --git a/openapi/components/schemas/WriteCapabilities.yaml b/openapi/components/schemas/WriteCapabilities.yaml new file mode 100644 index 00000000..b99ca82f --- /dev/null +++ b/openapi/components/schemas/WriteCapabilities.yaml @@ -0,0 +1,28 @@ +type: object +required: + - supported +properties: + supported: + type: boolean + description: Whether this server supports write operations + example: true + storage_locations: + type: array + items: + $ref: './StorageLocation.yaml' + description: Available storage locations for write operations + max_object_size: + type: integer + format: int64 + description: Maximum size in bytes for uploaded objects (null means no limit) + example: 5497558138880 + supported_checksums: + type: array + items: + type: string + description: Supported checksum algorithms for validation + example: ["md5", "sha256", "crc32c"] + upload_url_ttl: + type: integer + description: Default time-to-live for upload URLs in seconds + example: 3600 \ No newline at end of file diff --git a/openapi/data_repository_service.openapi.yaml b/openapi/data_repository_service.openapi.yaml index 033f42e9..c7909ce4 100644 --- a/openapi/data_repository_service.openapi.yaml +++ b/openapi/data_repository_service.openapi.yaml @@ -1,7 +1,7 @@ -openapi: 3.0.3 +openapi: 3.0.4 info: title: Data Repository Service - version: 1.5.0 + version: 1.6.0-draft x-logo: url: 'https://www.ga4gh.org/wp-content/themes/ga4gh/dist/assets/svg/logos/logo-full-color.svg' termsOfService: 'https://www.ga4gh.org/terms-and-conditions/' @@ -34,6 +34,9 @@ tags: - name: Authorization & Authentication description: $ref: ./tags/Auth.md + - name: Write Operations + description: + $ref: ./tags/WriteOperations.md # Operations - name: Objects @@ -64,6 +67,22 @@ tags: x-displayName: Error description: | + - name: StorageLocationModel + x-displayName: StorageLocation + description: | + + - name: WriteCapabilitiesModel + x-displayName: WriteCapabilities + description: | + + - name: UploadURLModel + x-displayName: UploadURL + description: | + + - name: AuthorizedLocationModel + x-displayName: AuthorizedLocation + description: | + # Appendices - name: Motivation @@ -90,6 +109,7 @@ x-tagGroups: - Introduction - DRS API Principles - Authorization & Authentication + - Write Operations - name: Operations tags: - Objects @@ -102,6 +122,10 @@ x-tagGroups: - ContentsObjectModel - DrsObjectModel - ErrorModel + - StorageLocationModel + - WriteCapabilitiesModel + - UploadURLModel + - AuthorizedLocationModel - name: Appendices tags: - Motivation @@ -116,11 +140,19 @@ paths: /objects/{object_id}: $ref: ./paths/objects@{object_id}.yaml /objects: - $ref: ./paths/bulkobjects@{object_id}.yaml + $ref: ./paths/objects.yaml /objects/{object_id}/access/{access_id}: $ref: ./paths/objects@{object_id}@access@{access_id}.yaml + /objects/{object_id}/upload-urls: + $ref: ./paths/objects@{object_id}@upload-urls.yaml + /objects/{object_id}/finalize: + $ref: ./paths/objects@{object_id}@finalize.yaml /objects/access: $ref: ./paths/bulkobjects@access@{access_id}.yaml + /bulk/objects: + $ref: ./paths/bulkobjects@{object_id}.yaml + /write/authorizations: + $ref: ./paths/write@authorizations.yaml components: securitySchemes: BasicAuth: diff --git a/openapi/paths/objects.yaml b/openapi/paths/objects.yaml new file mode 100644 index 00000000..3abb0f12 --- /dev/null +++ b/openapi/paths/objects.yaml @@ -0,0 +1,34 @@ +post: + summary: Create a new DRS object + description: |- + Initialize a new DRS object with metadata and receive upload URLs for the target storage location. + The object will be in 'pending' state until finalized after successful upload. + operationId: CreateObject + tags: + - Objects + requestBody: + content: + application/json: + schema: + $ref: '../components/schemas/CreateObjectRequest.yaml' + required: true + responses: + '201': + description: The DRS object was successfully created and upload URL provided + content: + application/json: + schema: + $ref: '../components/schemas/CreateObjectResponse.yaml' + '400': + $ref: '../components/responses/400BadRequest.yaml' + '401': + $ref: '../components/responses/401Unauthorized.yaml' + '403': + $ref: '../components/responses/403Forbidden.yaml' + '413': + $ref: '../components/responses/413RequestTooLarge.yaml' + '500': + $ref: '../components/responses/500InternalServerError.yaml' + security: + - BearerAuth: [] + - BasicAuth: [] \ No newline at end of file diff --git a/openapi/paths/objects@{object_id}.yaml b/openapi/paths/objects@{object_id}.yaml index d7fee2a7..da817203 100644 --- a/openapi/paths/objects@{object_id}.yaml +++ b/openapi/paths/objects@{object_id}.yaml @@ -82,3 +82,74 @@ post: - $ref: '../components/parameters/ObjectId.yaml' requestBody: $ref: '../components/requestBodies/PostObjectBody.yaml' + +put: + summary: Update a DRS object + description: |- + Update a DRS object's metadata. If size, checksums, or target_storage_location are changed, + this triggers a content update and the object will transition to 'pending' state with new upload URLs provided. + Metadata-only updates (name, description, etc.) do not change the object state. + operationId: UpdateObject + tags: + - Objects + parameters: + - $ref: '../components/parameters/ObjectId.yaml' + requestBody: + content: + application/json: + schema: + $ref: '../components/schemas/UpdateObjectRequest.yaml' + required: true + responses: + '200': + description: The DRS object was successfully updated (metadata only) + content: + application/json: + schema: + $ref: '../components/schemas/DrsObject.yaml' + '201': + description: The DRS object update requires content change - upload URL provided + content: + application/json: + schema: + $ref: '../components/schemas/CreateObjectResponse.yaml' + '400': + $ref: '../components/responses/400BadRequest.yaml' + '401': + $ref: '../components/responses/401Unauthorized.yaml' + '403': + $ref: '../components/responses/403Forbidden.yaml' + '404': + $ref: '../components/responses/404NotFoundDrsObject.yaml' + '413': + $ref: '../components/responses/413RequestTooLarge.yaml' + '500': + $ref: '../components/responses/500InternalServerError.yaml' + security: + - BearerAuth: [] + - BasicAuth: [] + +delete: + summary: Delete a DRS object + description: |- + Delete a DRS object and all associated data from storage locations. + This operation is irreversible. + operationId: DeleteObject + tags: + - Objects + parameters: + - $ref: '../components/parameters/ObjectId.yaml' + responses: + '204': + description: The DRS object was successfully deleted + '401': + $ref: '../components/responses/401Unauthorized.yaml' + '403': + $ref: '../components/responses/403Forbidden.yaml' + '404': + $ref: '../components/responses/404NotFoundDrsObject.yaml' + '500': + $ref: '../components/responses/500InternalServerError.yaml' + security: + - BearerAuth: [] + - BasicAuth: [] diff --git a/openapi/paths/objects@{object_id}@finalize.yaml b/openapi/paths/objects@{object_id}@finalize.yaml new file mode 100644 index 00000000..ad7e7795 --- /dev/null +++ b/openapi/paths/objects@{object_id}@finalize.yaml @@ -0,0 +1,49 @@ +post: + summary: Finalize a DRS object after upload + description: |- + Mark a DRS object as finalized after successful upload(s) to storage location(s). + The server will verify checksums and transition the object to 'available' state. + Access methods will be populated based on uploaded locations. + operationId: FinalizeObject + tags: + - Objects + parameters: + - $ref: '../components/parameters/ObjectId.yaml' + requestBody: + content: + application/json: + schema: + $ref: '../components/schemas/FinalizeObjectRequest.yaml' + required: true + responses: + '200': + description: The DRS object was successfully finalized and is now available + content: + application/json: + schema: + $ref: '../components/schemas/DrsObject.yaml' + '400': + $ref: '../components/responses/400BadRequest.yaml' + '401': + $ref: '../components/responses/401Unauthorized.yaml' + '403': + $ref: '../components/responses/403Forbidden.yaml' + '404': + $ref: '../components/responses/404NotFoundDrsObject.yaml' + '409': + description: Object is not in a state that can be finalized + content: + application/json: + schema: + $ref: '../components/schemas/Error.yaml' + '422': + description: Checksum verification failed or other validation error + content: + application/json: + schema: + $ref: '../components/schemas/Error.yaml' + '500': + $ref: '../components/responses/500InternalServerError.yaml' + security: + - BearerAuth: [] + - BasicAuth: [] \ No newline at end of file diff --git a/openapi/paths/objects@{object_id}@upload-urls.yaml b/openapi/paths/objects@{object_id}@upload-urls.yaml new file mode 100644 index 00000000..710ca733 --- /dev/null +++ b/openapi/paths/objects@{object_id}@upload-urls.yaml @@ -0,0 +1,43 @@ +post: + summary: Request additional upload URLs + description: |- + Request an upload URL for an additional storage location for a pending DRS object. + This allows uploading the same object to multiple locations before finalizing. + Can only be used when the object is in 'pending' state. + operationId: RequestUploadURL + tags: + - Objects + parameters: + - $ref: '../components/parameters/ObjectId.yaml' + requestBody: + content: + application/json: + schema: + $ref: '../components/schemas/UploadURLRequest.yaml' + required: true + responses: + '200': + description: Upload URL successfully generated + content: + application/json: + schema: + $ref: '../components/schemas/UploadURLResponse.yaml' + '400': + $ref: '../components/responses/400BadRequest.yaml' + '401': + $ref: '../components/responses/401Unauthorized.yaml' + '403': + $ref: '../components/responses/403Forbidden.yaml' + '404': + $ref: '../components/responses/404NotFoundDrsObject.yaml' + '409': + description: Object is not in a state that allows additional upload URLs + content: + application/json: + schema: + $ref: '../components/schemas/Error.yaml' + '500': + $ref: '../components/responses/500InternalServerError.yaml' + security: + - BearerAuth: [] + - BasicAuth: [] \ No newline at end of file diff --git a/openapi/paths/write@authorizations.yaml b/openapi/paths/write@authorizations.yaml new file mode 100644 index 00000000..46d58147 --- /dev/null +++ b/openapi/paths/write@authorizations.yaml @@ -0,0 +1,23 @@ +get: + summary: Get write authorizations for authenticated user + description: |- + Returns the storage locations and permissions available to the authenticated user + for write operations. This should be called before attempting to create or update objects + to determine which storage locations are accessible. + operationId: GetWriteAuthorizations + tags: + - Objects + responses: + '200': + description: Write authorizations successfully retrieved + content: + application/json: + schema: + $ref: '../components/schemas/WriteAuthorizations.yaml' + '401': + $ref: '../components/responses/401Unauthorized.yaml' + '500': + $ref: '../components/responses/500InternalServerError.yaml' + security: + - BearerAuth: [] + - BasicAuth: [] \ No newline at end of file diff --git a/openapi/tags/WriteOperations.md b/openapi/tags/WriteOperations.md new file mode 100644 index 00000000..b5509af5 --- /dev/null +++ b/openapi/tags/WriteOperations.md @@ -0,0 +1,69 @@ +# DRS Write Operations + +DRS 1.6 introduces optional write operations that allow clients to create, update, and delete DRS objects. These operations are designed to work with cloud storage systems through a two-phase upload process. + +## Write Capability Discovery + +Servers advertise write support through the `service-info` endpoint in the `drs.writeCapabilities` section: + +- `supported`: Whether write operations are available +- `storage_locations`: Available storage backends (S3, GCS, Azure, etc.) +- `max_object_size`: Maximum file size limit +- `supported_checksums`: Checksum algorithms for validation + +## Authorization Model + +Write operations require authentication and use a permission-based authorization model: + +- `GET /write/authorizations` returns storage locations accessible to the authenticated user +- Each location specifies available permissions: `create`, `update`, `delete` +- Quota information helps clients understand storage limits + +## Two-Phase Upload Process + +### Phase 1: Initialize Object +- `POST /objects` creates a DRS object in `pending` state +- Server returns presigned upload URL for the specified storage location +- Object metadata is stored but content is not yet available + +### Phase 2: Upload and Finalize +- Client uploads data directly to cloud storage using provided URL +- `POST /objects/{id}/finalize` marks upload complete with checksum verification +- Object transitions to `available` state with populated access methods + +## Multi-Location Support + +Objects can be uploaded to multiple storage locations for redundancy: + +- `POST /objects/{id}/upload-urls` requests additional upload URLs +- Each location upload is tracked independently +- Finalize operation reports all successfully uploaded locations + +## Object States + +- `pending`: Object created, awaiting upload +- `uploading`: Upload in progress (optional state) +- `available`: Upload complete and verified +- `failed`: Upload or validation failed +- `deleted`: Object has been removed + +## Update Operations + +- Metadata-only updates (name, description) don't change object state +- Content updates (size, checksums) trigger new upload process +- `PUT /objects/{id}` returns upload URLs if content change detected + +## Error Handling + +- `400 Bad Request`: Malformed request data +- `409 Conflict`: Object state conflicts (e.g., already exists) +- `422 Unprocessable Entity`: Checksum validation failures +- `413 Request Too Large`: Exceeds server size limits + +## Best Practices + +- Always check write authorizations before creating objects +- Verify checksums locally before finalizing uploads +- Handle upload URL expiration gracefully +- Use appropriate storage locations based on access patterns +- Monitor quota usage to avoid hitting limits \ No newline at end of file