diff --git a/openapi/components/requestBodies/AccessMethodUpdateBody.yaml b/openapi/components/requestBodies/AccessMethodUpdateBody.yaml new file mode 100644 index 00000000..3691a481 --- /dev/null +++ b/openapi/components/requestBodies/AccessMethodUpdateBody.yaml @@ -0,0 +1,6 @@ +description: Request body for updating access methods of a DRS object +required: true +content: + application/json: + schema: + $ref: '../schemas/AccessMethodUpdateRequest.yaml' \ No newline at end of file diff --git a/openapi/components/requestBodies/BulkAccessMethodUpdateBody.yaml b/openapi/components/requestBodies/BulkAccessMethodUpdateBody.yaml new file mode 100644 index 00000000..7a0f70f0 --- /dev/null +++ b/openapi/components/requestBodies/BulkAccessMethodUpdateBody.yaml @@ -0,0 +1,6 @@ +description: Request body for bulk updating access methods of multiple DRS objects +required: true +content: + application/json: + schema: + $ref: '../schemas/BulkAccessMethodUpdateRequest.yaml' \ No newline at end of file diff --git a/openapi/components/requestBodies/BulkDeleteBody.yaml b/openapi/components/requestBodies/BulkDeleteBody.yaml new file mode 100644 index 00000000..042aaaa3 --- /dev/null +++ b/openapi/components/requestBodies/BulkDeleteBody.yaml @@ -0,0 +1,72 @@ +required: true +content: + application/json: + schema: + $ref: '../schemas/BulkDeleteRequest.yaml' + examples: + bulk_metadata_delete: + summary: Bulk delete metadata only + description: Delete multiple DRS objects metadata while preserving underlying storage data (default and safest option) + value: + bulk_object_ids: + - "drs_object_123456" + - "drs_object_789012" + - "drs_object_345678" + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + delete_storage_data: false + bulk_full_delete: + summary: Bulk delete metadata and storage data + description: Delete both metadata and storage data for multiple objects (requires server support via deleteStorageDataSupported) + value: + bulk_object_ids: + - "drs_object_123456" + - "drs_object_789012" + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + delete_storage_data: true + bulk_no_auth_delete: + summary: Bulk delete without authentication + description: Bulk delete operation without GA4GH Passport authentication (for public objects or when using Bearer token in headers) + value: + bulk_object_ids: + - "drs_object_123456" + - "drs_object_789012" + delete_storage_data: false + large_bulk_delete: + summary: Large bulk delete operation + description: Delete many objects in a single request (check maxBulkDeleteLength in service-info for limits) + value: + bulk_object_ids: + - "drs_object_001" + - "drs_object_002" + - "drs_object_003" + - "drs_object_004" + - "drs_object_005" + - "drs_object_006" + - "drs_object_007" + - "drs_object_008" + - "drs_object_009" + - "drs_object_010" + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + delete_storage_data: false + mixed_object_types: + summary: Mixed object types deletion + description: Delete objects with different ID formats and types in a single request + value: + bulk_object_ids: + - "drs://example.org/123456" + - "local_object_789" + - "uuid:550e8400-e29b-41d4-a716-446655440000" + - "compact:prefix:identifier" + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + delete_storage_data: false + minimal_bulk_request: + summary: Minimal bulk delete request + description: Simplest bulk delete request with required fields only + value: + bulk_object_ids: + - "drs_object_123456" + - "drs_object_789012" \ No newline at end of file diff --git a/openapi/components/requestBodies/BulkObjectBody.yaml b/openapi/components/requestBodies/BulkObjectBody.yaml new file mode 100644 index 00000000..3f66904b --- /dev/null +++ b/openapi/components/requestBodies/BulkObjectBody.yaml @@ -0,0 +1,38 @@ +required: true +content: + application/json: + schema: + type: object + required: + - bulk_object_ids + properties: + passports: + type: array + items: + type: string + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM + description: the encoded JWT GA4GH Passport that contains embedded Visas. The overall JWT is signed as are the individual Passport Visas. + bulk_object_ids: + type: array + items: + type: string + minItems: 1 + description: An array of ObjectIDs to retrieve metadata for + examples: + bulk_retrieve: + summary: Bulk retrieve objects + description: Retrieve metadata for multiple existing DRS objects using their IDs + value: + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + bulk_object_ids: + - "drs_object_123456" + - "drs_object_789012" + - "drs_object_345678" + bulk_retrieve_no_auth: + summary: Bulk retrieve without authentication + description: Retrieve metadata for public DRS objects + value: + bulk_object_ids: + - "drs_object_public_123" + - "drs_object_public_456" \ No newline at end of file diff --git a/openapi/components/requestBodies/DeleteBody.yaml b/openapi/components/requestBodies/DeleteBody.yaml new file mode 100644 index 00000000..f33ea265 --- /dev/null +++ b/openapi/components/requestBodies/DeleteBody.yaml @@ -0,0 +1,44 @@ +required: false +content: + application/json: + schema: + $ref: '../schemas/DeleteRequest.yaml' + examples: + metadata_only_delete: + summary: Delete metadata only (default) + description: Delete DRS object metadata while preserving underlying storage data. This is the default and safest option. + value: + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + delete_storage_data: false + full_delete: + summary: Delete metadata and storage data + description: Delete both DRS object metadata and underlying storage data (requires server support via deleteStorageDataSupported) + value: + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + delete_storage_data: true + no_auth_delete: + summary: Delete without authentication + description: Delete operation without GA4GH Passport authentication (for public objects or when using Bearer token in headers) + value: + delete_storage_data: false + minimal_request: + summary: Minimal delete request + description: Simplest delete request with no authentication and default behavior (metadata only) + value: {} + multiple_passports: + summary: Multiple GA4GH Passports + description: Delete request with multiple GA4GH Passports for complex authorization scenarios + value: + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + - "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.AbCdEfGhIjKlMnOpQrStUvWxYz" + delete_storage_data: false + update_workflow: + summary: Safe update workflow + description: Delete metadata only to enable safe update pattern (delete metadata, then re-register with new metadata) + value: + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + delete_storage_data: false \ No newline at end of file diff --git a/openapi/components/requestBodies/PostObjectBody.yaml b/openapi/components/requestBodies/PostObjectBody.yaml index c93a5d3f..0b70d65f 100644 --- a/openapi/components/requestBodies/PostObjectBody.yaml +++ b/openapi/components/requestBodies/PostObjectBody.yaml @@ -25,3 +25,21 @@ content: type: string example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM description: the encoded JWT GA4GH Passport that contains embedded Visas. The overall JWT is signed as are the individual Passport Visas. + + examples: + retrieve_with_auth: + summary: Retrieve object with authentication + description: Request object metadata with passport authentication + value: + expand: false + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + retrieve_expanded_bundle: + summary: Retrieve expanded bundle with authentication + description: Request expanded bundle contents with passport authentication + value: + expand: true + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + - "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.additional_passport_signature" + diff --git a/openapi/components/requestBodies/RegisterObjectsBody.yaml b/openapi/components/requestBodies/RegisterObjectsBody.yaml new file mode 100644 index 00000000..95d1eae2 --- /dev/null +++ b/openapi/components/requestBodies/RegisterObjectsBody.yaml @@ -0,0 +1,66 @@ +description: Request body for registering DRS objects after upload +required: true +content: + application/json: + schema: + type: object + required: + - candidates + properties: + candidates: + type: array + items: + $ref: '../schemas/DrsObjectCandidate.yaml' + minItems: 1 + description: Array of DRS object candidates to register (server will mint IDs and timestamps) + passports: + type: array + items: + type: string + description: Optional array of GA4GH Passport JWTs for authorization + examples: + single_object_registration: + summary: Register a single object + description: Register one DRS object after upload + value: + candidates: + - name: "sample_data.vcf" + size: 1048576 + mime_type: "text/plain" + checksums: + - checksum: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + type: "sha-256" + description: "Variant call format file for sample analysis" + access_methods: + - type: "s3" + access_url: + url: "s3://my-bucket/uploads/sample_data.vcf" + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + bulk_object_registration: + summary: Register multiple objects + description: Register multiple DRS objects in a single request + value: + candidates: + - name: "genome_assembly.fasta" + size: 3221225472 + mime_type: "text/plain" + checksums: + - checksum: "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3" + type: "sha-256" + description: "Human genome reference assembly" + access_methods: + - type: "s3" + access_url: + url: "s3://genomics-bucket/assemblies/hg38.fasta" + - name: "annotations.gff3" + size: 524288000 + mime_type: "text/plain" + checksums: + - checksum: "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" + type: "sha-256" + description: "Gene annotations in GFF3 format" + access_methods: + - type: "https" + access_url: + url: "https://data.example.org/files/annotations.gff3" \ No newline at end of file diff --git a/openapi/components/requestBodies/UploadRequestBody.yaml b/openapi/components/requestBodies/UploadRequestBody.yaml new file mode 100644 index 00000000..fb620d86 --- /dev/null +++ b/openapi/components/requestBodies/UploadRequestBody.yaml @@ -0,0 +1,65 @@ +required: true +content: + application/json: + schema: + $ref: '../schemas/UploadRequest.yaml' + examples: + single_file: + summary: Single file upload request + description: Request upload methods for a single file + value: + requests: + - name: "sample_data.vcf" + size: 1048576 + mime_type: "text/plain" + checksums: + - checksum: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + type: "sha-256" + description: "Variant call format file for sample analysis" + aliases: + - "sample_001_variants" + - "vcf_batch_2024" + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + multiple_files: + summary: Multiple files upload request + description: Request upload methods for multiple files with different types + value: + requests: + - name: "genome_assembly.fasta" + size: 3221225472 + mime_type: "text/plain" + checksums: + - checksum: "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3" + type: "sha-256" + - checksum: "098f6bcd4621d373cade4e832627b4f6" + type: "md5" + description: "Human genome reference assembly" + aliases: + - "hg38_reference" + - name: "annotations.gff3" + size: 524288000 + mime_type: "text/plain" + checksums: + - checksum: "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" + type: "sha-256" + description: "Gene annotations in GFF3 format" + - name: "metadata.json" + size: 2048 + mime_type: "application/json" + checksums: + - checksum: "c89e4c5c7f2c8c8e8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c" + type: "sha-256" + description: "Sample metadata and experimental conditions" + no_passports: + summary: Upload request without authentication + description: Request for public upload endpoints that don't require authentication + value: + requests: + - name: "public_dataset.csv" + size: 10240 + mime_type: "text/csv" + checksums: + - checksum: "d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35" + type: "sha-256" + description: "Public research dataset" \ No newline at end of file diff --git a/openapi/components/responses/200AccessMethodUpdate.yaml b/openapi/components/responses/200AccessMethodUpdate.yaml new file mode 100644 index 00000000..5aba5318 --- /dev/null +++ b/openapi/components/responses/200AccessMethodUpdate.yaml @@ -0,0 +1,7 @@ +description: >- + Access methods successfully updated. Returns the updated DRS object with new access methods + and updated timestamp. +content: + application/json: + schema: + $ref: '../schemas/DrsObject.yaml' \ No newline at end of file diff --git a/openapi/components/responses/200BulkAccessMethodUpdate.yaml b/openapi/components/responses/200BulkAccessMethodUpdate.yaml new file mode 100644 index 00000000..17508ef5 --- /dev/null +++ b/openapi/components/responses/200BulkAccessMethodUpdate.yaml @@ -0,0 +1,15 @@ +description: >- + Access methods successfully updated for all objects. Returns updated DRS objects with + new access methods and updated timestamps. +content: + application/json: + schema: + type: object + required: + - objects + properties: + objects: + type: array + items: + $ref: '../schemas/DrsObject.yaml' + description: Array of updated DRS objects \ No newline at end of file diff --git a/openapi/components/responses/200UploadRequest.yaml b/openapi/components/responses/200UploadRequest.yaml new file mode 100644 index 00000000..239517d8 --- /dev/null +++ b/openapi/components/responses/200UploadRequest.yaml @@ -0,0 +1,93 @@ +description: Upload request processed successfully. Returns upload methods and temporary credentials for the requested files. +content: + application/json: + schema: + $ref: '../schemas/UploadResponse.yaml' + examples: + s3_upload: + summary: S3 upload method response + description: Response with S3 upload method and temporary credentials + value: + responses: + - name: "sample_data.vcf" + size: 1048576 + mime_type: "text/plain" + checksums: + - checksum: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + type: "sha-256" + description: "Variant call format file for sample analysis" + aliases: + - "sample_001_variants" + - "vcf_batch_2024" + upload_methods: + - type: "s3" + access_url: + url: "https://my-bucket.s3.amazonaws.com/uploads/drs_object_123456" + region: "us-east-1" + upload_details: + bucket: "my-bucket" + key: "uploads/drs_object_123456" + access_key_id: "AKIAIOSFODNN7EXAMPLE" + secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + session_token: "AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/LTo6UDdyJwOOvEVPvLXCrrrUtdnniCEXAMPLE/IvU1dYUg2RVAJBanLiHb4IgRmpRV3zrkuWJOgQs8IZZaIv2BXIa2R4OlgkBN9bkUDNCJiBeb/AXlzBBko7b15fjrBs2+cTQtpZ3CYWFXG8C5zqx37wnOE49mRl/+OtkIKGO7fAE" + expires_at: "2024-01-01T12:00:00Z" + https_upload: + summary: HTTPS upload method response + description: Response with HTTPS presigned POST URL for direct upload + value: + responses: + - name: "genome_assembly.fasta" + size: 3221225472 + mime_type: "text/plain" + checksums: + - checksum: "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3" + type: "sha-256" + - checksum: "098f6bcd4621d373cade4e832627b4f6" + type: "md5" + description: "Human genome reference assembly" + aliases: + - "hg38_reference" + upload_methods: + - type: "https" + access_url: + url: "https://upload.example.org/v1/files/drs_object_789012" + upload_details: + post_url: "https://upload.example.org/v1/files/drs_object_789012?signature=abc123" + multiple_methods: + summary: Multiple upload methods response + description: Response offering multiple upload method options for flexibility + value: + responses: + - name: "annotations.gff3" + size: 524288000 + mime_type: "text/plain" + checksums: + - checksum: "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" + type: "sha-256" + description: "Gene annotations in GFF3 format" + upload_methods: + - type: "s3" + access_url: + url: "https://genomics-bucket.s3.us-west-2.amazonaws.com/uploads/drs_object_345678" + region: "us-west-2" + upload_details: + bucket: "genomics-bucket" + key: "uploads/drs_object_345678" + access_key_id: "AKIAI44QH8DHBEXAMPLE" + secret_access_key: "je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY" + session_token: "temporary_session_token_here" + expires_at: "2024-01-01T12:00:00Z" + - type: "https" + access_url: + url: "https://upload-api.example.org/files/drs_object_345678" + upload_details: + post_url: "https://upload-api.example.org/files/drs_object_345678?token=upload_token_12345" + - type: "gs" + access_url: + url: "https://storage.googleapis.com/genomics-uploads/drs_object_345678" + region: "us-central1" + upload_details: + bucket: "genomics-uploads" + key: "drs_object_345678" + access_token: "ya29.AHES6ZRVmB7fkLtd1XTmq6mo0S1wqZZi3-Lh_s-6Uw7p8vtgSwg" + expires_at: "2024-01-01T12:00:00Z" \ No newline at end of file diff --git a/openapi/components/responses/201ObjectsCreated.yaml b/openapi/components/responses/201ObjectsCreated.yaml new file mode 100644 index 00000000..9c9f53cb --- /dev/null +++ b/openapi/components/responses/201ObjectsCreated.yaml @@ -0,0 +1,72 @@ +description: DRS objects were successfully registered as an atomic transaction. Returns the complete DRS objects with server-minted IDs and timestamps. All candidate objects were validated and registered together - if any had failed, none would have been registered. +content: + application/json: + schema: + type: object + required: + - objects + properties: + objects: + type: array + items: + $ref: '../schemas/DrsObject.yaml' + description: Array of registered DRS objects in the same order as the candidates in the request + examples: + single_object_created: + summary: Single object registered + description: Response after registering one DRS object + value: + objects: + - id: "drs_obj_a1b2c3d4e5f6" + self_uri: "drs://drs.example.org/drs_obj_a1b2c3d4e5f6" + name: "sample_data.vcf" + size: 1048576 + mime_type: "text/plain" + created_time: "2024-01-15T10:30:00Z" + updated_time: "2024-01-15T10:30:00Z" + version: "1.0" + checksums: + - checksum: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + type: "sha-256" + access_methods: + - type: "s3" + access_url: + url: "s3://my-bucket/uploads/sample_data.vcf" + description: "Variant call format file for sample analysis" + multiple_objects_created: + summary: Multiple objects registered + description: Response after registering multiple DRS objects + value: + objects: + - id: "drs_obj_a1b2c3d4e5f6" + self_uri: "drs://drs.example.org/drs_obj_a1b2c3d4e5f6" + name: "genome_assembly.fasta" + size: 3221225472 + mime_type: "text/plain" + created_time: "2024-01-15T09:00:00Z" + updated_time: "2024-01-15T09:00:00Z" + version: "1.0" + checksums: + - checksum: "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3" + type: "sha-256" + access_methods: + - type: "s3" + access_url: + url: "s3://genomics-bucket/assemblies/hg38.fasta" + description: "Human genome reference assembly" + - id: "drs_obj_f6e5d4c3b2a1" + self_uri: "drs://drs.example.org/drs_obj_f6e5d4c3b2a1" + name: "annotations.gff3" + size: 524288000 + mime_type: "text/plain" + created_time: "2024-01-15T09:15:00Z" + updated_time: "2024-01-15T09:15:00Z" + version: "1.0" + checksums: + - checksum: "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" + type: "sha-256" + access_methods: + - type: "https" + access_url: + url: "https://data.example.org/files/annotations.gff3" + description: "Gene annotations in GFF3 format" \ No newline at end of file diff --git a/openapi/components/responses/204DeleteSuccess.yaml b/openapi/components/responses/204DeleteSuccess.yaml new file mode 100644 index 00000000..f653d647 --- /dev/null +++ b/openapi/components/responses/204DeleteSuccess.yaml @@ -0,0 +1,4 @@ +description: >- + All DRS objects were successfully deleted. For bulk operations, this indicates that + the entire atomic transaction completed successfully - all requested objects have been + deleted. Storage data deletion (if requested) was attempted but success is not guaranteed. \ No newline at end of file diff --git a/openapi/components/responses/400BadRequestDelete.yaml b/openapi/components/responses/400BadRequestDelete.yaml new file mode 100644 index 00000000..644f21a1 --- /dev/null +++ b/openapi/components/responses/400BadRequestDelete.yaml @@ -0,0 +1,24 @@ +description: "The delete request is malformed or contains unsupported parameters (e.g., delete_storage_data: true when server doesn't support storage data deletion)." +content: + application/json: + schema: + $ref: '../schemas/Error.yaml' + examples: + unsupported_storage_deletion: + summary: Storage data deletion not supported + description: Client requested storage data deletion but server doesn't support it + value: + msg: "Server does not support storage data deletion. Set delete_storage_data to false or omit the parameter." + status_code: 400 + invalid_request_format: + summary: Malformed request body + description: Request body contains invalid JSON or missing required fields + value: + msg: "Invalid request body: bulk_object_ids is required for bulk delete operations" + status_code: 400 + empty_object_list: + summary: Empty object ID list + description: Bulk delete request with empty object ID array + value: + msg: "bulk_object_ids cannot be empty" + status_code: 400 \ No newline at end of file diff --git a/openapi/components/responses/403ForbiddenDelete.yaml b/openapi/components/responses/403ForbiddenDelete.yaml new file mode 100644 index 00000000..db06acda --- /dev/null +++ b/openapi/components/responses/403ForbiddenDelete.yaml @@ -0,0 +1,24 @@ +description: The client is not authorized to delete the requested DRS object. +content: + application/json: + schema: + $ref: '../schemas/Error.yaml' + examples: + insufficient_permissions: + summary: Insufficient delete permissions + description: Client lacks permission to delete the specified object + value: + msg: "Client lacks delete permission for object drs_object_123456" + status_code: 403 + invalid_passport: + summary: Invalid GA4GH Passport + description: Provided GA4GH Passport is invalid or expired + value: + msg: "Invalid or expired GA4GH Passport provided" + status_code: 403 + missing_visa: + summary: Missing required visa + description: GA4GH Passport lacks required visa for delete operation + value: + msg: "GA4GH Passport does not contain required visa for delete operation on this object" + status_code: 403 \ No newline at end of file diff --git a/openapi/components/responses/404NotFoundDelete.yaml b/openapi/components/responses/404NotFoundDelete.yaml new file mode 100644 index 00000000..6b861a3f --- /dev/null +++ b/openapi/components/responses/404NotFoundDelete.yaml @@ -0,0 +1,24 @@ +description: The requested DRS object for deletion wasn't found, or delete endpoints are not supported by this server. +content: + application/json: + schema: + $ref: '../schemas/Error.yaml' + examples: + object_not_found: + summary: DRS object not found + description: The specified DRS object does not exist + value: + msg: "DRS object drs_object_123456 does not exist" + status_code: 404 + delete_not_supported: + summary: Delete operations not supported + description: This server does not support delete operations + value: + msg: "Delete operations are not supported by this server" + status_code: 404 + endpoint_not_found: + summary: Delete endpoint not available + description: Delete endpoints are not implemented on this server + value: + msg: "The requested endpoint /objects/delete is not available on this server" + status_code: 404 \ No newline at end of file diff --git a/openapi/components/responses/413RequestTooLarge.yaml b/openapi/components/responses/413RequestTooLarge.yaml index f4d2615a..4744108b 100644 --- a/openapi/components/responses/413RequestTooLarge.yaml +++ b/openapi/components/responses/413RequestTooLarge.yaml @@ -3,3 +3,16 @@ content: application/json: schema: $ref: '../schemas/Error.yaml' + examples: + bulk_limit_exceeded: + summary: Bulk delete limit exceeded + description: Request contains more objects than server's maximum bulk delete limit + value: + msg: "Bulk delete request contains 150 objects but server maximum is 100. Check maxBulkDeleteLength in service-info." + status_code: 413 + request_size_too_large: + summary: Request payload too large + description: The overall request payload exceeds server limits + value: + msg: "Request payload size exceeds server limit of 1MB" + status_code: 413 diff --git a/openapi/components/schemas/AccessMethodUpdateRequest.yaml b/openapi/components/schemas/AccessMethodUpdateRequest.yaml new file mode 100644 index 00000000..48b1614b --- /dev/null +++ b/openapi/components/schemas/AccessMethodUpdateRequest.yaml @@ -0,0 +1,15 @@ +type: object +required: + - access_methods +properties: + access_methods: + type: array + items: + $ref: './AccessMethod.yaml' + minItems: 1 + description: New access methods for the DRS object + passports: + type: array + items: + type: string + description: Optional GA4GH Passport JWTs for authorization \ No newline at end of file diff --git a/openapi/components/schemas/BulkAccessMethodUpdateRequest.yaml b/openapi/components/schemas/BulkAccessMethodUpdateRequest.yaml new file mode 100644 index 00000000..8cb6b060 --- /dev/null +++ b/openapi/components/schemas/BulkAccessMethodUpdateRequest.yaml @@ -0,0 +1,28 @@ +type: object +required: + - updates +properties: + updates: + type: array + items: + type: object + required: + - object_id + - access_methods + properties: + object_id: + type: string + description: DRS object ID to update + access_methods: + type: array + items: + $ref: './AccessMethod.yaml' + minItems: 1 + description: New access methods for this object + minItems: 1 + description: Array of access method updates to perform + passports: + type: array + items: + type: string + description: Optional GA4GH Passport JWTs for authorization \ No newline at end of file diff --git a/openapi/components/schemas/BulkDeleteRequest.yaml b/openapi/components/schemas/BulkDeleteRequest.yaml new file mode 100644 index 00000000..7365ea09 --- /dev/null +++ b/openapi/components/schemas/BulkDeleteRequest.yaml @@ -0,0 +1,28 @@ +type: object +description: Request body for bulk delete operations +required: + - bulk_object_ids +properties: + bulk_object_ids: + type: array + items: + type: string + description: Array of DRS object IDs to delete + example: + - "drs_object_123456" + - "drs_object_789012" + - "drs_object_345678" + passports: + type: array + items: + type: string + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM + description: the encoded JWT GA4GH Passport that contains embedded Visas. The overall JWT is signed as are the individual Passport Visas. + delete_storage_data: + type: boolean + default: false + description: >- + If true, delete both DRS object metadata and underlying storage data (follows server's deleteStorageDataSupported capability). + If false (default), only delete DRS object metadata while preserving underlying storage data. + Clients must explicitly set this to true to enable storage data deletion, ensuring + intentional choice for this potentially destructive operation. \ No newline at end of file diff --git a/openapi/components/schemas/BulkDeleteResponse.yaml b/openapi/components/schemas/BulkDeleteResponse.yaml new file mode 100644 index 00000000..fc68a3da --- /dev/null +++ b/openapi/components/schemas/BulkDeleteResponse.yaml @@ -0,0 +1,18 @@ +type: object +description: Response for bulk delete operations with multi-status results +properties: + results: + type: array + items: + $ref: './DeleteResult.yaml' + description: Results for each object in the delete request + example: + - object_id: "drs_object_123456" + status: 204 + message: "Successfully deleted" + - object_id: "drs_object_789012" + status: 404 + message: "Object not found" + error: + msg: "DRS object drs_object_789012 does not exist" + status_code: 404 \ No newline at end of file diff --git a/openapi/components/schemas/DeleteRequest.yaml b/openapi/components/schemas/DeleteRequest.yaml new file mode 100644 index 00000000..db7733d6 --- /dev/null +++ b/openapi/components/schemas/DeleteRequest.yaml @@ -0,0 +1,17 @@ +type: object +description: Request body for single object delete operations +properties: + passports: + type: array + items: + type: string + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM + description: the encoded JWT GA4GH Passport that contains embedded Visas. The overall JWT is signed as are the individual Passport Visas. + delete_storage_data: + type: boolean + default: false + description: >- + If true, delete both DRS object metadata and underlying storage data (follows server's deleteStorageDataSupported capability). + If false (default), only delete DRS object metadata while preserving underlying storage data. + Clients must explicitly set this to true to enable storage data deletion, ensuring + intentional choice for this potentially destructive operation. \ No newline at end of file diff --git a/openapi/components/schemas/DeleteResult.yaml b/openapi/components/schemas/DeleteResult.yaml new file mode 100644 index 00000000..103865cf --- /dev/null +++ b/openapi/components/schemas/DeleteResult.yaml @@ -0,0 +1,21 @@ +type: object +description: Result for an individual object in a bulk delete operation +required: + - object_id + - status +properties: + object_id: + type: string + description: The DRS object ID + example: "drs_object_123456" + status: + type: integer + description: HTTP status code for this object + example: 204 + message: + type: string + description: Human-readable status message + example: "Successfully deleted" + error: + $ref: './Error.yaml' + description: Error details if deletion failed \ No newline at end of file diff --git a/openapi/components/schemas/DrsObjectCandidate.yaml b/openapi/components/schemas/DrsObjectCandidate.yaml new file mode 100644 index 00000000..41ed7828 --- /dev/null +++ b/openapi/components/schemas/DrsObjectCandidate.yaml @@ -0,0 +1,87 @@ +type: object +required: + - size + - checksums +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]. + size: + type: integer + format: int64 + description: |- + For blobs, the blob size in bytes. + For bundles, the cumulative size, in bytes, of items in the `contents` field. + version: + type: string + description: >- + A string representing a version. + + (Some systems may use checksum, a RFC3339 timestamp, or an incrementing version number.) + mime_type: + type: string + description: A string providing the mime-type of the `DrsObject`. + example: + application/json + checksums: + type: array + minItems: 1 + items: + $ref: './Checksum.yaml' + description: >- + The checksum of the `DrsObject`. At least one checksum must be provided. + + For blobs, the checksum is computed over the bytes in the blob. + + For bundles, the checksum is computed over a sorted concatenation of the + checksums of its top-level contained objects (not recursive, names not included). + The list of checksums is sorted alphabetically (hex-code) before concatenation + and a further checksum is performed on the concatenated checksum value. + + For example, if a bundle contains blobs with the following checksums: + + md5(blob1) = 72794b6d + + md5(blob2) = 5e089d29 + + Then the checksum of the bundle is: + + md5( concat( sort( md5(blob1), md5(blob2) ) ) ) + + = md5( concat( sort( 72794b6d, 5e089d29 ) ) ) + + = md5( concat( 5e089d29, 72794b6d ) ) + + = md5( 5e089d2972794b6d ) + + = f7a29a04 + access_methods: + type: array + minItems: 1 + items: + $ref: './AccessMethod.yaml' + description: |- + The list of access methods that can be used to fetch the `DrsObject`. + Required for single blobs; optional for bundles. + contents: + type: array + description: >- + If not set, this `DrsObject` is a single blob. + + If set, this `DrsObject` is a bundle containing the listed `ContentsObject` s (some of which may be further nested). + items: + $ref: './ContentsObject.yaml' + description: + type: string + description: A human readable description of the `DrsObject`. + aliases: + type: array + items: + type: string + description: >- + A list of strings that can be used to find other metadata + about this `DrsObject` from external metadata sources. These + aliases can be used to represent secondary + accession numbers or external GUIDs. \ No newline at end of file diff --git a/openapi/components/schemas/DrsService.yaml b/openapi/components/schemas/DrsService.yaml index cca41c35..97b79a2b 100644 --- a/openapi/components/schemas/DrsService.yaml +++ b/openapi/components/schemas/DrsService.yaml @@ -29,6 +29,124 @@ 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. + uploadRequestSupported: + type: boolean + description: >- + Indicates whether this DRS server supports upload request operations via the `/upload-request` endpoint. + If true, clients can request upload methods and credentials for uploading files. + If false or missing, the server does not support upload request coordination. + default: false + objectRegistrationSupported: + type: boolean + description: >- + Indicates whether this DRS server supports object registration operations via the `/objects/register` endpoint. + If true, clients can register uploaded files or existing data as DRS objects. + If false or missing, the server does not support object registration. + default: false + supportedUploadMethods: + type: array + items: + type: string + enum: + - s3 + - gs + - https + - ftp + - sftp + description: >- + List of upload methods supported by this DRS server. Only present when uploadRequestSupported is true. + Clients can use this information to determine which upload methods are available before making upload requests. + + - **s3**: Direct S3 upload with temporary AWS credentials + - **gs**: Google Cloud Storage upload with access tokens + - **https**: Presigned POST URL for HTTP uploads + - **ftp**: File Transfer Protocol uploads + - **sftp**: Secure File Transfer Protocol uploads + - **gsiftp**: GridFTP secure file transfer + - **globus**: Globus transfer service for high-performance data movement + maxUploadSize: + type: integer + format: int64 + description: >- + Maximum file size in bytes that can be uploaded via the upload endpoints. + Only present when uploadRequestSupported is true. If not specified, there is no explicit size limit. + maxUploadRequestLength: + type: integer + description: >- + Maximum number of files that can be included in a single upload request. + Only present when uploadRequestSupported is true. If not specified, defaults to the same value as maxBulkRequestLength. + maxRegisterRequestLength: + type: integer + description: >- + Maximum number of candidate objects that can be included in a single registration request. + Only present when objectRegistrationSupported is true. If not specified, defaults to the same value as maxBulkRequestLength. + validateUploadChecksums: + type: boolean + description: >- + Indicates whether this DRS server validates uploaded file checksums against the provided metadata. + If true, the server will verify that uploaded files match their declared checksums and may reject uploads with mismatches. + If false or missing, the server does not perform checksum validation and relies on client-provided metadata. + Only present when uploadRequestSupported or objectRegistrationSupported is true. + default: false + validateUploadFileSizes: + type: boolean + description: >- + Indicates whether this DRS server validates uploaded file sizes against the provided metadata. + If true, the server will verify that uploaded files match their declared sizes and may reject uploads with mismatches. + If false or missing, the server does not perform file size validation and relies on client-provided metadata. + Only present when uploadRequestSupported or objectRegistrationSupported is true. + default: false + relatedFileStorageSupported: + type: boolean + description: >- + Indicates whether this DRS server supports storing files from the same upload request under a common prefix or folder structure. + If true, the server will organize related files together in storage, enabling bioinformatics workflows that expect co-located files (e.g., CRAM + CRAI, VCF + TBI). + If false or missing, the server may distribute files across different storage locations or prefixes. + Only present when uploadRequestSupported is true. This feature is particularly valuable for genomics tools like samtools that expect index files to be co-located with data files. + default: false + deleteSupported: + type: boolean + description: >- + Indicates whether this DRS server supports delete operations via the delete endpoints. + If true, clients can delete DRS objects using POST requests to `/objects/{object_id}/delete` and `/objects/delete`. + If false or missing, the server does not support delete operations and will return 404 for delete endpoint requests. + Like upload functionality, delete support is entirely optional and servers remain DRS compliant without it. + default: false + maxBulkDeleteLength: + type: integer + description: >- + Maximum number of objects that can be deleted in a single bulk delete request via `/objects/delete`. + Only present when deleteSupported is true. If not specified when delete is supported, defaults to the same value as maxBulkRequestLength. + Servers may enforce lower limits for delete operations compared to other bulk operations for safety reasons. + deleteStorageDataSupported: + type: boolean + description: >- + Indicates whether this DRS server supports attempting to delete underlying storage data when clients request it. + If true, the server will attempt to delete both metadata and storage files when `delete_storage_data: true` is specified in delete requests. + If false or missing, the server only supports metadata deletion regardless of client request, preserving underlying storage data. + Only present when deleteSupported is true. This is a capability flag indicating what the server can attempt, not a default behavior setting. + Note: Storage deletion attempts may fail due to permissions, network issues, or storage service errors. + default: false + accessMethodUpdateSupported: + type: boolean + description: >- + Indicates whether this DRS server supports updating access methods for existing objects. + If true, clients can update access methods using `/objects/{object_id}/access-methods` and `/objects/access-methods` endpoints. + If false or missing, the server does not support access method updates. + default: false + maxBulkAccessMethodUpdateLength: + type: integer + description: >- + Maximum number of objects that can be updated in a single bulk access method update request. + Only present when accessMethodUpdateSupported is true. If not specified, defaults to maxBulkRequestLength. + validateAccessMethodUpdates: + type: boolean + description: >- + Indicates whether this DRS server validates new access methods by verifying they point to the same data. + If true, the server will attempt to verify checksums/content before updating access methods. + If false or missing, the server trusts client-provided access methods without validation. + Only present when accessMethodUpdateSupported is true. + default: false diff --git a/openapi/components/schemas/UploadMethod.yaml b/openapi/components/schemas/UploadMethod.yaml new file mode 100644 index 00000000..f2fd71e7 --- /dev/null +++ b/openapi/components/schemas/UploadMethod.yaml @@ -0,0 +1,46 @@ +type: object +required: + - type + - access_url +properties: + type: + type: string + enum: + - s3 + - gs + - https + - ftp + - sftp + - gsiftp + - globus + description: >- + Type of upload method. Implementations MAY support any subset of these types. + + The 'https' type can be used to return a presigned POST URL and is expected to be + the most common implementation for typical file uploads. This method provides a + simple HTTP POST interface that works with standard web clients. + + The 's3' type is primarily intended to support uploads of large files that want + to take advantage of multipart uploads and automatic retries implemented in AWS + libraries. This method provides direct access to S3-specific upload capabilities. + + Other common implementations include 'gs' for Google Cloud Storage and 'sftp' + for secure FTP uploads. + access_url: + allOf: + - $ref: './AccessURL.yaml' + - description: >- + An `AccessURL` that specifies where the file will be accessible after upload. + This URL will be used as the access_url in the eventual DRS object, ensuring + consistency between upload and retrieval operations. + region: + type: string + description: >- + Cloud region for the upload location. Optional for non-cloud storage types. + example: us-east-1 + upload_details: + type: object + additionalProperties: true + description: >- + A dictionary of upload-specific configuration details that vary by upload method type. + The contents and structure depend on the specific upload method being used. \ No newline at end of file diff --git a/openapi/components/schemas/UploadRequest.yaml b/openapi/components/schemas/UploadRequest.yaml new file mode 100644 index 00000000..dac6a3ac --- /dev/null +++ b/openapi/components/schemas/UploadRequest.yaml @@ -0,0 +1,15 @@ +type: object +required: + - requests +properties: + requests: + type: array + items: + $ref: './UploadRequestObject.yaml' + minItems: 1 + description: Array of upload requests for files + passports: + type: array + items: + type: string + description: Optional array of GA4GH Passport JWTs for authorization \ No newline at end of file diff --git a/openapi/components/schemas/UploadRequestObject.yaml b/openapi/components/schemas/UploadRequestObject.yaml new file mode 100644 index 00000000..b3fd88aa --- /dev/null +++ b/openapi/components/schemas/UploadRequestObject.yaml @@ -0,0 +1,31 @@ +type: object +required: + - name + - size + - mime_type + - checksums +properties: + name: + type: string + description: The name of the file to upload + size: + type: integer + format: int64 + description: Size of the file in bytes + mime_type: + type: string + description: MIME type of the file + checksums: + type: array + items: + $ref: './Checksum.yaml' + minItems: 1 + description: Array of checksums for file integrity verification + description: + type: string + description: Optional description of the file + aliases: + type: array + items: + type: string + description: Optional array of alternative names for the file \ No newline at end of file diff --git a/openapi/components/schemas/UploadResponse.yaml b/openapi/components/schemas/UploadResponse.yaml new file mode 100644 index 00000000..701df8f6 --- /dev/null +++ b/openapi/components/schemas/UploadResponse.yaml @@ -0,0 +1,9 @@ +type: object +required: + - responses +properties: + responses: + type: array + items: + $ref: './UploadResponseObject.yaml' + description: List of upload responses for the requested files \ No newline at end of file diff --git a/openapi/components/schemas/UploadResponseObject.yaml b/openapi/components/schemas/UploadResponseObject.yaml new file mode 100644 index 00000000..78e3472e --- /dev/null +++ b/openapi/components/schemas/UploadResponseObject.yaml @@ -0,0 +1,36 @@ +type: object +required: + - name + - size + - mime_type + - checksums +properties: + name: + type: string + description: The name of the file + size: + type: integer + format: int64 + description: Size of the file in bytes + mime_type: + type: string + description: MIME type of the file + checksums: + type: array + items: + $ref: './Checksum.yaml' + minItems: 1 + description: Array of checksums for file integrity verification + description: + type: string + description: Optional description of the file + aliases: + type: array + items: + type: string + description: Optional array of alternative names + upload_methods: + type: array + items: + $ref: './UploadMethod.yaml' + description: Available methods for uploading this file \ No newline at end of file diff --git a/openapi/data_repository_service.openapi.yaml b/openapi/data_repository_service.openapi.yaml index 033f42e9..a814b3b9 100644 --- a/openapi/data_repository_service.openapi.yaml +++ b/openapi/data_repository_service.openapi.yaml @@ -37,6 +37,10 @@ tags: # Operations - name: Objects + - name: Upload Request + - name: Access Method Updates + description: + $ref: ./tags/AccessMethodUpdate.md - name: Service Info # Models @@ -60,10 +64,50 @@ tags: x-displayName: DrsObject description: | + - name: DrsObjectCandidateModel + x-displayName: DrsObjectCandidate + description: | + - name: ErrorModel x-displayName: Error description: | + - name: UploadRequestModel + x-displayName: UploadRequest + description: | + + - name: UploadResponseModel + x-displayName: UploadResponse + description: | + + - name: UploadRequestObjectModel + x-displayName: UploadRequestObject + description: | + + - name: UploadResponseObjectModel + x-displayName: UploadResponseObject + description: | + + - name: UploadMethodModel + x-displayName: UploadMethod + description: | + + - name: DeleteRequestModel + x-displayName: DeleteRequest + description: | + + - name: BulkDeleteRequestModel + x-displayName: BulkDeleteRequest + description: | + + - name: DeleteResultModel + x-displayName: DeleteResult + description: | + + - name: BulkDeleteResponseModel + x-displayName: BulkDeleteResponse + description: | + # Appendices - name: Motivation @@ -84,6 +128,15 @@ tags: - name: GA4GH Service Registry description: $ref: './tags/ServiceRegistry.md' + - name: Upload Requests and Object Registration + description: + $ref: ./tags/UploadRequest.md + - name: Object Deletion + description: + $ref: ./tags/ObjectDeletion.md + - name: Access Method Update + description: + $ref: ./tags/AccessMethodUpdate.md x-tagGroups: - name: Overview tags: @@ -93,6 +146,7 @@ x-tagGroups: - name: Operations tags: - Objects + - Upload Request - Service Info - name: Models tags: @@ -101,7 +155,17 @@ x-tagGroups: - ChecksumModel - ContentsObjectModel - DrsObjectModel + - DrsObjectCandidateModel - ErrorModel + - UploadRequestModel + - UploadResponseModel + - UploadRequestObjectModel + - UploadResponseObjectModel + - UploadMethodModel + - DeleteRequestModel + - BulkDeleteRequestModel + - DeleteResultModel + - BulkDeleteResponseModel - name: Appendices tags: - Motivation @@ -110,17 +174,32 @@ x-tagGroups: - Compact Identifier-Based URIs - Hostname-Based URIs - GA4GH Service Registry + - Upload Requests and Object Registration + - Object Deletion + - Access Method Update paths: /service-info: $ref: ./paths/service-info.yaml /objects/{object_id}: $ref: ./paths/objects@{object_id}.yaml + /objects/{object_id}/delete: + $ref: ./paths/objects@{object_id}@delete.yaml + /objects/delete: + $ref: ./paths/objects@delete.yaml /objects: - $ref: ./paths/bulkobjects@{object_id}.yaml + $ref: ./paths/objects.yaml + /objects/register: + $ref: ./paths/objects@register.yaml /objects/{object_id}/access/{access_id}: $ref: ./paths/objects@{object_id}@access@{access_id}.yaml /objects/access: - $ref: ./paths/bulkobjects@access@{access_id}.yaml + $ref: ./paths/objects@access.yaml + /objects/{object_id}/access-methods: + $ref: ./paths/objects@{object_id}@access-methods.yaml + /objects/access-methods: + $ref: ./paths/objects@access-methods.yaml + /upload-request: + $ref: ./paths/upload-request.yaml components: securitySchemes: BasicAuth: diff --git a/openapi/paths/bulkobjects@{object_id}.yaml b/openapi/paths/objects.yaml similarity index 83% rename from openapi/paths/bulkobjects@{object_id}.yaml rename to openapi/paths/objects.yaml index 022ea2db..6cfe26aa 100644 --- a/openapi/paths/bulkobjects@{object_id}.yaml +++ b/openapi/paths/objects.yaml @@ -33,7 +33,12 @@ options: post: summary: Get info about multiple DrsObjects with an optional Passport(s). description: >- - Returns an array of object metadata, and a list of access methods that can be used to fetch objects' bytes. Currently this is limited to use passports (one or more) or a single bearer token, so make sure your bulk request is for objects that all use the same passports/token. + Returns an array of object metadata and access methods for the specified object IDs. + + The request is limited to use passports (one or more) or a single bearer token, + so make sure your bulk request is for objects that all use the same passports/token. + + **Note**: To register new DRS objects, use the dedicated `/objects/register` endpoint. operationId: GetBulkObjects security: - PassportAuth: [] @@ -60,8 +65,4 @@ post: - Objects x-swagger-router-controller: ga4gh.drs.server requestBody: - required: true - content: - application/json: - schema: - $ref: '../components/parameters/BulkObjectId.yaml' + $ref: '../components/requestBodies/BulkObjectBody.yaml' \ No newline at end of file diff --git a/openapi/paths/objects@access-methods.yaml b/openapi/paths/objects@access-methods.yaml new file mode 100644 index 00000000..21e965d0 --- /dev/null +++ b/openapi/paths/objects@access-methods.yaml @@ -0,0 +1,39 @@ +post: + summary: Bulk update access methods for multiple DRS objects + description: >- + **Optional Endpoint**: Not all DRS servers support access method updates. + + Update access methods for multiple DRS objects in a single atomic transaction. + If ANY object fails to update, the ENTIRE request fails and NO objects are updated. + Only access methods are modified - core object metadata remains unchanged. + + Note that existing access methods are overwritten, if clients want to add additional + access methods they should first retrieve the current methods and include them along + with the new methods in this request. + + **Authentication**: GA4GH Passports can be provided in the request body. + operationId: bulkUpdateAccessMethods + requestBody: + $ref: '../components/requestBodies/BulkAccessMethodUpdateBody.yaml' + responses: + '200': + $ref: '../components/responses/200BulkAccessMethodUpdate.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: + - {} + - BasicAuth: [] + - BearerAuth: [] + - PassportAuth: [] + tags: + - Objects \ No newline at end of file diff --git a/openapi/paths/bulkobjects@access@{access_id}.yaml b/openapi/paths/objects@access.yaml similarity index 99% rename from openapi/paths/bulkobjects@access@{access_id}.yaml rename to openapi/paths/objects@access.yaml index 0878eb1d..306783eb 100644 --- a/openapi/paths/bulkobjects@access@{access_id}.yaml +++ b/openapi/paths/objects@access.yaml @@ -35,4 +35,4 @@ post: content: application/json: schema: - $ref: '../components/parameters/BulkObjectAccessId.yaml' + $ref: '../components/parameters/BulkObjectAccessId.yaml' \ No newline at end of file diff --git a/openapi/paths/objects@delete.yaml b/openapi/paths/objects@delete.yaml new file mode 100644 index 00000000..af93ef20 --- /dev/null +++ b/openapi/paths/objects@delete.yaml @@ -0,0 +1,144 @@ +post: + summary: Delete multiple DRS objects + description: >- + **Optional Endpoint**: This endpoint is not required for DRS server implementations. + Not all DRS servers support delete functionality. + + Delete multiple DRS objects in a single atomic transaction. If ANY object fails to be deleted, + the ENTIRE request fails and NO objects are deleted. This ensures data consistency and prevents + partial deletion scenarios. + + **RECOMMENDED - Transactional Behavior**: + Deletion operations SHOULD be atomic transactions. If ANY object fails validation or deletion, + the ENTIRE request SHOULD fail and NO objects SHOULD be deleted. Servers SHOULD implement this as an + all-or-nothing operation to ensure data consistency, but MAY implement partial deletion with + appropriate error reporting if transactional behavior is not feasible. + + **Authentication**: GA4GH Passports can be provided in the request body for authorization. + + **Storage Data Deletion**: The `delete_storage_data` parameter controls whether the server will attempt to delete underlying + storage files along with DRS metadata. This defaults to false for safety. + Servers will make a best effort attempt to delete storage data, but success is not guaranteed. + + **Server Responsibilities**: + - SHOULD treat deletion as an atomic transaction (all succeed or all fail) + - SHOULD validate ALL object IDs exist and are accessible before deleting ANY + - SHOULD roll back any partial changes if any object fails deletion + - SHOULD return 400 if any object ID is invalid or inaccessible when using transactional behavior + + **Client Responsibilities**: + - Provide valid object IDs for all objects to be deleted + - Handle potential failure of entire batch if any single object cannot be deleted + - Check service-info for `maxBulkDeleteLength` limits before making requests + operationId: bulkDeleteObjects + tags: + - Objects + requestBody: + $ref: '../components/requestBodies/BulkDeleteBody.yaml' + responses: + '204': + $ref: '../components/responses/204DeleteSuccess.yaml' + '400': + $ref: '../components/responses/400BadRequestDelete.yaml' + '401': + $ref: '../components/responses/401Unauthorized.yaml' + '403': + $ref: '../components/responses/403ForbiddenDelete.yaml' + '404': + $ref: '../components/responses/404NotFoundDelete.yaml' + '413': + $ref: '../components/responses/413RequestTooLarge.yaml' + '500': + $ref: '../components/responses/500InternalServerError.yaml' + security: + - {} + - BasicAuth: [] + - BearerAuth: [] + - PassportAuth: [] + x-codegen-request-body-name: body + examples: + successful_bulk_delete: + summary: Successful bulk deletion + description: Complete example of successfully deleting multiple objects + value: + request: + method: POST + url: "/objects/delete" + headers: + Content-Type: "application/json" + body: + bulk_object_ids: + - "drs_object_123456" + - "drs_object_789012" + - "drs_object_345678" + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + delete_storage_data: false + response: + status: 204 + headers: + Content-Length: "0" + failed_bulk_delete: + summary: Failed bulk deletion (transactional) + description: Complete example of bulk deletion failing due to one invalid object ID - no objects are deleted + value: + request: + method: POST + url: "/objects/delete" + headers: + Content-Type: "application/json" + body: + bulk_object_ids: + - "drs_object_123456" + - "nonexistent_object" + - "drs_object_345678" + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + delete_storage_data: false + response: + status: 404 + headers: + Content-Type: "application/json" + body: + msg: "Object 'nonexistent_object' not found. No objects were deleted due to transactional behavior." + status_code: 404 + bulk_limit_exceeded: + summary: Bulk limit exceeded error + description: Complete example when bulk request exceeds server limits + value: + request: + method: POST + url: "/objects/delete" + headers: + Content-Type: "application/json" + body: + bulk_object_ids: ["obj1", "obj2", "obj3", "...150 objects total"] + delete_storage_data: false + response: + status: 413 + headers: + Content-Type: "application/json" + body: + msg: "Bulk delete request contains 150 objects but server maximum is 100. Check maxBulkDeleteLength in service-info." + status_code: 413 + unsupported_storage_deletion: + summary: Unsupported storage deletion error + description: Complete example when client requests storage deletion but server doesn't support it + value: + request: + method: POST + url: "/objects/delete" + headers: + Content-Type: "application/json" + body: + bulk_object_ids: + - "drs_object_123456" + - "drs_object_789012" + delete_storage_data: true + response: + status: 400 + headers: + Content-Type: "application/json" + body: + msg: "Server does not support storage data deletion. Set delete_storage_data to false or omit the parameter." + status_code: 400 \ No newline at end of file diff --git a/openapi/paths/objects@register.yaml b/openapi/paths/objects@register.yaml new file mode 100644 index 00000000..8d09c73b --- /dev/null +++ b/openapi/paths/objects@register.yaml @@ -0,0 +1,59 @@ +post: + summary: Register DRS objects + description: >- + **Optional Endpoint**: This endpoint is not required for DRS server implementations. + Not all DRS servers support object registration. + + Registers one or more "candidate" DRS objects with the server. If it accepts the request, the server will create + unique object IDs for each registered object and return them in fully-formed DRS objects in response. + + This endpoint can be used after uploading files using methods negotiated with the `/upload-request` endpoint + to register the uploaded files as DRS objects, or to register existinf data. The request body should contain candidate + DRS objects with all required metadata including access methods that correspond + to the upload methods used during file upload. + + **RECOMMENDED - Transactional Behavior**: + Registration operations SHOULD be atomic transactions. If ANY candidate object fails validation + or registration, the ENTIRE request SHOULD fail and NO objects SHOULD be registered. Servers SHOULD + implement this as an all-or-nothing operation to ensure data consistency, but MAY implement partial + registration with appropriate error reporting if transactional behavior is not feasible. + + **Authentication**: GA4GH Passports can be provided in the request body for authorization. Bearer tokens can be supplied in headers. + + **Server Responsibilities**: + - SHOULD treat registration as an atomic transaction (all succeed or all fail) + - SHOULD validate ALL candidate objects before registering ANY + - Create unique object IDs for each registered object + - Add timestamps (created_time, updated_time) + - SHOULD roll back any partial changes if any candidate fails validation + + **Client Responsibilities**: + - Provide required DRS object metadata for all candidates + - Include access methods corresponding to uploaded file locations + - Ensure checksums match uploaded file content + - Handle potential failure of entire batch if any single object is invalid + operationId: RegisterObjects + security: + - {} + - BasicAuth: [] + - BearerAuth: [] + - PassportAuth: [] + requestBody: + $ref: '../components/requestBodies/RegisterObjectsBody.yaml' + responses: + 201: + $ref: '../components/responses/201ObjectsCreated.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' + tags: + - Objects + x-swagger-router-controller: ga4gh.drs.server + x-codegen-request-body-name: body \ No newline at end of file diff --git a/openapi/paths/objects@{object_id}.yaml b/openapi/paths/objects@{object_id}.yaml index d7fee2a7..3c66c6c6 100644 --- a/openapi/paths/objects@{object_id}.yaml +++ b/openapi/paths/objects@{object_id}.yaml @@ -54,9 +54,13 @@ get: post: summary: Get info about a DrsObject through POST'ing a Passport. description: >- - Returns object metadata, and a list of access methods that can be used to fetch object bytes. - - Method is a POST to accommodate a JWT GA4GH Passport sent in the formData in order to authorize access. + Returns object metadata and a list of access methods that can be used to fetch object bytes. + Method is a POST to accommodate a JWT GA4GH Passport sent in the request body in order to authorize access. + + **Note**: To upload new files and register them as DRS objects, use the `/upload-request` endpoint + to obtain upload methods and temporary credentials, then use POST `/objects/register` endpoint + to register multiple objects at once. Note that upload functionality is optional and not all DRS + servers implement the upload endpoints. operationId: PostObject security: - PassportAuth: [] diff --git a/openapi/paths/objects@{object_id}@access-methods.yaml b/openapi/paths/objects@{object_id}@access-methods.yaml new file mode 100644 index 00000000..1972347d --- /dev/null +++ b/openapi/paths/objects@{object_id}@access-methods.yaml @@ -0,0 +1,44 @@ +post: + summary: Update access methods for a DRS object + description: >- + **Optional Endpoint**: Not all DRS servers support access method updates. + + Update the access methods for an existing DRS object. Only access methods are modified - + core object metadata (size, checksums, name) remains unchanged. Servers MAY validate + that new access methods point to the same data. + + Note that existing access methods are overwritten, if clients want to add additional + access methods they should first retrieve the current methods and include them along + with the new methods in this request. + + **Authentication**: GA4GH Passports can be provided in the request body. + operationId: updateObjectAccessMethods + parameters: + - name: object_id + in: path + required: true + schema: + type: string + description: DRS object identifier + requestBody: + $ref: '../components/requestBodies/AccessMethodUpdateBody.yaml' + responses: + '200': + $ref: '../components/responses/200AccessMethodUpdate.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' + '500': + $ref: '../components/responses/500InternalServerError.yaml' + security: + - {} + - BasicAuth: [] + - BearerAuth: [] + - PassportAuth: [] + tags: + - Objects \ No newline at end of file diff --git a/openapi/paths/objects@{object_id}@delete.yaml b/openapi/paths/objects@{object_id}@delete.yaml new file mode 100644 index 00000000..a700ce91 --- /dev/null +++ b/openapi/paths/objects@{object_id}@delete.yaml @@ -0,0 +1,119 @@ +post: + summary: Delete a DRS object (optional endpoint) + description: >- + **Optional Endpoint**: This endpoint is not required for DRS server implementations. + Not all DRS servers support delete functionality. + + Deletes a DRS object by ID. This operation removes the DRS object metadata and optionally + attempts to delete the underlying storage data based on the delete_storage_data parameter and server capabilities. + + By default, only DRS object metadata is deleted while preserving underlying storage data. + To attempt storage data deletion, clients must explicitly set delete_storage_data to true and the + server must support storage data deletion (advertised via `deleteStorageDataSupported` in service-info). + Servers will make a best effort attempt to delete storage data, but success is not guaranteed. + + This endpoint uses POST method to accommodate GA4GH Passport authentication in the request body, + ensuring compatibility across all HTTP clients and proxies. + + **Important**: HTTP responses (204 No Content) indicate metadata deletion success only, + not storage deletion success (which are not guaranteed to complete synchronously if they occur at all) + operationId: DeleteObject + security: + - {} + - BasicAuth: [] + - BearerAuth: [] + - PassportAuth: [] + parameters: + - $ref: '../components/parameters/ObjectId.yaml' + requestBody: + $ref: '../components/requestBodies/DeleteBody.yaml' + responses: + 204: + $ref: '../components/responses/204DeleteSuccess.yaml' + 400: + $ref: '../components/responses/400BadRequestDelete.yaml' + 401: + $ref: '../components/responses/401Unauthorized.yaml' + 403: + $ref: '../components/responses/403ForbiddenDelete.yaml' + 404: + $ref: '../components/responses/404NotFoundDelete.yaml' + 500: + $ref: '../components/responses/500InternalServerError.yaml' + tags: + - Objects + x-swagger-router-controller: ga4gh.drs.server + x-codegen-request-body-name: body + examples: + successful_metadata_delete: + summary: Successful metadata-only deletion + description: Complete example of successfully deleting DRS object metadata while preserving storage data + value: + request: + method: POST + url: "/objects/drs_object_123456/delete" + headers: + Content-Type: "application/json" + body: + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + delete_storage_data: false + response: + status: 204 + headers: + Content-Length: "0" + successful_full_delete: + summary: Successful full deletion + description: Complete example of successfully deleting both metadata and storage data + value: + request: + method: POST + url: "/objects/drs_object_123456/delete" + headers: + Content-Type: "application/json" + body: + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnYTRnaF9wYXNzcG9ydF92MSI6W119.JJ5rN0ktP0qwyZmIPpxmF_p7JsxAZH6L6brUxtad3CM" + delete_storage_data: true + response: + status: 204 + headers: + Content-Length: "0" + object_not_found: + summary: Object not found error + description: Complete example when trying to delete a non-existent object + value: + request: + method: POST + url: "/objects/nonexistent_object/delete" + headers: + Content-Type: "application/json" + body: + delete_storage_data: false + response: + status: 404 + headers: + Content-Type: "application/json" + body: + msg: "DRS object nonexistent_object does not exist" + status_code: 404 + insufficient_permissions: + summary: Insufficient permissions error + description: Complete example when client lacks delete permissions + value: + request: + method: POST + url: "/objects/drs_object_123456/delete" + headers: + Content-Type: "application/json" + body: + passports: + - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbnZhbGlkX3Bhc3Nwb3J0IjoidHJ1ZSJ9.invalid_signature" + delete_storage_data: false + response: + status: 403 + headers: + Content-Type: "application/json" + body: + msg: "Client lacks delete permission for object drs_object_123456" + status_code: 403 \ No newline at end of file diff --git a/openapi/paths/service-info.yaml b/openapi/paths/service-info.yaml index b2c994bd..531d03b2 100644 --- a/openapi/paths/service-info.yaml +++ b/openapi/paths/service-info.yaml @@ -2,6 +2,7 @@ get: summary: Retrieve information about this service description: |- Returns information about the DRS service along with stats pertaning to total object count and cumulative size in bytes. + Also indicates whether the server supports optional upload and delete operations and which methods are available. Extends the [v1.0.0 GA4GH Service Info specification](https://github.com/ga4gh-discovery/ga4gh-service-info) @@ -14,7 +15,7 @@ get: * a `type.group` value of `org.ga4gh` * a `type.artifact` value of `drs` - e.g. + **Example 1: Server with upload and delete capabilities** ``` { "id": "com.example.drs", @@ -28,10 +29,62 @@ get: ... "drs":{ "maxBulkRequestLength": 200, - "stats": { - "objectCount": 774560, - "totalObjectSize": 4018437188907752 - } + "objectCount": 774560, + "totalObjectSize": 4018437188907752, + "uploadRequestSupported": true, + "objectRegistrationSupported": true, + "supportedUploadMethods": ["s3", "https", "gs"], + "maxUploadSize": 5368709120, + "maxUploadRequestLength": 50, + "validateUploadChecksums": true, + "validateUploadFileSizes": false, + "relatedFileStorageSupported": true, + "deleteSupported": true, + "maxBulkDeleteLength": 100, + "deleteStorageDataSupported": true + } + } + ``` + + **Example 2: Read-only server (no upload or delete)** + ``` + { + "id": "com.example.readonly-drs", + "description": "Read-only DRS service", + ... + "type": { + "group": "org.ga4gh", + "artifact": "drs", + "version": "1.5" + } + ... + "drs":{ + "maxBulkRequestLength": 500, + "objectCount": 1250000, + "totalObjectSize": 8500000000000000 + } + } + ``` + + **Example 3: Server with metadata-only delete capability** + ``` + { + "id": "com.example.metadata-drs", + "description": "DRS service with metadata-only delete", + ... + "type": { + "group": "org.ga4gh", + "artifact": "drs", + "version": "1.5" + } + ... + "drs":{ + "maxBulkRequestLength": 200, + "objectCount": 500000, + "totalObjectSize": 2500000000000000, + "deleteSupported": true, + "maxBulkDeleteLength": 50, + "deleteStorageDataSupported": false } } ``` diff --git a/openapi/paths/upload-request.yaml b/openapi/paths/upload-request.yaml new file mode 100644 index 00000000..f690eb0d --- /dev/null +++ b/openapi/paths/upload-request.yaml @@ -0,0 +1,60 @@ +post: + summary: Request upload methods for files + description: >- + **Optional Endpoint**: This endpoint is not required for DRS server implementations. + Not all DRS servers support upload functionality. + + Request upload method details and temporary credentials for uploading one or more files to an underlying storage service. + This endpoint allows clients to obtain the necessary information to upload files before they are registered as DRS objects. + + **Discovery**: Before using this endpoint, clients should check the `/service-info` endpoint + to determine if upload operations are supported. Look for `drs.uploadRequestSupported: true` and + `drs.supportedUploadMethods` to understand which upload methods are available. Also check + `drs.maxUploadSize` and `drs.maxUploadRequestLength` for server limits. + + **Usage Flow:** + + 1. **Discovery**: Client checks `/service-info` endpoint to confirm upload support (`drs.uploadRequestSupported: true`) and available methods (`drs.supportedUploadMethods`) + + 2. Client sends an upload request with file metadata (name, size, checksums, MIME type) + + 3. Server responds with available upload methods (S3, HTTPS, Google Cloud Storage, etc.) and temporary credentials + + 4. Client selects one or more upload methods from the response and uses the corresponding credentials to upload the file to the storage service + + 5. Once uploaded, the client registers the files as DRS objects including access methods that correspond to the upload methods used with a POST request to `/objects/register`, the server will return fully formed DRS objects with server minted unique IDs. + + 6. The registered DRS object becomes accessible through standard DRS API endpoints + + + **Authentication:** + + The endpoint supports multiple authentication methods including GA4GH Passport tokens sent in the request body. + Passport tokens enable fine-grained authorization based on data access policies. + + **Upload Methods**: Response may include multiple options (s3, https, gs, ftp/sftp) for flexibility. Note that servers may return a subset of their advertised `supportedUploadMethods` based on file-specific factors such as file type, size, or server policies. + + **File Integrity**: All requests must include at least one checksum per file (SHA-256, MD5, or other IANA-registered algorithms). + + **Server Validation**: Servers MAY validate checksums/sizes but are not required to. Check service-info for validation behavior. Servers do not validate MIME types against actual file content - clients are responsible for providing accurate MIME type information. + operationId: PostUploadRequest + security: + - {} + - BasicAuth: [] + - BearerAuth: [] + - PassportAuth: [] + requestBody: + $ref: '../components/requestBodies/UploadRequestBody.yaml' + responses: + '200': + $ref: '../components/responses/200UploadRequest.yaml' + '400': + $ref: '../components/responses/400BadRequest.yaml' + '401': + $ref: '../components/responses/401Unauthorized.yaml' + '403': + $ref: '../components/responses/403Forbidden.yaml' + '500': + $ref: '../components/responses/500InternalServerError.yaml' + tags: + - Upload Request \ No newline at end of file diff --git a/openapi/tags/AccessMethodUpdate.md b/openapi/tags/AccessMethodUpdate.md new file mode 100644 index 00000000..a938812a --- /dev/null +++ b/openapi/tags/AccessMethodUpdate.md @@ -0,0 +1,176 @@ +# Access Method Updates + +> **Optional Functionality**: Access method updates are optional extensions to the DRS API. Not all DRS servers are required to implement this functionality. Clients should check `/service-info` for `accessMethodUpdateSupported` before attempting to use these endpoints. + +Access method update endpoints allows authorized clients to modify how existing DRS objects can be accessed without changing the core object metadata (size, checksums, name). This is useful for storage migrations, adding mirrors, or updating URLs. + +These endpoints will overwrite existing access methods for an object, if clients want to add access methods in addition to existing ones for objects they should first retrieve the current access methods and include them in the update request along with the new methods. + +## Use Cases + +- **Storage Migration**: Move data between storage providers while keeping same DRS object +- **Mirror Addition**: Add CDN or regional access points for better performance +- **URL Refresh**: Update changed domain names +- **Access Optimization**: Add or remove access methods based on performance or cost + +## Design Principles + +- **Optional**: Access method update support is completely optional +- **Immutable Core**: Only access methods can be updated - size, checksums, name remain unchanged +- **Atomic Bulk Operations**: All updates succeed or all fail (transactional) +- **Optional Validation**: Servers MAY validate new access methods point to same data +- **Flexible Authentication**: Supports GA4GH Passports, Bearer tokens, API keys + +## Service Discovery + +Check `/service-info` for access method update capabilities: + +```json +{ + "drs": { + "accessMethodUpdateSupported": true, + "maxBulkAccessMethodUpdateLength": 100, + "validateAccessMethodUpdates": false + } +} +``` + +- **`accessMethodUpdateSupported`**: Whether server supports access method updates +- **`maxBulkAccessMethodUpdateLength`**: Maximum objects per bulk update request +- **`validateAccessMethodUpdates`**: Whether server validates new access methods + +## Single Object Update + +Update access methods for a single DRS object: + +```bash +curl -X POST "https://drs.example.org/objects/obj_123/access-methods" \ + -H "Content-Type: application/json" \ + -d '{ + "access_methods": [ + { + "type": "https", + "access_url": { + "url": "https://new-cdn.example.org/data/file.bam" + } + }, + { + "type": "s3", + "access_id": "s3, + "access_url": { + "url": "s3://new-bucket/migrated/file.bam" + } + } + ] + }' +``` + +## Bulk Object Update + +Update access methods for multiple objects atomically: + +```bash +curl -X POST "https://drs.example.org/objects/access-methods" \ + -H "Content-Type: application/json" \ + -d '{ + "updates": [ + { + "object_id": "obj_123", + "access_methods": [ + { + "type": "https", + "access_url": {"url": "https://new-location.com/file1.bam"} + } + ] + }, + { + "object_id": "obj_456", + "access_methods": [ + { + "type": "s3", + "access_url": {"url": "s3://new-bucket/file2.vcf"} + } + ] + } + ] + }' +``` + +## Authentication + +**GA4GH Passports** (in request body): +```json +{ + "access_methods": [...], + "passports": ["eyJhbGci..."] +} +``` + +**Bearer Tokens** (in headers): +```bash +curl -H "Authorization: Bearer token" -d '{"access_methods": [...]}' ... +``` + +## Validation + +Servers MAY validate that new access methods point to the same data by checkingm file availability, checksums or file content. Validation behavior is advertised in `validateAccessMethodUpdates` service-info field. + + +## Error Responses + +- **400**: Invalid access methods or validation failure +- **401**: Authentication required +- **403**: Insufficient permissions for object(s) +- **404**: Object not found or access method updates not supported +- **413**: Bulk request exceeds `maxBulkAccessMethodUpdateLength` limit + +## Examples + +**Storage Migration:** +```bash +# Check server capabilities +curl "https://drs.example.org/service-info" + +# Update single object after migration +curl -X POST "https://drs.example.org/objects/obj_123/access-methods" \ + -d '{"access_methods": [{"type": "s3", "access_url": {"url": "s3://new-bucket/file.bam"}}]}' +``` + +**Add CDN Mirror:** +```bash +# Add additional access method without removing existing ones +curl -X POST "https://drs.example.org/objects/obj_456/access-methods" \ + -d '{ + "access_methods": [ + {"type": "https", "access_url": {"url": "https://origin.example.org/file.vcf"}}, + {"type": "https", "access_url": {"url": "https://cdn.example.org/file.vcf"}} + ] + }' +``` + +**Bulk Migration:** +```bash +# Migrate multiple objects atomically +curl -X POST "https://drs.example.org/objects/access-methods" \ + -d '{ + "updates": [ + {"object_id": "obj_1", "access_methods": [...]}, + {"object_id": "obj_2", "access_methods": [...]} + ] + }' +``` + +## Best Practices + +**Clients**: Check service-info first, handle atomic transaction failures, respect bulk limits, verify permissions + +**Servers**: Advertise capabilities clearly, implement atomic transactions for bulk operations, validate permissions, consider optional validation for data integrity + +## Backward Compatibility + +Access method update functionality is designed to be backward compatible: + +- **No Impact on Existing Endpoints**: All existing DRS endpoints remain unchanged +- **Optional Implementation**: Servers can ignore this functionality entirely +- **Graceful Degradation**: Clients receive 404 responses when not supported +- **Safe Defaults**: New service-info fields have safe default values \ No newline at end of file diff --git a/openapi/tags/ObjectDeletion.md b/openapi/tags/ObjectDeletion.md new file mode 100644 index 00000000..cd0cc623 --- /dev/null +++ b/openapi/tags/ObjectDeletion.md @@ -0,0 +1,168 @@ +# Object Deletion + +> **Optional Functionality**: Delete support is an **optional** extension to the DRS API. Not all DRS servers are required to implement delete functionality. Clients should check for the availability of delete endpoints before attempting to use them. + +DRS delete functionality allows suitably authenticated clients to request that DRS objects are removed from the server and, optionally, to request that the server attempt to delete the underlying data. + +Servers should ensure that they trust clients from whom they receive delete requests, and may choose to implement "soft" deletes to minimise the risk of accidental or malicious requests. The DRS specification does not currently provide explicit support for soft deletes. Because delete support is optional, servers operating in untrusted environments may choose not to support delete operations at all. + +In combination with the `/objects/register` endpoint, metadata only delete requests offer a means for clients to update DRS metadata without affecting the underlying data, and without introducing additional update operations which would complicate server implementation. + +Clients can express a preference that the underlying data referred to by the deleted DRS object(s) is deleted with the `delete_storage_data` parameter. Servers are free to interpret this as they choose, and can advertise whether they support it at all with the `deleteStorageDataSupported` flag. Servers that choose to attempt to honour the request need not perform this operation synchronously and may, for example, register the file for later deletion. Implementations may also choose to ensure that no other DRS object registered in the server refers to the underlying data before deleting. Servers may not have the necessary permissions to delete the data from the backend even if they would like to do so, or may encounter errors when they attempt deletion. In the case that a DRS object refers to data stored in multiple backends (e.g. has multiple `access_method`s) the server may attempt to delete the data from all or only some of the backends. + +For these reasons clients MUST NOT depend on the server deleting the underlying storage data even if the server advertises that `deleteStorageDataSupported` and the client sets the `delete_storage_data` flag. + +In situations where the DRS server controls the storage backend, DRS delete support offers a convenient vendor-neutral way for clients to update and delete DRS objects and corresponding data. + +For bulk deletes using the `/objects/delete` endpoint the server SHOULD implement transaction semantics: if any object fails validation or deletion, the entire request should fail and no objects are deleted and no attempt is made to delete from underlying storage for any object. + +## Design principles + +- **Optional**: Delete support is completely optional +- **Safety**: Preserves underlying data in storage unless explicitly requested +- **Backward compatible**: No impact on existing DRS functionality +- **Flexible authentication**: Supports GA4GH Passports, Bearer tokens, API keys +- **Use POST rather than DELETE**: GA4GH Passports require request bodies, which DELETE methods don't reliably support across all HTTP infrastructure. POST ensures broad compatibility. + +## Service Discovery + +Check `/service-info` for delete capabilities: + +```json +{ + "drs": { + "uploadRequestSupported": true, + "objectRegistrationSupported": true, + "supportedUploadMethods": ["s3", "https"], + "relatedFileStorageSupported": true, + "deleteSupported": true, + "maxBulkDeleteLength": 100, + "deleteStorageDataSupported": true + } +} +``` + +- **`deleteSupported`**: Whether server supports deletion +- **`maxBulkDeleteLength`**: Maximum objects per bulk delete request +- **`deleteStorageDataSupported`**: Whether server can attempt to delete underlying storage files + +### Single Object Delete: `POST /objects/{object_id}/delete` + +```bash +curl -X POST "https://drs.example.org/objects/drs_object_123456/delete" \ + -H "Content-Type: application/json" \ + -d '{"passports": ["..."], "delete_storage_data": false}' +# Response: 204 No Content (indicates metadata deletion success only) +``` + +**Note**: HTTP responses indicate metadata deletion status only. Storage deletion (`delete_storage_data: true`) is a best effort attempt with no guarantee of success. + +### Bulk Object Delete: `POST /objects/delete` + +```bash +curl -X POST "https://drs.example.org/objects/delete" \ + -H "Content-Type: application/json" \ + -d '{ + "bulk_object_ids": ["obj_1", "obj_2", "obj_3"], + "passports": ["..."], + "delete_storage_data": false + }' +# Response: 204 No Content (all metadata deleted) or 4xx error (no objects deleted) +``` + +## Authentication + +**GA4GH Passports** (in request body): + +```json +{"passports": ["eyJhbGci..."], "delete_storage_data": false} +``` + +**Bearer Tokens** (in headers): + +```bash +curl -H "Authorization: Bearer token" -d '{"delete_storage_data": false}' ... +``` + +## Underlying Storage Data + +**Important**: Storage data deletion is never guaranteed. Even when `delete_storage_data: true` is requested and the server supports it, the actual deletion may fail due to permissions, network issues, or storage service errors. Clients shoud not depend on storage deletion success. + +Clients can request that the server attempts to delete the underlying data referred to by the DRS object using the `delete_storage_data` parameter. + +**`delete_storage_data: false`** (default): Removes DRS object metadata only, preserves underlying storage files + +**`delete_storage_data: true`**: Removes metadata AND requests server attempt to delete underlying storage files (requires `deleteStorageDataSupported: true`, **success not guaranteed**) + +## Update Pattern + +Rather than introducing additional operations and endpoints for updating DRS objects, servers can allow clients to use the metadata-only deletion and object registration endpoints to create a new DRS object with updated metadata while leaving the underlying data in place. + +**Metadata update steps:** + +1. Delete metadata only: `POST /objects/{id}/delete` with `delete_storage_data: false` +2. Re-register object: `POST /objects/register` with updated metadata + +```bash +# Delete metadata (preserves storage) +curl -X POST ".../objects/obj_123/delete" -d '{"delete_storage_data": false}' +# Re-register with updates +curl -X POST ".../objects/register" -d '{"candidates": [{"name": "updated.txt", ...}]}' +``` + +## Error Responses + +- **400**: Unsupported storage deletion or invalid request parameters +- **403**: Insufficient permissions for any object in the request +- **404**: Any object not found or delete endpoints not supported by server +- **413**: Bulk request exceeds `maxBulkDeleteLength` limit + +## Examples + +**Metadata Update:** + +```bash +curl ".../service-info" # Check capabilities +curl -X POST ".../objects/obj_123/delete" -d '{"delete_storage_data": false}' +curl -X POST ".../objects/register" -d '{"candidates": [{"name": "updated.vcf", ...}]}' +``` + +**Complete Removal:** + +```bash +curl -X POST ".../objects/obj_456/delete" -H "Authorization: Bearer token" \ + -d '{"delete_storage_data": true}' +``` + +**Bulk Delete (Atomic):** + +```bash +curl -X POST ".../objects/delete" -d '{ + "bulk_object_ids": ["obj_1", "obj_2"], + "passports": ["..."], + "delete_storage_data": false +}' +# All objects deleted or none deleted (transactional) +``` + +## Best Practices + +**Clients:** Check service-info, default to safe deletion, handle transactional failures, respect limits, confirm destructive operations, do not rely on underlying storage deletion + +**Servers:** Advertise capabilities, validate permissions, implement atomic transactions, implement limits, use versioning to avoid inadvertent deletion. + +## Security Considerations + +- **Authentication**: Validate GA4GH Passports and Bearer tokens +- **HTTPS Required**: Protect credentials in transit +- **Rate Limiting**: Prevent abuse of delete endpoints +- **Input Validation**: Sanitize all request parameters + +## Backward Compatibility + +Delete functionality is designed to be backward compatible: + +- **No Impact on Existing Endpoints**: All existing DRS endpoints remain unchanged +- **Optional Implementation**: Servers can ignore delete functionality entirely +- **Graceful Degradation**: Clients receive 404 responses when delete is not supported +- **Safe Defaults**: New fields in service-info have safe default values, and requests default to leaving underlying data in place. diff --git a/openapi/tags/UploadRequest.md b/openapi/tags/UploadRequest.md new file mode 100644 index 00000000..07fbca7d --- /dev/null +++ b/openapi/tags/UploadRequest.md @@ -0,0 +1,480 @@ +# Upload Requests and Object Registration + +> **Optional Functionality**: Upload and object registration are optional DRS extensions. Clients should check `/service-info` for `uploadRequestSupported` and `objectRegistrationSupported` before attempting to use these endpoints. + +The DRS upload and object registration endpoints allows clients to negotiate with servers on mutually convenient storage backends and then register uploads as DRS objects through a three-phase workflow: + +1. **Request Upload URLs**: POST `/upload-request` with file metadata to receive upload methods and credentials +2. **Upload Files**: Use returned URLs and credentials to upload files to storage using existing upload mechanisms. DRS is not involved in this step at all, DRS simply enables clients and servers to agree on a mutually convenient storage service. +3. **Register Objects**: POST `/objects/register` to register "candidate" DRS objects with the server + +This approach separates storage service and credential negotiation from file transfer and object registration, supporting a vendor-neutral means of sharing data in a DRS network. + +The `/objects/register` endpoint can be used independently to register existing data without using the `/upload-request` endpoint, and servers can choose to only support object registration and not file uploads by setting the `uploadRequestSupported` and `objectRegistrationSupported` flags appropriately in `/service-info`. + +Upload requests and object registration endpoints only support bulk requests to simplify implementation and reflect real-world usage patterns. Bioinformatics workflows often involve uploading multiple related files together (e.g., BAM and VCF files with their indices, or analysis result sets), making bulk operations a natural fit. Single files/objects are handled as lists with one element. Implementations of the `/objects/register` endpoint SHOULD implement transaction semantics so that either all of the objects are successfully registered or none of them are, and clients should be robust to this behaviour. Transaction semantics for the `/upload-request` are encouraged but not required due to the variety and complexity of data transfer technologies. + +The `/upload-request` endpoint does not require any state to be maintained on the DRS server (intermediate DRS object IDs etc.) it is simply a means for a server to provide details of where a client can upload data, and it should ensure that it trusts the client before providing such details. This means that if uploads fail and there is no later call to `/objects/register` there is no DRS state to manage, simplifying server implementation. + +Servers SHOULD ensure that any data from unsuccessful uploads (e.g. incomplete multi-part uploads) are cleaned up, for example by using lifecycle configuration in the backend storage. There is _no_ means of requiring that a client ultimately registers a DRS object pointing at data uploaded, and so servers should consider implementing some form of storage "garbage" collection (or simply set a short lifecycle policy on the upload location and move uploaded data that is later registered as DRS objects to other locations, updating the `access_method`s accordingly). Servers should also implement some means of constraining upload size (quotas etc.) to protect against accidental or malicious unconstrained uploads. + +The `/upload-request` endpoint can return one or more `upload_method`s of different types for each requested file, and backend specific details such as bucket names, object keys and credentials are supplied in a generic `upload_details` field. A straightforward implementation might return an single time-limited pre-signed POST URL as the `post_url` for an `upload_method` of type `https` which incorporates authentication into the URL, but because DRS is often used for large files such as BAMs and CRAMs we also want to support more sophisticated upload approaches implemented by storage backends such as multi-part uploads, automatic retries etc. The `upload_details` field can also be used to include bucket names, keys and temporary credentials that can be used in native clients and SDKs. This offers a natural way to adapt this protocol to new storage technologies. Refer to the examples below for some suggested implementations. + +## Service Discovery + +Check `/service-info` for upload capabilities: + +```json +{ + "drs": { + "uploadRequestSupported": true, + "objectRegistrationSupported": true, + "supportedUploadMethods": ["s3", "https", "gs"], + "maxUploadSize": 5368709120, + "maxUploadRequestLength": 50, + "maxRegisterRequestLength": 50, + "validateUploadChecksums": true, + "validateUploadFileSizes": false, + "relatedFileStorageSupported": true + } +} +``` + +Upload related fields: + +- `uploadRequestSupported`: Upload request operations available via `/upload-request` +- `objectRegistrationSupported`: Object registration operations available via `/objects/register` +- `supportedUploadMethods`: Available storage backends +- `maxUploadSize`: File size limit (bytes) +- `maxUploadRequestLength`: Files per request limit for upload requests +- `maxRegisterRequestLength`: Candidate objects per request limit for registration +- `validateUploadChecksums`/`validateUploadFileSizes`: Server validation behavior +- `relatedFileStorageSupported`: Files from same upload request stored under common prefixes + +## Upload Methods + +Upon receipt of a request for an upload method for a specific file, the server will respond with one or more `upload_method` with associated `type` and corresponding `upload_details` with upload locations, temporary credentials etc. These details are specific to backend implementations. + +Example storage backends: + +- **https**: Presigned POST URLs for HTTP uploads +- **s3**: Direct S3 upload with temporary AWS credentials +- **gs**: Google Cloud Storage with OAuth2 tokens +- **ftp/sftp**: Traditional file transfer protocols using negotiated credentials + +Servers may return a subset of advertised methods based on file characteristics, for example they may choose to store large objects such as WGS BAM files in different backends to small csv files. + +## Related File Storage (Optional) + +Servers MAY support storing files from the same upload request under common prefixes, enabling bioinformatics workflows that expect co-located files: + +- **CRAM + CRAI**: Alignment files with index files +- **VCF + TBI**: Variant files with tabix indexes +- **FASTQ.ora + ORADATA.tar.gz**: Compressed files with associated reference data + +Check `relatedFileStorageSupported` in service-info or examine upload URLs for common prefixes. + +## Object Registration + +After upload, clients can register files in bulk as DRS objects using POST `/objects/register`. Registration is all-or-nothing. If any candidate object fails to be registered in the server, the entire request fails and no objects are registered. + +**Candidate DRS object equirements**: + +- Complete metadata (name, size, checksums, MIME type) +- Access methods pointing to file locations +- Valid authorization (if required) +- Do not include server-generated fields (id, self_uri, timestamps) + +Upon receipt of candidate objects for registration the server will create unique object IDs and returns complete DRS objects. Note that the server is not obliged to retain the clients supplied `access_method`s and is free to move data to different locations/backends once the object is registered. This means that a server can choose to receive uploads in a dedicated "dropzone", with hard quotas and additional security, and then move them to more permanent storage once the DRS object is registered. Clients SHOULD NOT cache the response from `/objects/register` as the `access_method`s might change after registration. + +The `/objects/register` endpoint can also be used independently to register existing data that is already stored in accessible locations, without using the `/upload-request` workflow. This is useful for registering pre-existing datasets or files uploaded through other means. Servers may choose only to support registration and not uploads, and should advertise this in `/service-info` + +## Authentication & Validation + +**Authentication**: Supports GA4GH Passports, Basic auth, and Bearer tokens. + +**Checksums**: Required for all files (SHA-256, MD5, or IANA-registered algorithms). Servers MAY validate checksums and file sizes as advertised in service-info flags. + +## Error Handling + +**Client Errors (4xx)**: + +- Invalid metadata (400) +- Missing auth (401) +- Insufficient permissions (403) + +**Server Errors (5xx)**: + +- Storage unavailable (500) +- Capacity limits (503) + +## Best Practices + +**Clients**: Check service-info first, calculate checksums, be robust to failed object registration +**Servers**: Use short-lived tightly scoped credentials, support multiple upload methods, implement rate limiting, ensure unique storage backend names to avoid inadvertent overwrites (e.g. using UUIDs), ensure that quotas are enforced and incomplete or unregistered uploads are deleted +**Security**: Time-limited credentials, single-use URLs, logging for audit + +## Security Considerations + +**Credential Scoping**: Implementers SHOULD scope upload credentials to the minimum necessary permissions and duration. Credentials should: + +- Allow write access only to the specific upload URL/path provided +- Have the shortest practical expiration time (e.g. 15 minutes to 1 hour) +- Be restricted to the specific file size and content type when possible +- Not grant broader storage access beyond the intended upload location + +This principle of least privilege reduces security exposure if credentials are compromised or misused. + +## Example Workflows + +### Simple HTTPS Upload + +Upload Request: + +```http +POST /upload-request +Content-Type: application/json + +{ + "requests": [ + { + "name": "variants.vcf", + "size": 52428800, + "mime_type": "text/plain", + "checksums": [ + { + "checksum": "5d41402abc4b2a76b9719d911017c592", + "type": "md5" + } + ] + } + ] +} +``` + +Response: + +```json +{ + "responses": [ + { + "name": "variants.vcf", + "size": 52428800, + "mime_type": "text/plain", + "checksums": [ + { + "checksum": "5d41402abc4b2a76b9719d911017c592", + "type": "md5" + } + ], + "upload_methods": [ + { + "type": "https", + "access_url": { + "url": "https://uploads.example.org/variants.vcf" + }, + "upload_details": { + "post_url": "https://uploads.example.org/presigned-upload?signature=FAKE_SIG" + } + } + ] + } + ] +} +``` + +Upload via HTTPS: + +```bash +# Simple PUT upload to presigned URL +curl -X PUT "https://uploads.example.org/presigned-upload?signature=FAKE_SIG" \ + --data-binary @variants.vcf +``` + +Register DRS Object: + +```http +POST /objects/register +Content-Type: application/json + +{ + "candidates": [ + { + "name": "variants.vcf", + "size": 52428800, + "mime_type": "text/plain", + "checksums": [ + { + "checksum": "5d41402abc4b2a76b9719d911017c592", + "type": "md5" + } + ], + "access_methods": [ + { + "type": "https", + "access_url": { + "url": "https://uploads.example.org/variants.vcf" + } + } + ], + "description": "Variant calls in VCF format" + } + ] +} +``` + +Response: + +```json +{ + "objects": [ + { + "id": "drs_obj_f6e5d4c3b2a1", + "self_uri": "drs://drs.example.org/drs_obj_f6e5d4c3b2a1", + "name": "variants.vcf", + "size": 52428800, + "mime_type": "text/plain", + "created_time": "2024-01-15T10:45:00Z", + "updated_time": "2024-01-15T10:45:00Z", + "version": "1.0", + "checksums": [ + { + "checksum": "5d41402abc4b2a76b9719d911017c592", + "type": "md5" + } + ], + "access_methods": [ + { + "type": "https", + "access_url": { + "url": "https://uploads.example.org/variants.vcf" + } + } + ], + "description": "Variant calls in VCF format" + } + ] +} +``` + +### S3 Bulk Upload (BAM + Index) + +Request Upload Methods for Related Files + +```http +POST /upload-request +Content-Type: application/json + +{ + "requests": [ + { + "name": "sample.bam", + "size": 1073741824, + "mime_type": "application/octet-stream", + "checksums": [ + { + "checksum": "d41d8cd98f00b204e9800998ecf8427e", + "type": "md5" + } + ] + }, + { + "name": "sample.bam.bai", + "size": 2097152, + "mime_type": "application/octet-stream", + "checksums": [ + { + "checksum": "098f6bcd4621d373cade4e832627b4f6", + "type": "md5" + } + ] + } + ] +} +``` + +Response: + +```json +{ + "responses": [ + { + "name": "sample.bam", + "size": 1073741824, + "mime_type": "application/octet-stream", + "checksums": [ + { + "checksum": "d41d8cd98f00b204e9800998ecf8427e", + "type": "md5" + } + ], + "upload_methods": [ + { + "type": "s3", + "access_url": { + "url": "s3://genomics-uploads/x7k9m/sample.bam" + }, + "upload_details": { + "bucket": "genomics-uploads", + "key": "x7k9m/sample.bam", + "access_key_id": "FAKE_ACCESS_KEY_123", + "secret_access_key": "FAKE_SECRET_KEY_456", + "session_token": "FAKE_SESSION_TOKEN_789", + "expires_at": "2024-01-15T12:00:00Z" + } + } + ] + }, + { + "name": "sample.bam.bai", + "size": 2097152, + "mime_type": "application/octet-stream", + "checksums": [ + { + "checksum": "098f6bcd4621d373cade4e832627b4f6", + "type": "md5" + } + ], + "upload_methods": [ + { + "type": "s3", + "access_url": { + "url": "s3://genomics-uploads/x7k9m/sample.bam.bai" + }, + "upload_details": { + "bucket": "genomics-uploads", + "key": "x7k9m/sample.bam.bai", + "access_key_id": "FAKE_ACCESS_KEY_123", + "secret_access_key": "FAKE_SECRET_KEY_456", + "session_token": "FAKE_SESSION_TOKEN_789", + "expires_at": "2024-01-15T12:00:00Z" + } + } + ] + } + ] +} +``` + +Upload Both Files to S3: + +```bash +# Upload BAM and index files using the supplied credentials (note common prefix) +aws s3 cp sample.bam s3://genomics-uploads/x7k9m/sample.bam +aws s3 cp sample.bam.bai s3://genomics-uploads/x7k9m/sample.bam.bai +``` + +Register Both DRS Objects: + +```http +POST /objects/register +Content-Type: application/json + +{ + "candidates": [ + { + "name": "sample.bam", + "size": 1073741824, + "mime_type": "application/octet-stream", + "checksums": [ + { + "checksum": "d41d8cd98f00b204e9800998ecf8427e", + "type": "md5" + } + ], + "access_methods": [ + { + "type": "s3", + "access_id": "s3", + "access_url": { + "url": "s3://genomics-uploads/x7k9m/sample.bam" + } + } + ], + "description": "BAM alignment file" + }, + { + "name": "sample.bam.bai", + "size": 2097152, + "mime_type": "application/octet-stream", + "checksums": [ + { + "checksum": "098f6bcd4621d373cade4e832627b4f6", + "type": "md5" + } + ], + "access_methods": [ + { + "type": "s3", + "access_id": "s3", + "access_url": { + "url": "s3://genomics-uploads/x7k9m/sample.bam.bai" + } + } + ], + "description": "BAM index file" + } + ] +} +``` + +Response: + +```json +{ + "objects": [ + { + "id": "drs_obj_a1b2c3d4e5f6", + "self_uri": "drs://drs.example.org/drs_obj_a1b2c3d4e5f6", + "name": "sample.bam", + "size": 1073741824, + "mime_type": "application/octet-stream", + "created_time": "2024-01-15T10:30:00Z", + "updated_time": "2024-01-15T10:30:00Z", + "version": "1.0", + "checksums": [ + { + "checksum": "d41d8cd98f00b204e9800998ecf8427e", + "type": "md5" + } + ], + "access_methods": [ + { + "type": "s3", + "access_id": "s3", + "access_url": { + "url": "s3://genomics-uploads/x7k9m/sample.bam" + } + } + ], + "description": "BAM alignment file" + }, + { + "id": "drs_obj_b2c3d4e5f6a1", + "self_uri": "drs://drs.example.org/drs_obj_b2c3d4e5f6a1", + "name": "sample.bam.bai", + "size": 2097152, + "mime_type": "application/octet-stream", + "created_time": "2024-01-15T10:30:00Z", + "updated_time": "2024-01-15T10:30:00Z", + "version": "1.0", + "checksums": [ + { + "checksum": "098f6bcd4621d373cade4e832627b4f6", + "type": "md5" + } + ], + "access_methods": [ + { + "type": "s3", + "access_id": "s3", + "access_url": { + "url": "s3://genomics-uploads/x7k9m/sample.bam.bai" + } + } + ], + "description": "BAM index file" + } + ] +} +```