Skip to content

Commit

Permalink
Merge pull request #4 from aws-amplify/master
Browse files Browse the repository at this point in the history
merge from source
  • Loading branch information
abhi7cr authored Jun 23, 2021
2 parents bc549b0 + e3e51f5 commit da07429
Show file tree
Hide file tree
Showing 91 changed files with 1,637 additions and 237 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/lock.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: 'Lock Threads'

on:
schedule:
- cron: '0 * * * *'
- cron: '30 1 * * *'

jobs:
lock:
Expand All @@ -11,7 +11,7 @@ jobs:
- uses: dessant/lock-threads@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
issue-lock-inactive-days: '30'
issue-lock-inactive-days: '60'
issue-lock-reason: 'resolved'
issue-lock-comment: |
This issue has been automatically locked since there hasn't been any recent activity after it was closed. Please open a [new issue](https://github.com/aws-amplify/amplify-cli/issues/new?assignees=&labels=&template=1.bug_report.yaml) for related bugs.
Expand Down
4 changes: 2 additions & 2 deletions packages/amplify-appsync-simulator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@
"slash": "^3.0.0",
"steed": "^1.1.3",
"uuid": "^3.4.0",
"ws": "^7.2.3"
"ws": "^7.4.6"
},
"devDependencies": {
"@types/cors": "^2.8.6",
"@types/express": "^4.17.2",
"@types/moment-timezone": "0.5.12",
"@types/node": "^12.12.6",
"@types/pino": "5.15.3",
"@types/ws": "^7.2.3",
"@types/ws": "^7.4.4",
"amplify-graphiql-explorer": "1.6.1"
},
"jest": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ export class JavaMap {
key,
value,
},
this.mapper
)
)
this.mapper,
),
),
);

return new JavaArray(entries, this.mapper);
Expand All @@ -46,7 +46,7 @@ export class JavaMap {

get(key) {
if (this.map.has(key.toString())) {
return this.map.get(key);
return this.map.get(key.toString());
}
return null;
}
Expand Down Expand Up @@ -95,7 +95,7 @@ export class JavaMap {
...sum,
[key]: toJSON(value),
}),
{}
{},
);
}
}
Expand Down
33 changes: 32 additions & 1 deletion packages/amplify-category-api/src/commands/api/add.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const inquirer = require('inquirer');
const subcommand = 'add';
const category = 'api';
const apiGatewayService = 'API Gateway';

let options;

