Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/short-dodos-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@smithy/core": minor
---

separate error schema objects from error ctor
28 changes: 15 additions & 13 deletions packages/core/src/submodules/cbor/SmithyRpcV2CborProtocol.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ErrorSchema } from "@smithy/core/schema";
import { error, list, map, op, SCHEMA, struct, TypeRegistry } from "@smithy/core/schema";
import { HttpRequest, HttpResponse } from "@smithy/protocol-http";
import type { ResponseMetadata, RetryableTrait, SchemaRef } from "@smithy/types";
Expand Down Expand Up @@ -311,21 +312,24 @@ describe(SmithyRpcV2CborProtocol.name, () => {
public modeledProperty: string = "";
}

const ns = TypeRegistry.for("ns");
const synthetic = TypeRegistry.for("smithy.ts.sdk.synthetic.ns");

beforeEach(() => {
TypeRegistry.for("ns").destroy();
ns.clear();
});

const modeledExceptionSchema = error("ns", "ModeledException", 0, ["modeledProperty"], [0], null);
const baseServiceExceptionSchema = error("smithy.ts.sdk.synthetic.ns", "BaseServiceException", 0, [], [], null);

it("should throw the schema error ctor if one exists", async () => {
// this is for modeled exceptions.

TypeRegistry.for("ns").register(
"ns#ModeledException",
error("ns", "ModeledException", 0, ["modeledProperty"], [0], ModeledExceptionCtor)
);
TypeRegistry.for("ns").register(
"smithy.ts.sdk.synthetic.ns#BaseServiceException",
error("smithy.ts.sdk.synthetic.ns", "BaseServiceException", 0, [], [], ServiceBaseException)
);
ns.register(modeledExceptionSchema.getName(), modeledExceptionSchema);
ns.registerError(modeledExceptionSchema, ModeledExceptionCtor);

synthetic.register(baseServiceExceptionSchema.getName(), baseServiceExceptionSchema);
synthetic.registerError(baseServiceExceptionSchema, ServiceBaseException);

try {
await protocol.deserializeResponse(operation, serdeContext as any, errorResponse);
Expand All @@ -340,10 +344,8 @@ describe(SmithyRpcV2CborProtocol.name, () => {
it("should throw a base error if available in the namespace, when no error schema is modeled", async () => {
// this is the expected fallback case for all generated clients.

TypeRegistry.for("ns").register(
"smithy.ts.sdk.synthetic.ns#BaseServiceException",
error("smithy.ts.sdk.synthetic.ns", "BaseServiceException", 0, [], [], ServiceBaseException)
);
synthetic.register(baseServiceExceptionSchema.getName(), baseServiceExceptionSchema);
synthetic.registerError(baseServiceExceptionSchema, ServiceBaseException);

try {
await protocol.deserializeResponse(operation, serdeContext as any, errorResponse);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,19 @@ export class SmithyRpcV2CborProtocol extends RpcProtocol {
if (dataObject.Message) {
dataObject.message = dataObject.Message;
}
const baseExceptionSchema = TypeRegistry.for("smithy.ts.sdk.synthetic." + namespace).getBaseException();
const synthetic = TypeRegistry.for("smithy.ts.sdk.synthetic." + namespace);
const baseExceptionSchema = synthetic.getBaseException();
if (baseExceptionSchema) {
const ErrorCtor = baseExceptionSchema.ctor;
const ErrorCtor = synthetic.getErrorCtor(baseExceptionSchema);
throw Object.assign(new ErrorCtor({ name: errorName }), errorMetadata, dataObject);
}
throw Object.assign(new Error(errorName), errorMetadata, dataObject);
}

const ns = NormalizedSchema.of(errorSchema);
const ErrorCtor = registry.getErrorCtor(errorSchema);
const message = dataObject.message ?? dataObject.Message ?? "Unknown";
const exception = new errorSchema.ctor(message);
const exception = new ErrorCtor(message);

const output = {} as any;
for (const [name, member] of ns.structIterator()) {
Expand Down
25 changes: 20 additions & 5 deletions packages/core/src/submodules/schema/TypeRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export class TypeRegistry {

private constructor(
public readonly namespace: string,
private schemas: Map<string, ISchema> = new Map()
private schemas: Map<string, ISchema> = new Map(),
private exceptions: Map<ErrorSchema, any> = new Map()
) {}

/**
Expand All @@ -34,8 +35,7 @@ export class TypeRegistry {
*/
public register(shapeId: string, schema: ISchema) {
const qualifiedName = this.normalizeShapeId(shapeId);
const registry = TypeRegistry.for(this.getNamespace(shapeId));
registry.schemas.set(qualifiedName, schema);
this.schemas.set(qualifiedName, schema);
}

/**
Expand All @@ -50,6 +50,21 @@ export class TypeRegistry {
return this.schemas.get(id)!;
}

/**
* Associates an error schema with its constructor.
*/
public registerError(errorSchema: ErrorSchema, ctor: any) {
this.exceptions.set(errorSchema, ctor);
}

/**
* @param errorSchema - query.
* @returns Error constructor that extends the service's base exception.
*/
public getErrorCtor(errorSchema: ErrorSchema): any {
return this.exceptions.get(errorSchema);
}

/**
* 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
Expand Down Expand Up @@ -83,9 +98,9 @@ export class TypeRegistry {
/**
* Unloads the current TypeRegistry.
*/
public destroy() {
TypeRegistry.registries.delete(this.namespace);
public clear() {
this.schemas.clear();
this.exceptions.clear();
}

private normalizeShapeId(shapeId: string) {
Expand Down
11 changes: 8 additions & 3 deletions packages/core/src/submodules/schema/schemas/ErrorSchema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { SchemaRef, SchemaTraits } from "@smithy/types";

import { TypeRegistry } from "../TypeRegistry";
import { Schema } from "./Schema";
import { StructureSchema } from "./StructureSchema";

Expand All @@ -14,6 +13,9 @@ import { StructureSchema } from "./StructureSchema";
*/
export class ErrorSchema extends StructureSchema {
public static readonly symbol = Symbol.for("@smithy/err");
/**
* @deprecated - field unused.
*/
public ctor!: any;
protected readonly symbol = ErrorSchema.symbol;
}
Expand All @@ -36,13 +38,16 @@ export const error = (
traits: SchemaTraits,
memberNames: string[],
memberList: SchemaRef[],
ctor: any
/**
* @deprecated - field unused.
*/
ctor?: any
): ErrorSchema =>
Schema.assign(new ErrorSchema(), {
name,
namespace,
traits,
memberNames,
memberList,
ctor,
ctor: null,
});
9 changes: 4 additions & 5 deletions packages/core/src/submodules/schema/schemas/schemas.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,19 @@ describe("schemas", () => {
});

describe(ErrorSchema.name, () => {
const schema = error("ack", "Error", 0, [], [], Error);
const schema = error("ack", "Error", 0, [], []);

it("is a StructureSchema", () => {
expect(schema).toBeInstanceOf(StructureSchema);
expect(schema).toBeInstanceOf(ErrorSchema);
});

it("additionally defines an error constructor", () => {
expect(schema.ctor).toBeInstanceOf(Function);
expect(new schema.ctor()).toBeInstanceOf(schema.ctor);
it("deprecated reference to the error constructor", () => {
expect(schema.ctor).toBe(null);
});

it("has a factory and the factory registers the schema", () => {
expect(error("ack", "Error", 0, [], [], Error)).toEqual(schema);
expect(error("ack", "Error", 0, [], [])).toEqual(schema);
expect(TypeRegistry.for("ack").getSchema(schema.name)).toEqual(schema);
});

Expand Down
13 changes: 4 additions & 9 deletions private/smithy-rpcv2-cbor-schema/src/schemas/schemas_0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export const _p = "path";
export const _rM = "recursiveMember";
export const _s = "sparse";
export const _sBM = "sparseBooleanMap";
export const _sC = "smithy.ts.sdk.synthetic.smithy.protocoltests.rpcv2Cbor";
export const _sL = "stringList";
export const _sLt = "structureList";
export const _sNM = "sparseNumberMap";
Expand All @@ -131,15 +132,9 @@ export const n2 = "smithy.protocoltests.shared";

// smithy-typescript generated code
import { RpcV2ProtocolServiceException as __RpcV2ProtocolServiceException } from "../models/RpcV2ProtocolServiceException";
import { error } from "@smithy/core/schema";
import { TypeRegistry, error } from "@smithy/core/schema";

/* eslint no-var: 0 */

export var RpcV2ProtocolServiceException = error(
"smithy.ts.sdk.synthetic.smithy.protocoltests.rpcv2Cbor",
"RpcV2ProtocolServiceException",
0,
[],
[],
__RpcV2ProtocolServiceException
);
export var RpcV2ProtocolServiceException = error(_sC, "RpcV2ProtocolServiceException", 0, [], [], null);
TypeRegistry.for(_sC).registerError(RpcV2ProtocolServiceException, __RpcV2ProtocolServiceException);
17 changes: 10 additions & 7 deletions private/smithy-rpcv2-cbor-schema/src/schemas/schemas_1_Rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ import {
n1,
n2,
} from "./schemas_0";
import { error, list, map, op, struct } from "@smithy/core/schema";
import { TypeRegistry, error, list, map, op, struct } from "@smithy/core/schema";

/* eslint no-var: 0 */

Expand All @@ -142,9 +142,10 @@ export var ValidationException = error(
},
[_m, _fL],
[0, () => ValidationExceptionFieldList],

__ValidationException
null
);
TypeRegistry.for(n0).registerError(ValidationException, __ValidationException);

export var ValidationExceptionField = struct(n0, _VEF, 0, [_p, _m], [0, 0]);
export var ClientOptionalDefaults = struct(n1, _COD, 0, [_me], [1]);
export var ComplexError = error(
Expand All @@ -155,9 +156,10 @@ export var ComplexError = error(
},
[_TL, _N],
[0, () => ComplexNestedErrorData],

__ComplexError
null
);
TypeRegistry.for(n1).registerError(ComplexError, __ComplexError);

export var ComplexNestedErrorData = struct(n1, _CNED, 0, [_F], [0]);
export var Defaults = struct(
n1,
Expand Down Expand Up @@ -199,9 +201,10 @@ export var InvalidGreeting = error(
},
[_M],
[0],

__InvalidGreeting
null
);
TypeRegistry.for(n1).registerError(InvalidGreeting, __InvalidGreeting);

export var OperationWithDefaultsInput = struct(
n1,
_OWDI,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,14 +302,20 @@ private void writeStructureSchema(StructureShape shape) {
);
writer.openBlock("""
export var $L = error($L, $L,""",
"",
", null);",
getShapeVariableName(shape),
checkImportString(shape, shape.getId().getNamespace(), "n"),
checkImportString(shape, shape.getId().getName()),
() -> doWithMembers(shape)
);
writer.writeInline(",$L", exceptionCtorSymbolName);
writer.write(");");
writer.addImportSubmodule("TypeRegistry", null, TypeScriptDependency.SMITHY_CORE, "/schema");
writer.write("""
TypeRegistry.for($L).registerError($L, $L);
""",
checkImportString(shape, shape.getId().getNamespace(), "n"),
getShapeVariableName(shape),
exceptionCtorSymbolName
);
} else {
writer.addImportSubmodule("struct", "struct", TypeScriptDependency.SMITHY_CORE, "/schema");
writer.openBlock("""
Expand All @@ -335,20 +341,28 @@ private void writeBaseError() {
String namespace = settings.getService(model).getId().getNamespace();

String exceptionCtorSymbolName = "__" + serviceExceptionName;
writer.addImportSubmodule("error", "error", TypeScriptDependency.SMITHY_CORE, "/schema");
writer.addImportSubmodule("error", null, TypeScriptDependency.SMITHY_CORE, "/schema");
writer.addRelativeImport(
serviceExceptionName,
exceptionCtorSymbolName,
Paths.get("..", "models", serviceExceptionName)
);

String syntheticNamespace = store.var("smithy.ts.sdk.synthetic." + namespace);
writer.write("""
export var $L = error($S, $S, 0, [], []""",
export var $L = error($L, $S, 0, [], [], null);""",
serviceExceptionName,
"smithy.ts.sdk.synthetic." + namespace,
syntheticNamespace,
serviceExceptionName
);
writer.writeInline(",$L", exceptionCtorSymbolName);
writer.write(");");
writer.addImportSubmodule("TypeRegistry", null, TypeScriptDependency.SMITHY_CORE, "/schema");
writer.write("""
TypeRegistry.for($L).registerError($L, $L);
""",
syntheticNamespace,
serviceExceptionName,
exceptionCtorSymbolName
);
}

private void writeUnionSchema(UnionShape shape) {
Expand Down
Loading