@omnigraph/odata
Version:
869 lines (868 loc) • 40.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processDirectives = void 0;
exports.loadNonExecutableGraphQLSchemaFromOData = loadNonExecutableGraphQLSchemaFromOData;
exports.loadGraphQLSchemaFromOData = loadGraphQLSchemaFromOData;
exports.loadODataSubgraph = loadODataSubgraph;
const tslib_1 = require("tslib");
// eslint-disable-next-line import/no-nodejs-modules
const node_events_1 = tslib_1.__importDefault(require("node:events"));
const fast_xml_parser_1 = require("fast-xml-parser");
const graphql_1 = require("graphql");
const graphql_compose_1 = require("graphql-compose");
const graphql_scalars_1 = require("graphql-scalars");
const pascal_case_1 = require("pascal-case");
const url_join_1 = tslib_1.__importDefault(require("url-join"));
const cross_helpers_1 = require("@graphql-mesh/cross-helpers");
const string_interpolation_1 = require("@graphql-mesh/string-interpolation");
const utils_1 = require("@graphql-mesh/utils");
const directives_js_1 = require("./directives.js");
Object.defineProperty(exports, "processDirectives", { enumerable: true, get: function () { return directives_js_1.processDirectives; } });
const getTypeNameFromRef_js_1 = require("./utils/getTypeNameFromRef.js");
const QueryOptionsFields_js_1 = require("./utils/QueryOptionsFields.js");
async function loadNonExecutableGraphQLSchemaFromOData(name, { endpoint: nonInterpolatedBaseUrl, operationHeaders, importFn, logger, fetchFn, source, baseDir, schemaHeaders, batch, expandNavProps, }) {
const eventEmitterSet = new Set();
const endpoint = string_interpolation_1.stringInterpolator.parse(nonInterpolatedBaseUrl, {
env: cross_helpers_1.process.env,
});
const schemaComposer = new graphql_compose_1.SchemaComposer();
schemaComposer.add(graphql_scalars_1.GraphQLBigInt);
schemaComposer.add(graphql_scalars_1.GraphQLGUID);
schemaComposer.add(graphql_scalars_1.GraphQLDateTime);
schemaComposer.add(graphql_scalars_1.GraphQLJSON);
schemaComposer.add(graphql_scalars_1.GraphQLByte);
schemaComposer.add(graphql_scalars_1.GraphQLDate);
schemaComposer.add(graphql_scalars_1.GraphQLISO8601Duration);
const aliasNamespaceMap = new Map();
const metadataUrl = (0, url_join_1.default)(endpoint, '$metadata');
const metadataText = await (0, utils_1.readFileOrUrl)(source || metadataUrl, {
allowUnknownExtensions: true,
cwd: baseDir,
headers: schemaHeaders,
fetch: fetchFn,
logger,
importFn,
});
const xmlParser = new fast_xml_parser_1.XMLParser({
attributeNamePrefix: '',
attributesGroupName: 'attributes',
textNodeName: 'innerText',
ignoreAttributes: false,
removeNSPrefix: true,
isArray: (_, __, ___, isAttribute) => !isAttribute,
allowBooleanAttributes: true,
preserveOrder: false,
});
const metadataJson = await xmlParser.parse(metadataText);
const schemas = metadataJson.Edmx[0].DataServices[0].Schema;
const multipleSchemas = schemas.length > 1;
const namespaces = new Set();
schemaComposer.createEnumTC({
name: 'InlineCount',
values: {
allpages: {
value: 'allpages',
description: 'The OData MUST include a count of the number of entities in the collection identified by the URI (after applying any $filter System Query Options present on the URI)',
},
none: {
value: 'none',
description: 'The OData service MUST NOT include a count in the response. This is equivalence to a URI that does not include a $inlinecount query string parameter.',
},
},
});
schemaComposer.createInputTC({
name: 'QueryOptions',
fields: QueryOptionsFields_js_1.QUERY_OPTIONS_FIELDS,
});
const { args: commonArgs } = (0, string_interpolation_1.parseInterpolationStrings)([
...Object.values(operationHeaders || {}),
endpoint,
]);
function getTCByTypeNames(...typeNames) {
for (const typeName of typeNames) {
try {
return schemaComposer.getAnyTC(typeName);
}
catch { }
}
return null;
}
function buildName({ schemaNamespace, name }) {
const alias = aliasNamespaceMap.get(schemaNamespace) || schemaNamespace;
const ref = alias + '.' + name;
return multipleSchemas ? (0, pascal_case_1.pascalCase)(ref.split('.').join('_')) : name;
}
schemas?.forEach((schemaObj) => {
const schemaNamespace = schemaObj.attributes.Namespace;
namespaces.add(schemaNamespace);
const schemaAlias = schemaObj.attributes.Alias;
if (schemaAlias) {
aliasNamespaceMap.set(schemaNamespace, schemaAlias);
}
});
schemas?.forEach((schemaObj) => {
const schemaNamespace = schemaObj.attributes.Namespace;
schemaObj.EnumType?.forEach((enumObj) => {
const values = {};
enumObj.Member?.forEach((memberObj) => {
const key = memberObj.attributes.Name;
// This doesn't work.
// const value = memberElement.getAttribute('Value')!;
values[key] = {
value: key,
extensions: { memberObj },
};
});
const enumTypeName = buildName({ schemaNamespace, name: enumObj.attributes.Name });
schemaComposer.createEnumTC({
name: enumTypeName,
values,
extensions: { enumObj },
});
});
const allTypes = (schemaObj.EntityType || []).concat(schemaObj.ComplexType || []);
const typesWithBaseType = allTypes.filter((typeObj) => typeObj.attributes.BaseType);
allTypes?.forEach((typeObj) => {
const entityTypeName = buildName({ schemaNamespace, name: typeObj.attributes.Name });
const isOpenType = typeObj.attributes.OpenType === 'true';
const isAbstract = typeObj.attributes.Abstract === 'true';
const eventEmitter = new node_events_1.default();
eventEmitter.setMaxListeners(Infinity);
eventEmitterSet.add(eventEmitter);
const extensions = {
directives: {
entityInfo: {
actualFields: [],
navigationFields: [],
isOpenType,
},
},
eventEmitter,
};
schemaComposer.addDirective(directives_js_1.EntityInfoDirective);
const inputType = schemaComposer.createInputTC({
name: entityTypeName + 'Input',
fields: {},
extensions: () => extensions,
});
let abstractType;
if (typesWithBaseType.some((typeObj) => typeObj.attributes.BaseType.includes(`.${entityTypeName}`)) ||
isAbstract) {
abstractType = schemaComposer.createInterfaceTC({
name: isAbstract ? entityTypeName : `I${entityTypeName}`,
extensions: extensions,
});
const directiveExtensions = (extensions.directives ||= {});
directiveExtensions.abstractType = {
entityTypeName,
isAbstract,
get aliasNamespaceMap() {
return [...aliasNamespaceMap.entries()];
},
multipleSchemas,
get namespaces() {
return [...namespaces];
},
};
schemaComposer.addDirective(directives_js_1.AbstractTypeDirective);
}
const outputType = schemaComposer.createObjectTC({
name: isAbstract ? `T${entityTypeName}` : entityTypeName,
extensions: extensions,
interfaces: abstractType ? [abstractType] : [],
});
abstractType?.setInputTypeComposer(inputType);
outputType.setInputTypeComposer(inputType);
const propertyRefObj = typeObj.Key && typeObj.Key[0].PropertyRef[0];
if (propertyRefObj) {
extensions.directives.entityInfo.identifierFieldName = propertyRefObj.attributes.Name;
}
typeObj.Property?.forEach((propertyObj) => {
const propertyName = propertyObj.attributes.Name;
extensions.directives.entityInfo.actualFields.push(propertyName);
const propertyTypeRef = propertyObj.attributes.Type;
if (propertyName === extensions.directives.entityInfo.identifierFieldName) {
extensions.directives.entityInfo.identifierFieldTypeRef = propertyTypeRef;
}
const isRequired = propertyObj.attributes.Nullable === 'false';
inputType.addFields({
[propertyName]: {
type: (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: propertyTypeRef,
isInput: true,
isRequired,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
}),
extensions: { propertyObj },
},
});
const field = {
type: (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: propertyTypeRef,
isInput: false,
isRequired,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
}),
extensions: { propertyObj },
};
abstractType?.addFields({
[propertyName]: field,
});
outputType.addFields({
[propertyName]: field,
});
});
typeObj.NavigationProperty?.forEach((navigationPropertyObj) => {
const navigationPropertyName = navigationPropertyObj.attributes.Name;
extensions.directives.entityInfo.navigationFields.push(navigationPropertyName);
const navigationPropertyTypeRef = navigationPropertyObj.attributes.Type;
const isRequired = navigationPropertyObj.attributes.Nullable === 'false';
const isList = navigationPropertyTypeRef.startsWith('Collection(');
if (isList) {
const singularField = {
type: (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: navigationPropertyTypeRef,
isInput: false,
isRequired,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
})
.replace('[', '')
.replace(']', ''),
args: {
...commonArgs,
id: {
type: 'ID',
},
},
extensions: {
navigationPropertyObj,
directives: {
singularNav: {
navigationPropertyName,
},
},
},
};
schemaComposer.addDirective(directives_js_1.SingularNavDirective);
const pluralField = {
type: (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: navigationPropertyTypeRef,
isInput: false,
isRequired,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
}),
args: {
...commonArgs,
queryOptions: { type: 'QueryOptions' },
},
extensions: {
navigationPropertyObj,
directives: {
pluralNav: {
navigationPropertyName,
},
},
},
};
schemaComposer.addDirective(directives_js_1.PluralNavDirective);
abstractType?.addFields({
[navigationPropertyName]: pluralField,
[`${navigationPropertyName}ById`]: singularField,
});
outputType.addFields({
[navigationPropertyName]: pluralField,
[`${navigationPropertyName}ById`]: singularField,
});
}
else {
const field = {
type: (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: navigationPropertyTypeRef,
isInput: false,
isRequired,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
}),
args: {
...commonArgs,
},
extensions: {
navigationPropertyObj,
directives: {
navProp: {
navigationPropertyName,
},
},
},
};
schemaComposer.addDirective(directives_js_1.NavPropDirective);
abstractType?.addFields({
[navigationPropertyName]: field,
});
outputType.addFields({
[navigationPropertyName]: field,
});
}
});
if (isOpenType || outputType.getFieldNames().length === 0) {
extensions.directives.entityInfo.isOpenType = true;
inputType.addFields({
rest: {
type: 'JSON',
},
});
abstractType?.addFields({
rest: {
type: 'JSON',
extensions: {
directives: {
resolveRoot: {},
},
},
},
});
schemaComposer.addDirective(directives_js_1.ResolveRootDirective);
outputType.addFields({
rest: {
type: 'JSON',
},
});
}
const updateInputType = inputType.clone(`${entityTypeName}UpdateInput`);
updateInputType
.getFieldNames()
?.forEach(fieldName => updateInputType.makeOptional(fieldName));
// Types might be considered as unused implementations of interfaces so we must prevent that
schemaComposer.addSchemaMustHaveType(outputType);
});
const handleUnboundFunctionObj = (unboundFunctionObj) => {
const functionName = unboundFunctionObj.attributes.Name;
const returnTypeRef = unboundFunctionObj.ReturnType[0].attributes.Type;
const returnType = (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: returnTypeRef,
isInput: false,
isRequired: false,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
});
schemaComposer.Query.addFields({
[functionName]: {
type: returnType,
args: {
...commonArgs,
},
extensions: {
directives: {
unboundFunction: {
functionName,
},
},
},
},
});
schemaComposer.addDirective(directives_js_1.UnboundFunctionDirective);
unboundFunctionObj.Parameter?.forEach((parameterObj) => {
const parameterName = parameterObj.attributes.Name;
const parameterTypeRef = parameterObj.attributes.Type;
const isRequired = parameterObj.attributes.Nullable === 'false';
const parameterType = (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: parameterTypeRef,
isInput: true,
isRequired,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
});
schemaComposer.Query.addFieldArgs(functionName, {
[parameterName]: {
type: parameterType,
},
});
});
};
const handleBoundFunctionObj = (boundFunctionObj) => {
const functionName = boundFunctionObj.attributes.Name;
const functionRef = schemaNamespace + '.' + functionName;
const returnTypeRef = boundFunctionObj.ReturnType[0].attributes.Type;
const returnType = (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: returnTypeRef,
isInput: false,
isRequired: false,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
});
const args = {
...commonArgs,
};
// eslint-disable-next-line prefer-const
let entitySetPath = boundFunctionObj.attributes.EntitySetPath?.split('/')[0];
let field;
let boundEntityTypeName;
boundFunctionObj.Parameter?.forEach((parameterObj) => {
const parameterName = parameterObj.attributes.Name;
const parameterTypeRef = parameterObj.attributes.Type;
const isRequired = parameterObj.attributes.Nullable === 'false';
const parameterTypeName = (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: parameterTypeRef,
isInput: true,
isRequired,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
});
// If entitySetPath is not available, take first parameter as entity
// The first segment of the entity set path must match the binding parameter name
// (see: http://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/odata-csdl-xml-v4.01.html#_Toc38530388)
entitySetPath = (entitySetPath && entitySetPath.split('/')[0]) || parameterName;
if (entitySetPath === parameterName) {
boundEntityTypeName = (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: parameterTypeRef,
isInput: false,
isRequired: false,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
})
.replace('[', '')
.replace(']', '');
field = {
type: returnType,
args,
extensions: {
directives: {
boundFunction: {
functionRef,
},
},
},
};
schemaComposer.addDirective(directives_js_1.BoundFunctionDirective);
}
args[parameterName] = {
type: parameterTypeName,
};
});
const boundEntityType = schemaComposer.getAnyTC(boundEntityTypeName);
const boundEntityOtherType = getTCByTypeNames('I' + boundEntityTypeName, 'T' + boundEntityTypeName);
boundEntityType.addFields({
[functionName]: field,
});
boundEntityOtherType?.addFields({
[functionName]: field,
});
};
schemaObj.Function?.forEach((functionObj) => {
if (functionObj.attributes?.IsBound === 'true') {
handleBoundFunctionObj(functionObj);
}
else {
handleUnboundFunctionObj(functionObj);
}
});
const handleUnboundActionObj = (unboundActionObj) => {
const actionName = unboundActionObj.attributes.Name;
schemaComposer.Mutation.addFields({
[actionName]: {
type: 'JSON',
args: {
...commonArgs,
},
extensions: {
directives: {
unboundAction: {
actionName,
},
},
},
},
});
schemaComposer.addDirective(directives_js_1.UnboundActionDirective);
unboundActionObj.Parameter?.forEach((parameterObj) => {
const parameterName = parameterObj.attributes.Name;
const parameterTypeRef = parameterObj.attributes.Type;
const isRequired = parameterObj.attributes.Nullable === 'false';
const parameterType = (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: parameterTypeRef,
isInput: true,
isRequired,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
});
schemaComposer.Mutation.addFieldArgs(actionName, {
[parameterName]: {
type: parameterType,
},
});
});
};
const boundActionEntityParameterDetails = (parameterObj) => {
const parameterName = parameterObj.attributes.Name;
const parameterTypeRef = parameterObj.attributes.Type;
const isRequired = parameterObj.attributes.Nullable === 'false';
const parameterTypeName = (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: parameterTypeRef,
isInput: true,
isRequired,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
});
const boundEntityTypeName = (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: parameterTypeRef,
isInput: false,
isRequired: false,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
})
.replace('[', '')
.replace(']', ''); // Todo temp workaround
return { boundEntityTypeName, argName: parameterName, argType: parameterTypeName };
};
const boundActionEntitySetDetails = (entitySetObj) => {
const entitySetTypeRef = entitySetObj.attributes.EntityType;
const boundEntityTypeName = (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: entitySetTypeRef,
isInput: false,
isRequired: false,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
});
const entityOutputTC = getTCByTypeNames('I' + boundEntityTypeName, boundEntityTypeName);
const entityTypeExtensions = entityOutputTC.getExtensions();
const argName = entityTypeExtensions.directives.entityInfo.identifierFieldName;
const argType = entityOutputTC.getFieldTypeName(argName);
return { boundEntityTypeName, argName, argType };
};
const handleBoundActionObj = (boundActionObj) => {
const actionName = boundActionObj.attributes.Name;
const actionRef = schemaNamespace + '.' + actionName;
const entitySetPath = boundActionObj.attributes.EntitySetPath;
// If entitySetPath is not specified, take first parameter as entity
const entityParameterObj = boundActionObj.Parameter?.find((parameterObj) => !entitySetPath || parameterObj.attributes.Name === entitySetPath);
// Try to find EntitySet matching entitySetPath:
const entitySetObj = schemas.flatMap(schemaObj => schemaObj.EntityContainer?.flatMap(entityContainerObj => entityContainerObj.EntitySet?.flatMap(entitySetObj => entitySetObj.attributes.Name === entitySetPath ? [entitySetObj] : []) ?? []) ?? [])?.[0];
const { boundEntityTypeName, argName, argType } = entitySetObj
? boundActionEntitySetDetails(entitySetObj)
: entityParameterObj
? boundActionEntityParameterDetails(entityParameterObj)
: (() => {
throw new Error(`EntitySet ${entitySetPath} not found in schema ${schemaNamespace}`);
})();
const args = {
...commonArgs,
[argName]: {
type: argType,
},
};
// add remaining parameters to args:
boundActionObj.Parameter?.forEach((parameterObj) => {
const { argName, argType } = boundActionEntityParameterDetails(parameterObj);
args[argName] = {
type: argType,
};
});
const boundField = {
type: 'JSON',
args,
extensions: {
directives: {
boundAction: {
actionRef,
},
},
},
};
schemaComposer.addDirective(directives_js_1.BoundActionDirective);
const boundEntityType = schemaComposer.getAnyTC(boundEntityTypeName);
boundEntityType.addFields({
[actionName]: boundField,
});
const otherType = getTCByTypeNames(`I${boundEntityTypeName}`, `T${boundEntityTypeName}`);
otherType?.addFields({
[actionName]: boundField,
});
};
schemaObj.Action?.forEach((actionObj) => {
if (actionObj.attributes?.IsBound === 'true') {
handleBoundActionObj(actionObj);
}
else {
handleUnboundActionObj(actionObj);
}
});
// Rearrange fields for base types and implementations
typesWithBaseType?.forEach((typeObj) => {
const typeName = buildName({
schemaNamespace,
name: typeObj.attributes.Name,
});
const inputType = schemaComposer.getITC(typeName + 'Input');
const abstractType = getTCByTypeNames('I' + typeName, typeName);
const outputType = getTCByTypeNames('T' + typeName, typeName);
const baseTypeRef = typeObj.attributes.BaseType;
const { directives: { entityInfo }, eventEmitter, } = outputType.getExtensions();
const baseTypeName = (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: baseTypeRef,
isInput: false,
isRequired: false,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
});
const baseInputType = schemaComposer.getAnyTC(baseTypeName + 'Input');
const baseAbstractType = getTCByTypeNames('I' + baseTypeName, baseTypeName);
const baseOutputType = getTCByTypeNames('T' + baseTypeName, baseTypeName);
const { directives: { entityInfo: baseEntityInfo }, eventEmitter: baseEventEmitter, } = baseOutputType.getExtensions();
const baseEventEmitterListener = () => {
inputType.addFields(baseInputType.getFields());
entityInfo.identifierFieldName =
baseEntityInfo.identifierFieldName || entityInfo.identifierFieldName;
entityInfo.identifierFieldTypeRef =
baseEntityInfo.identifierFieldTypeRef || entityInfo.identifierFieldTypeRef;
entityInfo.actualFields.unshift(...baseEntityInfo.actualFields);
abstractType?.addFields(baseAbstractType?.getFields());
outputType.addFields(baseOutputType.getFields());
if (baseAbstractType instanceof graphql_compose_1.InterfaceTypeComposer) {
// abstractType.addInterface(baseAbstractType.getTypeName());
outputType.addInterface(baseAbstractType.getTypeName());
}
eventEmitter.emit('onFieldChange');
};
baseEventEmitter.on('onFieldChange', baseEventEmitterListener);
baseEventEmitterListener();
});
});
schemas?.forEach((schemaObj) => {
schemaObj.EntityContainer?.forEach((entityContainerObj) => {
entityContainerObj.Singleton?.forEach((singletonObj) => {
const singletonName = singletonObj.attributes.Name;
const singletonTypeRef = singletonObj.attributes.Type;
const singletonTypeName = (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: singletonTypeRef,
isInput: false,
isRequired: false,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
});
schemaComposer.Query.addFields({
[singletonName]: {
type: singletonTypeName,
args: {
...commonArgs,
},
extensions: {
directives: {
singleton: {
singletonName,
},
},
},
},
});
schemaComposer.addDirective(directives_js_1.SingletonDirective);
});
entityContainerObj?.EntitySet?.forEach((entitySetObj) => {
const entitySetName = entitySetObj.attributes.Name;
const entitySetTypeRef = entitySetObj.attributes.EntityType;
const entityTypeName = (0, getTypeNameFromRef_js_1.getTypeNameFromRef)({
typeRef: entitySetTypeRef,
isInput: false,
isRequired: false,
aliasNamespaceMap,
namespaces,
multipleSchemas,
isEnumType: typeName => schemaComposer.isEnumType(typeName),
});
const entityOutputTC = getTCByTypeNames('I' + entityTypeName, entityTypeName);
const { directives: { entityInfo }, } = entityOutputTC.getExtensions();
const identifierFieldName = entityInfo.identifierFieldName;
const identifierFieldTypeRef = entityInfo.identifierFieldTypeRef;
const identifierFieldTypeName = entityOutputTC.getFieldTypeName(identifierFieldName);
const typeName = entityOutputTC.getTypeName();
schemaComposer.addDirective(directives_js_1.EntitySetDirective);
schemaComposer.addDirective(directives_js_1.EntitySetByIdentifierDirective);
schemaComposer.addDirective(directives_js_1.EntitySetCountDirective);
schemaComposer.addDirective(directives_js_1.CreateEntitySetDirective);
schemaComposer.addDirective(directives_js_1.DeleteEntitySetDirective);
schemaComposer.addDirective(directives_js_1.UpdateEntitySetDirective);
const commonFields = {
[entitySetName]: {
type: `[${typeName}]`,
args: {
...commonArgs,
queryOptions: { type: 'QueryOptions' },
},
extensions: {
directives: {
entitySet: {
entitySetName,
},
},
},
},
[`${entitySetName}By${identifierFieldName}`]: {
type: typeName,
args: {
...commonArgs,
[identifierFieldName]: {
type: identifierFieldTypeName,
},
},
extensions: {
directives: {
entitySetByIdentifier: {
entitySetName,
identifierFieldName,
identifierFieldTypeRef,
},
},
},
},
};
schemaComposer.Query.addFields({
...commonFields,
[`${entitySetName}Count`]: {
type: 'Int',
args: {
...commonArgs,
queryOptions: { type: 'QueryOptions' },
},
extensions: {
directives: {
entitySetCount: {
entitySetName,
},
},
},
},
});
schemaComposer.Mutation.addFields({
...commonFields,
[`create${entitySetName}`]: {
type: typeName,
args: {
...commonArgs,
input: {
type: entityTypeName + 'Input',
},
},
extensions: {
directives: {
createEntitySet: {
entitySetName,
},
},
},
},
[`delete${entitySetName}By${identifierFieldName}`]: {
type: 'JSON',
args: {
...commonArgs,
[identifierFieldName]: {
type: identifierFieldTypeName,
},
},
extensions: {
directives: {
deleteEntitySet: {
entitySetName,
identifierFieldName,
identifierFieldTypeRef,
},
},
},
},
[`update${entitySetName}By${identifierFieldName}`]: {
type: typeName,
args: {
...commonArgs,
[identifierFieldName]: {
type: identifierFieldTypeName,
},
input: {
type: entityTypeName + 'UpdateInput',
},
},
extensions: {
directives: {
updateEntitySet: {
entitySetName,
identifierFieldName,
identifierFieldTypeRef,
},
},
},
},
});
});
});
});
// graphql-compose doesn't add @defer and @stream to the schema
graphql_1.specifiedDirectives.forEach(directive => schemaComposer.addDirective(directive));
const schema = schemaComposer.buildSchema();
const schemaExtensions = (schema.extensions ||= {});
const directiveExtensions = (schemaExtensions.directives ||= {});
directiveExtensions.transport = {
kind: 'odata',
subgraph: name,
location: endpoint,
headers: Object.entries(schemaHeaders || []),
options: {
batch,
expandNavProps,
},
};
eventEmitterSet.forEach(ee => ee.removeAllListeners());
eventEmitterSet.clear();
return schema;
}
async function loadGraphQLSchemaFromOData(name, opts) {
const schema = await loadNonExecutableGraphQLSchemaFromOData(name, opts);
return (0, directives_js_1.processDirectives)({
schema,
fetchFn: opts.fetchFn,
});
}
function loadODataSubgraph(name, options) {
return (ctx) => ({
name,
schema$: loadNonExecutableGraphQLSchemaFromOData(name, {
...options,
importFn: utils_1.defaultImportFn,
logger: ctx.logger,
fetchFn: ctx.fetch,
baseDir: ctx.cwd,
}),
});
}