deno.land / x / obsidian@v8.0.0 / src / Obsidian.ts

نووسراو ببینە
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
import { graphql } from 'https://cdn.pika.dev/graphql@15.0.0';import { renderPlaygroundPage } from 'https://deno.land/x/oak_graphql@0.6.2/graphql-playground-html/render-playground-html.ts';import { makeExecutableSchema } from 'https://deno.land/x/oak_graphql@0.6.2/graphql-tools/schema/makeExecutableSchema.ts';import { Cache } from './quickCache.js';import queryDepthLimiter from './DoSSecurity.ts';import { restructure } from './restructure.ts';import { normalizeObject } from './normalize.ts';import { isMutation, invalidateCache } from './invalidateCacheCheck.ts';import { mapSelectionSet } from './mapSelections.js';import { HashTable } from './queryHash.js';
interface Constructable<T> { new (...args: any): T & OakRouter;}
interface OakRouter { post: any; get: any; obsidianSchema?: any;}
export interface ObsidianRouterOptions<T> { Router: Constructable<T>; path?: string; typeDefs: any; resolvers: ResolversProps; context?: (ctx: any) => any; usePlayground?: boolean; useCache?: boolean; redisPort?: number; policy?: string; maxmemory?: string; searchTerms?: string[]; persistQueries?: boolean; hashTableSize?: number; maxQueryDepth?: number; customIdentifier?: string[]; mutationTableMap?: Record<string, unknown>; // Deno recommended type name}
export interface ResolversProps { Query?: any; Mutation?: any; [dynamicProperty: string]: any;}
// Export developer chosen port for redis database connection //export let redisPortExport: number = 6379;
// tentative fix to get invalidateCacheCheck.ts access to the cache;export const scope: Record<string, unknown> = {};
/** * * @param param0 * @returns */export async function ObsidianRouter<T>({ Router, path = '/graphql', typeDefs, resolvers, context, usePlayground = false, useCache = true, // default to true redisPort = 6379, policy = 'allkeys-lru', maxmemory = '2000mb', searchTerms = [], // Developer can pass in array of search categories persistQueries = false, // default to false hashTableSize = 16, // default to 16 maxQueryDepth = 0, customIdentifier = ['__typename', '_id'], mutationTableMap = {}, // Developer passes in object where keys are add mutations and values are arrays of affected tables}: ObsidianRouterOptions<T>): Promise<T> { const router = new Router(); const schema = makeExecutableSchema({ typeDefs, resolvers });
let cache, hashTable; if (useCache) { cache = new Cache(); scope.cache = cache; cache.connect(redisPort, policy, maxmemory); } if (persistQueries) { hashTable = new HashTable(hashTableSize); }
//post await router.post(path, async (ctx: any) => {
const { response, request } = ctx; if (!request.hasBody) return;
try { let queryStr; let body = await request.body().value; if (persistQueries && body.hash && !body.query) { const { hash } = body; queryStr = hashTable.get(hash); // if not found in hash table, respond so we can send full query. if (!queryStr) { response.status = 204; return; } } else if (persistQueries && body.hash && body.query) { const { hash, query } = body; hashTable.add(hash, query); queryStr = query; } else if (persistQueries && !body.hash) { throw new Error('Unable to process request because hashed query was not provided'); } else if (!persistQueries) { queryStr = body.query; } else { throw new Error('Unable to process request because query argument not provided'); }
const contextResult = context ? await context(ctx) : undefined; // const selectedFields = mapSelectionSet(queryStr); // Gets requested fields from query and saves into an array if (maxQueryDepth) queryDepthLimiter(queryStr, maxQueryDepth); // If a securty limit is set for maxQueryDepth, invoke queryDepthLimiter, which throws error if query depth exceeds maximum let restructuredBody = { query: restructure({query: queryStr}) }; // Restructure gets rid of variables and fragments from the query
// IF WE ARE USING A CACHE if (useCache) {
let cacheQueryValue = await cache.read(queryStr); // Parses query string into query key and checks cache for that key
// ON CACHE MISS if (!cacheQueryValue) { // QUERY THE DATABASE const gqlResponse = await (graphql as any)( schema, queryStr, resolvers, contextResult, body.variables || undefined, body.operationName || undefined );
// customIdentifier is a default param for Obsidian Router - defaults to ['__typename', '_id] const normalizedGQLResponse = normalizeObject( // Recursively flattens an arbitrarily nested object into an objects with hash key and hashable object pairs gqlResponse, customIdentifier );
// If operation is mutation, invalidate relevant responses in cache if (isMutation(restructuredBody)) { invalidateCache(normalizedGQLResponse, queryStr, mutationTableMap); // ELSE, simply write to the cache } else { await cache.write(queryStr, normalizedGQLResponse, searchTerms); } // AFTER HANDLING THE CACHE, RETURN THE ORIGINAL RESPONSE response.status = 200; response.body = gqlResponse; return; // ON CACHE HIT } else { response.status = 200; response.body = cacheQueryValue; // Returns response from cache return; } // IF NOT USING A CACHE } else { // DIRECTLY QUERY THE DATABASE const gqlResponse = await (graphql as any)( schema, queryStr, resolvers, contextResult, body.variables || undefined, body.operationName || undefined );
response.status = 200; response.body = gqlResponse; // Returns response from database return; } } catch (error) { response.status = 400; response.body = { data: null, errors: [ { message: error.message ? error.message : error, }, ], }; console.error('Error: ', error.message); } });
// serve graphql playground // deno-lint-ignore require-await await router.get(path, async (ctx: any) => { const { request, response } = ctx; if (usePlayground) { const prefersHTML = request.accepts('text/html'); const optionsObj: any = { 'schema.polling.enable': false, // enables automatic schema polling };
if (prefersHTML) { const playground = renderPlaygroundPage({ endpoint: request.url.origin + path, subscriptionEndpoint: request.url.origin, settings: optionsObj, }); response.status = 200; response.body = playground; return; } } });
return router;}
obsidian

Version Info

Tagged at
a year ago