Skip to content

Commit

Permalink
relax object accumulator to accumulate parts with different versions …
Browse files Browse the repository at this point in the history
…with same major version
  • Loading branch information
Amplifiyer committed Aug 27, 2024
1 parent b73e775 commit 633e22e
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 7 deletions.
6 changes: 6 additions & 0 deletions .changeset/hot-tigers-speak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@aws-amplify/client-config': patch
'@aws-amplify/platform-core': patch
---

relax object accumulator to accumulate parts with different versions with same major version
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

90 changes: 90 additions & 0 deletions packages/client-config/src/unified_client_config_generator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,96 @@ void describe('UnifiedClientConfigGenerator', () => {
assert.deepStrictEqual(result, expectedClientConfig);
});

void it('can generate client config for apps using custom config of V1', async () => {
const stubOutput: UnifiedBackendOutput = {
[platformOutputKey]: {
version: '1',
payload: {
deploymentType: 'branch',
region: 'us-east-1',
},
},
[authOutputKey]: {
version: '1',
payload: {
identityPoolId: 'testIdentityPoolId',
userPoolId: 'testUserPoolId',
webClientId: 'testWebClientId',
authRegion: 'us-east-1',
passwordPolicyMinLength: '8',
passwordPolicyRequirements:
'["REQUIRES_NUMBERS","REQUIRES_LOWERCASE","REQUIRES_UPPERCASE"]',
mfaTypes: '["SMS","TOTP"]',
mfaConfiguration: 'OPTIONAL',
verificationMechanisms: '["email","phone_number"]',
usernameAttributes: '["email"]',
signupAttributes: '["email"]',
allowUnauthenticatedIdentities: 'true',
},
},
[customOutputKey]: {
version: '1',
payload: {
customOutputs: JSON.stringify({
version: '1', // Uses old configuration
custom: {
output1: 'val1',
output2: 'val2',
},
}),
},
},
};
const outputRetrieval = mock.fn(async () => stubOutput);
const modelSchemaAdapter = new ModelIntrospectionSchemaAdapter(
stubClientProvider
);

mock.method(
modelSchemaAdapter,
'getModelIntrospectionSchemaFromS3Uri',
() => undefined
);
const configContributors = new ClientConfigContributorFactory(
modelSchemaAdapter
).getContributors('1.1'); //Generate with new configuration format
const clientConfigGenerator = new UnifiedClientConfigGenerator(
outputRetrieval,
configContributors
);
const result = await clientConfigGenerator.generateClientConfig();
const expectedClientConfig: ClientConfig = {
auth: {
user_pool_id: 'testUserPoolId',
aws_region: 'us-east-1',
user_pool_client_id: 'testWebClientId',
identity_pool_id: 'testIdentityPoolId',
mfa_methods: ['SMS', 'TOTP'],
standard_required_attributes: ['email'],
username_attributes: ['email'],
user_verification_types: ['email', 'phone_number'],
mfa_configuration: 'OPTIONAL',

password_policy: {
min_length: 8,
require_lowercase: true,
require_numbers: true,
require_symbols: false,
require_uppercase: true,
},

unauthenticated_identities_enabled: true,
},
custom: {
output1: 'val1',
output2: 'val2',
},
version: '1.1', // The max version prevails
};

assert.deepStrictEqual(result, expectedClientConfig);
});

void it('throws user error if there are overlapping values', async () => {
const customOutputs = {
auth: { user_pool_id: 'overrideUserPoolId' },
Expand Down
1 change: 1 addition & 0 deletions packages/platform-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@aws-sdk/client-sts": "^3.624.0",
"is-ci": "^3.0.1",
"lodash.mergewith": "^4.6.2",
"semver": "^7.6.3",
"uuid": "^9.0.1",
"zod": "^3.22.2"
}
Expand Down
92 changes: 91 additions & 1 deletion packages/platform-core/src/object_accumulator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,96 @@ void describe('Object accumulator', () => {
});
});

void it('should merge two objects with same major version 1.1 and 1.2', () => {
const object1 = {
myVersionKey: '1.2',
a1: 'valueA1',
b1: {
c1: 'valueC1',
d1: {
e1: 'valueE1',
},
},
};
const object2 = {
myVersionKey: '1.1',
a2: 'valueA2',
b2: {
c2: 'valueC2',
d2: {
e2: 'valueE2',
},
},
};
const accumulatedObject = new ObjectAccumulator({}, 'myVersionKey')
.accumulate(object1)
.accumulate(object2)
.getAccumulatedObject();

assert.deepEqual(accumulatedObject, {
myVersionKey: '1.2',
a1: 'valueA1',
a2: 'valueA2',
b1: {
c1: 'valueC1',
d1: {
e1: 'valueE1',
},
},
b2: {
c2: 'valueC2',
d2: {
e2: 'valueE2',
},
},
});
});

void it('should merge two objects with same major version 1.1 and 1', () => {
const object1 = {
myVersionKey: '1.1',
a1: 'valueA1',
b1: {
c1: 'valueC1',
d1: {
e1: 'valueE1',
},
},
};
const object2 = {
myVersionKey: '1.0',
a2: 'valueA2',
b2: {
c2: 'valueC2',
d2: {
e2: 'valueE2',
},
},
};
const accumulatedObject = new ObjectAccumulator({}, 'myVersionKey')
.accumulate(object1)
.accumulate(object2)
.getAccumulatedObject();

assert.deepEqual(accumulatedObject, {
myVersionKey: '1.1',
a1: 'valueA1',
a2: 'valueA2',
b1: {
c1: 'valueC1',
d1: {
e1: 'valueE1',
},
},
b2: {
c2: 'valueC2',
d2: {
e2: 'valueE2',
},
},
});
});

void it('should throw on property override attempt', () => {
assert.throws(
() => {
Expand All @@ -153,7 +243,7 @@ void describe('Object accumulator', () => {
);
});

void it('should throw on version mismatch of objects', () => {
void it('should throw on major version mismatch of objects', () => {
assert.throws(
() => {
new ObjectAccumulator({}) // using default version key of `version`
Expand Down
24 changes: 18 additions & 6 deletions packages/platform-core/src/object_accumulator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DeepPartialAmplifyGeneratedConfigs } from '@aws-amplify/plugin-types';
import mergeWith from 'lodash.mergewith';

import semver from 'semver';
/**
* This error is thrown when there's a collision in the object keys
*/
Expand Down Expand Up @@ -58,11 +58,23 @@ export class ObjectAccumulator<T> {
return existingValue.concat(incomingValue);
}
if (existingValue && typeof existingValue !== 'object') {
if (key === this.versionKey && existingValue !== incomingValue) {
throw new ObjectAccumulatorVersionMismatchError(
existingValue,
incomingValue
);
if (key === this.versionKey) {
const incomingVersion = semver.coerce(incomingValue);
const existingVersion = semver.coerce(existingValue);
if (incomingVersion && existingVersion) {
// Only throw if the major version is not equal
if (incomingVersion.major !== existingVersion.major) {
throw new ObjectAccumulatorVersionMismatchError(
existingValue,
incomingValue
);
} else {
// We always get the max version to persist in the accumulated object
return semver.gte(incomingVersion, existingVersion)
? incomingValue
: existingValue;
}
}
} else if (key !== this.versionKey) {
throw new ObjectAccumulatorPropertyAlreadyExistsError(
key,
Expand Down

0 comments on commit 633e22e

Please sign in to comment.