-
Notifications
You must be signed in to change notification settings - Fork 104
feat(core/schema): add schema classes #1595
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+1,750
−9
Merged
Changes from 3 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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/core": minor | ||
"@smithy/middleware-serde": patch | ||
--- | ||
|
||
add schema classes |
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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,27 @@ | ||
import { describe, expect, test as it } from "vitest"; | ||
|
||
import { error } from "./schemas/ErrorSchema"; | ||
import { list } from "./schemas/ListSchema"; | ||
import { map } from "./schemas/MapSchema"; | ||
import { struct } from "./schemas/StructureSchema"; | ||
import { TypeRegistry } from "./TypeRegistry"; | ||
|
||
describe(TypeRegistry.name, () => { | ||
const [List, Map, Struct] = [list("ack", "List", { sparse: 1 }, 0), map("ack", "Map", 0, 0, 1), () => schema]; | ||
const schema = struct("ack", "Structure", {}, ["list", "map", "struct"], [List, Map, Struct]); | ||
|
||
const tr = TypeRegistry.for("ack"); | ||
|
||
it("stores and retrieves schema objects", () => { | ||
expect(tr.getSchema("List")).toBe(List); | ||
expect(tr.getSchema("Map")).toBe(Map); | ||
expect(tr.getSchema("Structure")).toBe(schema); | ||
}); | ||
|
||
it("has a helper method to retrieve a synthetic base exception", () => { | ||
// the service namespace is appended to the synthetic prefix. | ||
const err = error("smithyts.client.synthetic.ack", "UhOhServiceException", 0, [], [], Error); | ||
const tr = TypeRegistry.for("smithyts.client.synthetic.ack"); | ||
expect(tr.getBaseException()).toEqual(err); | ||
}); | ||
}); |
This file contains hidden or 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,102 @@ | ||
import type { Schema as ISchema } from "@smithy/types"; | ||
|
||
import { ErrorSchema } from "./schemas/ErrorSchema"; | ||
|
||
/** | ||
* A way to look up schema by their ShapeId values. | ||
* | ||
* @alpha | ||
*/ | ||
export class TypeRegistry { | ||
public static active: TypeRegistry | null = null; | ||
kuhe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public static readonly registries = new Map<string, TypeRegistry>(); | ||
|
||
private constructor( | ||
public readonly namespace: string, | ||
private schemas: Map<string, ISchema> = new Map() | ||
) {} | ||
|
||
/** | ||
* @param namespace - specifier. | ||
* @returns the schema for that namespace, creating it if necessary. | ||
*/ | ||
public static for(namespace: string): TypeRegistry { | ||
if (!TypeRegistry.registries.has(namespace)) { | ||
TypeRegistry.registries.set(namespace, new TypeRegistry(namespace)); | ||
} | ||
return TypeRegistry.registries.get(namespace)!; | ||
} | ||
|
||
/** | ||
* Adds the given schema to a type registry with the same namespace. | ||
* | ||
* @param shapeId - to be registered. | ||
* @param schema - to be registered. | ||
*/ | ||
public register(shapeId: string, schema: ISchema) { | ||
const qualifiedName = this.normalizeShapeId(shapeId); | ||
const registry = TypeRegistry.for(this.getNamespace(shapeId)); | ||
registry.schemas.set(qualifiedName, schema); | ||
} | ||
|
||
/** | ||
* @param shapeId - query. | ||
* @returns the schema. | ||
*/ | ||
public getSchema(shapeId: string): ISchema { | ||
const id = this.normalizeShapeId(shapeId); | ||
if (!this.schemas.has(id)) { | ||
throw new Error(`@smithy/core/schema - schema not found for ${id}`); | ||
} | ||
return this.schemas.get(id)!; | ||
} | ||
|
||
/** | ||
* The smithy-typescript code generator generates a synthetic (i.e. unmodeled) base exception, | ||
* because generated SDKs before the introduction of schemas have the notion of a ServiceBaseException, which | ||
* is unique per service/model. | ||
* | ||
* This is generated under a unique prefix that is combined with the service namespace, and this | ||
* method is used to retrieve it. | ||
* | ||
* The base exception synthetic schema is used when an error is returned by a service, but we cannot | ||
* determine what existing schema to use to deserialize it. | ||
* | ||
* @returns the synthetic base exception of the service namespace associated with this registry instance. | ||
*/ | ||
public getBaseException(): ErrorSchema | undefined { | ||
for (const [id, schema] of this.schemas.entries()) { | ||
if (id.startsWith("smithyts.client.synthetic.") && id.endsWith("ServiceException")) { | ||
return schema as ErrorSchema; | ||
} | ||
} | ||
return undefined; | ||
} | ||
|
||
/** | ||
* @param predicate - criterion. | ||
* @returns a schema in this registry matching the predicate. | ||
*/ | ||
public find(predicate: (schema: ISchema) => boolean) { | ||
return [...this.schemas.values()].find(predicate); | ||
} | ||
|
||
/** | ||
* Unloads the current TypeRegistry. | ||
*/ | ||
public destroy() { | ||
TypeRegistry.registries.delete(this.namespace); | ||
this.schemas.clear(); | ||
} | ||
|
||
private normalizeShapeId(shapeId: string) { | ||
if (shapeId.includes("#")) { | ||
return shapeId; | ||
} | ||
return this.namespace + "#" + shapeId; | ||
} | ||
|
||
private getNamespace(shapeId: string) { | ||
return this.normalizeShapeId(shapeId).split("#")[0]; | ||
} | ||
} |
This file contains hidden or 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,12 @@ | ||
import type { Schema, SchemaRef } from "@smithy/types"; | ||
|
||
/** | ||
* Dereferences a SchemaRef if needed. | ||
* @internal | ||
*/ | ||
export const deref = (schemaRef: SchemaRef): Schema => { | ||
if (typeof schemaRef === "function") { | ||
return schemaRef(); | ||
} | ||
return schemaRef; | ||
}; |
This file contains hidden or 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,12 @@ | ||
export * from "./deref"; | ||
export * from "./middleware/getSchemaSerdePlugin"; | ||
export * from "./schemas/ListSchema"; | ||
export * from "./schemas/MapSchema"; | ||
export * from "./schemas/OperationSchema"; | ||
export * from "./schemas/ErrorSchema"; | ||
export * from "./schemas/NormalizedSchema"; | ||
export * from "./schemas/Schema"; | ||
export * from "./schemas/SimpleSchema"; | ||
export * from "./schemas/StructureSchema"; | ||
export * from "./schemas/sentinels"; | ||
export * from "./TypeRegistry"; |
49 changes: 49 additions & 0 deletions
49
packages/core/src/submodules/schema/middleware/getSchemaSerdePlugin.ts
This file contains hidden or 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,49 @@ | ||
import { | ||
DeserializeHandlerOptions, | ||
MetadataBearer, | ||
MiddlewareStack, | ||
Pluggable, | ||
SerdeFunctions, | ||
SerializeHandlerOptions, | ||
} from "@smithy/types"; | ||
|
||
import { PreviouslyResolved } from "./schema-middleware-types"; | ||
import { schemaDeserializationMiddleware } from "./schemaDeserializationMiddleware"; | ||
import { schemaSerializationMiddleware } from "./schemaSerializationMiddleware"; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export const deserializerMiddlewareOption: DeserializeHandlerOptions = { | ||
name: "deserializerMiddleware", | ||
step: "deserialize", | ||
tags: ["DESERIALIZER"], | ||
override: true, | ||
}; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export const serializerMiddlewareOption: SerializeHandlerOptions = { | ||
name: "serializerMiddleware", | ||
step: "serialize", | ||
tags: ["SERIALIZER"], | ||
override: true, | ||
}; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export function getSchemaSerdePlugin<InputType extends object = any, OutputType extends MetadataBearer = any>( | ||
config: PreviouslyResolved | ||
): Pluggable<InputType, OutputType> { | ||
return { | ||
applyToStack: (commandStack: MiddlewareStack<InputType, OutputType>) => { | ||
commandStack.add(schemaSerializationMiddleware(config), serializerMiddlewareOption); | ||
commandStack.add(schemaDeserializationMiddleware(config), deserializerMiddlewareOption); | ||
// `config` is fully resolved at the point of applying plugins. | ||
// As such, config qualifies as SerdeContext. | ||
config.protocol.setSerdeContext(config as SerdeFunctions); | ||
}, | ||
}; | ||
} |
12 changes: 12 additions & 0 deletions
12
packages/core/src/submodules/schema/middleware/schema-middleware-types.ts
This file contains hidden or 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,12 @@ | ||
import type { ClientProtocol, SerdeContext, UrlParser } from "@smithy/types"; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export type PreviouslyResolved = Omit< | ||
SerdeContext & { | ||
urlParser: UrlParser; | ||
protocol: ClientProtocol<any, any>; | ||
}, | ||
"endpoint" | ||
>; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this property for? i don't think it's used anywhere in the current revision