Skip to content

Commit

Permalink
Add option to specify default flexSearch case-sensitivity
Browse files Browse the repository at this point in the history
  • Loading branch information
Yogu committed Feb 18, 2022
1 parent c3bbff9 commit e4ee2d1
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 27 deletions.
2 changes: 1 addition & 1 deletion spec/regression/regression-suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class RegressionSuite {
authRoles: context.authRoles,
flexSearchMaxFilterableAndSortableAmount: context.flexSearchMaxFilterableAndSortableAmount
}),
modelValidationOptions: {
modelOptions: {
forbiddenRootEntityNames: []
},
...options,
Expand Down
25 changes: 18 additions & 7 deletions src/config/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,39 @@ export interface SchemaOptions {
readonly maxOrderByRootEntityDepth?: number;
}

export interface ModelValidationOptions {
/**
* A list of root entity names that are not allowed.
*/
readonly forbiddenRootEntityNames?: ReadonlyArray<string>;
}

export interface ModelOptions {
/**
* Determines whether a slash in a source name indicates the target namespace for that source
*
* Defaults to true. Explicitly specify false to disable this.
*/
readonly useSourceDirectoriesAsNamespaces?: boolean;

/**
* Specifies the default for case-sensitiveness of flexSearch fields (can be overridden with the decorator)
*
* Default is true
*/
readonly isFlexSearchIndexCaseSensitiveByDefault?: boolean;

/**
* A list of root entity names that are not allowed.
*/
readonly forbiddenRootEntityNames?: ReadonlyArray<string>;
}

export type ModelValidationOptions = ModelOptions;

export interface ProjectOptions {
readonly loggerProvider?: LoggerProvider;
readonly profileConsumer?: (profile: RequestProfile) => void;
readonly getExecutionOptions?: (args: ExecutionOptionsCallbackArgs) => ExecutionOptions;

readonly schemaOptions?: SchemaOptions;

/**
* @deprecated use modelOptions instead
*/
readonly modelValidationOptions?: ModelValidationOptions;
readonly modelOptions?: ModelOptions;

Expand Down
4 changes: 2 additions & 2 deletions src/model/config/model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ModelValidationOptions } from '../../config/interfaces';
import { ModelOptions, ModelValidationOptions } from '../../config/interfaces';
import { ValidationMessage } from '../validation';
import { BillingConfig } from './billing';
import { LocalizationConfig } from './i18n';
Expand All @@ -11,6 +11,6 @@ export interface ModelConfig {
readonly validationMessages?: ReadonlyArray<ValidationMessage>;
readonly i18n?: ReadonlyArray<LocalizationConfig>;
readonly billing?: BillingConfig;
readonly modelValidationOptions?: ModelValidationOptions;
readonly timeToLiveConfigs?: ReadonlyArray<TimeToLiveConfig>;
readonly options?: ModelOptions;
}
31 changes: 20 additions & 11 deletions src/model/create-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
TypeDefinitionNode,
valueFromAST
} from 'graphql';
import { ModelValidationOptions } from '../config/interfaces';
import { ModelOptions, ModelValidationOptions, ProjectOptions } from '../config/interfaces';
import {
ParsedGraphQLProjectSource,
ParsedObjectProjectSource,
Expand Down Expand Up @@ -115,16 +115,16 @@ import { parseI18nConfigs } from './parse-i18n';
import { parseTTLConfigs } from './parse-ttl';
import { ValidationContext, ValidationMessage } from './validation';

export function createModel(parsedProject: ParsedProject, modelValidationOptions?: ModelValidationOptions): Model {
export function createModel(parsedProject: ParsedProject, options?: ModelOptions): Model {
const validationContext = new ValidationContext();
return new Model({
types: createTypeInputs(parsedProject, validationContext),
types: createTypeInputs(parsedProject, validationContext, options ?? {}),
permissionProfiles: extractPermissionProfiles(parsedProject),
i18n: extractI18n(parsedProject),
validationMessages: validationContext.validationMessages,
billing: extractBilling(parsedProject),
modelValidationOptions,
timeToLiveConfigs: extractTimeToLive(parsedProject)
timeToLiveConfigs: extractTimeToLive(parsedProject),
options
});
}

Expand All @@ -144,7 +144,11 @@ const VALIDATION_ERROR_MISSING_OBJECT_TYPE_DIRECTIVE = `Add one of @${ROOT_ENTIT
const VALIDATION_ERROR_INVALID_DEFINITION_KIND =
'This kind of definition is not allowed. Only object and enum type definitions are allowed.';

function createTypeInputs(parsedProject: ParsedProject, context: ValidationContext): ReadonlyArray<TypeConfig> {
function createTypeInputs(
parsedProject: ParsedProject,
context: ValidationContext,
options: ModelOptions
): ReadonlyArray<TypeConfig> {
const graphQLSchemaParts = parsedProject.sources.filter(
parsedSource => parsedSource.kind === ParsedProjectSourceBaseKind.GRAPHQL
) as ReadonlyArray<ParsedGraphQLProjectSource>;
Expand Down Expand Up @@ -173,7 +177,7 @@ function createTypeInputs(parsedProject: ParsedProject, context: ValidationConte
};
return enumTypeInput;
case OBJECT_TYPE_DEFINITION:
return createObjectTypeInput(definition, schemaPart, context);
return createObjectTypeInput(definition, schemaPart, context, options);
default:
return undefined;
}
Expand All @@ -196,15 +200,16 @@ function createEnumValues(valueNodes: ReadonlyArray<EnumValueDefinitionNode>): R
function createObjectTypeInput(
definition: ObjectTypeDefinitionNode,
schemaPart: ParsedGraphQLProjectSource,
context: ValidationContext
context: ValidationContext,
options: ModelOptions
): ObjectTypeConfig {
const entityType = getKindOfObjectTypeNode(definition, context);

const common = {
name: definition.name.value,
description: definition.description ? definition.description.value : undefined,
astNode: definition,
fields: (definition.fields || []).map(field => createFieldInput(field, context)),
fields: (definition.fields || []).map(field => createFieldInput(field, context, options)),
namespacePath: getNamespacePath(definition, schemaPart.namespacePath),
flexSearchLanguage: getDefaultLanguage(definition, context)
};
Expand Down Expand Up @@ -457,7 +462,11 @@ function getLanguage(fieldNode: FieldDefinitionNode, context: ValidationContext)
}
}

function createFieldInput(fieldNode: FieldDefinitionNode, context: ValidationContext): FieldConfig {
function createFieldInput(
fieldNode: FieldDefinitionNode,
context: ValidationContext,
options: ModelOptions
): FieldConfig {
const inverseOfASTNode = getInverseOfASTNode(fieldNode, context);
const relationDeleteActionASTNode = getRelationDeleteActionASTNode(fieldNode, context);
const referenceDirectiveASTNode = findDirectiveWithName(fieldNode, REFERENCE_DIRECTIVE);
Expand Down Expand Up @@ -500,7 +509,7 @@ function createFieldInput(fieldNode: FieldDefinitionNode, context: ValidationCon
isFlexSearchIndexCaseSensitive:
flexSearchIndexCaseSensitiveNode?.value.kind === 'BooleanValue'
? flexSearchIndexCaseSensitiveNode.value.value
: undefined,
: options.isFlexSearchIndexCaseSensitiveByDefault,
isFlexSearchIndexedASTNode: findDirectiveWithName(fieldNode, FLEX_SEARCH_INDEXED_DIRECTIVE),
isFlexSearchFulltextIndexed: hasDirectiveWithName(fieldNode, FLEX_SEARCH_FULLTEXT_INDEXED_DIRECTIVE),
isFlexSearchFulltextIndexedASTNode: findDirectiveWithName(fieldNode, FLEX_SEARCH_FULLTEXT_INDEXED_DIRECTIVE),
Expand Down
15 changes: 10 additions & 5 deletions src/model/implementation/model.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { groupBy, uniqBy } from 'lodash';
import memorize from 'memorize-decorator';
import { ModelValidationOptions } from '../../config/interfaces';
import { ModelOptions } from '../../config/interfaces';
import { flatMap, objectEntries, objectValues } from '../../utils/utils';
import { ModelConfig, TypeKind } from '../config';
import { NamespacedPermissionProfileConfigMap } from '../index';
Expand Down Expand Up @@ -31,7 +31,11 @@ export class Model implements ModelComponent {
readonly i18n: ModelI18n;
readonly permissionProfiles: ReadonlyArray<PermissionProfile>;
readonly billingEntityTypes: ReadonlyArray<BillingEntityType>;
readonly modelValidationOptions?: ModelValidationOptions;
/**
* @deprecated use options
*/
readonly modelValidationOptions?: ModelOptions;
readonly options?: ModelOptions;
readonly timeToLiveTypes: ReadonlyArray<TimeToLiveType>;

constructor(private input: ModelConfig) {
Expand All @@ -50,7 +54,8 @@ export class Model implements ModelComponent {
this.billingEntityTypes = input.billing
? input.billing.billingEntities.map(value => new BillingEntityType(value, this))
: [];
this.modelValidationOptions = input.modelValidationOptions;
this.options = input.options;
this.modelValidationOptions = input.options;
this.timeToLiveTypes = input.timeToLiveConfigs
? input.timeToLiveConfigs.map(ttlConfig => new TimeToLiveType(ttlConfig, this))
: [];
Expand Down Expand Up @@ -243,10 +248,10 @@ export class Model implements ModelComponent {
}

get forbiddenRootEntityNames(): ReadonlyArray<string> {
if (!this.modelValidationOptions || !this.modelValidationOptions.forbiddenRootEntityNames) {
if (!this.options || !this.options.forbiddenRootEntityNames) {
return ['BillingEntity'];
}
return this.modelValidationOptions!.forbiddenRootEntityNames;
return this.options!.forbiddenRootEntityNames;
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/schema/schema-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ export function validateAndPrepareSchema(project: Project): { validationResult:

const preparedProject = executePreMergeTransformationPipeline({ sources: validParsedSources });

const model = createModel(preparedProject, project.options.modelValidationOptions);
const model = createModel(preparedProject, {
...project.options.modelValidationOptions,
...project.options.modelOptions
});

const mergedSchema: DocumentNode = mergeSchemaDefinition(preparedProject);

Expand Down

0 comments on commit e4ee2d1

Please sign in to comment.