Skip to content

Commit

Permalink
Feature: enable deep merge default directive config with FF
Browse files Browse the repository at this point in the history
  • Loading branch information
marcvberg authored and phani-srikar committed Aug 16, 2022
1 parent fb04727 commit b1fdf59
Show file tree
Hide file tree
Showing 17 changed files with 72 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export class AuthTransformer extends TransformerAuthBase implements TransformerA
if (context.metadata.has('joinTypeList')) {
isJoinType = context.metadata.get<Array<string>>('joinTypeList')!.includes(typeName);
}
this.rules = getAuthDirectiveRules(new DirectiveWrapper(directive));
this.rules = getAuthDirectiveRules(new DirectiveWrapper(directive), context.featureFlags);

// validate rules
validateRules(this.rules, this.configuredAuthProviders, def.name.value);
Expand All @@ -187,7 +187,7 @@ export class AuthTransformer extends TransformerAuthBase implements TransformerA
this.addTypeToResourceReferences(def.name.value, this.rules);
// turn rules into roles and add into acm and roleMap
this.convertRulesToRoles(acm, this.rules, isJoinType, undefined, undefined, context);
this.modelDirectiveConfig.set(typeName, getModelConfig(modelDirective, typeName, context.isProjectUsingDataStore()));
this.modelDirectiveConfig.set(typeName, getModelConfig(modelDirective, typeName, context.featureFlags, context.isProjectUsingDataStore()));
this.authModelConfig.set(typeName, acm);
};

Expand Down Expand Up @@ -225,8 +225,8 @@ export class AuthTransformer extends TransformerAuthBase implements TransformerA
const modelDirective = parent.directives?.find(dir => dir.name.value === 'model');
const typeName = parent.name.value;
const fieldName = field.name.value;
const rules: AuthRule[] = getAuthDirectiveRules(new DirectiveWrapper(directive), true);
validateFieldRules(new DirectiveWrapper(directive), isParentTypeBuiltinType, modelDirective !== undefined, field.name.value);
const rules: AuthRule[] = getAuthDirectiveRules(new DirectiveWrapper(directive), context.featureFlags, true);
validateFieldRules(new DirectiveWrapper(directive), isParentTypeBuiltinType, modelDirective !== undefined, field.name.value, context.featureFlags);
validateRules(rules, this.configuredAuthProviders, field.name.value);

