@graphql-box/connection-resolver 5.4.2 → 5.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@graphql-box/connection-resolver",
3
3
  "description": "The GraphQL Box connection resolver module.",
4
- "version": "5.4.2",
4
+ "version": "5.4.4",
5
5
  "author": "Dylan Aubrey",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/badbatch/graphql-box",
@@ -41,14 +41,16 @@
41
41
  "@graphql-box/core": "5.4.2"
42
42
  },
43
43
  "peerDependencies": {
44
- "graphql": "<17"
44
+ "graphql": "<17",
45
+ "winston": "<4"
45
46
  },
46
47
  "devDependencies": {
47
48
  "@cachemap/map": "^5.0.8",
48
49
  "@jest/globals": "^29.3.1",
49
50
  "cts-types": "^0.0.8",
50
51
  "del-cli": "^6.0.0",
51
- "graphql": "^16.9.0"
52
+ "graphql": "^16.9.0",
53
+ "winston": "^3.17.0"
52
54
  },
53
55
  "keywords": [
54
56
  "client",
@@ -1,20 +1,30 @@
1
1
  import { type Core } from '@cachemap/core';
2
2
  import { type PlainObject } from '@graphql-box/core';
3
- import { type Getters, type Node, type ResourceResolver } from '../types.ts';
3
+ import { type Getters, type Node, type ResourceResolver, type SetCacheMetadata } from '../types.ts';
4
4
  import { cacheCursors } from './cacheCursors.ts';
5
5
  import { makeEdges } from './makeEdges.ts';
6
6
 
7
7
  export type Context<Resource extends PlainObject, ResourceNode extends Node> = {
8
8
  cursorCache: Core;
9
+ fieldPath: string;
9
10
  getters: Getters<Resource, ResourceNode>;
10
11
  groupCursor: string;
11
12
  makeIDCursor: (id: string | number) => string;
12
13
  resourceResolver: ResourceResolver<Resource>;
14
+ setCacheMetadata: SetCacheMetadata | undefined;
13
15
  };
14
16
 
15
17
  export const requestAndCachePages = async <Resource extends PlainObject, ResourceNode extends Node>(
16
18
  pages: number[],
17
- { cursorCache, getters, groupCursor, makeIDCursor, resourceResolver }: Context<Resource, ResourceNode>,
19
+ {
20
+ cursorCache,
21
+ fieldPath,
22
+ getters,
23
+ groupCursor,
24
+ makeIDCursor,
25
+ resourceResolver,
26
+ setCacheMetadata,
27
+ }: Context<Resource, ResourceNode>,
18
28
  ) => {
19
29
  const errors: Error[] = [];
20
30
 
@@ -28,6 +38,10 @@ export const requestAndCachePages = async <Resource extends PlainObject, Resourc
28
38
  page,
29
39
  });
30
40
 
