diff --git a/README.md b/README.md index 7f648fe..597ef4c 100644 --- a/README.md +++ b/README.md @@ -218,11 +218,11 @@ Scan (full table) | 5000 ops | 234.56 ms | 21320 ops/sec - ✅ ProjectionExpression - ✅ BatchWriteItem - ✅ BatchGetItem +- ✅ Global Secondary Indexes ### Coming Soon - ⏳ UpdateItem (works but needs UPDATED_NEW/UPDATED_OLD response filtering to work for full Dynamo Parity) -- ⏳ Global Secondary Indexes - ⏳ Local Secondary Indexes ## Configuration diff --git a/http.odin b/http.odin index 053a7f6..729bf24 100644 --- a/http.odin +++ b/http.odin @@ -6,6 +6,7 @@ import vmem "core:mem/virtual" import "core:net" import "core:strings" import "core:strconv" +import "core:thread" // HTTP Method enumeration HTTP_Method :: enum { @@ -100,7 +101,6 @@ response_set_body :: proc(resp: ^HTTP_Response, data: []byte) { } // Request handler function type -// Takes context pointer, request, and request-scoped allocator Request_Handler :: #type proc(ctx: rawptr, request: ^HTTP_Request, request_alloc: mem.Allocator) -> HTTP_Response // Server configuration @@ -122,6 +122,13 @@ default_server_config :: proc() -> Server_Config { } } +// Connection task data - passed to worker threads +Connection_Task_Data :: struct { + server: ^Server, + conn: net.TCP_Socket, + source: net.Endpoint, +} + // Server Server :: struct { allocator: mem.Allocator, @@ -168,9 +175,9 @@ server_start :: proc(server: ^Server) -> bool { server.socket = socket server.running = true - fmt.printfln("HTTP server listening on %v", server.endpoint) + fmt.printfln("HTTP server listening on %v (thread-per-connection)", server.endpoint) - // Accept loop + // Accept loop - spawn a thread for each connection for server.running { conn, source, accept_err := net.accept_tcp(socket) if accept_err != nil { @@ -180,9 +187,24 @@ server_start :: proc(server: ^Server) -> bool { continue } - // Handle connection in separate goroutine would go here - // For now, handle synchronously (should spawn thread) - handle_connection(server, conn, source) + // Allocate connection data + conn_data := new(Connection_Task_Data, server.allocator) + conn_data.server = server + conn_data.conn = conn + conn_data.source = source + + // Spawn a new thread for this connection + t := thread.create(connection_worker_thread) + if t != nil { + t.init_context = context + t.data = conn_data + thread.start(t) + // Thread will clean itself up when done + } else { + // Failed to create thread, close connection + net.close(conn) + free(conn_data, server.allocator) + } } return true @@ -190,12 +212,24 @@ server_start :: proc(server: ^Server) -> bool { server_stop :: proc(server: ^Server) { server.running = false + + // Close listening socket if sock, ok := server.socket.?; ok { net.close(sock) server.socket = nil } } +// Worker thread procedure +connection_worker_thread :: proc(t: ^thread.Thread) { + defer thread.destroy(t) // Clean up thread when done + + conn_data := cast(^Connection_Task_Data)t.data + defer free(conn_data, conn_data.server.allocator) + + handle_connection(conn_data.server, conn_data.conn, conn_data.source) +} + // Handle a single connection handle_connection :: proc(server: ^Server, conn: net.TCP_Socket, source: net.Endpoint) { defer net.close(conn) @@ -214,7 +248,7 @@ handle_connection :: proc(server: ^Server, conn: net.TCP_Socket, source: net.End request_alloc := vmem.arena_allocator(&arena) - // TODO: Double check if we want *all* downstream allocations to use the request arena? + // Set request arena as context allocator for downstream allocations old := context.allocator context.allocator = request_alloc defer context.allocator = old diff --git a/open_api_doc.yaml b/open_api_doc.yaml new file mode 100644 index 0000000..7b4eba8 --- /dev/null +++ b/open_api_doc.yaml @@ -0,0 +1,884 @@ +openapi: 3.0.3 +info: + title: JormunDB DynamoDB Wire API + version: 0.1.0 + description: | + DynamoDB-compatible JSON-over-HTTP API implemented by JormunDB. + Requests are POSTed to a single endpoint (/) and routed by the required `X-Amz-Target` header. +servers: + - url: http://localhost:8002 + +paths: + /: + post: + summary: DynamoDB JSON API endpoint + description: | + Send DynamoDB JSON protocol requests to this endpoint and set `X-Amz-Target` to the operation name, + e.g. `DynamoDB_20120810.GetItem`. The request and response media type is typically + `application/x-amz-json-1.0`. + parameters: + - $ref: '#/components/parameters/XAmzTarget' + - $ref: '#/components/parameters/XAmzDate' + - $ref: '#/components/parameters/Authorization' + - $ref: '#/components/parameters/XAmzSecurityToken' + - $ref: '#/components/parameters/XAmzContentSha256' + requestBody: + required: true + content: + application/x-amz-json-1.0: + schema: + oneOf: + - $ref: '#/components/schemas/CreateTableRequest' + - $ref: '#/components/schemas/DeleteTableRequest' + - $ref: '#/components/schemas/DescribeTableRequest' + - $ref: '#/components/schemas/ListTablesRequest' + - $ref: '#/components/schemas/PutItemRequest' + - $ref: '#/components/schemas/GetItemRequest' + - $ref: '#/components/schemas/DeleteItemRequest' + - $ref: '#/components/schemas/UpdateItemRequest' + - $ref: '#/components/schemas/QueryRequest' + - $ref: '#/components/schemas/ScanRequest' + - $ref: '#/components/schemas/BatchWriteItemRequest' + - $ref: '#/components/schemas/BatchGetItemRequest' + - $ref: '#/components/schemas/TransactWriteItemsRequest' + - $ref: '#/components/schemas/TransactGetItemsRequest' + examples: + CreateTable: + summary: Create a table with a HASH key + value: + TableName: ExampleTable + KeySchema: + - AttributeName: pk + KeyType: HASH + AttributeDefinitions: + - AttributeName: pk + AttributeType: S + responses: + '200': + description: Successful operation response + content: + application/x-amz-json-1.0: + schema: + oneOf: + - $ref: '#/components/schemas/CreateTableResponse' + - $ref: '#/components/schemas/DeleteTableResponse' + - $ref: '#/components/schemas/DescribeTableResponse' + - $ref: '#/components/schemas/ListTablesResponse' + - $ref: '#/components/schemas/PutItemResponse' + - $ref: '#/components/schemas/GetItemResponseUnion' + - $ref: '#/components/schemas/DeleteItemResponse' + - $ref: '#/components/schemas/UpdateItemResponseUnion' + - $ref: '#/components/schemas/QueryResponse' + - $ref: '#/components/schemas/ScanResponse' + - $ref: '#/components/schemas/BatchWriteItemResponse' + - $ref: '#/components/schemas/BatchGetItemResponse' + - $ref: '#/components/schemas/TransactWriteItemsResponse' + - $ref: '#/components/schemas/TransactGetItemsResponse' + '400': + description: Client error (ValidationException, SerializationException, etc.) + content: + application/x-amz-json-1.0: + schema: + oneOf: + - $ref: '#/components/schemas/DynamoDbError' + - $ref: '#/components/schemas/TransactionCanceledException' + '500': + description: Server error + content: + application/x-amz-json-1.0: + schema: + $ref: '#/components/schemas/DynamoDbError' + +components: + parameters: + XAmzTarget: + name: X-Amz-Target + in: header + required: true + description: | + DynamoDB JSON protocol operation selector. + JormunDB recognizes targets with the `DynamoDB_20120810.` prefix. + Note: `UpdateTable` may be recognized but not implemented. + schema: + type: string + enum: + - DynamoDB_20120810.CreateTable + - DynamoDB_20120810.DeleteTable + - DynamoDB_20120810.DescribeTable + - DynamoDB_20120810.ListTables + - DynamoDB_20120810.UpdateTable + - DynamoDB_20120810.PutItem + - DynamoDB_20120810.GetItem + - DynamoDB_20120810.DeleteItem + - DynamoDB_20120810.UpdateItem + - DynamoDB_20120810.Query + - DynamoDB_20120810.Scan + - DynamoDB_20120810.BatchGetItem + - DynamoDB_20120810.BatchWriteItem + - DynamoDB_20120810.TransactGetItems + - DynamoDB_20120810.TransactWriteItems + example: DynamoDB_20120810.GetItem + + XAmzDate: + name: X-Amz-Date + in: header + required: false + schema: + type: string + description: Optional SigV4 timestamp header (kept for SDK compatibility). + + Authorization: + name: Authorization + in: header + required: false + schema: + type: string + description: Optional SigV4 Authorization header (kept for SDK compatibility). + + XAmzSecurityToken: + name: X-Amz-Security-Token + in: header + required: false + schema: + type: string + description: Optional SigV4 session token header (kept for SDK compatibility). + + XAmzContentSha256: + name: X-Amz-Content-Sha256 + in: header + required: false + schema: + type: string + description: Optional SigV4 payload hash header (kept for SDK compatibility). + + schemas: + EmptyObject: + type: object + description: Empty JSON object. + additionalProperties: false + + # ------------------------- + # AttributeValue & helpers + # ------------------------- + AttributeValue: + description: DynamoDB AttributeValue (JSON wire format). + type: object + minProperties: 1 + maxProperties: 1 + oneOf: + - $ref: '#/components/schemas/AttrS' + - $ref: '#/components/schemas/AttrN' + - $ref: '#/components/schemas/AttrB' + - $ref: '#/components/schemas/AttrBOOL' + - $ref: '#/components/schemas/AttrNULL' + - $ref: '#/components/schemas/AttrSS' + - $ref: '#/components/schemas/AttrNS' + - $ref: '#/components/schemas/AttrBS' + - $ref: '#/components/schemas/AttrL' + - $ref: '#/components/schemas/AttrM' + + AttrS: + type: object + additionalProperties: false + required: [S] + properties: + S: + type: string + example: hello + + AttrN: + type: object + additionalProperties: false + required: [N] + properties: + N: + type: string + description: Numeric values are encoded as strings in DynamoDB's JSON protocol. + example: "42" + + AttrB: + type: object + additionalProperties: false + required: [B] + properties: + B: + type: string + description: Base64-encoded binary value. + example: AAECAwQ= + + AttrBOOL: + type: object + additionalProperties: false + required: [BOOL] + properties: + BOOL: + type: boolean + example: true + + AttrNULL: + type: object + additionalProperties: false + required: [NULL] + properties: + NULL: + type: boolean + enum: [true] + example: true + + AttrSS: + type: object + additionalProperties: false + required: [SS] + properties: + SS: + type: array + items: { type: string } + example: [a, b] + + AttrNS: + type: object + additionalProperties: false + required: [NS] + properties: + NS: + type: array + description: Numeric set values are encoded as strings. + items: { type: string } + example: ["1", "2"] + + AttrBS: + type: object + additionalProperties: false + required: [BS] + properties: + BS: + type: array + description: Base64-encoded binary set values. + items: { type: string } + example: [AAE=, AgM=] + + AttrL: + type: object + additionalProperties: false + required: [L] + properties: + L: + type: array + items: + $ref: '#/components/schemas/AttributeValue' + + AttrM: + type: object + additionalProperties: false + required: [M] + properties: + M: + $ref: '#/components/schemas/AttributeMap' + + AttributeMap: + type: object + additionalProperties: + $ref: '#/components/schemas/AttributeValue' + example: + pk: { S: "user#1" } + sk: { S: "meta" } + age: { N: "30" } + + ExpressionAttributeNames: + type: object + additionalProperties: { type: string } + example: + "#pk": "pk" + + ExpressionAttributeValues: + type: object + additionalProperties: + $ref: '#/components/schemas/AttributeValue' + example: + ":v": { S: "user#1" } + + Key: + allOf: + - $ref: '#/components/schemas/AttributeMap' + description: Primary key map (HASH, optionally RANGE) encoded as an AttributeMap. + + ReturnValues: + type: string + description: ReturnValues selector used by UpdateItem. + enum: [NONE, ALL_OLD, UPDATED_OLD, ALL_NEW, UPDATED_NEW] + example: ALL_NEW + + # ------------------------- + # Table shapes + # ------------------------- + ScalarAttributeType: + type: string + enum: [S, N, B] + example: S + + AttributeDefinition: + type: object + additionalProperties: false + required: [AttributeName, AttributeType] + properties: + AttributeName: { type: string } + AttributeType: { $ref: '#/components/schemas/ScalarAttributeType' } + + KeyType: + type: string + enum: [HASH, RANGE] + example: HASH + + KeySchemaElement: + type: object + additionalProperties: false + required: [AttributeName, KeyType] + properties: + AttributeName: { type: string } + KeyType: { $ref: '#/components/schemas/KeyType' } + + ProjectionType: + type: string + enum: [KEYS_ONLY, INCLUDE, ALL] + example: ALL + + Projection: + type: object + additionalProperties: false + required: [ProjectionType] + properties: + ProjectionType: { $ref: '#/components/schemas/ProjectionType' } + NonKeyAttributes: + type: array + items: { type: string } + + GlobalSecondaryIndex: + type: object + additionalProperties: false + required: [IndexName, KeySchema, Projection] + properties: + IndexName: { type: string } + KeySchema: + type: array + items: { $ref: '#/components/schemas/KeySchemaElement' } + minItems: 1 + Projection: { $ref: '#/components/schemas/Projection' } + + TableStatus: + type: string + enum: [CREATING, UPDATING, DELETING, ACTIVE, ARCHIVING, ARCHIVED] + example: ACTIVE + + TableDescription: + type: object + additionalProperties: false + required: [TableName, TableStatus] + properties: + TableName: { type: string } + TableStatus: { $ref: '#/components/schemas/TableStatus' } + CreationDateTime: + type: integer + format: int64 + description: Unix epoch seconds. + KeySchema: + type: array + items: { $ref: '#/components/schemas/KeySchemaElement' } + AttributeDefinitions: + type: array + items: { $ref: '#/components/schemas/AttributeDefinition' } + GlobalSecondaryIndexes: + type: array + items: + allOf: + - $ref: '#/components/schemas/GlobalSecondaryIndex' + - type: object + properties: + IndexStatus: + type: string + enum: [ACTIVE] + + # ------------------------- + # Error shapes + # ------------------------- + DynamoDbError: + type: object + additionalProperties: false + required: [__type, message] + properties: + __type: + type: string + description: DynamoDB error type identifier. + example: com.amazonaws.dynamodb.v20120810#ValidationException + message: + type: string + example: Invalid request + + TransactionCanceledException: + type: object + additionalProperties: false + required: [__type, message, CancellationReasons] + properties: + __type: + type: string + enum: [com.amazonaws.dynamodb.v20120810#TransactionCanceledException] + message: + type: string + CancellationReasons: + type: array + items: + type: object + additionalProperties: false + required: [Code, Message] + properties: + Code: { type: string, example: ConditionalCheckFailed } + Message: { type: string, example: The conditional request failed } + + # ------------------------- + # API: CreateTable + # ------------------------- + CreateTableRequest: + type: object + additionalProperties: true + required: [TableName, KeySchema, AttributeDefinitions] + properties: + TableName: { type: string } + KeySchema: + type: array + items: { $ref: '#/components/schemas/KeySchemaElement' } + minItems: 1 + AttributeDefinitions: + type: array + items: { $ref: '#/components/schemas/AttributeDefinition' } + minItems: 1 + GlobalSecondaryIndexes: + type: array + items: { $ref: '#/components/schemas/GlobalSecondaryIndex' } + description: | + CreateTable request. JormunDB focuses on TableName, KeySchema, AttributeDefinitions, and optional GSI definitions. + + CreateTableResponse: + type: object + additionalProperties: false + required: [TableDescription] + properties: + TableDescription: + type: object + additionalProperties: false + required: [TableName, TableStatus, CreationDateTime] + properties: + TableName: { type: string } + TableStatus: { $ref: '#/components/schemas/TableStatus' } + CreationDateTime: { type: integer, format: int64 } + + # ------------------------- + # API: DeleteTable / DescribeTable / ListTables + # ------------------------- + DeleteTableRequest: + type: object + additionalProperties: true + required: [TableName] + properties: + TableName: { type: string } + + DeleteTableResponse: + type: object + additionalProperties: false + required: [TableDescription] + properties: + TableDescription: + type: object + additionalProperties: false + required: [TableName, TableStatus] + properties: + TableName: { type: string } + TableStatus: + type: string + enum: [DELETING] + + DescribeTableRequest: + type: object + additionalProperties: true + required: [TableName] + properties: + TableName: { type: string } + + DescribeTableResponse: + type: object + additionalProperties: false + required: [Table] + properties: + Table: { $ref: '#/components/schemas/TableDescription' } + + ListTablesRequest: + type: object + additionalProperties: true + description: ListTables request. JormunDB ignores request fields for this operation. + + ListTablesResponse: + type: object + additionalProperties: false + required: [TableNames] + properties: + TableNames: + type: array + items: { type: string } + + # ------------------------- + # API: PutItem / GetItem / DeleteItem + # ------------------------- + PutItemRequest: + type: object + additionalProperties: true + required: [TableName, Item] + properties: + TableName: { type: string } + Item: { $ref: '#/components/schemas/AttributeMap' } + ConditionExpression: { type: string } + ExpressionAttributeNames: { $ref: '#/components/schemas/ExpressionAttributeNames' } + ExpressionAttributeValues: { $ref: '#/components/schemas/ExpressionAttributeValues' } + + PutItemResponse: + $ref: '#/components/schemas/EmptyObject' + + GetItemRequest: + type: object + additionalProperties: true + required: [TableName, Key] + properties: + TableName: { type: string } + Key: { $ref: '#/components/schemas/Key' } + ProjectionExpression: { type: string } + ExpressionAttributeNames: { $ref: '#/components/schemas/ExpressionAttributeNames' } + + GetItemResponse: + type: object + additionalProperties: false + required: [Item] + properties: + Item: { $ref: '#/components/schemas/AttributeMap' } + + GetItemResponseUnion: + oneOf: + - $ref: '#/components/schemas/EmptyObject' + - $ref: '#/components/schemas/GetItemResponse' + + DeleteItemRequest: + type: object + additionalProperties: true + required: [TableName, Key] + properties: + TableName: { type: string } + Key: { $ref: '#/components/schemas/Key' } + ConditionExpression: { type: string } + ExpressionAttributeNames: { $ref: '#/components/schemas/ExpressionAttributeNames' } + ExpressionAttributeValues: { $ref: '#/components/schemas/ExpressionAttributeValues' } + + DeleteItemResponse: + $ref: '#/components/schemas/EmptyObject' + + # ------------------------- + # API: UpdateItem + # ------------------------- + UpdateItemRequest: + type: object + additionalProperties: true + required: [TableName, Key, UpdateExpression] + properties: + TableName: { type: string } + Key: { $ref: '#/components/schemas/Key' } + UpdateExpression: { type: string } + ConditionExpression: { type: string } + ExpressionAttributeNames: { $ref: '#/components/schemas/ExpressionAttributeNames' } + ExpressionAttributeValues: { $ref: '#/components/schemas/ExpressionAttributeValues' } + ReturnValues: { $ref: '#/components/schemas/ReturnValues' } + + UpdateItemResponse: + type: object + additionalProperties: false + required: [Attributes] + properties: + Attributes: { $ref: '#/components/schemas/AttributeMap' } + + UpdateItemResponseUnion: + oneOf: + - $ref: '#/components/schemas/EmptyObject' + - $ref: '#/components/schemas/UpdateItemResponse' + + # ------------------------- + # API: Query / Scan + # ------------------------- + QueryRequest: + type: object + additionalProperties: true + required: [TableName, KeyConditionExpression] + properties: + TableName: { type: string } + IndexName: { type: string } + KeyConditionExpression: { type: string } + FilterExpression: { type: string } + ProjectionExpression: { type: string } + ExpressionAttributeNames: { $ref: '#/components/schemas/ExpressionAttributeNames' } + ExpressionAttributeValues: { $ref: '#/components/schemas/ExpressionAttributeValues' } + Limit: + type: integer + format: int32 + minimum: 1 + description: Maximum items to return (default 100 if omitted/0 in JormunDB). + ExclusiveStartKey: { $ref: '#/components/schemas/Key' } + ScanIndexForward: + type: boolean + description: Sort order for RANGE key queries (if applicable). + + ScanRequest: + type: object + additionalProperties: true + required: [TableName] + properties: + TableName: { type: string } + IndexName: { type: string } + FilterExpression: { type: string } + ProjectionExpression: { type: string } + ExpressionAttributeNames: { $ref: '#/components/schemas/ExpressionAttributeNames' } + ExpressionAttributeValues: { $ref: '#/components/schemas/ExpressionAttributeValues' } + Limit: + type: integer + format: int32 + minimum: 1 + description: Maximum items to return (default 100 if omitted/0 in JormunDB). + ExclusiveStartKey: { $ref: '#/components/schemas/Key' } + + ItemsPage: + type: object + additionalProperties: false + required: [Items, Count, ScannedCount] + properties: + Items: + type: array + items: { $ref: '#/components/schemas/AttributeMap' } + Count: + type: integer + format: int32 + ScannedCount: + type: integer + format: int32 + LastEvaluatedKey: + $ref: '#/components/schemas/Key' + + QueryResponse: + allOf: + - $ref: '#/components/schemas/ItemsPage' + + ScanResponse: + allOf: + - $ref: '#/components/schemas/ItemsPage' + + # ------------------------- + # API: BatchWriteItem + # ------------------------- + WriteRequest: + type: object + additionalProperties: false + properties: + PutRequest: + type: object + additionalProperties: false + required: [Item] + properties: + Item: { $ref: '#/components/schemas/AttributeMap' } + DeleteRequest: + type: object + additionalProperties: false + required: [Key] + properties: + Key: { $ref: '#/components/schemas/Key' } + oneOf: + - required: [PutRequest] + - required: [DeleteRequest] + + BatchWriteItemRequest: + type: object + additionalProperties: true + required: [RequestItems] + properties: + RequestItems: + type: object + description: Map of table name to write requests. + additionalProperties: + type: array + items: { $ref: '#/components/schemas/WriteRequest' } + + BatchWriteItemResponse: + type: object + additionalProperties: false + required: [UnprocessedItems] + properties: + UnprocessedItems: + type: object + additionalProperties: + type: array + items: { $ref: '#/components/schemas/WriteRequest' } + + # ------------------------- + # API: BatchGetItem + # ------------------------- + KeysAndAttributes: + type: object + additionalProperties: true + required: [Keys] + properties: + Keys: + type: array + items: { $ref: '#/components/schemas/Key' } + ProjectionExpression: { type: string } + ExpressionAttributeNames: { $ref: '#/components/schemas/ExpressionAttributeNames' } + + BatchGetItemRequest: + type: object + additionalProperties: true + required: [RequestItems] + properties: + RequestItems: + type: object + additionalProperties: + $ref: '#/components/schemas/KeysAndAttributes' + + BatchGetItemResponse: + type: object + additionalProperties: false + required: [Responses, UnprocessedKeys] + properties: + Responses: + type: object + additionalProperties: + type: array + items: { $ref: '#/components/schemas/AttributeMap' } + UnprocessedKeys: + type: object + additionalProperties: + $ref: '#/components/schemas/KeysAndAttributes' + + # ------------------------- + # API: TransactWriteItems / TransactGetItems + # ------------------------- + TransactWriteItemsRequest: + type: object + additionalProperties: true + required: [TransactItems] + properties: + TransactItems: + type: array + minItems: 1 + maxItems: 100 + items: + $ref: '#/components/schemas/TransactWriteItem' + + TransactWriteItem: + type: object + additionalProperties: false + oneOf: + - required: [Put] + - required: [Delete] + - required: [Update] + - required: [ConditionCheck] + properties: + Put: + $ref: '#/components/schemas/TransactPut' + Delete: + $ref: '#/components/schemas/TransactDelete' + Update: + $ref: '#/components/schemas/TransactUpdate' + ConditionCheck: + $ref: '#/components/schemas/TransactConditionCheck' + + TransactPut: + type: object + additionalProperties: true + required: [TableName, Item] + properties: + TableName: { type: string } + Item: { $ref: '#/components/schemas/AttributeMap' } + ConditionExpression: { type: string } + ExpressionAttributeNames: { $ref: '#/components/schemas/ExpressionAttributeNames' } + ExpressionAttributeValues: { $ref: '#/components/schemas/ExpressionAttributeValues' } + + TransactDelete: + type: object + additionalProperties: true + required: [TableName, Key] + properties: + TableName: { type: string } + Key: { $ref: '#/components/schemas/Key' } + ConditionExpression: { type: string } + ExpressionAttributeNames: { $ref: '#/components/schemas/ExpressionAttributeNames' } + ExpressionAttributeValues: { $ref: '#/components/schemas/ExpressionAttributeValues' } + + TransactUpdate: + type: object + additionalProperties: true + required: [TableName, Key, UpdateExpression] + properties: + TableName: { type: string } + Key: { $ref: '#/components/schemas/Key' } + UpdateExpression: { type: string } + ConditionExpression: { type: string } + ExpressionAttributeNames: { $ref: '#/components/schemas/ExpressionAttributeNames' } + ExpressionAttributeValues: { $ref: '#/components/schemas/ExpressionAttributeValues' } + + TransactConditionCheck: + type: object + additionalProperties: true + required: [TableName, Key, ConditionExpression] + properties: + TableName: { type: string } + Key: { $ref: '#/components/schemas/Key' } + ConditionExpression: { type: string } + ExpressionAttributeNames: { $ref: '#/components/schemas/ExpressionAttributeNames' } + ExpressionAttributeValues: { $ref: '#/components/schemas/ExpressionAttributeValues' } + + TransactWriteItemsResponse: + $ref: '#/components/schemas/EmptyObject' + + TransactGetItemsRequest: + type: object + additionalProperties: true + required: [TransactItems] + properties: + TransactItems: + type: array + minItems: 1 + maxItems: 100 + items: + type: object + additionalProperties: false + required: [Get] + properties: + Get: + $ref: '#/components/schemas/TransactGet' + + TransactGet: + type: object + additionalProperties: true + required: [TableName, Key] + properties: + TableName: { type: string } + Key: { $ref: '#/components/schemas/Key' } + ProjectionExpression: { type: string } + ExpressionAttributeNames: { $ref: '#/components/schemas/ExpressionAttributeNames' } + + TransactGetItemResult: + oneOf: + - $ref: '#/components/schemas/EmptyObject' + - type: object + additionalProperties: false + required: [Item] + properties: + Item: { $ref: '#/components/schemas/AttributeMap' } + + TransactGetItemsResponse: + type: object + additionalProperties: false + required: [Responses] + properties: + Responses: + type: array + items: { $ref: '#/components/schemas/TransactGetItemResult' }