Skip to content

Commit

Permalink
feat(core): add API for partially config Amplify
Browse files Browse the repository at this point in the history
  • Loading branch information
Di Wu committed Nov 3, 2023
1 parent 7a5440a commit 0191532
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 1 deletion.
71 changes: 70 additions & 1 deletion packages/core/__tests__/singleton/Singleton.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { AuthClass as Auth } from '../../src/singleton/Auth';
import { decodeJWT } from '../../src/singleton/Auth/utils';
import { AWSCredentialsAndIdentityId } from '../../src/singleton/Auth/types';
import { TextEncoder, TextDecoder } from 'util';
import { fetchAuthSession } from '../../src';
import { fetchAuthSession, ResourcesConfig } from '../../src';
import { extend } from 'lodash';
Object.assign(global, { TextDecoder, TextEncoder });
type ArgumentTypes<F extends Function> = F extends (...args: infer A) => any
? A
Expand All @@ -17,6 +18,74 @@ const MOCK_AUTH_CONFIG = {
},
};

describe('Partial configure', () => {
it('should update partial config', () => {
const mockConfig: ResourcesConfig = {
Auth: {
Cognito: {
allowGuestAccess: true,
identityPoolId: 'aws_cognito_identity_pool_id',
userPoolClientId: 'aws_user_pools_web_client_id',
userPoolId: 'aws_user_pools_id',
passwordFormat: {
minLength: 8,
requireLowercase: false,
requireNumbers: false,
requireSpecialCharacters: false,
requireUppercase: false,
},
},
},
};

Amplify.configure(mockConfig);
Amplify.updateConfig(
config =>
config
.atKey('Auth')
.atKey('Cognito')
.atKey('passwordFormat')
.atKey('minLength'),
12
);
expect(Amplify.getConfig()).toStrictEqual({
Auth: {
Cognito: {
allowGuestAccess: true,
identityPoolId: 'aws_cognito_identity_pool_id',
userPoolClientId: 'aws_user_pools_web_client_id',
userPoolId: 'aws_user_pools_id',
passwordFormat: {
minLength: 12,
requireLowercase: false,
requireNumbers: false,
requireSpecialCharacters: false,
requireUppercase: false,
},
},
},
});
});

it('should fail if update non existing field', () => {
const mockConfig: ResourcesConfig = {
Analytics: {
Pinpoint: {
appId: '123',
region: 'us-east-1',
},
},
};
Amplify.configure(mockConfig);
expect(() =>
Amplify.updateConfig(
config => config.atKey('Auth').atKey('Cognito').atKey('identityPoolId'),
'0000'
)
).toThrow();
});
});

describe('Amplify.configure() and Amplify.getConfig()', () => {
it('should take the legacy CLI shaped config object for configuring and return it from getConfig()', () => {
const mockLegacyConfig = {
Expand Down
18 changes: 18 additions & 0 deletions packages/core/src/singleton/Amplify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Hub, AMPLIFY_SYMBOL } from '../Hub';
import { LegacyConfig, LibraryOptions, ResourcesConfig } from './types';
import { parseAWSExports } from '../parseAWSExports';
import { deepFreeze } from '../utils';
import { Lens } from '../utils/Lens';

export class AmplifyClass {
resourcesConfig: ResourcesConfig;
Expand Down Expand Up @@ -78,6 +79,23 @@ export class AmplifyClass {
getConfig(): Readonly<ResourcesConfig> {
return this.resourcesConfig;
}

/**
* Partially update the configuration
*
* @param selector Selector to define which field to set.
* @param value The value to set.
* */
updateConfig<T>(
selector: (
config: Lens<ResourcesConfig, ResourcesConfig>
) => Lens<ResourcesConfig, T>,
value: T
): void {
Amplify.configure(
selector(Lens.of<ResourcesConfig>()).set(value)(this.resourcesConfig)
);
}
}

/**
Expand Down
35 changes: 35 additions & 0 deletions packages/core/src/utils/Lens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

type Getter<W, P> = (w: W) => P;
type Setter<W, P> = (p: P) => (w: W) => W;

export class Lens<W, P> {
private readonly get: Getter<W, P>;

/** @internal */
readonly set: Setter<W, P>;

static of<T>(): Lens<T, T> {
return new Lens<T, T>(
t => t,
t => () => t
);
}

constructor(get: Getter<W, P>, set: Setter<W, P>) {
this.get = get;
this.set = set;
}

atKey<Q extends keyof P>(key: Q): Lens<W, P[Q]> {
return new Lens<W, P[Q]>(
w => this.get(w)[key],
pq => w =>
this.set({
...this.get(w),
[key]: pq,
})(w)
);
}
}

0 comments on commit 0191532

Please sign in to comment.