41
+ if (pageResultData) {
42
+ setCacheMetadata?.(fieldPath, pageResultHeaders);
43
+ }
44
+
31
45
  if (pageResultData && !pageResultErrors?.length) {
32
46
  const edges = makeEdges(getters.nodes(pageResultData), node => makeIDCursor(node.id));
33
47
 
@@ -1,6 +1,13 @@
1
1
  import { type Core } from '@cachemap/core';
2
2
  import { type PlainObject } from '@graphql-box/core';
3
- import { type ConnectionInputOptions, type Getters, type Node, type ResourceResolver } from '../types.ts';
3
+ import { type Logger } from 'winston';
4
+ import {
5
+ type ConnectionInputOptions,
6
+ type Getters,
7
+ type Node,
8
+ type ResourceResolver,
9
+ type SetCacheMetadata,
10
+ } from '../types.ts';
4
11
  import { extractEdges } from './extractEdges.ts';
5
12
  import { extractNodes } from './extractNodes.ts';
6
13
  import { getInRangeCachedEdges } from './getInRangeCachedEdges.ts';
@@ -11,22 +18,28 @@ import { retrieveCachedConnection } from './retrieveCachedConnection.ts';
11
18
 
12
19
  export type Context<Resource extends PlainObject, ResourceNode extends Node> = {
13
20
  cursorCache: Core;
21
+ fieldPath: string;
14
22
  getters: Getters<Resource, ResourceNode>;
15
23
  groupCursor: string;
24
+ logger: Logger | undefined;
16
25
  makeIDCursor: (id: string | number) => string;
17
26
  resourceResolver: ResourceResolver<Resource>;
18
27
  resultsPerPage: number;
28
+ setCacheMetadata: SetCacheMetadata | undefined;
19
29
  };
20
30
 
21
31
  export const resolveConnection = async <Resource extends PlainObject, ResourceNode extends Node>(
22
32
  args: PlainObject & ConnectionInputOptions,
23
33
  {
24
34
  cursorCache,
35
+ fieldPath,
25
36
  getters,
26
37
  groupCursor,
38
+ logger,
27
39
  makeIDCursor,
28
40
  resourceResolver,
29
41
  resultsPerPage,
42
+ setCacheMetadata,
30
43
  }: Context<Resource, ResourceNode>,
31
44
  ) => {
32
45
  const { cachedEdges, hasNextPage, hasPreviousPage, indexes, missingPages, totalResults } =
@@ -38,6 +51,7 @@ export const resolveConnection = async <Resource extends PlainObject, ResourceNo
38
51
 
39
52
  if (missingPages.length === 0) {
40
53
  const edges = extractEdges(cachedEdges);
54
+ logger?.info(`Successfully resolved ${fieldPath} from cache`);
41
55
 
42
56
  return {
43
57
  edges,
@@ -55,10 +69,12 @@ export const resolveConnection = async <Resource extends PlainObject, ResourceNo
55
69
 
56
70
  const { cachedEdges: missingCachedEdges, errors } = await requestAndCachePages<Resource, ResourceNode>(missingPages, {
57
71
  cursorCache,
72
+ fieldPath,
58
73
  getters,
59
74
  groupCursor,
60
75
  makeIDCursor,
61
76
  resourceResolver,
77
+ setCacheMetadata,
62
78
  });
63
79
 
64
80
  const mergedCachedEdges = getInRangeCachedEdges(mergeCachedEdges(cachedEdges, missingCachedEdges), {
@@ -68,6 +84,7 @@ export const resolveConnection = async <Resource extends PlainObject, ResourceNo
68
84
  });
69
85
 
70
86
  const edges = extractEdges(mergedCachedEdges);
87
+ logger?.info(`Successfully resolved ${fieldPath} from api`);
71
88
 
72
89
  return {
73
90
  edges,
package/src/main/index.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  type Connection,
9
9
  type ConnectionInputOptions,
10
10
  type ConnectionResolverUserOptions,
11
+ type Context,
11
12
  type Node,
12
13
  } from '../types.ts';
13
14
 
@@ -15,7 +16,7 @@ export const makeConnectionResolver =
15
16
  <
16
17
  Source extends PlainObject | undefined,
17
18
  Args extends PlainObject,
18
- Ctx extends PlainObject,
19
+ Ctx extends Context,
19
20
  Resource extends PlainObject,
20
21
  ResourceNode extends Node,
21
22
  >({
@@ -35,6 +36,19 @@ export const makeConnectionResolver =
35
36
  const { makeGroupCursor, makeIDCursor } = createMakeCursors(source, args, context, info);
36
37
  const resourceResolver = createResourceResolver(source, args, context, info);
37
38
  const groupCursor = makeGroupCursor();
39
+ const { logger, operationName, requestID, setCacheMetadata, tmdbGuestSessionId } = context;
40
+ const { fieldName: fieldPath } = info;
41
+
42
+ const childLogger = logger?.child({
43
+ args,
44
+ fieldPath,
45
+ groupCursor,
46
+ operationName,
47
+ requestID,
48
+ tmdbGuestSessionId,
49
+ });
50
+
51
+ childLogger?.info(`Resolving ${fieldPath}`);
38
52
 
39
53
  if (isCursorSupplied(args)) {
40
54
  const cursorError = await validateCursor(args, info, {
@@ -44,6 +58,10 @@ export const makeConnectionResolver =
44
58
  });
45
59
 
46
60
  if (cursorError) {
61
+ childLogger?.error(`Failed to resolve ${fieldPath}, validation cursor error`, {
62
+ errors: [cursorError],
63
+ });
64
+
47
65
  return resolver({
48
66
  edges: [],
49
67
  errors: [cursorError],
@@ -59,11 +77,14 @@ export const makeConnectionResolver =
59
77
  return resolver(
60
78
  await resolveConnection(args, {
61
79
  cursorCache,
80
+ fieldPath,
62
81
  getters,
63
82
  groupCursor,
83
+ logger: childLogger,
64
84
  makeIDCursor,
65
85
  resourceResolver,
66
86
  resultsPerPage,
87
+ setCacheMetadata,
67
88
  }),
68
89
  );
69
90
  }
@@ -72,31 +93,39 @@ export const makeConnectionResolver =
72
93
  return resolver(
73
94
  await resolveConnection(args, {
74
95
  cursorCache,
96
+ fieldPath,
75
97
  getters,
76
98
  groupCursor,
99
+ logger: childLogger,
77
100
  makeIDCursor,
78
101
  resourceResolver,
79
102
  resultsPerPage,
103
+ setCacheMetadata,
80
104
  }),
81
105
  );
82
106
  }
83
107
 
84
108
  await requestAndCachePages<Resource, ResourceNode>([1], {
85
109
  cursorCache,
110
+ fieldPath,
86
111
  getters,
87
112
  groupCursor,
88
113
  makeIDCursor,
89
114
  resourceResolver,
115
+ setCacheMetadata,
90
116
  });
91
117
 
92
118
  return resolver(
93
119
  await resolveConnection(args, {
94
120
  cursorCache,
121
+ fieldPath,
95
122
  getters,
96
123
  groupCursor,
124
+ logger: childLogger,
97
125
  makeIDCursor,
98
126
  resourceResolver,
99
127
  resultsPerPage,
128
+ setCacheMetadata,
100
129
  }),
101
130
  );
102
131
  };
package/src/types.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { type Core } from '@cachemap/core';
2
2
  import { type PlainObject } from '@graphql-box/core';
3
- import { type GraphQLResolveInfo } from 'graphql';
3
+ import { type GraphQLResolveInfo, type OperationTypeNode } from 'graphql';
4
4
  import { type SetOptional } from 'type-fest';
5
+ import { type Logger } from 'winston';
5
6
 
6
7
  export type Direction = 'backward' | 'forward';
7
8
 
@@ -60,10 +61,17 @@ export type ResourceResolver<Resource extends PlainObject> = (args: {
60
61
  page: number;
61
62
  }) => Promise<ResourceResponse<Resource>>;
62
63
 
64
+ export type SetCacheMetadata = (key: string, headers: Headers, operation?: OperationTypeNode) => void;
65
+
66
+ export type Context = PlainObject & {
67
+ logger?: Logger;
68
+ setCacheMetadata?: SetCacheMetadata;
69
+ };
70
+
63
71
  export type CreateResourceResolver<
64
72
  Source extends PlainObject | undefined,
65
73
  Args extends PlainObject,
66
- Ctx extends PlainObject,
74
+ Ctx extends Context,
67
75
  Resource extends PlainObject,
68
76
  > = (source: Source, args: Args, context: Ctx, info: GraphQLResolveInfo) => ResourceResolver<Resource>;
69
77
 
@@ -77,7 +85,7 @@ export interface Getters<Resource extends PlainObject, ResourceNode extends Node
77
85
  export interface ConnectionResolverUserOptions<
78
86
  Source extends PlainObject | undefined,
79
87
  Args extends PlainObject,
80
- Ctx extends PlainObject,
88
+ Ctx extends Context,
81
89
  Resource extends PlainObject,
82
90
  ResourceNode extends Node,
83
91
  > {
@@ -100,7 +108,7 @@ export interface ConnectionResolverUserOptions<
100
108
  export type ConnectionResolver = (
101
109
  source: PlainObject,
102
110
  args: PlainObject & ConnectionInputOptions,
103
- context: PlainObject,
111
+ context: Context,
104
112
  info: GraphQLResolveInfo,
105
113
  ) => Promise<Connection>;
106
114