Skip to content

Commit

Permalink
feat(amplify-category-api): add global sandbox mode directive on sche…
Browse files Browse the repository at this point in the history
…ma generation (aws-amplify#8074)

* feat(amplify-category-api): add global sandbox mode directive on schema generation

* test(amplify-e2e-tests): add e2e tests for sandbox mode

* test(amplify-category-api): add unit test for generating sandbox mode directive; rm unused method
  • Loading branch information
danielleadams authored and SwaySway committed Sep 24, 2021
1 parent 3ec3fe3 commit 075a333
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 87 deletions.
188 changes: 115 additions & 73 deletions .circleci/config.yml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { defineGlobalSandboxMode } from '../../../../provider-utils/awscloudformation/utils/global-sandbox-mode';
import { $TSContext } from 'amplify-cli-core';

describe('global sandbox mode GraphQL directive', () => {
it('returns AMPLIFY_DIRECTIVE type with code comment, directive, and env name', () => {
const envName = 'envone';
const ctx = <$TSContext>{
amplify: {
getEnvInfo() {
return { envName };
},
},
};

expect(defineGlobalSandboxMode(ctx))
.toBe(`# This allows public create, read, update, and delete access for a limited time to all models via API Key.
# To configure PRODUCTION-READY authorization rules, review: https://docs.amplify.aws/cli/graphql-transformer/auth
type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key(in: \"${envName}\") # FOR TESTING ONLY!\n
`);
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isResourceNameUnique } from 'amplify-cli-core';
import { isResourceNameUnique, FeatureFlags } from 'amplify-cli-core';
import {
AddApiRequest,
AppSyncServiceConfiguration,
Expand Down Expand Up @@ -90,7 +90,20 @@ class CfnApiArtifactHandler implements ApiArtifactHandler {
authConfig,
});

this.context.amplify.updateamplifyMetaAfterResourceAdd(category, serviceConfig.apiName, this.createAmplifyMeta(authConfig));
const useExperimentalPipelineTransformer = FeatureFlags.getBoolean('graphQLTransformer.useExperimentalPipelinedTransformer');
let globalSandboxModeConfig;

if (useExperimentalPipelineTransformer) {
const envName = this.context.amplify.getEnvInfo().envName;
globalSandboxModeConfig = {};
globalSandboxModeConfig[envName] = { enabled: true };
}

this.context.amplify.updateamplifyMetaAfterResourceAdd(
category,
serviceConfig.apiName,
this.createAmplifyMeta(authConfig, globalSandboxModeConfig),
);
return serviceConfig.apiName;
};

Expand Down Expand Up @@ -135,11 +148,12 @@ class CfnApiArtifactHandler implements ApiArtifactHandler {

private getResourceDir = (apiName: string) => path.join(this.context.amplify.pathManager.getBackendDirPath(), category, apiName);

private createAmplifyMeta = authConfig => ({
private createAmplifyMeta = (authConfig, globalSandboxModeConfig) => ({
service: 'AppSync',
providerPlugin: provider,
output: {
authConfig,
globalSandboxModeConfig,
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
$TSContext,
open,
} from 'amplify-cli-core';
import { defineGlobalSandboxMode } from '../utils/global-sandbox-mode';

const serviceName = 'AppSync';
const elasticContainerServiceName = 'ElasticContainer';
Expand Down Expand Up @@ -380,6 +381,7 @@ const updateApiInputWalkthrough = async (context, project, resolverConfig, model

export const serviceWalkthrough = async (context: $TSContext, defaultValuesFilename, serviceMetadata) => {
const resourceName = resourceAlreadyExists(context);
const useExperimentalPipelineTransformer = FeatureFlags.getBoolean('graphQLTransformer.useExperimentalPipelinedTransformer');

if (resourceName) {
const errMessage =
Expand All @@ -397,7 +399,7 @@ export const serviceWalkthrough = async (context: $TSContext, defaultValuesFilen
let askToEdit = true;

// Schema template selection
const schemaTemplateOptions = FeatureFlags.getBoolean('graphQLTransformer.useExperimentalPipelinedTransformer') ? schemaTemplatesV2 : schemaTemplatesV1;
const schemaTemplateOptions = useExperimentalPipelineTransformer ? schemaTemplatesV2 : schemaTemplatesV1;
const templateSelectionQuestion = {
type: inputs[4].type,
name: inputs[4].key,
Expand All @@ -408,7 +410,8 @@ export const serviceWalkthrough = async (context: $TSContext, defaultValuesFilen

const { templateSelection } = await inquirer.prompt(templateSelectionQuestion);
const schemaFilePath = path.join(graphqlSchemaDir, templateSelection);
schemaContent = fs.readFileSync(schemaFilePath, 'utf8');
schemaContent += useExperimentalPipelineTransformer ? defineGlobalSandboxMode(context) : '';
schemaContent += fs.readFileSync(schemaFilePath, 'utf8');

return {
...basicInfoAnswers,
Expand Down Expand Up @@ -668,9 +671,11 @@ export async function askAdditionalAuthQuestions(context, authConfig, defaultAut
if (await context.prompt.confirm('Configure additional auth types?')) {
// Get additional auth configured
const remainingAuthProviderChoices = authProviderChoices.filter(p => p.value !== defaultAuthType);
const currentAdditionalAuth = ((currentAuthConfig && currentAuthConfig.additionalAuthenticationProviders
? currentAuthConfig.additionalAuthenticationProviders
: []) as any[]).map(authProvider => authProvider.authenticationType);
const currentAdditionalAuth = (
(currentAuthConfig && currentAuthConfig.additionalAuthenticationProviders
? currentAuthConfig.additionalAuthenticationProviders
: []) as any[]
).map(authProvider => authProvider.authenticationType);

const additionalProvidersQuestion: CheckboxQuestion = {
type: 'checkbox',
Expand Down Expand Up @@ -857,9 +862,10 @@ function validateDays(input) {
}

function validateIssuerUrl(input) {
const isValid = /^(((?!http:\/\/(?!localhost))([a-zA-Z0-9.]{1,}):\/\/([a-zA-Z0-9-._~:?#@!$&'()*+,;=/]{1,})\/)|(?!http)(?!https)([a-zA-Z0-9.]{1,}):\/\/)$/.test(
input,
);
const isValid =
/^(((?!http:\/\/(?!localhost))([a-zA-Z0-9.]{1,}):\/\/([a-zA-Z0-9-._~:?#@!$&'()*+,;=/]{1,})\/)|(?!http)(?!https)([a-zA-Z0-9.]{1,}):\/\/)$/.test(
input,
);

if (!isValid) {
return 'The value must be a valid URI with a trailing forward slash. HTTPS must be used instead of HTTP unless you are using localhost.';
Expand Down Expand Up @@ -959,8 +965,8 @@ const buildPolicyResource = (resourceName: string, path: string | null) => {
{
Ref: `${category}${resourceName}GraphQLAPIIdOutput`,
},
...(path ? [path] : [])
]
...(path ? [path] : []),
],
],
};
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function defineGlobalSandboxMode(context: any): string {
const envName = context.amplify.getEnvInfo().envName;

return `# This allows public create, read, update, and delete access for a limited time to all models via API Key.
# To configure PRODUCTION-READY authorization rules, review: https://docs.amplify.aws/cli/graphql-transformer/auth
type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key(in: \"${envName}\") # FOR TESTING ONLY!\n
`;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type AMPLIFY_GLOBAL @allow_public_data_access_with_api_key(in: "dev")

type Todo @model {
id: ID!
content: String
}
45 changes: 45 additions & 0 deletions packages/amplify-e2e-tests/src/__tests__/sandbox-mode.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
initJSProjectWithProfile,
deleteProject,
createNewProjectDir,
deleteProjectDir,
addApiWithSchema,
amplifyPush,
getProjectMeta,
} from 'amplify-e2e-core';
import { testSchema } from '../schema-api-directives';

describe('api directives @allow_public_data_access_with_api_key', () => {
let projectDir: string;
const envName = 'dev';

beforeEach(async () => {
projectDir = await createNewProjectDir('model');
await initJSProjectWithProfile(projectDir, { envName });
});

afterEach(async () => {
await deleteProject(projectDir);
deleteProjectDir(projectDir);
});

it('schema and files generate with sandbox mode', async () => {
await addApiWithSchema(projectDir, 'model_with_sandbox_mode.graphql');
await amplifyPush(projectDir);

const meta = getProjectMeta(projectDir);
const { output } = meta.api.simplemodel;
const { authConfig, globalSandboxModeConfig, GraphQLAPIIdOutput, GraphQLAPIEndpointOutput, GraphQLAPIKeyOutput } = output;

expect(globalSandboxModeConfig[envName].enabled).toBe(true);
expect(authConfig.defaultAuthentication.authenticationType).toBe('API_KEY');
expect(authConfig.defaultAuthentication.apiKeyConfig.apiKeyExpirationDate).toBeDefined();

expect(GraphQLAPIIdOutput).toBeDefined();
expect(GraphQLAPIEndpointOutput).toBeDefined();
expect(GraphQLAPIKeyOutput).toBeDefined();

const testresult = await testSchema(projectDir, 'model', 'generates');
expect(testresult).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,15 @@ export class GraphQLTransform {
aws_iam: true,
aws_oidc: true,
aws_cognito_user_pools: true,
allow_public_data_access_with_api_key: true,
deprecated: true,
},
);
let allModelDefinitions = [...context.inputDocument.definitions];

const ampGlobalIdx = allModelDefinitions.findIndex(el => el.kind === 'ObjectTypeDefinition' && el.name.value === 'AMPLIFY_GLOBAL');
if (ampGlobalIdx > -1) allModelDefinitions.splice(ampGlobalIdx, 1);

for (const transformer of this.transformers) {
allModelDefinitions = allModelDefinitions.concat(...transformer.typeDefinitions, transformer.directive);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ directive @aws_api_key on FIELD_DEFINITION | OBJECT
directive @aws_iam on FIELD_DEFINITION | OBJECT
directive @aws_oidc on FIELD_DEFINITION | OBJECT
directive @aws_cognito_user_pools(cognito_groups: [String!]) on FIELD_DEFINITION | OBJECT
directive @allow_public_data_access_with_api_key(in: [String!]) on OBJECT
# Allows transformer libraries to deprecate directive arguments.
directive @deprecated(reason: String) on FIELD_DEFINITION | INPUT_FIELD_DEFINITION | ENUM | ENUM_VALUE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ export class TransformerResolver implements TransformerResolverProvider {
}
break;
default:
throw new Error('Unknow DataSource type');
throw new Error('Unknown DataSource type');
}
}
api.host.addResolver(
Expand Down

0 comments on commit 075a333

Please sign in to comment.