// regardless if a model directive is used we generate the policy for iam auth
Expand All @@ -239,7 +239,7 @@ export class AuthTransformer extends TransformerAuthBase implements TransformerA
let acm: AccessControlMatrix;
// check if the parent is already in the model config if not add it
if (!this.modelDirectiveConfig.has(typeName)) {
this.modelDirectiveConfig.set(typeName, getModelConfig(modelDirective, typeName, context.isProjectUsingDataStore()));
this.modelDirectiveConfig.set(typeName, getModelConfig(modelDirective, typeName, context.featureFlags, context.isProjectUsingDataStore()));
acm = new AccessControlMatrix({
name: parent.name.value,
operations: MODEL_OPERATIONS,
Expand Down Expand Up @@ -345,7 +345,7 @@ export class AuthTransformer extends TransformerAuthBase implements TransformerA
// check if searchable if included in the typeName
if (searchableDirective) {
// protect search query
const config = getSearchableConfig(searchableDirective, modelName);
const config = getSearchableConfig(searchableDirective, modelName, context.featureFlags);
this.protectSearchResolver(context, def, context.output.getQueryTypeName()!, config.queries.search, acm);
}
// get fields specified in the schema
Expand Down Expand Up @@ -515,7 +515,7 @@ export class AuthTransformer extends TransformerAuthBase implements TransformerA
}
// @searchable
if (searchableDirective) {
const config = getSearchableConfig(searchableDirective, def.name.value);
const config = getSearchableConfig(searchableDirective, def.name.value, ctx.featureFlags);
addServiceDirective(ctx.output.getQueryTypeName(), 'search', config.queries.search);
}

Expand Down
6 changes: 3 additions & 3 deletions packages/amplify-graphql-auth-transformer/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DirectiveWrapper, InvalidDirectiveError } from '@aws-amplify/graphql-transformer-core';
import { AppSyncAuthMode, TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces';
import { AppSyncAuthMode, FeatureFlagProvider, TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces';
import { Stack } from '@aws-cdk/core';
import { ObjectTypeDefinitionNode } from 'graphql';
import { MODEL_OPERATIONS, READ_MODEL_OPERATIONS } from './constants';
Expand Down Expand Up @@ -35,7 +35,7 @@ export const splitRoles = (roles: Array<RoleDefinition>): RolesByProvider => ({
/**
* returns @auth directive rules
*/
export const getAuthDirectiveRules = (authDir: DirectiveWrapper, isField = false): AuthRule[] => {
export const getAuthDirectiveRules = (authDir: DirectiveWrapper, featureFlags: FeatureFlagProvider, isField = false): AuthRule[] => {
const splitReadOperation = (rule: AuthRule): void => {
const operations: (ModelOperation | 'read')[] = rule.operations ?? [];
const indexOfRead = operations.indexOf('read', 0);
Expand All @@ -51,7 +51,7 @@ export const getAuthDirectiveRules = (authDir: DirectiveWrapper, isField = false
}
};

const { rules } = authDir.getArguments<{ rules: Array<AuthRule> }>({ rules: [] });
const { rules } = authDir.getArguments<{ rules: Array<AuthRule> }>({ rules: [] }, featureFlags);
rules.forEach(rule => {
const operations: (ModelOperation | 'read')[] = rule.operations ?? MODEL_OPERATIONS;

Expand Down
18 changes: 9 additions & 9 deletions packages/amplify-graphql-auth-transformer/src/utils/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
QueryFieldType,
MutationFieldType,
TransformerTransformSchemaStepContextProvider,
TransformerContextProvider,
TransformerContextProvider, FeatureFlagProvider,
} from '@aws-amplify/graphql-transformer-interfaces';
import {
ObjectTypeDefinitionNode, FieldDefinitionNode, DirectiveNode, NamedTypeNode,
Expand Down Expand Up @@ -42,7 +42,7 @@ export const fieldIsList = (fields: ReadonlyArray<FieldDefinitionNode>, fieldNam
/**
* getModelConfig
*/
export const getModelConfig = (directive: DirectiveNode, typeName: string, isDataStoreEnabled = false): ModelDirectiveConfiguration => {
export const getModelConfig = (directive: DirectiveNode, typeName: string, featureFlags: FeatureFlagProvider, isDataStoreEnabled = false): ModelDirectiveConfiguration => {
const directiveWrapped: DirectiveWrapper = new DirectiveWrapper(directive);
const options = directiveWrapped.getArguments<ModelDirectiveConfiguration>({
queries: {
Expand All @@ -65,20 +65,20 @@ export const getModelConfig = (directive: DirectiveNode, typeName: string, isDat
createdAt: 'createdAt',
updatedAt: 'updatedAt',
},
});
}, featureFlags);
return options;
};

/**
* getSearchableConfig
*/
export const getSearchableConfig = (directive: DirectiveNode, typeName: string): SearchableConfig | null => {
export const getSearchableConfig = (directive: DirectiveNode, typeName: string, featureFlags: FeatureFlagProvider): SearchableConfig | null => {
const directiveWrapped: DirectiveWrapper = new DirectiveWrapper(directive);
const options = directiveWrapped.getArguments<SearchableConfig>({
queries: {
search: graphqlName(`search${plurality(toUpper(typeName), true)}`),
},
});
}, featureFlags);
return options;
};
/*
Expand Down Expand Up @@ -111,7 +111,7 @@ export const getRelationalPrimaryMap = (
const args = directiveWrapped.getArguments({
indexName: undefined,
fields: undefined,
});
}, ctx.featureFlags);
// we only generate a primary map if a index name or field is specified
// if both are undefined then @hasMany will create a new gsi with a new readonly field
// we don't need a primary map since this readonly field is not a auth field
Expand All @@ -135,10 +135,10 @@ export const getRelationalPrimaryMap = (
fields: [
getConnectionAttributeName(ctx.featureFlags, def.name.value, field.name.value, relatedModel.name.value),
...getSortKeyFieldNames(relatedModel).map(
it => getSortKeyConnectionAttributeName(def.name.value, field.name.value, it),
)
(it) => getSortKeyConnectionAttributeName(def.name.value, field.name.value, it),
),
],
});
}, ctx.featureFlags);
const relatedPrimaryFields = getKeyFields(ctx, relatedModel);
// the fields provided by the directive (implicit/explicit) need to match the total amount of fields used for the primary key in the related table
// otherwise the get request is incomplete
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DirectiveWrapper, InvalidDirectiveError } from '@aws-amplify/graphql-transformer-core';
import { AuthRule, ConfiguredAuthProviders } from './definitions';
import {FeatureFlagProvider} from "@aws-amplify/graphql-transformer-interfaces";

export const validateRuleAuthStrategy = (rule: AuthRule, configuredAuthProviders: ConfiguredAuthProviders) => {
//
Expand Down Expand Up @@ -103,8 +104,9 @@ export const validateFieldRules = (
isParentTypeBuiltinType: boolean,
parentHasModelDirective: boolean,
fieldName: string,
featureFlags: FeatureFlagProvider,
) => {
const rules = authDir.getArguments<{ rules: Array<AuthRule> }>({ rules: [] }).rules;
const rules = authDir.getArguments<{ rules: Array<AuthRule> }>({ rules: [] }, featureFlags).rules;

if (rules.length === 0) {
throw new InvalidDirectiveError(`@auth on ${fieldName} does not have any auth rules.`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export class DefaultValueTransformer extends TransformerPluginBase {
object: parent as ObjectTypeDefinitionNode,
field: definition,
directive,
} as DefaultValueDirectiveConfiguration);
} as DefaultValueDirectiveConfiguration, ctx.featureFlags);
validate(ctx, config);

if (!this.directiveMap.has(parent.name.value)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class FunctionTransformer extends TransformerPluginBase {
const args = directiveWrapped.getArguments({
resolverTypeName: parent.name.value,
resolverFieldName: definition.name.value,
} as FunctionDirectiveConfiguration);
} as FunctionDirectiveConfiguration, acc.featureFlags);
let resolver = this.resolverGroups.get(definition);

if (resolver === undefined) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export class HttpTransformer extends TransformerPluginBase {
resolverTypeName: parent.name.value,
resolverFieldName: definition.name.value,
supportsBody: false,
} as HttpDirectiveConfiguration);
} as HttpDirectiveConfiguration, context.featureFlags);

if (!VALID_PROTOCOLS_REGEX.test(args.url)) {
throw new TransformerContractError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class IndexTransformer extends TransformerPluginBase {
object: parent as ObjectTypeDefinitionNode,
field: definition,
directive,
} as IndexDirectiveConfiguration);
} as IndexDirectiveConfiguration, context.featureFlags);

/**
* Impute Optional Fields
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class PrimaryKeyTransformer extends TransformerPluginBase {
object: parent as ObjectTypeDefinitionNode,
field: definition,
directive,
} as PrimaryKeyDirectiveConfiguration);
} as PrimaryKeyDirectiveConfiguration, context.featureFlags);

if (!args.sortKeyFields) {
args.sortKeyFields = [];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {
DirectiveWrapper,
FieldWrapper,
getFieldNameFor,
InputObjectDefinitionWrapper,
InvalidDirectiveError,
MappingTemplate,
ObjectDefinitionWrapper,
SyncConfig,
SyncUtils,
TransformerModelBase,
TransformerNestedStack,
FieldWrapper,
InputObjectDefinitionWrapper,
ObjectDefinitionWrapper,
getFieldNameFor,
} from '@aws-amplify/graphql-transformer-core';
import {
AppSyncDataSourceType,
Expand All @@ -18,21 +18,20 @@ import {
MutationFieldType,
QueryFieldType,
SubscriptionFieldType,
TransformerBeforeStepContextProvider,
TransformerContextProvider,
TransformerModelProvider,
TransformerPrepareStepContextProvider,
TransformerResolverProvider,
TransformerSchemaVisitStepContextProvider,
TransformerTransformSchemaStepContextProvider,
TransformerValidationStepContextProvider,
TransformerBeforeStepContextProvider,
} from '@aws-amplify/graphql-transformer-interfaces';
import {
AttributeType, CfnTable, ITable, StreamViewType, Table, TableEncryption,
} from '@aws-cdk/aws-dynamodb';
import {AttributeType, CfnTable, ITable, StreamViewType, Table, TableEncryption,} from '@aws-cdk/aws-dynamodb';
import * as iam from '@aws-cdk/aws-iam';
import {CfnRole} from '@aws-cdk/aws-iam';
import * as cdk from '@aws-cdk/core';
import { CfnDataSource } from '@aws-cdk/aws-appsync';
import {CfnDataSource} from '@aws-cdk/aws-appsync';
import {
DirectiveNode,
FieldDefinitionNode,
Expand All @@ -57,7 +56,6 @@ import {
toCamelCase,
toPascalCase,
} from 'graphql-transformer-common';
import { CfnRole } from '@aws-cdk/aws-iam';
import {
addDirectivesToOperation,
addModelConditionInputs,
Expand Down Expand Up @@ -91,8 +89,8 @@ import {
generateListRequestTemplate,
generateSyncRequestTemplate,
} from './resolvers/query';
import { API_KEY_DIRECTIVE } from './definitions';
import { SubscriptionLevel, ModelDirectiveConfiguration } from './directive';
import {API_KEY_DIRECTIVE} from './definitions';
import {ModelDirectiveConfiguration, SubscriptionLevel} from './directive';

/**
* Nullable
Expand Down Expand Up @@ -225,7 +223,29 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
createdAt: 'createdAt',
updatedAt: 'updatedAt',
},
});
}, ctx.featureFlags);

// This property override is specifically to address parity between V1 and V2 when the FF is disabled
// If one subscription is defined, just let the others go to null without FF. But if public and none defined, default all subs
if (!ctx.featureFlags.getBoolean('graphQLTransformer.shouldDeepMergeDirectiveConfigDefaults')) {
const publicSubscriptionDefaults = {
onCreate: [getFieldNameFor('onCreate', typeName)],
onDelete: [getFieldNameFor('onDelete', typeName)],
onUpdate: [getFieldNameFor('onUpdate', typeName)],
};

const baseArgs = directiveWrapped.getArguments(
{
subscriptions: {
level: SubscriptionLevel.on,
...publicSubscriptionDefaults,
},
}, ctx.featureFlags);
if (baseArgs?.subscriptions?.level === SubscriptionLevel.public
&& !(baseArgs?.subscriptions?.onCreate || baseArgs?.subscriptions?.onDelete || baseArgs?.subscriptions?.onUpdate)) {
options.subscriptions = { level: SubscriptionLevel.public, ...publicSubscriptionDefaults };
}
}

if (options.subscriptions?.onCreate && !Array.isArray(options.subscriptions.onCreate)) {
options.subscriptions.onCreate = [options.subscriptions.onCreate];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class PredictionsTransformer extends TransformerPluginBase {
const args = directiveWrapped.getArguments({
resolverTypeName: parent.name.value,
resolverFieldName: definition.name.value,
} as PredictionsDirectiveConfiguration);
} as PredictionsDirectiveConfiguration, context.featureFlags);

if (!Array.isArray(args.actions)) {
args.actions = [args.actions as unknown as string];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class BelongsToTransformer extends TransformerPluginBase {
object: parent as ObjectTypeDefinitionNode,
field: definition,
directive,
} as BelongsToDirectiveConfiguration);
} as BelongsToDirectiveConfiguration, context.featureFlags);

validate(args, context as TransformerContextProvider);
this.directiveList.push(args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export class HasManyTransformer extends TransformerPluginBase {
field: definition,
directive,
limit: defaultLimit,
} as HasManyDirectiveConfiguration);
} as HasManyDirectiveConfiguration, context.featureFlags);

validate(args, context as TransformerContextProvider);
this.directiveList.push(args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class HasOneTransformer extends TransformerPluginBase {
object: parent as ObjectTypeDefinitionNode,
field: definition,
directive,
} as HasOneDirectiveConfiguration);
} as HasOneDirectiveConfiguration, context.featureFlags);

validate(args, context as TransformerContextProvider);
this.directiveList.push(args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export class ManyToManyTransformer extends TransformerPluginBase {
field: definition,
directive,
limit: defaultLimit,
} as ManyToManyDirectiveConfiguration);
} as ManyToManyDirectiveConfiguration, context.featureFlags);

validateModelDirective(args);
args.connectionFields = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ export class SearchableModelTransformer extends TransformerPluginBase {
}

const directiveWrapped = new DirectiveWrapper(directive);
const directiveArguments = directiveWrapped.getArguments({}) as any;
const directiveArguments = directiveWrapped.getArguments({}, ctx.featureFlags) as any;
let shouldMakeSearch = true;
let searchFieldNameOverride;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { ArgumentNode, DirectiveNode, NameNode, valueFromASTUntyped, ValueNode, Location } from 'graphql';
import _ from 'lodash';
import { FeatureFlagProvider } from "@aws-amplify/graphql-transformer-interfaces";

export class ArgumentWrapper {
public readonly name: NameNode;
Expand Down Expand Up @@ -32,14 +34,17 @@ export class DirectiveWrapper {
arguments: this.arguments.map(arg => arg.serialize()),
};
};
public getArguments = <T>(defaultValue: Required<T>): Required<T> => {
public getArguments = <T>(defaultValue: Required<T>, featureFlags: FeatureFlagProvider): Required<T> => {
const argValues = this.arguments.reduce(
(acc: Record<string, any>, arg: ArgumentWrapper) => ({
...acc,
[arg.name.value]: valueFromASTUntyped(arg.value),
}),
{},
);
if (featureFlags.getBoolean('graphQLTransformer.shouldDeepMergeDirectiveConfigDefaults')) {
return _.merge(defaultValue, argValues);
}
return Object.assign(defaultValue, argValues);
};
}

0 comments on commit b1fdf59

Please sign in to comment.