Skip to content

Commit

Permalink
feat(TypeMapper): add static methods for checking string definition k…
Browse files Browse the repository at this point in the history
…inds
  • Loading branch information
nodkz committed Apr 16, 2019
1 parent a9d79d7 commit 535530a
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 21 deletions.
42 changes: 42 additions & 0 deletions src/TypeMapper.d.ts
Expand Up @@ -51,6 +51,48 @@ declare class TypeMapper<TContext> {

public constructor(schemaComposer: SchemaComposer<TContext>);

/**
* Check that string is a valid GraphQL Type name.
* According to spec valid mask is `/^[_A-Za-z][_0-9A-Za-z]*$/`.
*/
public static isTypeNameString(str: string): boolean;

/**
* Checks that string is SDL definition of some type
* eg. `type Out { name: String! }` or `input Filter { minAge: Int }` etc.
*/
public static isTypeDefinitionString(str: string): boolean;

/**
* Checks that string is OutputType SDL definition
* eg. `type Out { name: String! }`
*/
public static isOutputTypeDefinitionString(str: string): boolean;

/**
* Checks that string is InputType SDL definition
* eg. `input Filter { minAge: Int }`
*/
public static isInputTypeDefinitionString(str: string): boolean;

/**
* Checks that string is EnumType SDL definition
* eg. `enum Sort { ASC DESC }`
*/
public static isEnumTypeDefinitionString(str: string): boolean;

/**
* Checks that string is ScalarType SDL definition
* eg. `scalar UInt`
*/
public static isScalarTypeDefinitionString(str: string): boolean;

/**
* Checks that string is InterfaceType SDL definition
* eg. `interface User { name: String }`
*/
public static isInterfaceTypeDefinitionString(str: string): boolean;

public get(name: string): GraphQLNamedType | void;

public set(name: string, type: AnyType<any>): void;
Expand Down
58 changes: 38 additions & 20 deletions src/TypeMapper.js
Expand Up @@ -116,12 +116,6 @@ function isInputType(type: any): boolean {
);
}

const RegexpOutputTypeDefinition = /type\s[^{]+\{[^}]+\}/im;
const RegexpInputTypeDefinition = /input\s[^{]+\{[^}]+\}/im;
const RegexpEnumTypeDefinition = /enum\s[^{]+\{[^}]+\}/im;
const RegexpScalarTypeDefinition = /scalar\s/im;
const RegexpInterfaceTypeDefinition = /interface\s/im;

