diff --git a/e2e/odata-trippin/__snapshots__/odata-trippin.test.ts.snap b/e2e/odata-trippin/__snapshots__/odata-trippin.test.ts.snap index 165cf5b43583a..164ba6ccefebb 100644 --- a/e2e/odata-trippin/__snapshots__/odata-trippin.test.ts.snap +++ b/e2e/odata-trippin/__snapshots__/odata-trippin.test.ts.snap @@ -3,7 +3,28 @@ exports[`executes a query 1`] = ` { "data": { - "Me": null, + "Me": { + "AddressInfo": [ + { + "Address": "P.O. Box 555", + "City": { + "CountryRegion": "United States", + "Name": "Lander", + "Region": "WY", + }, + }, + ], + "FavoriteFeature": "Feature1", + "FirstName": "April", + "Gender": "Female", + "LastName": "Cline", + "Trips": [ + { + "Description": "Trip in US", + }, + ], + "UserName": "aprilcline", + }, }, } `; diff --git a/examples/odata-trippin/tests/__snapshots__/odata-trippin.test.ts.snap b/examples/odata-trippin/tests/__snapshots__/odata-trippin.test.ts.snap index 388f77e2266c8..ccb39d2bb1883 100644 --- a/examples/odata-trippin/tests/__snapshots__/odata-trippin.test.ts.snap +++ b/examples/odata-trippin/tests/__snapshots__/odata-trippin.test.ts.snap @@ -4,6 +4,185 @@ exports[`OData TripPin should generate correct schema: odata-trippin-schema 1`] { "__schema": { "directives": [ + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "aliasNamespaceMap", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + }, + }, + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "entityTypeName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "isAbstract", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null, + }, + }, + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "multipleSchemas", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null, + }, + }, + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "namespaces", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "INTERFACE", + "OBJECT", + ], + "name": "abstractType", + }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "actionRef", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "FIELD_DEFINITION", + ], + "name": "boundAction", + }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "functionRef", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "FIELD_DEFINITION", + ], + "name": "boundFunction", + }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "entitySetName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "FIELD_DEFINITION", + ], + "name": "createEntitySet", + }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "entitySetName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "identifierFieldName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "identifierFieldTypeRef", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "FIELD_DEFINITION", + ], + "name": "deleteEntitySet", + }, { "args": [ { @@ -27,6 +206,161 @@ exports[`OData TripPin should generate correct schema: odata-trippin-schema 1`] ], "name": "deprecated", }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "actualFields", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + }, + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "identifierFieldName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "identifierFieldTypeRef", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "isOpenType", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null, + }, + }, + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "navigationFields", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "INTERFACE", + "OBJECT", + ], + "name": "entityInfo", + }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "entitySetName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "FIELD_DEFINITION", + ], + "name": "entitySet", + }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "entitySetName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "identifierFieldName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "identifierFieldTypeRef", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "FIELD_DEFINITION", + ], + "name": "entitySetByIdentifier", + }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "entitySetName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "FIELD_DEFINITION", + ], + "name": "entitySetCount", + }, { "args": [ { @@ -53,6 +387,26 @@ exports[`OData TripPin should generate correct schema: odata-trippin-schema 1`] ], "name": "include", }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "navigationPropertyName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "FIELD_DEFINITION", + ], + "name": "navProp", + }, { "args": [], "isRepeatable": false, @@ -61,6 +415,66 @@ exports[`OData TripPin should generate correct schema: odata-trippin-schema 1`] ], "name": "oneOf", }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "navigationPropertyName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "FIELD_DEFINITION", + ], + "name": "pluralNav", + }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "singletonName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "FIELD_DEFINITION", + ], + "name": "singleton", + }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "navigationPropertyName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "FIELD_DEFINITION", + ], + "name": "singularNav", + }, { "args": [ { @@ -111,6 +525,88 @@ exports[`OData TripPin should generate correct schema: odata-trippin-schema 1`] ], "name": "specifiedBy", }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "actionName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "FIELD_DEFINITION", + ], + "name": "unboundAction", + }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "functionName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "FIELD_DEFINITION", + ], + "name": "unboundFunction", + }, + { + "args": [ + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "entitySetName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "identifierFieldName", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + { + "defaultValue": null, + "deprecationReason": null, + "isDeprecated": false, + "name": "identifierFieldTypeRef", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + ], + "isRepeatable": false, + "locations": [ + "FIELD_DEFINITION", + ], + "name": "updateEntitySet", + }, ], "mutationType": { "name": "Mutation", diff --git a/packages/loaders/odata/src/directives.ts b/packages/loaders/odata/src/directives.ts index b4b9f8248533a..2e26a40801600 100644 --- a/packages/loaders/odata/src/directives.ts +++ b/packages/loaders/odata/src/directives.ts @@ -5,8 +5,8 @@ import { GraphQLList, GraphQLNonNull, GraphQLScalarType, - type GraphQLSchema, GraphQLString, + type GraphQLSchema, } from 'graphql'; import { process } from '@graphql-mesh/cross-helpers'; import { @@ -16,21 +16,22 @@ import { } from '@graphql-mesh/string-interpolation'; import type { MeshFetch } from '@graphql-mesh/types'; import { getDirectiveExtensions, MapperKind, mapSchema } from '@graphql-tools/utils'; -import { getDataloaderFactory } from './getDataloaderFactory'; -import { createAbstractTypeResolver } from './resolvers/abstractTypeResolver'; -import { createBoundActionResolver } from './resolvers/boundActionResolver'; -import { createBoundFunctionResolver } from './resolvers/boundFunctionResolver'; -import { createCreateEntitySetResolver } from './resolvers/createEntitySetResolver'; -import { createDeleteEntitySetByIdentifierResolver } from './resolvers/deleteEntitySetByIdentifierResolver'; -import { createEntitySetByIdentifierResolver } from './resolvers/entitySetByIdentifierResolver'; -import { createEntitySetCountResolver } from './resolvers/entitySetCountResolver'; -import { createEntitySetResolver } from './resolvers/entitySetResolver'; -import { createNavPropResolver } from './resolvers/navPropResolver'; -import { rootResolver } from './resolvers/rootResolver'; -import { createSingularNavResolver } from './resolvers/singularNavResolver'; -import { createUnboundActionResolver } from './resolvers/unboundActionResolver'; -import { createUnboundFunctionResolver } from './resolvers/unboundFunction'; -import { createUpdateEntitySetResolver } from './resolvers/updateEntitySetResolver'; +import { getDataloaderFactory } from './getDataloaderFactory.js'; +import { createAbstractTypeResolver } from './resolvers/abstractTypeResolver.js'; +import { createBoundActionResolver } from './resolvers/boundActionResolver.js'; +import { createBoundFunctionResolver } from './resolvers/boundFunctionResolver.js'; +import { createCreateEntitySetResolver } from './resolvers/createEntitySetResolver.js'; +import { createDeleteEntitySetByIdentifierResolver } from './resolvers/deleteEntitySetByIdentifierResolver.js'; +import { createEntitySetByIdentifierResolver } from './resolvers/entitySetByIdentifierResolver.js'; +import { createEntitySetCountResolver } from './resolvers/entitySetCountResolver.js'; +import { createEntitySetResolver } from './resolvers/entitySetResolver.js'; +import { createNavPropResolver } from './resolvers/navPropResolver.js'; +import { rootResolver } from './resolvers/rootResolver.js'; +import { createSingletonResolver } from './resolvers/singletonResolver.js'; +import { createSingularNavResolver } from './resolvers/singularNavResolver.js'; +import { createUnboundActionResolver } from './resolvers/unboundActionResolver.js'; +import { createUnboundFunctionResolver } from './resolvers/unboundFunction.js'; +import { createUpdateEntitySetResolver } from './resolvers/updateEntitySetResolver.js'; export const EntityInfoDirective = new GraphQLDirective({ name: 'entityInfo', @@ -502,6 +503,15 @@ export function processDirectives({ schema, fetchFn }: ProcessDirectivesArgs) { }); } else if (fieldDirectives?.resolveRoot?.length) { fieldConfig.resolve = rootResolver; + } else if (fieldDirectives?.singleton?.length) { + const singletonDirective = fieldDirectives.singleton[0]; + fieldConfig.resolve = createSingletonResolver({ + singletonName: singletonDirective.singletonName, + endpoint, + expandNavProps: transportDirective.options?.expandNavProps, + dataloaderFactory, + headersFactory, + }); } else if (fieldDirectives?.singularNav?.length) { const singularNavDirective = fieldDirectives.singularNav[0]; fieldConfig.resolve = createSingularNavResolver({ diff --git a/packages/loaders/odata/src/resolvers/abstractTypeResolver.ts b/packages/loaders/odata/src/resolvers/abstractTypeResolver.ts index cccad796eef2a..7162fa977e014 100644 --- a/packages/loaders/odata/src/resolvers/abstractTypeResolver.ts +++ b/packages/loaders/odata/src/resolvers/abstractTypeResolver.ts @@ -1,5 +1,5 @@ import { isEnumType, type GraphQLTypeResolver } from 'graphql'; -import { getTypeNameFromRef } from '../utils/getTypeNameFromRef'; +import { getTypeNameFromRef } from '../utils/getTypeNameFromRef.js'; interface AbstractTypeResolverOpts { entityTypeName: string; diff --git a/packages/loaders/odata/src/resolvers/boundActionResolver.ts b/packages/loaders/odata/src/resolvers/boundActionResolver.ts index fb75f2a4d5097..78ef6cde1ee0f 100644 --- a/packages/loaders/odata/src/resolvers/boundActionResolver.ts +++ b/packages/loaders/odata/src/resolvers/boundActionResolver.ts @@ -2,7 +2,7 @@ import type { GraphQLFieldResolver } from 'graphql'; import urljoin from 'url-join'; import { mapMaybePromise } from '@graphql-mesh/utils'; import { Request } from '@whatwg-node/fetch'; -import type { DataloaderFactory } from '../getDataloaderFactory'; +import type { DataloaderFactory } from '../getDataloaderFactory.js'; import { getUrlString } from '../utils/getUrlString.js'; import { handleResponseText } from '../utils/handleResponseText.js'; diff --git a/packages/loaders/odata/src/resolvers/boundFunctionResolver.ts b/packages/loaders/odata/src/resolvers/boundFunctionResolver.ts index 27e2ab5035aa0..4ef818bd59979 100644 --- a/packages/loaders/odata/src/resolvers/boundFunctionResolver.ts +++ b/packages/loaders/odata/src/resolvers/boundFunctionResolver.ts @@ -3,7 +3,7 @@ import { parseResolveInfo, type ResolveTree } from 'graphql-parse-resolve-info'; import urljoin from 'url-join'; import { mapMaybePromise } from '@graphql-mesh/utils'; import { Request } from '@whatwg-node/fetch'; -import type { DataloaderFactory } from '../getDataloaderFactory'; +import type { DataloaderFactory } from '../getDataloaderFactory.js'; import { getUrlString } from '../utils/getUrlString.js'; import { handleResponseText } from '../utils/handleResponseText.js'; import { prepareSearchParams } from '../utils/prepareSearchParams.js';