Expand All @@ -10,7 +12,7 @@ module.exports = {
const servicesMetadata = require('../../provider-utils/supported-services').supportedServices;
return amplify
.serviceSelectionPrompt(context, category, servicesMetadata)
.then(result => {
.then(async result => {
options = {
service: result.service,
providerPlugin: result.providerName,
Expand All @@ -21,6 +23,10 @@ module.exports = {
return;
}

if ((await shouldUpdateExistingRestApi(context, result.service)) === true) {
return providerController.updateResource(context, category, result.service, { allowContainers: false });
}

return providerController.addResource(context, category, result.service, options);
})
.then(resourceName => {
Expand All @@ -42,3 +48,28 @@ module.exports = {
});
},
};

async function shouldUpdateExistingRestApi(context, selectedService) {
if (selectedService !== apiGatewayService) {
return false;
}

const { allResources } = await context.amplify.getResourceStatus();
const hasRestApis = allResources.some(resource => resource.service === apiGatewayService && resource.mobileHubMigrated !== true);

if (!hasRestApis) {
return false;
}

const question = [
{
name: 'update',
message: 'Would you like to add a new path to an existing REST API:',
type: 'confirm',
default: true,
},
];
const answer = await inquirer.prompt(question);

return answer.update;
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,10 @@ async function isRestContainer(context) {
}

export async function updateResource(context, category, service, options) {
const allowContainers = options?.allowContainers ?? true;
let useContainerResource = false;
let apiType = API_TYPE.GRAPHQL;
if (isContainersEnabled(context)) {
if (allowContainers && isContainersEnabled(context)) {
const {
hasAPIGatewayContainerResource,
hasAPIGatewayLambdaResource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export async function updateWalkthrough(context, defaultValuesFilename) {
name: 'operation',
message: 'What would you like to do',
type: 'list',
when: context.input.command !== 'add',
choices: [
{ name: 'Add another path', value: 'add' },
{ name: 'Update path', value: 'update' },
Expand All @@ -76,6 +77,12 @@ export async function updateWalkthrough(context, defaultValuesFilename) {

const updateApi = await inquirer.prompt(question);

// Inquirer does not currently support combining 'when' and 'default', so
// manually set the operation if the user ended up here via amplify api add.
if (context.input.command === 'add') {
updateApi.operation = 'add';
}

if (updateApi.resourceName === 'AdminQueries') {
const errMessage = `The Admin Queries API is maintained through the Auth category and should be updated using 'amplify update auth' command`;
context.print.warning(errMessage);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import { executeAmplifyHeadlessCommand } from '../../../lib';
import { ImportAuthRequest } from 'amplify-headless-interface';
import { messages } from '../../provider-utils/awscloudformation/assets/string-maps';
import { stateManager } from 'amplify-cli-core';

jest.mock('amplify-cli-core', () => ({
stateManager: {
setResourceParametersJson: jest.fn(),
getMeta: jest.fn().mockReturnValue({
providers: {
awscloudformation: {},
},
}),
},
}));

describe('import auth headless', () => {
let mockContext: any;
const USER_POOL_ID = 'user-pool-123';
const IDENTITY_POOL_ID = 'identity-pool-123';
const NATIVE_CLIENT_ID = 'native-app-client-123';
const WEB_CLIENT_ID = 'web-app-client-123';
const defaultUserPoolClients = [
{
UserPoolId: USER_POOL_ID,
ClientId: WEB_CLIENT_ID,
},
{
UserPoolId: USER_POOL_ID,
ClientId: NATIVE_CLIENT_ID,
ClientSecret: 'secret-123',
},
];
const headlessPayload: ImportAuthRequest = {
version: 1,
userPoolId: USER_POOL_ID,
identityPoolId: IDENTITY_POOL_ID,
nativeClientId: NATIVE_CLIENT_ID,
webClientId: WEB_CLIENT_ID,
};
const headlessPayloadString: string = JSON.stringify(headlessPayload);
const projectConfig = {
projectName: 'amplify-import-headless-auth-test',
};
const projectDetails = {
projectConfig,
amplifyMeta: {},
};
const getUserPoolDetails = {
Id: USER_POOL_ID,
MfaConfiguration: 'ON',
};
const identityPoolDetails = [
{
IdentityPoolId: IDENTITY_POOL_ID,
IdentityPoolName: 'identity-pool',
AllowUnauthenticatedIdentities: true,
CognitoIdentityProviders: [
{
ProviderName: `web-provider-${USER_POOL_ID}`,
ClientId: WEB_CLIENT_ID,
},
{
ProviderName: `native-provider-${USER_POOL_ID}`,
ClientId: NATIVE_CLIENT_ID,
},
],
},
];
const mfaResponse = {
SoftwareTokenMfaConfiguration: {
Enabled: true,
},
MfaConfiguration: 'ON',
};
const getIdentityPoolRolesResponse = {
authRoleArn: 'arn:authRole:123',
authRoleName: 'authRole',
unauthRoleName: 'unAuthRole',
unauthRoleArn: 'arn:unAuthRole:123',
};
// mock fns
const cognitoUserPoolServiceMock = jest.fn();
const cognitoIdentityPoolServiceMock = jest.fn();
const pluginInstanceMock = jest.fn();
const getUserPoolDetailsMock = jest.fn();
const listUserPoolClientsMock = jest.fn();
const getUserPoolMfaConfigMock = jest.fn();
const listIdentityPoolDetailsMock = jest.fn();
const getIdentityPoolRolesMock = jest.fn();
const getProjectConfigMock = jest.fn().mockReturnValue(projectConfig);
const getProjectDetailsMock = jest.fn().mockReturnValue(projectDetails);

beforeAll(() => {
const loadResourceParametersMock = jest.fn();
const updateAmplifyMetaAfterResourceAddMock = jest.fn();
const pluginInstance = {
loadResourceParameters: loadResourceParametersMock,
createCognitoUserPoolService: cognitoUserPoolServiceMock.mockReturnValue({
getUserPoolDetails: getUserPoolDetailsMock.mockResolvedValueOnce(getUserPoolDetails),
listUserPoolClients: listUserPoolClientsMock.mockResolvedValueOnce(defaultUserPoolClients),
getUserPoolMfaConfig: getUserPoolMfaConfigMock.mockResolvedValue(mfaResponse),
}),
createIdentityPoolService: cognitoIdentityPoolServiceMock.mockReturnValue({
listIdentityPoolDetails: listIdentityPoolDetailsMock.mockResolvedValue(identityPoolDetails),
getIdentityPoolRoles: getIdentityPoolRolesMock.mockResolvedValue(getIdentityPoolRolesResponse),
}),
};
mockContext = {
amplify: {
getProjectConfig: getProjectConfigMock,
getProjectDetails: getProjectDetailsMock,
updateamplifyMetaAfterResourceAdd: updateAmplifyMetaAfterResourceAddMock,
getPluginInstance: pluginInstanceMock.mockReturnValue(pluginInstance),
saveEnvResourceParameters: jest.fn(),
},
print: {
warning: jest.fn(),
info: jest.fn(),
error: jest.fn(),
},
parameters: {
first: 'mockFirst',
},
input: {
command: 'import',
},
};
});

beforeEach(() => {
jest.clearAllMocks();
});

it('should process command successfully', async () => {
await executeAmplifyHeadlessCommand(mockContext, headlessPayloadString);

expect(getUserPoolDetailsMock).toBeCalledWith(USER_POOL_ID);
expect(listUserPoolClientsMock).toBeCalledWith(USER_POOL_ID);
expect(getUserPoolMfaConfigMock).toBeCalledWith(USER_POOL_ID);
expect(listIdentityPoolDetailsMock).toBeCalledWith();
expect(getIdentityPoolRolesMock).toBeCalledWith(IDENTITY_POOL_ID);
});

it('should warn if auth has already been added', async () => {
getProjectDetailsMock.mockReturnValueOnce({
projectConfig,
amplifyMeta: {
auth: {
foo: 'bar',
},
},
});

await executeAmplifyHeadlessCommand(mockContext, headlessPayloadString);

expect(mockContext.print.warning).toBeCalledWith(messages.authExists);
});

it('should throw user pool not found exception', async () => {
try {
getUserPoolDetailsMock.mockRejectedValueOnce({
name: 'ResourceNotFoundException',
});

await executeAmplifyHeadlessCommand(mockContext, headlessPayloadString);

fail('should throw error');
} catch (e) {
expect(e.message).toBe(`The previously configured Cognito User Pool: '' (user-pool-123) cannot be found.`);
}
});

it('should throw web clients not found exception ', async () => {
try {
listUserPoolClientsMock.mockResolvedValue([]);

await executeAmplifyHeadlessCommand(mockContext, headlessPayloadString);

fail('should throw error');
} catch (e) {
expect(e.message).toBe(
'The selected Cognito User Pool does not have at least 1 Web app client configured. Web app clients are app clients without a client secret.',
);
}
});

it('should throw no matching identity pool found exception', async () => {
const INVALID_USER_POOL_ID = USER_POOL_ID + '-invalid';
const invalidHeadlessPayload = {
...headlessPayload,
userPoolId: INVALID_USER_POOL_ID,
};
const invalidHeadlessPayloadString = JSON.stringify(invalidHeadlessPayload);
try {
getUserPoolDetailsMock.mockResolvedValueOnce({
Id: INVALID_USER_POOL_ID,
MfaConfiguration: 'ON',
});
listUserPoolClientsMock.mockResolvedValueOnce(defaultUserPoolClients);

await executeAmplifyHeadlessCommand(mockContext, invalidHeadlessPayloadString);

fail('should throw error');
} catch (e) {
expect(e.message).toBe('There are no Identity Pools found which has the selected Cognito User Pool configured as identity provider.');
}
});
});
Loading

0 comments on commit da07429

Please sign in to comment.