Skip to content

Commit

Permalink
Merge pull request #904 from aws-amplify/main
Browse files Browse the repository at this point in the history
Release API Category
  • Loading branch information
phani-srikar authored Oct 24, 2022
2 parents a529853 + f65e925 commit 6c13a9c
Show file tree
Hide file tree
Showing 43 changed files with 2,090 additions and 368 deletions.
11 changes: 11 additions & 0 deletions .circleci/config.base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,17 @@ jobs:
path: client-test-apps/js/api-model-relationship-app/cypress/screenshots
- store_artifacts:
path: client-test-apps/js/api-model-relationship-app/cypress/videos
- run:
name: Emit Canary Success Metric
command: |
source .circleci/local_publish_helpers.sh
emitCanarySuccessMetric
- run:
name: Emit Canary Failure Metric
command: |
source .circleci/local_publish_helpers.sh
emitCanaryFailureMetric
when: on_fail

amplify_migration_tests_v5:
<<: *defaults
Expand Down
11 changes: 11 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,17 @@ jobs:
path: client-test-apps/js/api-model-relationship-app/cypress/screenshots
- store_artifacts:
path: client-test-apps/js/api-model-relationship-app/cypress/videos
- run:
name: Emit Canary Success Metric
command: |
source .circleci/local_publish_helpers.sh
emitCanarySuccessMetric
- run:
name: Emit Canary Failure Metric
command: |
source .circleci/local_publish_helpers.sh
emitCanaryFailureMetric
when: on_fail

# our single workflow, that triggers the setup job defined above
workflows:
Expand Down
34 changes: 33 additions & 1 deletion .circleci/local_publish_helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,36 @@ function runE2eTest {
else
yarn run e2e --detectOpenHandles --maxWorkers=3 $TEST_SUITE
fi
}
}

# Accepts the value as an input parameter, i.e. 1 for success, 0 for failure.
# Only executes if IS_CANARY env variable is set
function emitCanarySuccessMetric {
if [[ "$CIRCLE_BRANCH" = main ]]; then
USE_PARENT_ACCOUNT=1
setAwsAccountCredentials
aws cloudwatch \
put-metric-data \
--metric-name CanarySuccessRate \
--namespace amplify-category-api-e2e-tests \
--unit Count \
--value 1 \
--dimensions branch=main \
--region us-west-2
fi
}