export class TypeMapper<TContext> {
schemaComposer: SchemaComposer<TContext>;

Expand Down Expand Up @@ -202,16 +196,40 @@ export class TypeMapper<TContext> {
return undefined;
}

isTypeDefinitionString(str: string): boolean {
static isTypeNameString(str: string): boolean {
return /^[_A-Za-z][_0-9A-Za-z]*$/.test(str);
}

static isTypeDefinitionString(str: string): boolean {
return (
RegexpOutputTypeDefinition.test(str) ||
RegexpInputTypeDefinition.test(str) ||
RegexpEnumTypeDefinition.test(str) ||
RegexpScalarTypeDefinition.test(str) ||
RegexpInterfaceTypeDefinition.test(str)
this.isOutputTypeDefinitionString(str) ||
this.isInputTypeDefinitionString(str) ||
this.isEnumTypeDefinitionString(str) ||
this.isScalarTypeDefinitionString(str) ||
this.isInterfaceTypeDefinitionString(str)
);
}

static isOutputTypeDefinitionString(str: string): boolean {
return /type\s[^{]+\{[^}]+\}/im.test(str);
}

static isInputTypeDefinitionString(str: string): boolean {
return /input\s[^{]+\{[^}]+\}/im.test(str);
}

static isEnumTypeDefinitionString(str: string): boolean {
return /enum\s[^{]+\{[^}]+\}/im.test(str);
}

static isScalarTypeDefinitionString(str: string): boolean {
return /scalar\s/im.test(str);
}

static isInterfaceTypeDefinitionString(str: string): boolean {
return /interface\s/im.test(str);
}

createGraphQLType(str: TypeDefinitionString): GraphQLType | void {
const type = this.createType(str);
if (type) {
Expand Down Expand Up @@ -240,7 +258,7 @@ export class TypeMapper<TContext> {
if (this.schemaComposer.hasInstance(composeType, ObjectTypeComposer)) {
return this.schemaComposer.getOTC(composeType).getType();
} else if (typeof composeType === 'string') {
const type = this.isTypeDefinitionString(composeType)
const type = TypeMapper.isTypeDefinitionString(composeType)
? this.createGraphQLType(composeType)
: this.getWrapped(composeType);

Expand Down Expand Up @@ -320,7 +338,7 @@ export class TypeMapper<TContext> {

const fieldConfig: GraphQLFieldConfig<any, TContext> = ({}: any);
if (typeof composeType === 'string') {
if (RegexpInputTypeDefinition.test(composeType)) {
if (TypeMapper.isInputTypeDefinitionString(composeType)) {
throw new Error(
`${typeName}.${fieldName} should be OutputType, but got following type definition '${composeType}'`
);
Expand All @@ -334,7 +352,7 @@ export class TypeMapper<TContext> {
);
}
} else {
const type = this.isTypeDefinitionString(composeType)
const type = TypeMapper.isTypeDefinitionString(composeType)
? this.createGraphQLType(composeType)
: this.getWrapped(composeType);

Expand Down Expand Up @@ -466,7 +484,7 @@ export class TypeMapper<TContext> {

const argConfig: GraphQLArgumentConfig = ({}: any);
if (typeof composeType === 'string') {
if (RegexpOutputTypeDefinition.test(composeType)) {
if (TypeMapper.isOutputTypeDefinitionString(composeType)) {
throw new Error(
`${typeName}.${fieldName}@${argName} should be InputType, but got output type definition '${composeType}'`
);
Expand All @@ -480,7 +498,7 @@ export class TypeMapper<TContext> {
);
}
} else {
const type = this.isTypeDefinitionString(composeType)
const type = TypeMapper.isTypeDefinitionString(composeType)
? this.createGraphQLType(composeType)
: this.getWrapped(composeType);

Expand Down Expand Up @@ -606,7 +624,7 @@ export class TypeMapper<TContext> {

const fieldConfig: GraphQLInputFieldConfig = ({}: any);
if (typeof composeType === 'string') {
if (RegexpOutputTypeDefinition.test(composeType)) {
if (TypeMapper.isOutputTypeDefinitionString(composeType)) {
throw new Error(
`${typeName}.${fieldName} should be InputType, but got output type definition '${composeType}'`
);
Expand All @@ -620,7 +638,7 @@ export class TypeMapper<TContext> {
);
}
} else {
const type = this.isTypeDefinitionString(composeType)
const type = TypeMapper.isTypeDefinitionString(composeType)
? this.createGraphQLType(composeType)
: this.getWrapped(composeType);

Expand Down Expand Up @@ -694,7 +712,7 @@ export class TypeMapper<TContext> {
if (this.schemaComposer.hasInstance(composeType, InterfaceTypeComposer)) {
return this.schemaComposer.getIFTC(composeType).getType();
} else if (typeof composeType === 'string') {
const type = RegexpInterfaceTypeDefinition.test(composeType)
const type = TypeMapper.isInterfaceTypeDefinitionString(composeType)
? this.createGraphQLType(composeType)
: this.getWrapped(composeType);

Expand Down
40 changes: 40 additions & 0 deletions src/__tests__/TypeMapper-test.js
Expand Up @@ -1105,4 +1105,44 @@ describe('TypeMapper', () => {
});
});
});

describe('static methods', () => {
it('check SDL types', () => {
const output = 'type Out { name: String! }';
const input = 'input Filter { minAge: Int }';
const enumType = 'enum Sort { ASC DESC }';
const scalar = 'scalar UInt';
const iface = 'interface User { name: String }';

expect(TypeMapper.isTypeDefinitionString(output)).toBeTruthy();
expect(TypeMapper.isOutputTypeDefinitionString(output)).toBeTruthy();
expect(TypeMapper.isOutputTypeDefinitionString(input)).toBeFalsy();

expect(TypeMapper.isTypeDefinitionString(input)).toBeTruthy();
expect(TypeMapper.isInputTypeDefinitionString(input)).toBeTruthy();
expect(TypeMapper.isInputTypeDefinitionString(output)).toBeFalsy();

expect(TypeMapper.isTypeDefinitionString(enumType)).toBeTruthy();
expect(TypeMapper.isEnumTypeDefinitionString(enumType)).toBeTruthy();
expect(TypeMapper.isEnumTypeDefinitionString(output)).toBeFalsy();

expect(TypeMapper.isTypeDefinitionString(scalar)).toBeTruthy();
expect(TypeMapper.isScalarTypeDefinitionString(scalar)).toBeTruthy();
expect(TypeMapper.isScalarTypeDefinitionString(output)).toBeFalsy();

expect(TypeMapper.isTypeDefinitionString(iface)).toBeTruthy();
expect(TypeMapper.isInterfaceTypeDefinitionString(iface)).toBeTruthy();
expect(TypeMapper.isInterfaceTypeDefinitionString(output)).toBeFalsy();
});

it('check type name', () => {
expect(TypeMapper.isTypeNameString('aaaa')).toBeTruthy();
expect(TypeMapper.isTypeNameString('Aaaaa')).toBeTruthy();
expect(TypeMapper.isTypeNameString('A_')).toBeTruthy();
expect(TypeMapper.isTypeNameString('_A')).toBeTruthy();
expect(TypeMapper.isTypeNameString('A_123')).toBeTruthy();
expect(TypeMapper.isTypeNameString('123')).toBeFalsy();
expect(TypeMapper.isTypeNameString('A-')).toBeFalsy();
});
});
});
2 changes: 1 addition & 1 deletion src/utils/is.d.ts
@@ -1,6 +1,6 @@
export function isString(value: any): value is string;

export function isObject(value: any): value is boolean;
export function isObject(value: any): value is object;

// tslint:disable-next-line:ban-types
export function isFunction(value: any): value is Function;

0 comments on commit 535530a

Please sign in to comment.