graphql#GraphQLInterfaceType TypeScript Examples
The following examples show how to use
graphql#GraphQLInterfaceType.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: GraphQLStateFetcherWriter.ts From graphql-ts-client with MIT License | 6 votes |
protected importedNamesForSuperType(superType: GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType): string[] {
if (!this.ctx.triggerableTypes.has(superType)) {
return super.importedNamesForSuperType(superType);
}
return [
...super.importedNamesForSuperType(superType),
`${superType.name}FlatType`
];
}
Example #2
Source File: upper-case.directive.ts From nestjs-mercurius with MIT License | 6 votes |
visitFieldDefinition(
field: GraphQLField<any, any>,
_details: { objectType: GraphQLObjectType | GraphQLInterfaceType },
): GraphQLField<any, any> | void | null {
const { resolve = defaultFieldResolver } = field;
field.resolve = async function (...args) {
const result = await resolve.apply(this, args);
if (typeof result === 'string') {
return result.toUpperCase();
}
return result;
};
}
Example #3
Source File: GraphQLStateGenerator.ts From graphql-ts-client with MIT License | 6 votes |
protected additionalExportedTypeNamesForFetcher(
modelType: GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType,
ctx: FetcherContext
): ReadonlyArray<string> {
if (ctx.triggerableTypes.has(modelType)) {
return [
...super.additionalExportedTypeNamesForFetcher(modelType, ctx),
`${modelType.name}FlatType`
];
}
return super.additionalExportedTypeNamesForFetcher(modelType, ctx);
}
Example #4
Source File: interfaceType.ts From genql with MIT License | 6 votes |
interfaceType = (
type: GraphQLInterfaceType,
ctx: RenderContext,
) => {
if (!ctx.schema) {
throw new Error('schema is required to render unionType')
}
const typeNames = ctx.schema.getPossibleTypes(type).map((t) => t.name)
if (!typeNames.length) {
objectType(type, ctx)
} else {
ctx.addCodeBlock(
`${typeComment(type)}export type ${type.name} = (${typeNames.join(
' | ',
)}) & { __isUnion?: true }`,
)
}
}
Example #5
Source File: types.ts From tql with MIT License | 6 votes |
printField = (field: GraphQLField<any, any, any>): string => {
const { args } = field;
const isList = listType(field.type);
const isNonNull = field.type instanceof GraphQLNonNull;
const type = outputType(field.type);
const printVariables = () => {
return args.length > 0
? `(variables: { ${args.map(printVariable).join(", ")} })`
: "";
};
if (type instanceof GraphQLScalarType) {
return (
`${args.length > 0 ? "" : "readonly"} ${field.name}${printVariables()}: ${
isList ? `ReadonlyArray<${toPrimitive(type)}>` : `${toPrimitive(type)}`
}` + (isNonNull ? "" : " | null")
);
} else if (type instanceof GraphQLEnumType) {
return (
`${args.length > 0 ? "" : "readonly"} ${field.name}${printVariables()}: ${
isList ? `ReadonlyArray<${type.name}>` : `${type.name}`
}` + (isNonNull ? "" : " | null")
);
} else if (
type instanceof GraphQLInterfaceType ||
type instanceof GraphQLUnionType ||
type instanceof GraphQLObjectType
) {
return (
`${args.length > 0 ? "" : "readonly"} ${field.name}${printVariables()}: ${
isList ? `ReadonlyArray<I${type.name}>` : `I${type.name}`
}` + (isNonNull ? "" : " | null")
);
} else {
throw new Error("Unable to print field.");
}
}
Example #6
Source File: graphql.ts From amplify-codegen with Apache License 2.0 | 6 votes |
/**
* Not exactly the same as the executor's definition of getFieldDef, in this
* statically evaluated environment we do not always have an Object type,
* and need to handle Interface and Union types.
*/
export function getFieldDef(
schema: GraphQLSchema,
parentType: GraphQLCompositeType,
fieldAST: FieldNode
): GraphQLField<any, any> | undefined {
const name = fieldAST.name.value;
if (name === SchemaMetaFieldDef.name && schema.getQueryType() === parentType) {
return SchemaMetaFieldDef;
}
if (name === TypeMetaFieldDef.name && schema.getQueryType() === parentType) {
return TypeMetaFieldDef;
}
if (
name === TypeNameMetaFieldDef.name &&
(parentType instanceof GraphQLObjectType || parentType instanceof GraphQLInterfaceType || parentType instanceof GraphQLUnionType)
) {
return TypeNameMetaFieldDef;
}
if (parentType instanceof GraphQLObjectType || parentType instanceof GraphQLInterfaceType) {
return parentType.getFields()[name];
}
return undefined;
}
Example #7
Source File: schema-resolver.ts From graphql-mesh with MIT License | 6 votes |
private createObjectTypeConfig(soapType: SoapObjectType): GraphQLObjectTypeConfig<any, any> {
const fields = (): GraphQLFieldConfigMap<any, any> => {
const fieldMap: GraphQLFieldConfigMap<any, any> = {};
this.appendObjectTypeFields(fieldMap, soapType);
return fieldMap;
};
const interfaces = (): GraphQLInterfaceType[] => {
const interfaces: GraphQLInterfaceType[] = [];
this.appendInterfaces(interfaces, soapType);
return interfaces;
};
return {
name: this.options.outputNameResolver(soapType),
fields,
interfaces,
};
}
Example #8
Source File: serializeToJSON.ts From amplify-codegen with Apache License 2.0 | 6 votes |
function serializeInterfaceType(type: GraphQLInterfaceType) {
const { name, description } = type;
const fields = Object.values(type.getFields());
return {
kind: 'InterfaceType',
name,
description,
fields: fields.map(field => ({
name: field.name,
type: String(field.type),
description: field.description,
})),
};
}
Example #9
Source File: schema-resolver.ts From graphql-mesh with MIT License | 6 votes |
private resolveInterfaceType(soapType: SoapObjectType): GraphQLInterfaceType {
if (this.alreadyResolvedInterfaceTypes.has(soapType)) {
return this.alreadyResolvedInterfaceTypes.get(soapType);
}
const interfaceType: GraphQLInterfaceType = this.createInterfaceType(soapType);
this.alreadyResolvedInterfaceTypes.set(soapType, interfaceType);
return interfaceType;
}
Example #10
Source File: GeneratorConfig.ts From graphql-ts-client with MIT License | 5 votes |
export function validateConfigAndSchema(
config: GeneratorConfig,
schema: GraphQLSchema
) {
const typeMap = schema.getTypeMap();
for (const typeName in typeMap) {
const type = typeMap[typeName]!;
if (type instanceof GraphQLObjectType || type instanceof GraphQLInterfaceType) {
const fieldMap = type.getFields();
for (const fieldName in fieldMap) {
const field = fieldMap[fieldName]!;
if (BUILT_IN_FEILDS.has(field.name)) {
throw new Error(
`Illegal field '${field.name}' of type '${type.name}', ` +
"it's name is protected by 'graphql-ts-client-api', please change the server-side app"
);
}
}
}
}
const idFieldMap = config.idFieldMap;
if (idFieldMap !== undefined) {
for (const typeName in idFieldMap) {
const type = typeMap[typeName];
if (!(type instanceof GraphQLObjectType) && !(type instanceof GraphQLInterfaceType)) {
throw new Error(
`config.idFieldMap contains an illegal key '${typeName}', ` +
"that is neither a graphql object type nor graphql interface type"
);
}
const fieldMap = type.getFields();
const idField = fieldMap[idFieldMap[typeName]];
if (idField === undefined) {
throw new Error(
`config.idFieldMap['${typeName}'] is illegal, ` +
`there is not field named '${idFieldMap[typeName]}' in the type '${typeName}'`
);
}
if (targetTypeOf(idField.type) !== undefined) {
throw new Error(
`config.idFieldMap['${typeName}'] is illegal, ` +
`the field '${idFieldMap[typeName]}' of the type '${typeName}' is not scalar`
);
}
}
}
const excludeMap = config.defaultFetcherExcludeMap;
if (excludeMap !== undefined) {
for (const typeName in excludeMap) {
const type = typeMap[typeName];
if (!(type instanceof GraphQLObjectType) && !(type instanceof GraphQLInterfaceType)) {
throw new Error(
`config.defaultFetcherExcludeMap contains an illegal key '${typeName}' ` +
"that is neither a graphql object type nor graphql interface type"
);
}
const fieldMap = type.getFields();
const fieldNames = excludeMap[typeName];
if (!Array.isArray(fieldNames)) {
throw new Error(`config.defaultFetcherExcludeMap['${typeName}'] is not array`);
}
for (let i = fieldNames.length - 1; i >= 0; i--) {
const fieldName = fieldNames[i];
if (typeof fieldName !== 'string') {
throw new Error(`config.defaultFetcherExcludeMap['${typeName}'][${i}] is not string`);
}
if (fieldMap[fieldName] === undefined) {
throw new Error(
`config.defaultFetcherExcludeMap['${typeName}'][${i}] is illegal, ` +
`its value '${fieldName}' is not a field of graphql type '${typeName}'`
);
}
}
}
}
}
Example #11
Source File: schema-resolver.ts From graphql-mesh with MIT License | 5 votes |
private alreadyResolvedInterfaceTypes: Map<SoapType, GraphQLInterfaceType> = new Map();
Example #12
Source File: TypedConfigurationWriter.ts From graphql-ts-client with MIT License | 5 votes |
private writeFetcherType(fetcherType: GraphQLObjectType | GraphQLInterfaceType) {
const t = this.text.bind(this);
this.scope({type: "BLOCK", multiLines: true, suffix: ";\n"}, () => {
if (fetcherType.name !== 'Query') {
const idField = this.ctx.idFieldMap.get(fetcherType);
if (idField !== undefined) {
t(`readonly " $id": `);
this.typeRef(idField.type);
t(";\n");
}
t(`readonly " $evictEvent": ${fetcherType.name}EvictEvent;\n`);
t(`readonly " $changeEvent": ${fetcherType.name}ChangeEvent;\n`);
}
const fieldMap = fetcherType.getFields();
const associationTypeMap = this.associationTypeMapOf(fetcherType);
t(`readonly " $associationTypes": `);
this.scope({type: "BLOCK", multiLines: true, suffix: ";\n"}, () => {
for (const [fieldName, typeName] of associationTypeMap) {
this.separator(", ");
t(`readonly ${fieldName}: "${typeName}"`);
}
});
t(`readonly " $associationArgs": `);
this.scope({type: "BLOCK", multiLines: true, suffix: ";\n"}, () => {
for (const fieldName in fieldMap) {
if (fieldMap[fieldName].args.length !== 0) {
this.separator(", ");
t(`readonly ${fieldName}: ${fetcherType.name}Args["${fieldName}"]`);
}
}
});
t(`readonly " $associationTargetTypes": `);
this.scope({type: "BLOCK", multiLines: true, suffix: ";\n"}, () => {
const triggerableTypeNames = new Set<string>(
Array
.from(this.ctx.triggerableTypes)
.map(it => (it as GraphQLObjectType | GraphQLInterfaceType).name)
);
for (const [fieldName, typeName] of associationTypeMap) {
if (triggerableTypeNames.has(typeName)) {
this.separator(", ");
t(`readonly ${fieldName}: ${typeName}FlatType`);
}
}
});
})
}
Example #13
Source File: objectType.ts From genql with MIT License | 5 votes |
objectType = (
type: GraphQLObjectType | GraphQLInterfaceType,
ctx: RenderContext,
wrapper: 'Promise' | 'Observable',
) => {
// console.log(Object.keys(type.getFields()))
const fieldsMap: GraphQLFieldMap<any, any> = ctx.config?.sortProperties
? sortKeys(type.getFields())
: type.getFields()
const fieldStrings = Object.keys(fieldsMap).map((fieldName) => {
const field = fieldsMap[fieldName]
const resolvedType = getNamedType(field.type)
// leaf type, obly has.get() method
const stopChain =
isListType(field.type) ||
(isNonNullType(field.type) && isListType(field.type.ofType)) ||
isUnionType(resolvedType)
// non leaf type, has .get method
const resolvable = !(
isEnumType(resolvedType) || isScalarType(resolvedType)
)
const argsPresent = field.args.length > 0
const argsOptional = !field.args.find((a) => isNonNullType(a.type))
const argsString = toArgsString(field)
const executeReturnType = renderTyping(field.type, true, false, false)
const executeReturnTypeWithTypeMap = renderTyping(
field.type,
true,
false,
false,
(x: string) => `FieldsSelection<${x}, R>`,
)
// get: <R extends RepositoryRequest>(
// request: R,
// defaultValue?: Repository,
// ) => Promise<FieldsSelection<Repository, R>>
// }
const getFnType = `{get: <R extends ${requestTypeName(
resolvedType,
)}>(request: R, defaultValue?: ${executeReturnTypeWithTypeMap}) => ${wrapper}<${executeReturnTypeWithTypeMap}>}`
const fieldType = resolvable
? stopChain
? getFnType
: `${chainTypeName(resolvedType, wrapper)} & ${getFnType}`
: `{get: (request?: boolean|number, defaultValue?: ${executeReturnType}) => ${wrapper}<${executeReturnType}>}`
const result: string[] = []
if (argsPresent) {
result.push(
`((args${
argsOptional ? '?' : ''
}: ${argsString}) => ${fieldType})`,
)
}
if (!argsPresent || argsOptional) {
result.push(`(${fieldType})`)
}
return `${fieldComment(field)}${field.name}: ${result.join('&')}`
})
ctx.addImport(RUNTIME_LIB_NAME, false, 'FieldsSelection', true, true)
if (wrapper === 'Observable') {
ctx.addImport(RUNTIME_LIB_NAME, false, 'Observable', true, true)
}
ctx.addCodeBlock(
`${typeComment(type)}export interface ${chainTypeName(
type,
wrapper,
)}{\n ${fieldStrings.join(',\n ')}\n}`,
)
}
Example #14
Source File: Generator.ts From graphql-ts-client with MIT License | 5 votes |
private async generateFetcherTypes(ctx: FetcherContext) {
const dir = join(this.config.targetDir, "fetchers");
const emptyFetcherNameMap = new Map<GraphQLType, string>();
const defaultFetcherNameMap = new Map<GraphQLType, string>();
const promises = ctx.fetcherTypes
.map(async type => {
const stream = createStreamAndLog(
join(dir, `${type.name}${this.config?.fetcherSuffix ?? "Fetcher"}.ts`)
);
const writer = this.createFetcheWriter(
type,
ctx,
stream,
this.config
);
emptyFetcherNameMap.set(type, writer.emptyFetcherName);
if (writer.defaultFetcherName !== undefined) {
defaultFetcherNameMap.set(type, writer.defaultFetcherName);
}
writer.write();
await closeStream(stream);
});
await Promise.all([
...promises,
(async() => {
const stream = createStreamAndLog(join(dir, "index.ts"));
for (const type of ctx.fetcherTypes) {
const fetcherTypeName = `${type.name}${this.config?.fetcherSuffix ?? "Fetcher"}`;
stream.write(
`export type {${
[
fetcherTypeName,
(type instanceof GraphQLObjectType || type instanceof GraphQLInterfaceType) &&
ctx.typesWithParameterizedField.has(type) ?
`${type.name}Args` :
undefined,
...this.additionalExportedTypeNamesForFetcher(type, ctx)
]
.filter(text => text !== undefined)
.join(", ")
}} from './${fetcherTypeName}';\n`
);
const defaultFetcherName = defaultFetcherNameMap.get(type);
stream.write(
`export {${
emptyFetcherNameMap.get(type)
}${
defaultFetcherName !== undefined ?
`, ${defaultFetcherName}` :
''
}} from './${fetcherTypeName}';\n`
);
}
await stream.end();
})()
]);
}
Example #15
Source File: getFields.test.ts From amplify-codegen with Apache License 2.0 | 4 votes |
describe('getField', () => {
const nestedType = new GraphQLObjectType({
name: 'NestedObject',
fields: () => ({
level: { type: GraphQLInt },
subObj: { type: nestedType },
}),
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
foo: { type: GraphQLInt },
nested: { type: nestedType },
},
}),
});
it('should support simple scalar', () => {
const queries = schema.getQueryType().getFields();
expect(getFields(queries.foo, schema, 3, { useExternalFragmentForS3Object: false })).toEqual({
name: 'foo',
fields: [],
fragments: [],
hasBody: false,
});
expect(getFragment).not.toHaveBeenCalled();
});
it('it should recursively resolve fields up to max depth', () => {
const queries = schema.getQueryType().getFields();
expect(getFields(queries.nested, schema, 2, { useExternalFragmentForS3Object: false })).toEqual({
name: 'nested',
fields: [
{
name: 'level',
fields: [],
fragments: [],
hasBody: false,
},
{
name: 'subObj',
fields: [
{
name: 'level',
fields: [],
fragments: [],
hasBody: false,
},
],
fragments: [],
hasBody: true,
},
],
fragments: [],
hasBody: true,
});
});
it('should not return anything for complex type when the depth is < 1', () => {
const queries = schema.getQueryType().getFields();
expect(getFields(queries.nested, schema, 0, { useExternalFragmentForS3Object: false })).toBeUndefined();
});
describe('When type is an Interface', () => {
beforeEach(() => {
jest.resetAllMocks();
});
const shapeInterfaceType = new GraphQLInterfaceType({
name: 'Entity',
fields: {
name: { type: GraphQLString },
},
});
const rectangleType = new GraphQLObjectType({
name: 'Rectangle',
fields: {
name: { type: GraphQLString },
length: { type: GraphQLInt },
width: { type: GraphQLInt },
},
interfaces: () => [shapeInterfaceType],
});
const circleType = new GraphQLObjectType({
name: 'Circle',
fields: {
name: { type: GraphQLString },
radius: { type: GraphQLInt },
},
interfaces: () => [shapeInterfaceType],
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
shapeInterface: { type: shapeInterfaceType },
},
}),
types: [circleType, rectangleType],
});
it('interface - should return fragments of all the implementations', () => {
const maxDepth = 2;
const getPossibleTypeSpy = jest.spyOn(schema, 'getPossibleTypes');
getFields(schema.getQueryType().getFields().shapeInterface, schema, maxDepth, { useExternalFragmentForS3Object: false });
expect(getPossibleTypeSpy).toHaveBeenCalled();
expect(getFragment).toHaveBeenCalled();
const commonField = {
name: 'name',
fragments: [],
hasBody: false,
fields: [],
};
expect(getFragment.mock.calls[0][0]).toEqual(circleType);
expect(getFragment.mock.calls[0][1]).toEqual(schema);
expect(getFragment.mock.calls[0][2]).toEqual(maxDepth);
expect(getFragment.mock.calls[0][3]).toEqual([commonField]);
expect(getFragment.mock.calls[1][0]).toEqual(rectangleType);
expect(getFragment.mock.calls[1][1]).toEqual(schema);
expect(getFragment.mock.calls[1][2]).toEqual(maxDepth);
expect(getFragment.mock.calls[1][3]).toEqual([commonField]);
});
});
describe('When type is an union', () => {
beforeEach(() => {
jest.resetAllMocks();
});
const rectangleType = new GraphQLObjectType({
name: 'Rectangle',
fields: {
length: { type: GraphQLInt },
width: { type: GraphQLInt },
},
});
const circleType = new GraphQLObjectType({
name: 'Circle',
fields: {
radius: { type: GraphQLInt },
},
});
const shapeResultUnion = new GraphQLUnionType({
name: 'ShapeResultUnion',
types: [circleType, rectangleType],
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
shapeResult: { type: shapeResultUnion },
},
}),
});
it('union - should return fragments of all the types', () => {
const maxDepth = 2;
const getPossibleTypeSpy = jest.spyOn(schema, 'getPossibleTypes');
getFields(schema.getQueryType().getFields().shapeResult, schema, maxDepth, { useExternalFragmentForS3Object: false });
expect(getPossibleTypeSpy).toHaveBeenCalled();
expect(getFragment).toHaveBeenCalled();
const commonField = []; // unions don't have to have common field
expect(getFragment.mock.calls[0][0]).toEqual(circleType);
expect(getFragment.mock.calls[0][1]).toEqual(schema);
expect(getFragment.mock.calls[0][2]).toEqual(maxDepth);
expect(getFragment.mock.calls[0][3]).toEqual(commonField);
expect(getFragment.mock.calls[1][0]).toEqual(rectangleType);
expect(getFragment.mock.calls[1][1]).toEqual(schema);
expect(getFragment.mock.calls[1][2]).toEqual(maxDepth);
expect(getFragment.mock.calls[1][3]).toEqual(commonField);
});
});
describe('aggregateItems should generate two additional levels', () => {
beforeEach(() => {
jest.resetAllMocks();
});
const aggregateScalarResult = new GraphQLObjectType({
name: 'SearchableAggregateScalarResult',
fields: {
value: { type: GraphQLFloat },
},
});
const aggregateBucketResultItem = new GraphQLObjectType({
name: 'SearchableAggregateBucketResultItem',
fields: {
key: { type: GraphQLString },
doc_count: { type: GraphQLInt },
},
});
const aggregateBucketResult = new GraphQLObjectType({
name: 'SearchableAggregateBucketResult',
fields: {
buckets: { type: aggregateBucketResultItem },
},
});
const aggregateResult = new GraphQLUnionType({
name: 'SearchableAggregateGenericResult',
types: [aggregateScalarResult, aggregateBucketResult],
});
const aggregateItemsObject = new GraphQLObjectType({
name: 'SearchableAggregateResult',
fields: {
name: { type: GraphQLString },
result: { type: aggregateResult },
},
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
aggregateItems: { type: aggregateItemsObject },
},
}),
});
it('aggregateItems property should traverse two additional levels to generate required fields with default depth 2', () => {
const maxDepth = 2;
const getPossibleTypeSpy = jest.spyOn(schema, 'getPossibleTypes');
getFields(schema.getQueryType().getFields().aggregateItems, schema, maxDepth, { useExternalFragmentForS3Object: false });
expect(getPossibleTypeSpy).toHaveBeenCalled();
expect(getFragment).toHaveBeenCalled();
const commonField = []; // unions don't have to have common field
expect(getFragment.mock.calls[0][0]).toEqual(aggregateScalarResult);
expect(getFragment.mock.calls[0][1]).toEqual(schema);
expect(getFragment.mock.calls[0][2]).toEqual(maxDepth - 1);
expect(getFragment.mock.calls[0][3]).toEqual(commonField);
expect(getFragment.mock.calls[1][0]).toEqual(aggregateBucketResult);
expect(getFragment.mock.calls[1][1]).toEqual(schema);
expect(getFragment.mock.calls[1][2]).toEqual(maxDepth - 1);
expect(getFragment.mock.calls[1][3]).toEqual(commonField);
});
});
});
Example #16
Source File: FetcherWriter.ts From graphql-ts-client with MIT License | 4 votes |
constructor(
protected modelType: GraphQLObjectType | GraphQLInterfaceType | GraphQLUnionType,
protected ctx: FetcherContext,
stream: WriteStream,
config: GeneratorConfig
) {
super(stream, config);
this.fetcherTypeName = `${this.modelType.name}${config.fetcherSuffix ?? "Fetcher"}`;
if (modelType instanceof GraphQLUnionType) {
const map: { [key: string]: GraphQLField<any, any> } = {};
const itemCount = modelType.getTypes().length;
if (itemCount !== 0) {
const fieldCountMap = new Map<string, number>();
for (const type of modelType.getTypes()) {
for (const fieldName in type.getFields()) {
fieldCountMap.set(fieldName, (fieldCountMap.get(fieldName) ?? 0) + 1);
}
}
const firstTypeFieldMap = modelType.getTypes()[0].getFields();
for (const fieldName in firstTypeFieldMap) {
if (fieldCountMap.get(fieldName) === itemCount) {
map[fieldName] = firstTypeFieldMap[fieldName]!;
}
}
}
this.fieldMap = map;
} else {
this.fieldMap = modelType.getFields();
}
const fieldArgsMap = new Map<string, GraphQLArgument[]>();
const fieldCategoryMap = new Map<string, string>();
const defaultFetcherProps: string[] = [];
this.hasArgs = false;
for (const fieldName in this.fieldMap) {
const field = this.fieldMap[fieldName]!;
const targetType = targetTypeOf(field.type);
if (this.modelType.name !== "Query" &&
this.modelType.name !== "Mutation" &&
targetType === undefined &&
field.args.length === 0 &&
!field.isDeprecated) {
if (config.defaultFetcherExcludeMap !== undefined) {
const excludeProps = config.defaultFetcherExcludeMap[modelType.name];
if (excludeProps !== undefined && excludeProps.filter(name => name === fieldName).length !== 0) {
continue;
}
}
defaultFetcherProps.push(fieldName);
}
if (field.args.length !== 0) {
fieldArgsMap.set(fieldName, field.args);
}
const fieldCoreType =
field.type instanceof GraphQLNonNull ?
field.type.ofType :
field.type;
if (this.ctx.embeddedTypes.has(fieldCoreType)) {
fieldCategoryMap.set(fieldName, "SCALAR");
} else if (this.ctx.connections.has(fieldCoreType)) {
fieldCategoryMap.set(fieldName, "CONNECTION");
} else if (fieldCoreType instanceof GraphQLList) {
const elementType =
fieldCoreType.ofType instanceof GraphQLNonNull ?
fieldCoreType.ofType.ofType :
fieldCoreType.ofType;
if (elementType instanceof GraphQLObjectType ||
elementType instanceof GraphQLInterfaceType ||
elementType instanceof GraphQLUnionType
) {
fieldCategoryMap.set(fieldName, "LIST");
}
} else if (fieldCoreType instanceof GraphQLObjectType ||
fieldCoreType instanceof GraphQLInterfaceType ||
fieldCoreType instanceof GraphQLUnionType
) {
fieldCategoryMap.set(fieldName, "REFERENCE");
} else if (this.ctx.idFieldMap.get(this.modelType) === field) {
fieldCategoryMap.set(fieldName, "ID");
} else {
fieldCategoryMap.set(fieldName, "SCALAR");
}
if (field.args.length !== 0) {
this.hasArgs = true;
}
}
this.defaultFetcherProps = defaultFetcherProps;
this.fieldArgsMap = fieldArgsMap;
this.fieldCategoryMap = fieldCategoryMap;
let prefix = instancePrefix(this.modelType.name);
this.emptyFetcherName = `${prefix}$`;
this.defaultFetcherName = defaultFetcherProps.length !== 0 ? `${prefix}$$` : undefined;
}
Example #17
Source File: handler.spec.ts From graphql-mesh with MIT License | 4 votes |
describe('odata', () => {
let pubsub: MeshPubSub;
let cache: KeyValueCache;
let store: MeshStore;
let logger: Logger;
beforeEach(() => {
pubsub = new PubSub();
cache = new InMemoryLRUCache();
store = new MeshStore('odata', new InMemoryStoreStorageAdapter(), {
readonly: false,
validate: false,
});
logger = new DefaultLogger('ODataTest');
resetMocks();
});
it('should create a GraphQL schema from a simple OData endpoint', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
expect(printSchema(source.schema)).toMatchSnapshot();
});
it('should create correct GraphQL schema for functions with entity set paths', async () => {
addMock('http://sample.service.com/$metadata', async () => new Response(BasicMetadata));
const handler = new ODataHandler({
name: 'SampleService',
config: {
baseUrl: 'http://sample.service.com',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
expect(printSchema(source.schema)).toMatchSnapshot();
});
it('should declare arguments for fields created from bound functions', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const personType = source.schema.getType('IPerson') as GraphQLInterfaceType;
const getFriendsTripsFunction = personType.getFields().GetFriendsTrips;
expect(getFriendsTripsFunction.args).toHaveLength(2);
const personArg = getFriendsTripsFunction.args.find(arg => arg.name === 'person');
expect(personArg).not.toBeFalsy();
expect(personArg.type.toString()).toBe('PersonInput');
const userNameArg = getFriendsTripsFunction.args.find(arg => arg.name === 'userName');
expect(userNameArg).not.toBeFalsy();
expect(userNameArg.type.toString()).toBe('String!');
});
it('should generate correct HTTP request for requesting an EntitySet', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = 'https://services.odata.org/TripPinRESTierService/People';
const correctMethod = 'GET';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(JSON.stringify({ value: [PersonMockData] }));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
People {
UserName
FirstName
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
});
it('should generate correct HTTP request for requesting a single Entity by ID', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People/SOMEID/`;
const correctMethod = 'GET';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(JSON.stringify(PersonMockData));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
PeopleByUserName(UserName: "SOMEID") {
UserName
FirstName
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
});
it('should generate correct HTTP request for requesting a complex property', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/Airports/KSFO/?$select=IcaoCode,Location`;
const correctMethod = 'GET';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(
JSON.stringify({
'@odata.type': 'Microsoft.OData.Service.Sample.TrippinInMemory.Models.Airport',
IcaoCode: Date.now().toString(),
Location: {
'@odata.type': 'Microsoft.OData.Service.Sample.TrippinInMemory.Models.AirportLocation',
Loc: '',
},
})
);
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
AirportsByIcaoCode(IcaoCode: "KSFO") {
IcaoCode
Location {
Loc
}
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
});
it('should generate correct HTTP request for query options', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People?$filter=FirstName eq 'Scott'`;
const correctMethod = 'GET';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(JSON.stringify({ value: [PersonMockData] }));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
People(queryOptions: { filter: "FirstName eq 'Scott'" }) {
UserName
FirstName
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(decodeURIComponent(sentRequest!.url)).toBe(decodeURIComponent(correctUrl));
});
it('should generate correct HTTP request for $count', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People/$count`;
const correctMethod = 'GET';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(JSON.stringify(20));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
PeopleCount
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
});
it('should generate correct HTTP request for creating an entity', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People`;
const correctMethod = 'POST';
const correctBody = {
UserName: 'lewisblack',
FirstName: 'Lewis',
LastName: 'Black',
Emails: ['[email protected]'],
Gender: 'Male',
FavoriteFeature: 'Feature1',
Features: ['Feature1', 'Feature2'],
AddressInfo: [
{
Address: '187 Suffolk Ln.',
City: {
Name: 'Boise',
CountryRegion: 'United States',
Region: 'ID',
},
},
],
};
let sentRequest: any;
addMock(correctUrl, async request => {
sentRequest = request.clone();
const bodyObj = await request.json();
bodyObj['@odata.type'] = 'Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person';
return new Response(JSON.stringify(bodyObj));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
variables: {
input: correctBody,
},
document: parse(/* GraphQL */ `
mutation CreatePeople($input: PersonInput) {
createPeople(input: $input) {
UserName
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
expect(await sentRequest!.json()).toStrictEqual(correctBody);
});
it('should generate correct HTTP request for deleting an entity', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People/SOMEID/`;
const correctMethod = 'DELETE';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(JSON.stringify({}));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
mutation {
deletePeopleByUserName(UserName: "SOMEID")
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
});
it('should generate correct HTTP request for updating an entity', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People/SOMEID/`;
const correctMethod = 'PATCH';
const correctBody = {
FirstName: 'Mirs',
LastName: 'King',
};
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request.clone();
const returnBody = await request.json();
returnBody['@odata.type'] = 'Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person';
return new Response(JSON.stringify(returnBody));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
variables: {
UserName: 'SOMEID',
input: correctBody,
},
document: parse(/* GraphQL */ `
mutation UpdatePeople($UserName: String!, $input: PersonUpdateInput!) {
updatePeopleByUserName(UserName: $UserName, input: $input) {
FirstName
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
expect(await sentRequest!.text()).toBe(JSON.stringify(correctBody));
});
it('should generate correct HTTP request for invoking unbound functions', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/GetNearestAirport(lat = 33, lon = -118)?$select=IcaoCode,Name`;
const correctMethod = 'GET';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(
JSON.stringify({
'@odata.type': 'Microsoft.OData.Service.Sample.TrippinInMemory.Models.Airport',
IcaoCode: Date.now().toString(),
Name: 'Name',
})
);
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
GetNearestAirport(lat: 33, lon: -118) {
IcaoCode
Name
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(decodeURIComponent(sentRequest!.url)).toBe(correctUrl);
});
it('should generate correct HTTP request for invoking bound functions', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People/russellwhyte/Trips/0/Microsoft.OData.Service.Sample.TrippinInMemory.Models.GetInvolvedPeople?$select=UserName`;
const correctMethod = 'GET';
let sentRequest: Request;
addMock(`https://services.odata.org/TripPinRESTierService/People/russellwhyte/`, async () => {
return new Response(JSON.stringify(PersonMockData));
});
addMock(
`https://services.odata.org/TripPinRESTierService/People/russellwhyte/Trips?$filter=TripId eq 0&$select=TripId`,
async () => {
return new Response(JSON.stringify(TripMockData));
}
);
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(
JSON.stringify({
value: [],
})
);
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
PeopleByUserName(UserName: "russellwhyte") {
UserName
Trips(queryOptions: { filter: "TripId eq 0" }) {
TripId
GetInvolvedPeople {
UserName
}
}
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
});
it('should generate correct HTTP request for invoking bound functions with arguments', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People/russellwhyte/Microsoft.OData.Service.Sample.TrippinInMemory.Models.GetFriendsTrips(userName='ronaldmundy')?$select=TripId,Name`;
const correctMethod = 'GET';
let sentRequest: Request;
addMock(`https://services.odata.org/TripPinRESTierService/People/russellwhyte/`, async () => {
return new Response(JSON.stringify(PersonMockData));
});
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(
JSON.stringify({
value: [],
})
);
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
{
PeopleByUserName(UserName: "russellwhyte") {
UserName
GetFriendsTrips(userName: "ronaldmundy") {
TripId
Name
}
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url.replace(/'/g, '%27')).toBe(correctUrl.replace(/'/g, '%27')); // apostrophe gets percent-encoded
});
it('should generate correct HTTP request for invoking unbound actions', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/ResetDataSource`;
const correctMethod = 'POST';
let sentRequest: Request;
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(JSON.stringify(true));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
mutation {
ResetDataSource
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
});
it('should generate correct HTTP request for invoking bound actions', async () => {
addMock('https://services.odata.org/TripPinRESTierService/$metadata', async () => new Response(TripPinMetadata));
const correctUrl = `https://services.odata.org/TripPinRESTierService/People/russellwhyte/Microsoft.OData.Service.Sample.TrippinInMemory.Models.ShareTrip`;
const correctMethod = 'POST';
const correctBody = {
userName: 'scottketchum',
tripId: 0,
};
let sentRequest: Request;
addMock(`https://services.odata.org/TripPinRESTierService/People/russellwhyte/`, async () => {
return new Response(JSON.stringify(PersonMockData));
});
addMock(correctUrl, async request => {
sentRequest = request;
return new Response(JSON.stringify(true));
});
const handler = new ODataHandler({
name: 'TripPin',
config: {
baseUrl: 'https://services.odata.org/TripPinRESTierService',
customFetch: mockFetch,
},
pubsub,
cache,
store,
baseDir,
importFn,
logger,
});
const source = await handler.getMeshSource();
const graphqlResult = (await source.executor({
context: {},
document: parse(/* GraphQL */ `
mutation {
PeopleByUserName(UserName: "russellwhyte") {
ShareTrip(userName: "scottketchum", tripId: 0)
}
}
`),
})) as ExecutionResult;
expect(graphqlResult.errors).toBeFalsy();
expect(sentRequest!.method).toBe(correctMethod);
expect(sentRequest!.url).toBe(correctUrl);
expect(await sentRequest!.text()).toBe(JSON.stringify(correctBody));
});
});
Example #18
Source File: schema.ts From squid with GNU General Public License v3.0 | 4 votes |
function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType | GraphQLInterfaceType): void {
if (model[type.name]) return
let kind: 'entity' | 'object' | 'interface' = isEntityType(type)
? 'entity'
: type instanceof GraphQLInterfaceType ? 'interface' : 'object'
let properties: Record<string, Prop> = {}
let interfaces: string[] = []
let indexes: Index[] = type instanceof GraphQLObjectType ? checkEntityIndexes(type) : []
let description = type.description || undefined
switch(kind) {
case 'entity':
model[type.name] = {kind, properties, description, interfaces, indexes}
break
case 'object':
model[type.name] = {kind, properties, description, interfaces}
break
case 'interface':
model[type.name] = {kind, properties, description}
break
default:
throw unexpectedCase(kind)
}
let fields = type.getFields()
if (kind == 'entity') {
if (fields.id == null) {
properties.id = {
type: {kind: 'scalar', name: 'ID'},
nullable: false
}
} else {
let correctIdType = fields.id.type instanceof GraphQLNonNull
&& fields.id.type.ofType instanceof GraphQLScalarType
&& fields.id.type.ofType.name === 'ID'
if (!correctIdType) {
throw unsupportedFieldTypeError(type.name + '.id')
}
}
}
for (let key in fields) {
let f: GraphQLField<any, any> = fields[key]
handleFulltextDirective(model, type, f)
let propName = `${type.name}.${f.name}`
let fieldType = f.type
let nullable = true
let description = f.description || undefined
let derivedFrom = checkDerivedFrom(type, f)
let index = checkFieldIndex(type, f)
let unique = index?.unique || false
if (index) {
indexes.push(index)
}
if (fieldType instanceof GraphQLNonNull) {
nullable = false
fieldType = fieldType.ofType
}
let list = unwrapList(fieldType)
fieldType = list.item
if (fieldType instanceof GraphQLScalarType) {
properties[key] = {
type: wrapWithList(list.nulls, {
kind: 'scalar',
name: fieldType.name
}),
nullable,
description
}
} else if (fieldType instanceof GraphQLEnumType) {
addEnum(model, fieldType)
properties[key] = {
type: wrapWithList(list.nulls, {
kind: 'enum',
name: fieldType.name
}),
nullable,
description
}
} else if (fieldType instanceof GraphQLUnionType) {
addUnion(model, fieldType)
properties[key] = {
type: wrapWithList(list.nulls, {
kind: 'union',
name: fieldType.name
}),
nullable,
description
}
} else if (fieldType instanceof GraphQLObjectType) {
if (isEntityType(fieldType)) {
switch(list.nulls.length) {
case 0:
if (derivedFrom) {
if (!nullable) {
throw new SchemaError(`Property ${propName} must be nullable`)
}
properties[key] = {
type: {
kind: 'lookup',
entity: fieldType.name,
field: derivedFrom.field
},
nullable,
description
}
} else {
if (unique && nullable) {
throw new SchemaError(`Unique property ${propName} must be non-nullable`)
}
properties[key] = {
type: {
kind: 'fk',
foreignEntity: fieldType.name
},
nullable,
unique,
description
}
}
break
case 1:
if (derivedFrom == null) {
throw new SchemaError(`@derivedFrom directive is required on ${propName} declaration`)
}
properties[key] = {
type: {
kind: 'list-lookup',
entity: fieldType.name,
field: derivedFrom.field
},
nullable: false,
description
}
break
default:
throw unsupportedFieldTypeError(propName)
}
} else {
addEntityOrJsonObjectOrInterface(model, fieldType)
properties[key] = {
type: wrapWithList(list.nulls, {
kind: 'object',
name: fieldType.name
}),
nullable,
description
}
}
} else {
throw unsupportedFieldTypeError(propName)
}
}
if (kind != 'interface') {
type.getInterfaces().forEach(i => {
addEntityOrJsonObjectOrInterface(model, i)
interfaces.push(i.name)
})
}
}
Example #19
Source File: selectors.ts From tql with MIT License | 4 votes |
transform = (
ast: DocumentNode,
schema: GraphQLSchema
): ASTVisitor => {
// const Field = imp("Field@timkendall@tql");
// const Argument = imp("Argument@timkendall@tql");
// const Variable = imp("Variable@timkendall@tql");
// const InlineFragment = imp("InlineFragment@timkendall@tql");
return {
[Kind.DIRECTIVE_DEFINITION]: () => null,
[Kind.SCALAR_TYPE_DEFINITION]: () => null,
[Kind.ENUM_TYPE_DEFINITION]: (node) => {
return null;
},
[Kind.ENUM_VALUE_DEFINITION]: (node) => {
return null;
},
[Kind.INPUT_OBJECT_TYPE_DEFINITION]: (def) => {
return null;
},
[Kind.OBJECT_TYPE_DEFINITION]: (node) => {
const typename = node.name.value;
const type = schema.getType(typename);
invariant(
type instanceof GraphQLObjectType,
`Type "${typename}" was not instance of expected class GraphQLObjectType.`
);
const fields = Object.values(type.getFields());
return code`
${/* selector interface */ ""}
interface I${type.name}Selector {
readonly __typename: () => Field<"__typename">
${fields.map(printSignature).join("\n")}
}
${/* selector object */ ""}
const ${typename}Selector: I${typename}Selector = {
__typename: () => field("__typename"),
${fields.map(printMethod).join("\n")}
}
${/* select fn */ ""}
export const ${toLower(
typename
)} = <T extends ReadonlyArray<Selection>>(select: (t: I${typename}Selector) => T) => new SelectionBuilder<ISchema, "${type}", T>(SCHEMA as any, "${typename}", select(${typename}Selector))
`;
},
[Kind.INTERFACE_TYPE_DEFINITION]: (node) => {
const typename = node.name.value;
const type = schema.getType(typename);
invariant(
type instanceof GraphQLInterfaceType,
`Type "${typename}" was not instance of expected class GraphQLInterfaceType.`
);
// @note Get all implementors of this union
const implementations = schema
.getPossibleTypes(type)
.map((type) => type.name);
const fields = Object.values(type.getFields());
return code`
${/* selector interface */ ""}
interface I${type.name}Selector {
readonly __typename: () => Field<"__typename">
${fields.map(printSignature).join("\n")}
readonly on: <T extends ReadonlyArray<Selection>, F extends ${implementations
.map((name) => `"${name}"`)
.join(" | ")}>(
type: F,
select: (t: ${printConditionalSelectorArg(
implementations.map((name) => name)
)}) => T
) => InlineFragment<NamedType<F>, SelectionSet<T>>
}
${/* selector object */ ""}
const ${typename}Selector: I${typename}Selector = {
__typename: () => field("__typename"),
${fields.map(printMethod).join("\n")}
on: (
type,
select,
) => {
switch(type) {
${implementations
.map(
(name) => `
case "${name}": {
return inlineFragment(
namedType("${name}"),
selectionSet(select(${name}Selector as Parameters<typeof select>[0])),
)
}
`
)
.join("\n")}
default:
throw new TypeConditionError({
selectedType: type,
abstractType: "${type.name}",
})
}
},
}
${/* select fn */ ""}
export const ${toLower(
typename
)} = <T extends ReadonlyArray<Selection>>(select: (t: I${typename}Selector) => T) => new SelectionBuilder<ISchema, "${type}", T>(SCHEMA as any, "${typename}", select(${typename}Selector))
`;
},
[Kind.UNION_TYPE_DEFINITION]: (node) => {
const typename = node.name.value;
const type = schema.getType(typename);
invariant(
type instanceof GraphQLUnionType,
`Type "${typename}" was not instance of expected class GraphQLUnionType.`
);
// @note Get all implementors of this union
const implementations = schema
.getPossibleTypes(type)
.map((type) => type.name);
return code`
${/* selector interface */ ""}
interface I${type.name}Selector {
readonly __typename: () => Field<"__typename">
readonly on: <T extends ReadonlyArray<Selection>, F extends ${implementations
.map((name) => `"${name}"`)
.join(" | ")}>(
type: F,
select: (t: ${printConditionalSelectorArg(
implementations.map((name) => name)
)}) => T
) => InlineFragment<NamedType<F>, SelectionSet<T>>
}
${/* selector object */ ""}
const ${typename}Selector: I${typename}Selector = {
__typename: () => field("__typename"),
on: (
type,
select,
) => {
switch(type) {
${implementations
.map(
(name) => `
case "${name}": {
return inlineFragment(
namedType("${name}"),
selectionSet(select(${name}Selector as Parameters<typeof select>[0])),
)
}
`
)
.join("\n")}
default:
throw new TypeConditionError({
selectedType: type,
abstractType: "${type.name}",
})
}
},
}
${/* select fn */ ""}
export const ${toLower(
typename
)} = <T extends ReadonlyArray<Selection>>(select: (t: I${typename}Selector) => T) => new SelectionBuilder<ISchema, "${type}", T>(SCHEMA as any, "${typename}", select(${typename}Selector))
`;
},
[Kind.SCHEMA_DEFINITION]: (node) => {
return null;
},
};
}
Example #20
Source File: require-id-when-available.ts From graphql-eslint with MIT License | 4 votes |
rule: GraphQLESLintRule<[RequireIdWhenAvailableRuleConfig], true> = {
meta: {
type: 'suggestion',
// eslint-disable-next-line eslint-plugin/require-meta-has-suggestions
hasSuggestions: true,
docs: {
category: 'Operations',
description: 'Enforce selecting specific fields when they are available on the GraphQL type.',
url: `https://github.com/B2o5T/graphql-eslint/blob/master/docs/rules/${RULE_ID}.md`,
requiresSchema: true,
requiresSiblings: true,
examples: [
{
title: 'Incorrect',
code: /* GraphQL */ `
# In your schema
type User {
id: ID!
name: String!
}
# Query
query {
user {
name
}
}
`,
},
{
title: 'Correct',
code: /* GraphQL */ `
# In your schema
type User {
id: ID!
name: String!
}
# Query
query {
user {
id
name
}
}
# Selecting \`id\` with an alias is also valid
query {
user {
id: name
}
}
`,
},
],
recommended: true,
},
messages: {
[RULE_ID]:
"Field{{ pluralSuffix }} {{ fieldName }} must be selected when it's available on a type.\nInclude it in your selection set{{ addition }}.",
},
schema: {
definitions: {
asString: {
type: 'string',
},
asArray: ARRAY_DEFAULT_OPTIONS,
},
type: 'array',
maxItems: 1,
items: {
type: 'object',
additionalProperties: false,
properties: {
fieldName: {
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asArray' }],
default: DEFAULT_ID_FIELD_NAME,
},
},
},
},
},
create(context) {
const schema = requireGraphQLSchemaFromContext(RULE_ID, context);
const siblings = requireSiblingsOperations(RULE_ID, context);
const { fieldName = DEFAULT_ID_FIELD_NAME } = context.options[0] || {};
const idNames = asArray(fieldName);
// Check selections only in OperationDefinition,
// skip selections of OperationDefinition and InlineFragment
const selector = 'OperationDefinition SelectionSet[parent.kind!=/(^OperationDefinition|InlineFragment)$/]';
const typeInfo = new TypeInfo(schema);
function checkFragments(node: GraphQLESTreeNode<SelectionSetNode>): void {
for (const selection of node.selections) {
if (selection.kind !== Kind.FRAGMENT_SPREAD) {
continue;
}
const [foundSpread] = siblings.getFragment(selection.name.value);
if (!foundSpread) {
continue;
}
const checkedFragmentSpreads = new Set<string>();
const visitor = visitWithTypeInfo(typeInfo, {
SelectionSet(node, key, parent: ASTNode) {
if (parent.kind === Kind.FRAGMENT_DEFINITION) {
checkedFragmentSpreads.add(parent.name.value);
} else if (parent.kind !== Kind.INLINE_FRAGMENT) {
checkSelections(node, typeInfo.getType(), selection.loc.start, parent, checkedFragmentSpreads);
}
},
});
visit(foundSpread.document, visitor);
}
}
function checkSelections(
node: OmitRecursively<SelectionSetNode, 'loc'>,
type: GraphQLOutputType,
// Fragment can be placed in separate file
// Provide actual fragment spread location instead of location in fragment
loc: ESTree.Position,
// Can't access to node.parent in GraphQL AST.Node, so pass as argument
parent: any,
checkedFragmentSpreads = new Set<string>()
): void {
const rawType = getBaseType(type);
const isObjectType = rawType instanceof GraphQLObjectType;
const isInterfaceType = rawType instanceof GraphQLInterfaceType;
if (!isObjectType && !isInterfaceType) {
return;
}
const fields = rawType.getFields();
const hasIdFieldInType = idNames.some(name => fields[name]);
if (!hasIdFieldInType) {
return;
}
function hasIdField({ selections }: typeof node): boolean {
return selections.some(selection => {
if (selection.kind === Kind.FIELD) {
if (selection.alias && idNames.includes(selection.alias.value)) {
return true;
}
return idNames.includes(selection.name.value);
}
if (selection.kind === Kind.INLINE_FRAGMENT) {
return hasIdField(selection.selectionSet);
}
if (selection.kind === Kind.FRAGMENT_SPREAD) {
const [foundSpread] = siblings.getFragment(selection.name.value);
if (foundSpread) {
const fragmentSpread = foundSpread.document;
checkedFragmentSpreads.add(fragmentSpread.name.value);
return hasIdField(fragmentSpread.selectionSet);
}
}
return false;
});
}
const hasId = hasIdField(node);
checkFragments(node as GraphQLESTreeNode<SelectionSetNode>);
if (hasId) {
return;
}
const pluralSuffix = idNames.length > 1 ? 's' : '';
const fieldName = englishJoinWords(idNames.map(name => `\`${(parent.alias || parent.name).value}.${name}\``));
const addition =
checkedFragmentSpreads.size === 0
? ''
: ` or add to used fragment${checkedFragmentSpreads.size > 1 ? 's' : ''} ${englishJoinWords(
[...checkedFragmentSpreads].map(name => `\`${name}\``)
)}`;
const problem: ReportDescriptor = {
loc,
messageId: RULE_ID,
data: {
pluralSuffix,
fieldName,
addition,
},
};
// Don't provide suggestions for selections in fragments as fragment can be in a separate file
if ('type' in node) {
problem.suggest = idNames.map(idName => ({
desc: `Add \`${idName}\` selection`,
fix: fixer => fixer.insertTextBefore((node as any).selections[0], `${idName} `),
}));
}
context.report(problem);
}
return {
[selector](node: GraphQLESTreeNode<SelectionSetNode, true>) {
const typeInfo = node.typeInfo();
if (typeInfo.gqlType) {
checkSelections(node, typeInfo.gqlType, node.loc.start, node.parent);
}
},
};
},
}
Example #21
Source File: types.ts From tql with MIT License | 4 votes |
transform = (
ast: DocumentNode,
schema: GraphQLSchema
): ASTVisitor => {
// @note needed to serialize inline enum values correctly at runtime
const enumValues = new Set<string>();
return {
[Kind.DIRECTIVE_DEFINITION]: () => null,
[Kind.SCALAR_TYPE_DEFINITION]: () => null,
[Kind.ENUM_TYPE_DEFINITION]: (node) => {
const typename = node.name.value;
const values = node.values?.map((v) => v.name.value) ?? [];
const printMember = (member: string): string => {
return `${member} = "${member}"`;
};
return code`
export enum ${typename} {
${values.map(printMember).join(",\n")}
}
`;
},
[Kind.ENUM_VALUE_DEFINITION]: (node) => {
enumValues.add(node.name.value);
return null;
},
[Kind.INPUT_OBJECT_TYPE_DEFINITION]: (node) => {
const typename = node.name.value;
const type = schema.getType(typename);
invariant(
type instanceof GraphQLInputObjectType,
`Type "${typename}" was not instance of expected class GraphQLInputObjectType.`
);
const fields = Object.values(type.getFields());
const printField = (field: GraphQLInputField) => {
const isList = listType(field.type);
const isNonNull = isNonNullType(field.type);
const baseType = inputType(field.type);
let tsType: string;
if (baseType instanceof GraphQLScalarType) {
tsType = toPrimitive(baseType);
} else if (baseType instanceof GraphQLEnumType) {
tsType = baseType.name;
} else if (baseType instanceof GraphQLInputObjectType) {
tsType = "I" + baseType.name;
} else {
throw new Error("Unable to render inputField!");
}
return [
field.name,
isNonNull ? ":" : "?:",
" ",
tsType,
isList ? "[]" : "",
].join("");
};
return code`
export interface I${typename} {
${fields.map(printField).join("\n")}
}
`;
},
[Kind.OBJECT_TYPE_DEFINITION]: (node) => {
const typename = node.name.value;
const type = schema.getType(typename);
invariant(
type instanceof GraphQLObjectType,
`Type "${typename}" was not instance of expected class GraphQLObjectType.`
);
const fields = Object.values(type.getFields());
const interfaces = type.getInterfaces();
// @note TypeScript only requires new fields to be defined on interface extendors
const interfaceFields = interfaces.flatMap((i) =>
Object.values(i.getFields()).map((field) => field.name)
);
const uncommonFields = fields.filter(
(field) => !interfaceFields.includes(field.name)
);
// @todo extend any implemented interfaces
// @todo only render fields unique to this type
const extensions =
interfaces.length > 0
? `extends ${interfaces.map((i) => "I" + i.name).join(", ")}`
: "";
return code`
export interface I${typename} ${extensions} {
readonly __typename: ${`"${typename}"`}
${uncommonFields.map(printField).join("\n")}
}
`;
},
[Kind.INTERFACE_TYPE_DEFINITION]: (node) => {
const typename = node.name.value;
const type = schema.getType(typename);
invariant(
type instanceof GraphQLInterfaceType,
`Type "${typename}" was not instance of expected class GraphQLInterfaceType.`
);
// @note Get all implementors of this union
const implementations = schema
.getPossibleTypes(type)
.map((type) => type.name);
const fields = Object.values(type.getFields());
return code`
export interface I${typename} {
readonly __typename: ${implementations
.map((type) => `"${type}"`)
.join(" | ")}
${fields.map(printField).join("\n")}
}
`;
},
[Kind.UNION_TYPE_DEFINITION]: (node) => {
const typename = node.name.value;
const type = schema.getType(typename);
invariant(
type instanceof GraphQLUnionType,
`Type "${typename}" was not instance of expected class GraphQLUnionType.`
);
// @note Get all implementors of this union
const implementations = schema
.getPossibleTypes(type)
.map((type) => type.name);
return code`
export type ${"I" + type.name} = ${implementations
.map((type) => `I${type}`)
.join(" | ")}
`;
},
[Kind.SCHEMA_DEFINITION]: (_) => {
return null;
},
};
}