feat(glue-alpha): opaque Schema Type with Schema.custom, and stronger StorageParameter types - #38592
feat(glue-alpha): opaque Schema Type with Schema.custom, and stronger StorageParameter types#38592otaviomacedo wants to merge 4 commits into
Conversation
… StorageParameter types
Table schema `Type` was an open public interface, so a column type could be
hand-built as `{ isPrimitive, inputString }` — an uncontrolled escape hatch that
bypassed the `Schema` factories entirely, with no sanctioned way to express a
custom type. `Type` is now an opaque class produced only by `Schema` factories,
and `Schema.custom(inputString, isPrimitive?)` is the explicit escape hatch for
types the factories don't model. `Schema.decimal` now validates precision (1-38)
and scale (0..precision), matching the bounds `char`/`varchar` already enforce.
`StorageParameter` weak primitives are tightened: `custom(key, value)` takes a
`string` value instead of `any`, and `writeKmsKeyId` takes a `kms.IKey` instead of
a raw key-id string (rendering `key.keyId`).
Addresses the Type/Schema.custom, Schema.decimal-bounds, and
StorageParameter-weak-primitives findings from the aws-glue-alpha pre-GA API
review. (The overlapping `StorageParameters`-enum-vs-factory dedup is deferred —
it is entangled with the deprecated-`Table` deletion still in flight.)
BREAKING CHANGE: schema `Type` is now an opaque class; construct column types via
the `Schema` factories or `Schema.custom(...)` rather than `{ isPrimitive,
inputString }` literals. `StorageParameter.custom(key, value)` requires a `string`
value, and `StorageParameter.writeKmsKeyId` takes a `kms.IKey` instead of a string.
|
PRs without a linked issue will receive lower priority for review and merging. Please update the description to follow the PR template and include a line like |
✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.
| if (precision < 1 || precision > 38 || precision % 1 !== 0) { | ||
| throw new UnscopedValidationError(lit`DecimalPrecisionOutOfRange`, `decimal precision must be a positive integer between 1 and 38, got ${precision}`); | ||
| } | ||
| if (scale !== undefined && (scale < 0 || scale > 38 || scale % 1 !== 0)) { | ||
| throw new UnscopedValidationError(lit`DecimalScaleOutOfRange`, `decimal scale must be an integer between 0 and 38, got ${scale}`); | ||
| } |
There was a problem hiding this comment.
do these need any token resolution check (Token.isUnresolved) ?
| public static writeKmsKeyId(value: string): StorageParameter { | ||
| return new StorageParameter('write.kms.key.id', value); | ||
| public static writeKmsKeyId(key: kms.IKeyRef): StorageParameter { | ||
| return new StorageParameter('write.kms.key.id', key.keyRef.keyId); |
There was a problem hiding this comment.
can keyArn be used here instead of keyId ?
keyId drops the account and breaks cross-account imported keys -keyArn works for both same and cross-account
There was a problem hiding this comment.
Were you able to verify this works while deploying to CFN ?
| * | ||
| * @param inputString the Glue input string for the type (for example `interval_day_to_second`). | ||
| * @param isPrimitive whether the type is a primitive (non-nested) data type. | ||
| * @default isPrimitive - true |
There was a problem hiding this comment.
nit: I think @default is not needed here - You can mention the default behaviour on @param itself -
* @param isPrimitive whether the type is a primitive (non-nested) data type. Defaults to true.
|
|
||
| test('decimal rejects scale greater than 38', () => { | ||
| expect(() => Schema.decimal(38, 39)).toThrow(/decimal scale must be an integer between 0 and 38/); | ||
| }); |
There was a problem hiding this comment.
nit: should we add a test for non-integer precision/scale ?
test('decimal rejects non-integer precision or scale', () => {
expect(() => Schema.decimal(5.5)).toThrow(/.../);
expect(() => Schema.decimal(10, 2.5)).toThrow(/.../);
});
There was a problem hiding this comment.
Yeah, good point.
| isPrimitive: true, | ||
| inputString: scale !== undefined ? `decimal(${precision},${scale})` : `decimal(${precision})`, | ||
| }; | ||
| if (Token.isResolved(precision) && Token.isResolved(scale)) { |
There was a problem hiding this comment.
if scale is not provided and precision is not a token, then precision check will not be validated.
I think you will need to do the following instead -
if (Token.isResolved(precision) && (precision < 1 || precision > 38 || precision % 1 !== 0)) {
throw new UnscopedValidationError(lit`DecimalPrecisionOutOfRange`, `decimal precision must be a positive integer between 1 and 38, got ${precision}`);
}
if (scale !== undefined && Token.isResolved(scale) && (scale < 0 || scale > 38 || scale % 1 !== 0)) {
throw new UnscopedValidationError(lit`DecimalScaleOutOfRange`, `decimal scale must be an integer between 0 and 38, got ${scale}`);
}
Table schema
Typewas an open public interface, so a column type could be hand-built as{ isPrimitive, inputString }— an uncontrolled escape hatch that bypassed theSchemafactories entirely, with no sanctioned way to express a custom type.Typeis now an opaque class produced only bySchemafactories, andSchema.custom(inputString, isPrimitive?)is the explicit escape hatch for types the factories don't model.Schema.decimalnow validates precision (1-38) and scale (0..precision), matching the boundschar/varcharalready enforce.StorageParameterweak primitives are tightened:custom(key, value)takes astringvalue instead ofany, andwriteKmsKeyIdtakes akms.IKeyinstead of a raw key-id string (renderingkey.keyId).BREAKING CHANGE: schema
Typeis now an opaque class; construct column types via theSchemafactories orSchema.custom(...)rather than{ isPrimitive, inputString }literals.StorageParameter.custom(key, value)requires astringvalue, andStorageParameter.writeKmsKeyIdtakes akms.IKeyinstead of a string.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license