function emitCanaryFailureMetric {
if [[ "$CIRCLE_BRANCH" = main ]]; then
USE_PARENT_ACCOUNT=1
setAwsAccountCredentials
aws cloudwatch \
put-metric-data \
--metric-name CanarySuccessRate \
--namespace amplify-category-api-e2e-tests \
--unit Count \
--value 0 \
--dimensions branch=main \
--region us-west-2
fi
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ packages/amplify-dynamodb-simulator/__test__/dynamodb-data
.vscode/*
!.vscode/settings.json
!.vscode/extensions.json
.idea
packages/graphql*/lib
packages/amplify-graphql-transformer-*/lib
packages/amplify-appsync-simulator/lib
Expand Down
92 changes: 47 additions & 45 deletions packages/amplify-category-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,48 +41,48 @@
"@aws-amplify/graphql-transformer-core": "0.17.12",
"@aws-amplify/graphql-transformer-interfaces": "1.14.7",
"@aws-amplify/graphql-transformer-migrator": "1.4.7",
"@aws-cdk/assets": "~1.124.0",
"@aws-cdk/aws-apigateway": "~1.124.0",
"@aws-cdk/aws-apigatewayv2": "~1.124.0",
"@aws-cdk/aws-applicationautoscaling": "~1.124.0",
"@aws-cdk/aws-autoscaling": "~1.124.0",
"@aws-cdk/aws-autoscaling-hooktargets": "~1.124.0",
"@aws-cdk/aws-certificatemanager": "~1.124.0",
"@aws-cdk/aws-cloudformation": "~1.124.0",
"@aws-cdk/aws-cloudfront": "~1.124.0",
"@aws-cdk/aws-cloudwatch": "~1.124.0",
"@aws-cdk/aws-codebuild": "~1.124.0",
"@aws-cdk/aws-codeguruprofiler": "~1.124.0",
"@aws-cdk/aws-codepipeline": "~1.124.0",
"@aws-cdk/aws-codepipeline-actions": "~1.124.0",
"@aws-cdk/aws-cognito": "~1.124.0",
"@aws-cdk/aws-ec2": "~1.124.0",
"@aws-cdk/aws-ecr": "~1.124.0",
"@aws-cdk/aws-ecr-assets": "~1.124.0",
"@aws-cdk/aws-ecs": "~1.124.0",
"@aws-cdk/aws-efs": "~1.124.0",
"@aws-cdk/aws-elasticloadbalancing": "~1.124.0",
"@aws-cdk/aws-elasticloadbalancingv2": "~1.124.0",
"@aws-cdk/aws-events": "~1.124.0",
"@aws-cdk/aws-iam": "~1.124.0",
"@aws-cdk/aws-kms": "~1.124.0",
"@aws-cdk/aws-lambda": "~1.124.0",
"@aws-cdk/aws-logs": "~1.124.0",
"@aws-cdk/aws-route53": "~1.124.0",
"@aws-cdk/aws-route53-targets": "~1.124.0",
"@aws-cdk/aws-s3": "~1.124.0",
"@aws-cdk/aws-s3-assets": "~1.124.0",
"@aws-cdk/aws-secretsmanager": "~1.124.0",
"@aws-cdk/aws-servicediscovery": "~1.124.0",
"@aws-cdk/aws-sns": "~1.124.0",
"@aws-cdk/aws-sns-subscriptions": "~1.124.0",
"@aws-cdk/aws-sqs": "~1.124.0",
"@aws-cdk/aws-ssm": "~1.124.0",
"@aws-cdk/cloud-assembly-schema": "~1.124.0",
"@aws-cdk/core": "~1.124.0",
"@aws-cdk/custom-resources": "~1.124.0",
"@aws-cdk/cx-api": "~1.124.0",
"@aws-cdk/region-info": "~1.124.0",
"@aws-cdk/assets": "^1.159.0",
"@aws-cdk/aws-apigateway": "^1.159.0",
"@aws-cdk/aws-apigatewayv2": "^1.159.0",
"@aws-cdk/aws-applicationautoscaling": "^1.159.0",
"@aws-cdk/aws-autoscaling": "^1.159.0",
"@aws-cdk/aws-autoscaling-hooktargets": "^1.159.0",
"@aws-cdk/aws-certificatemanager": "^1.159.0",
"@aws-cdk/aws-cloudformation": "^1.159.0",
"@aws-cdk/aws-cloudfront": "^1.159.0",
"@aws-cdk/aws-cloudwatch": "^1.159.0",
"@aws-cdk/aws-codebuild": "^1.159.0",
"@aws-cdk/aws-codeguruprofiler": "^1.159.0",
"@aws-cdk/aws-codepipeline": "^1.159.0",
"@aws-cdk/aws-codepipeline-actions": "^1.159.0",
"@aws-cdk/aws-cognito": "^1.159.0",
"@aws-cdk/aws-ec2": "^1.159.0",
"@aws-cdk/aws-ecr": "^1.159.0",
"@aws-cdk/aws-ecr-assets": "^1.159.0",
"@aws-cdk/aws-ecs": "^1.159.0",
"@aws-cdk/aws-efs": "^1.159.0",
"@aws-cdk/aws-elasticloadbalancing": "^1.159.0",
"@aws-cdk/aws-elasticloadbalancingv2": "^1.159.0",
"@aws-cdk/aws-events": "^1.159.0",
"@aws-cdk/aws-iam": "^1.159.0",
"@aws-cdk/aws-kms": "^1.159.0",
"@aws-cdk/aws-lambda": "^1.159.0",
"@aws-cdk/aws-logs": "^1.159.0",
"@aws-cdk/aws-route53": "^1.159.0",
"@aws-cdk/aws-route53-targets": "^1.159.0",
"@aws-cdk/aws-s3": "^1.159.0",
"@aws-cdk/aws-s3-assets": "^1.159.0",
"@aws-cdk/aws-secretsmanager": "^1.159.0",
"@aws-cdk/aws-servicediscovery": "^1.159.0",
"@aws-cdk/aws-sns": "^1.159.0",
"@aws-cdk/aws-sns-subscriptions": "^1.159.0",
"@aws-cdk/aws-sqs": "^1.159.0",
"@aws-cdk/aws-ssm": "^1.159.0",
"@aws-cdk/cloud-assembly-schema": "^1.159.0",
"@aws-cdk/core": "^1.159.0",
"@aws-cdk/custom-resources": "^1.159.0",
"@aws-cdk/cx-api": "^1.159.0",
"@aws-cdk/region-info": "^1.159.0",
"@graphql-tools/merge": "^6.0.18",
"@octokit/rest": "^18.0.9",
"chalk": "^4.1.1",
Expand Down Expand Up @@ -116,12 +116,14 @@
"amplify-headless-interface": "^1.15.0",
"amplify-prompts": "^2.2.0",
"amplify-provider-awscloudformation": "^6.6.0",
"amplify-util-headless-input": "^1.9.5"
"amplify-util-headless-input": "^1.9.5",
"@aws-amplify/amplify-environment-parameters": "^1.1.1"
},
"devDependencies": {
"@aws-cdk/assertions": "~1.124.0",
"@aws-cdk/assertions": "^1.159.0",
"@types/js-yaml": "^4.0.0",
"amplify-util-headless-input": "^1.9.5"
"amplify-util-headless-input": "^1.9.5",
"@types/node": "^12.12.6"
},
"jest": {
"testURL": "http://localhost",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { collectDirectivesByType } from 'graphql-transformer-core';
import { parse } from 'graphql';
import {
$TSContext,
FeatureFlags,
pathManager,
stateManager,
} from 'amplify-cli-core';
import {
displayAuthNotification,
hasFieldAuthDirectives,
notifyFieldAuthSecurityChange,
notifyListQuerySecurityChange,
notifySecurityEnhancement,
} from '../../force-updates/auth-notifications';

jest.mock('amplify-cli-core');

const FeatureFlagsMock = FeatureFlags as jest.Mocked<typeof FeatureFlags>;
FeatureFlagsMock.getNumber.mockReturnValue(2);

const stateManagerMock = stateManager as jest.Mocked<typeof stateManager>;
// eslint-disable-next-line spellcheck/spell-checker
stateManagerMock.getCLIJSON.mockReturnValue({ features: { graphqltransformer: {} } });

const contextMock = {
amplify: {},
parameters: {
first: 'resourceName',
},
} as unknown as $TSContext;

describe('displayAuthNotification', () => {
it('level "off" returns true', () => {
const map: any = collectDirectivesByType(`
type MyModel @model(subscriptions: { level: off }) {
id: ID!
}
`);
const set: Set<string> = new Set(['MyModel']);

expect(displayAuthNotification(map, set)).toBe(true);
});

it('level "null" returns true', () => {
const map: any = collectDirectivesByType(`
type MyModel @model(subscriptions: { level: null }) {
id: ID!
}
`);
const set: Set<string> = new Set(['MyModel']);

expect(displayAuthNotification(map, set)).toBe(true);
});

it('subscriptions is null returns true', () => {
const map: any = collectDirectivesByType(`
type MyModel @model(subscriptions: null) {
id: ID!
}
`);
const set: Set<string> = new Set(['MyModel']);

expect(displayAuthNotification(map, set)).toBe(true);
});

it('"public" returns false', () => {
const map: any = collectDirectivesByType(`
type MyModel @model(subscriptions: { level: public }) {
id: ID!
}
`);
const set: Set<string> = new Set(['MyModel']);

expect(displayAuthNotification(map, set)).toBe(false);
});

it('"on" returns false', () => {
const map: any = collectDirectivesByType(`
type MyModel @model(subscriptions: { level: on }) {
id: ID!
}
`);
const set: Set<string> = new Set(['MyModel']);

expect(displayAuthNotification(map, set)).toBe(false);
});

it('absent value returns false', () => {
const map: any = collectDirectivesByType(`
type MyModel @model {
id: ID!
}
`);
const set: Set<string> = new Set(['MyModel']);

expect(displayAuthNotification(map, set)).toBe(false);
});
});

describe('hasFieldAuthDirectives', () => {
it('returns types with field auth directives', () => {
const doc = parse(`
type TypeWithFieldAuth @auth(rules: { allow: private, operations: [read] }) {
fieldWithAuth: String! @auth(rules: { allow: groups, group: "admin" })
}
type TypeWithoutFieldAuth @auth(rules: { allow: private, operations: [read] }) {
fieldWithoutAuth: String!
}
`);

const result = hasFieldAuthDirectives(doc);

expect(result).toContain('TypeWithFieldAuth');
expect(result).not.toContain('TypeWithoutFieldAuth');
});

it('returns empty set when no field auth', () => {
const doc = parse(`
type TypeWithoutFieldAuth @auth(rules: { allow: private, operations: [read] }) {
fieldWithoutAuth: String!
}
`);

const result = hasFieldAuthDirectives(doc);
expect(result.size).toBe(0);
});

it('returns empty set with nullable and field auth', () => {
const doc = parse(`
type TypeWithFieldAuth @auth(rules: { allow: private, operations: [read] }) {
fieldWithAuth: String @auth(rules: { allow: groups, group: "admin" })
}
`);

const result = hasFieldAuthDirectives(doc);
expect(result.size).toBe(0);
});
});

describe('push notifications', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('notifyFieldAuthSecurityChange should exit without fail when there is not api resource directory', async () => {
(<any>FeatureFlags.getBoolean).mockReturnValue(true);
(<any>pathManager.getResourceDirectoryPath).mockReturnValue('path-to-non-existing-resource-directory');
(<any>stateManager.getMeta).mockReturnValue({
api: {
'test-api-dev': {
service: 'AppSync',
output: {
name: 'test-api-dev',
},
},
},
});
(<any>FeatureFlags.ensureFeatureFlag).mockImplementation(() => { /* noop */ });
await notifyFieldAuthSecurityChange(contextMock);
// eslint-disable-next-line spellcheck/spell-checker
expect(<any>FeatureFlags.ensureFeatureFlag).toHaveBeenCalledWith('graphqltransformer', 'showFieldAuthNotification');
});

it('notifyListQuerySecurityChange should exit without fail when there is not api resource directory', async () => {
(<any>pathManager.getResourceDirectoryPath).mockReturnValue('path-to-non-existing-resource-directory');
(<any>stateManager.getMeta).mockReturnValue({
api: {
'test-api-dev': {
service: 'AppSync',
output: {
name: 'test-api-dev',
},
},
},
});
(<any>FeatureFlags.ensureFeatureFlag).mockImplementation(() => { /* noop */ });
await notifyListQuerySecurityChange(contextMock);
});

it('notifySecurityEnhancement should exit without fail when there is not api resource directory', async () => {
(<any>FeatureFlags.getBoolean).mockReturnValue(true);
(<any>pathManager.getResourceDirectoryPath).mockReturnValue('path-to-non-existing-resource-directory');
(<any>stateManager.getMeta).mockReturnValue({
api: {
'test-api-dev': {
service: 'AppSync',
output: {
name: 'test-api-dev',
},
},
},
});
(<any>FeatureFlags.ensureFeatureFlag).mockImplementation(() => { /* noop */ });
await notifySecurityEnhancement(contextMock);
// eslint-disable-next-line spellcheck/spell-checker
expect(<any>FeatureFlags.ensureFeatureFlag).toHaveBeenCalledWith('graphqltransformer', 'securityEnhancementNotification');
});
});
Loading

0 comments on commit 6c13a9c

Please sign in to comment.