Skip to content

feat(region-info): throw ValidationError instead of untyped Errors #33384

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
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
1 change: 0 additions & 1 deletion packages/aws-cdk-lib/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ baseConfig.rules['@cdklabs/no-throw-default-error'] = ['error'];
// not yet supported
const noThrowDefaultErrorNotYetSupported = [
'core',
'region-info',
];
baseConfig.overrides.push({
files: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import {
import { AWS_CDK_METADATA } from './metadata';
import {
AWS_REGIONS,
before,
RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN,
RULE_CLASSIC_PARTITION_BECOMES_OPT_IN,
AWS_REGIONS_AND_RULES,
} from '../lib/aws-entities';

export async function main(): Promise<void> {
Expand Down Expand Up @@ -169,10 +169,24 @@ function checkRegionsSubMap(map: Record<string, Record<string, Record<string, un
}
}

export function after(region: string, ruleOrRegion: string | symbol) {
function after(region: string, ruleOrRegion: string | symbol) {
return region !== ruleOrRegion && !before(region, ruleOrRegion);
}

/**
* Whether or not a region predates a given rule (or region).
*
* Unknown region => we have to assume no.
*/
function before(region: string, ruleOrRegion: string | symbol) {
const ruleIx = AWS_REGIONS_AND_RULES.indexOf(ruleOrRegion);
if (ruleIx === -1) {
throw new Error(`Unknown rule: ${String(ruleOrRegion)}`);
}
const regionIx = AWS_REGIONS_AND_RULES.indexOf(region);
return regionIx === -1 ? false : regionIx < ruleIx;
}

main().catch(e => {
// eslint-disable-next-line no-console
console.error(e);
Expand Down
27 changes: 0 additions & 27 deletions packages/aws-cdk-lib/region-info/lib/aws-entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,33 +80,6 @@ export const AWS_REGIONS = AWS_REGIONS_AND_RULES
.filter((x) => typeof x === 'string')
.sort() as readonly string[];

/**
* Whether or not a region predates a given rule (or region).
*
* Unknown region => we have to assume no.
*/
export function before(region: string, ruleOrRegion: string | symbol) {
const ruleIx = AWS_REGIONS_AND_RULES.indexOf(ruleOrRegion);
if (ruleIx === -1) {
throw new Error(`Unknown rule: ${String(ruleOrRegion)}`);
}
const regionIx = AWS_REGIONS_AND_RULES.indexOf(region);
return regionIx === -1 ? false : regionIx < ruleIx;
}

/**
* Return all regions before a given rule was introduced (or region)
*/
export function regionsBefore(ruleOrRegion: string | symbol): string[] {
const ruleIx = AWS_REGIONS_AND_RULES.indexOf(ruleOrRegion);
if (ruleIx === -1) {
throw new Error(`Unknown rule: ${String(ruleOrRegion)}`);
}
return AWS_REGIONS_AND_RULES.slice(0, ruleIx)
.filter((entry) => typeof entry === 'string')
.sort() as string[];
}

export interface Region { readonly partition: string; readonly domainSuffix: string }

const PARTITION_MAP: {readonly [region: string]: Region } = {
Expand Down
9 changes: 5 additions & 4 deletions packages/aws-cdk-lib/region-info/lib/fact.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AWS_REGIONS } from './aws-entities';
import { UnscopedValidationError } from '../../core/lib/errors';

/**
* A database of regional information.
Expand Down Expand Up @@ -56,7 +57,7 @@ export class Fact {
const foundFact = this.find(region, name);

if (!foundFact) {
throw new Error(`No fact ${name} could be found for region: ${region} and name: ${name}.`);
throw new UnscopedValidationError(`No fact ${name} could be found for region: ${region} and name: ${name}.`);
}

return foundFact;
Expand All @@ -71,7 +72,7 @@ export class Fact {
public static register(fact: IFact, allowReplacing = false): void {
const regionFacts = this.database[fact.region] || (this.database[fact.region] = {});
if (fact.name in regionFacts && regionFacts[fact.name] !== fact.value && !allowReplacing) {
throw new Error(`Region ${fact.region} already has a fact ${fact.name}, with value ${regionFacts[fact.name]}`);
throw new UnscopedValidationError(`Region ${fact.region} already has a fact ${fact.name}, with value ${regionFacts[fact.name]}`);
}
if (fact.value !== undefined) {
regionFacts[fact.name] = fact.value;
Expand All @@ -88,15 +89,15 @@ export class Fact {
public static unregister(region: string, name: string, value?: string): void {
const regionFacts = this.database[region] || {};
if (name in regionFacts && value && regionFacts[name] !== value) {
throw new Error(`Attempted to remove ${name} from ${region} with value ${value}, but the fact's value is ${regionFacts[name]}`);
throw new UnscopedValidationError(`Attempted to remove ${name} from ${region} with value ${value}, but the fact's value is ${regionFacts[name]}`);
}
delete regionFacts[name];
}

private static readonly database: { [region: string]: { [name: string]: string } } = {};

private constructor() {
throw new Error('Use the static methods of Fact instead!');
throw new UnscopedValidationError('Use the static methods of Fact instead!');
}
}

Expand Down
Loading