-
Notifications
You must be signed in to change notification settings - Fork 96
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
schema generator wip checkpoint: schema codegen compiles in typescript feat: scaffold schema types wip: codec/protocol/transport operation schema
- Loading branch information
Showing
37 changed files
with
2,042 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"@smithy/types": minor | ||
"@smithy/core": minor | ||
--- | ||
|
||
implement schema framework |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/** | ||
* Do not edit: | ||
* This is a compatibility redirect for contexts that do not understand package.json exports field. | ||
*/ | ||
declare module "@smithy/core/schema" { | ||
export * from "@smithy/core/dist-types/submodules/schema/index.d"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
|
||
/** | ||
* Do not edit: | ||
* This is a compatibility redirect for contexts that do not understand package.json exports field. | ||
*/ | ||
module.exports = require("./dist-cjs/submodules/schema/index.js"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { deref, ListSchema, StructureSchema } from "@smithy/core/schema"; | ||
import { copyDocumentWithTransform, parseEpochTimestamp } from "@smithy/smithy-client"; | ||
import { Codec, Schema, ShapeDeserializer, ShapeSerializer } from "@smithy/types"; | ||
|
||
import { cbor } from "./cbor"; | ||
import { dateToTag } from "./parseCborBody"; | ||
|
||
/* eslint @typescript-eslint/no-unused-vars: 0 */ | ||
|
||
export class CborCodec implements Codec { | ||
public createSerializer(): CborShapeSerializer { | ||
return new CborShapeSerializer(); | ||
} | ||
public createDeserializer(): CborShapeDeserializer { | ||
return new CborShapeDeserializer(); | ||
} | ||
} | ||
|
||
export class CborShapeSerializer implements ShapeSerializer { | ||
private value: unknown; | ||
|
||
public write(schema: Schema, value: unknown): void { | ||
// Uint8Array (blob) is already supported by cbor serializer. | ||
|
||
// As for timestamps, we don't actually need to refer to the schema since | ||
// all Date objects have a uniform serialization target. | ||
this.value = copyDocumentWithTransform(value, (_: any) => { | ||
if (_ instanceof Date) { | ||
return dateToTag(_); | ||
} | ||
return _; | ||
}); | ||
} | ||
|
||
public async flush(): Promise<Uint8Array> { | ||
const buffer = cbor.serialize(this.value); | ||
this.value = undefined; | ||
return buffer as Uint8Array; | ||
} | ||
} | ||
|
||
export class CborShapeDeserializer implements ShapeDeserializer { | ||
public read(schema: Schema, bytes: Uint8Array): any { | ||
const data: any = cbor.deserialize(bytes); | ||
return this.readValue(schema, data); | ||
} | ||
|
||
private readValue(schema: Schema, value: any): any { | ||
if (typeof schema === "string") { | ||
if (schema === "time" || schema === "epoch-seconds") { | ||
return parseEpochTimestamp(value); | ||
} | ||
if (schema === "blob") { | ||
return value; | ||
} | ||
} | ||
switch (typeof value) { | ||
case "undefined": | ||
case "boolean": | ||
case "number": | ||
case "string": | ||
case "bigint": | ||
case "symbol": | ||
return value; | ||
case "function": | ||
case "object": | ||
if (value === null) { | ||
return null; | ||
} | ||
if (Array.isArray(value)) { | ||
const newArray = new Array(value.length); | ||
let i = 0; | ||
for (const item of value) { | ||
newArray[i++] = this.readValue(schema instanceof ListSchema ? deref(schema.valueSchema) : void 0, item); | ||
} | ||
return newArray; | ||
} | ||
if ("byteLength" in (value as Uint8Array)) { | ||
return value; | ||
} | ||
if (value instanceof Date) { | ||
return value; | ||
} | ||
const newObject = {} as any; | ||
for (const key of Object.keys(value)) { | ||
newObject[key] = this.readValue( | ||
schema instanceof StructureSchema ? deref(schema.members[key]?.[0]) : void 0, | ||
value[key] | ||
); | ||
} | ||
return newObject; | ||
default: | ||
return value; | ||
} | ||
} | ||
|
||
public getContainerSize(): number { | ||
throw new Error("Method not implemented."); | ||
} | ||
} |
156 changes: 156 additions & 0 deletions
156
packages/core/src/submodules/cbor/SmithyRpcV2CborProtocol.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
import { struct } from "@smithy/core/schema"; | ||
import { HttpRequest } from "@smithy/protocol-http"; | ||
import { toBase64 } from "@smithy/util-base64"; | ||
import { describe, expect, test as it } from "vitest"; | ||
|
||
import { cbor } from "./cbor"; | ||
import { dateToTag } from "./parseCborBody"; | ||
import { SmithyRpcV2CborProtocol } from "./SmithyRpcV2CborProtocol"; | ||
|
||
describe(SmithyRpcV2CborProtocol.name, () => { | ||
const bytes = (arr: number[]) => Buffer.from(arr); | ||
|
||
const testCases = [ | ||
{ | ||
name: "document with timestamp and blob", | ||
schema: struct( | ||
"MyExtendedDocument", | ||
{}, | ||
{ | ||
timestamp: [() => "time", {}], | ||
blob: [() => "blob", {}], | ||
} | ||
), | ||
input: { | ||
bool: true, | ||
int: 5, | ||
float: -3.001, | ||
timestamp: new Date(1_000_000), | ||
blob: bytes([97, 98, 99, 100]), | ||
}, | ||
expected: { | ||
request: {}, | ||
body: { | ||
bool: true, | ||
int: 5, | ||
float: -3.001, | ||
timestamp: dateToTag(new Date(1_000_000)), | ||
blob: bytes([97, 98, 99, 100]), | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "write to header and query", | ||
schema: struct( | ||
"MyExtendedDocument", | ||
{}, | ||
{ | ||
bool: [, { httpQuery: "bool" }], | ||
timestamp: [ | ||
() => "time", | ||
{ | ||
httpHeader: "timestamp", | ||
}, | ||
], | ||
blob: [ | ||
() => "blob", | ||
{ | ||
httpHeader: "blob", | ||
}, | ||
], | ||
prefixHeaders: [, { httpPrefixHeaders: "anti-" }], | ||
searchParams: [, { httpQueryParams: {} }], | ||
} | ||
), | ||
input: { | ||
bool: true, | ||
timestamp: new Date(1_000_000), | ||
blob: bytes([97, 98, 99, 100]), | ||
prefixHeaders: { | ||
pasto: "cheese dodecahedron", | ||
clockwise: "left", | ||
}, | ||
searchParams: { | ||
a: 1, | ||
b: 2, | ||
}, | ||
}, | ||
expected: { | ||
request: { | ||
headers: { | ||
timestamp: new Date(1_000_000).toISOString(), | ||
blob: toBase64(bytes([97, 98, 99, 100])), | ||
"anti-clockwise": "left", | ||
"anti-pasto": "cheese dodecahedron", | ||
}, | ||
query: { bool: "true", a: "1", b: "2" }, | ||
}, | ||
body: {}, | ||
}, | ||
}, | ||
{ | ||
name: "timestamp with header", | ||
schema: struct( | ||
"MyShape", | ||
{}, | ||
{ | ||
myHeader: [ | ||
, | ||
{ | ||
httpHeader: "my-header", | ||
}, | ||
], | ||
myTimestamp: [() => "time", {}], | ||
} | ||
), | ||
input: { | ||
myHeader: "header!", | ||
myTimestamp: new Date(0), | ||
}, | ||
expected: { | ||
request: { | ||
headers: { | ||
["my-header"]: "header!", | ||
}, | ||
}, | ||
body: { | ||
myTimestamp: dateToTag(new Date(0)), | ||
}, | ||
}, | ||
}, | ||
]; | ||
|
||
for (const testCase of testCases) { | ||
it(`should serialize HTTP Requests: ${testCase.name}`, async () => { | ||
const protocol = new SmithyRpcV2CborProtocol(); | ||
expect(protocol).toBeDefined(); | ||
|
||
const httpRequest = await protocol.serializeRequest( | ||
{ | ||
input: testCase.schema, | ||
output: void 0, | ||
traits: {}, | ||
}, | ||
testCase.input, | ||
{ | ||
endpointV2: { | ||
url: new URL("https://example.com/"), | ||
}, | ||
} | ||
); | ||
|
||
const body = httpRequest.body; | ||
httpRequest.body = void 0; | ||
|
||
expect(httpRequest).toEqual( | ||
new HttpRequest({ | ||
protocol: "https:", | ||
hostname: "example.com", | ||
...testCase.expected.request, | ||
}) | ||
); | ||
|
||
expect(cbor.deserialize(body)).toEqual(testCase.expected.body); | ||
}); | ||
} | ||
}); |
Oops, something went wrong.