@aws-amplify/data-schema 1.5.0 → 1.6.0
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/dist/cjs/CustomOperation.js +21 -0
- package/dist/cjs/CustomOperation.js.map +1 -1
- package/dist/cjs/Handler.js +20 -1
- package/dist/cjs/Handler.js.map +1 -1
- package/dist/cjs/SchemaProcessor.js +54 -11
- package/dist/cjs/SchemaProcessor.js.map +1 -1
- package/dist/cjs/runtime/internals/operations/subscription.js +11 -3
- package/dist/cjs/runtime/internals/operations/subscription.js.map +1 -1
- package/dist/esm/ClientSchema/Core/ClientCustomOperations.d.ts +2 -2
- package/dist/esm/CustomOperation.d.ts +16 -4
- package/dist/esm/CustomOperation.mjs +21 -1
- package/dist/esm/CustomOperation.mjs.map +1 -1
- package/dist/esm/Handler.d.ts +57 -9
- package/dist/esm/Handler.mjs +20 -1
- package/dist/esm/Handler.mjs.map +1 -1
- package/dist/esm/SchemaProcessor.mjs +54 -12
- package/dist/esm/SchemaProcessor.mjs.map +1 -1
- package/dist/esm/runtime/internals/operations/subscription.mjs +12 -4
- package/dist/esm/runtime/internals/operations/subscription.mjs.map +1 -1
- package/dist/meta/cjs.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/ClientSchema/Core/ClientCustomOperations.ts +32 -3
- package/src/CustomOperation.ts +68 -5
- package/src/Handler.ts +92 -21
- package/src/SchemaProcessor.ts +73 -23
- package/src/runtime/internals/operations/subscription.ts +18 -10
package/package.json
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
CustomOperationParamShape,
|
|
3
|
+
UltimateFunctionHandlerAsyncType,
|
|
4
|
+
} from '../../CustomOperation';
|
|
5
|
+
import type { BaseModelField, ModelFieldType } from '../../ModelField';
|
|
3
6
|
import type { RefType } from '../../RefType';
|
|
4
7
|
import type { ResolveFieldRequirements } from '../../MappedTypes/ResolveFieldProperties';
|
|
5
8
|
import type { AppSyncResolverHandler } from 'aws-lambda';
|
|
@@ -36,7 +39,13 @@ export interface ClientCustomOperation<
|
|
|
36
39
|
*/
|
|
37
40
|
functionHandler: AppSyncResolverHandler<
|
|
38
41
|
CustomOpArguments<Op>,
|
|
39
|
-
|
|
42
|
+
// If the final handler is an async function, the Schema['fieldname']['functionhandler']
|
|
43
|
+
// should have a return type of `void`. This only applies to `functionHandler` and not
|
|
44
|
+
// `returnType` because `returnType` determines the type returned by the mutation / query
|
|
45
|
+
// in the data client.
|
|
46
|
+
Op['handlers'] extends UltimateFunctionHandlerAsyncType
|
|
47
|
+
? void
|
|
48
|
+
: LambdaReturnType<CustomOpReturnType<Op, RefBag>>
|
|
40
49
|
>;
|
|
41
50
|
|
|
42
51
|
/**
|
|
@@ -100,6 +109,26 @@ type Normalize<
|
|
|
100
109
|
? Exclude<RT, null | undefined>
|
|
101
110
|
: RT;
|
|
102
111
|
|
|
112
|
+
type _EventInvocationResponseBag = {
|
|
113
|
+
EventInvocationResponse: {
|
|
114
|
+
__entityType: 'customType';
|
|
115
|
+
type: '';
|
|
116
|
+
data: {
|
|
117
|
+
fields: {
|
|
118
|
+
success: {
|
|
119
|
+
data: {
|
|
120
|
+
fieldType: ModelFieldType.Boolean;
|
|
121
|
+
required: true;
|
|
122
|
+
array: false;
|
|
123
|
+
arrayRequired: false;
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
type: 'customType';
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
|
|
103
132
|
/**
|
|
104
133
|
* Computes the return type from the `returnType` of a custom operation shape.
|
|
105
134
|
*
|
package/src/CustomOperation.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SetTypeSubArg } from '@aws-amplify/data-schema-types';
|
|
2
2
|
import { Brand, brand } from './util';
|
|
3
|
-
import { InternalField, type BaseModelField } from './ModelField';
|
|
3
|
+
import { InternalField, ModelField, type BaseModelField } from './ModelField';
|
|
4
4
|
import {
|
|
5
5
|
AllowModifierForCustomOperation,
|
|
6
6
|
Authorization,
|
|
@@ -10,10 +10,12 @@ import { RefType, InternalRef } from './RefType';
|
|
|
10
10
|
import { EnumType } from './EnumType';
|
|
11
11
|
import { CustomType } from './CustomType';
|
|
12
12
|
import type {
|
|
13
|
+
AsyncFunctionHandler,
|
|
13
14
|
CustomHandler,
|
|
14
15
|
FunctionHandler,
|
|
15
16
|
HandlerType as Handler,
|
|
16
17
|
} from './Handler';
|
|
18
|
+
import { brandSymbol } from './util/Brand';
|
|
17
19
|
import { AiModel, InferenceConfiguration } from './ai/ModelType';
|
|
18
20
|
|
|
19
21
|
const queryBrand = 'queryCustomOperation';
|
|
@@ -28,14 +30,31 @@ type CustomOperationBrand =
|
|
|
28
30
|
| typeof generationBrand;
|
|
29
31
|
|
|
30
32
|
type CustomArguments = Record<string, BaseModelField | EnumType>;
|
|
31
|
-
|
|
32
33
|
type SubscriptionSource = RefType<any, any>;
|
|
33
34
|
type InternalSubscriptionSource = InternalRef;
|
|
34
|
-
|
|
35
35
|
type CustomReturnType = RefType<any> | CustomType<any>;
|
|
36
36
|
type InternalCustomArguments = Record<string, InternalField>;
|
|
37
37
|
type InternalCustomReturnType = InternalRef;
|
|
38
|
-
type HandlerInputType =
|
|
38
|
+
type HandlerInputType =
|
|
39
|
+
| FunctionHandler[]
|
|
40
|
+
| CustomHandler[]
|
|
41
|
+
| AsyncFunctionHandler[]
|
|
42
|
+
| HeterogeneousFunctionHandlerWithLastAsync
|
|
43
|
+
| HeterogeneousFunctionHandlerType
|
|
44
|
+
| Handler;
|
|
45
|
+
type HeterogeneousFunctionHandlerType = (
|
|
46
|
+
| FunctionHandler
|
|
47
|
+
| AsyncFunctionHandler
|
|
48
|
+
)[];
|
|
49
|
+
type HeterogeneousFunctionHandlerWithLastAsync = [
|
|
50
|
+
...HeterogeneousFunctionHandlerType,
|
|
51
|
+
AsyncFunctionHandler,
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
export type UltimateFunctionHandlerAsyncType =
|
|
55
|
+
| AsyncFunctionHandler
|
|
56
|
+
| AsyncFunctionHandler[]
|
|
57
|
+
| HeterogeneousFunctionHandlerWithLastAsync;
|
|
39
58
|
|
|
40
59
|
export const CustomOperationNames = [
|
|
41
60
|
'Query',
|
|
@@ -104,7 +123,14 @@ export type CustomOperation<
|
|
|
104
123
|
>;
|
|
105
124
|
handler<H extends HandlerInputType>(
|
|
106
125
|
handlers: H,
|
|
107
|
-
):
|
|
126
|
+
): [H] extends [UltimateFunctionHandlerAsyncType]
|
|
127
|
+
? CustomOperation<
|
|
128
|
+
AsyncFunctionCustomOperation<T>,
|
|
129
|
+
K | 'handler' | 'returns',
|
|
130
|
+
B
|
|
131
|
+
>
|
|
132
|
+
: CustomOperation<T, K | 'handler', B>;
|
|
133
|
+
|
|
108
134
|
for<Source extends SubscriptionSource>(
|
|
109
135
|
source: Source | Source[],
|
|
110
136
|
): CustomOperation<
|
|
@@ -183,6 +209,10 @@ function _custom<
|
|
|
183
209
|
? handlers
|
|
184
210
|
: ([handlers] as Handler[]);
|
|
185
211
|
|
|
212
|
+
if (lastHandlerIsAsyncFunction(handlers)) {
|
|
213
|
+
data.returnType = eventInvocationResponse;
|
|
214
|
+
}
|
|
215
|
+
|
|
186
216
|
return this;
|
|
187
217
|
},
|
|
188
218
|
for(source: SubscriptionSource | SubscriptionSource[]) {
|
|
@@ -307,6 +337,39 @@ export function subscription(): CustomOperation<
|
|
|
307
337
|
return _custom('Subscription', subscriptionBrand);
|
|
308
338
|
}
|
|
309
339
|
|
|
340
|
+
// #region async Lambda function related types
|
|
341
|
+
type AsyncFunctionCustomOperation<T extends CustomOperationParamShape> =
|
|
342
|
+
SetTypeSubArg<
|
|
343
|
+
SetTypeSubArg<T, 'returnType', EventInvocationResponseCustomType>,
|
|
344
|
+
'handlers',
|
|
345
|
+
AsyncFunctionHandler
|
|
346
|
+
>;
|
|
347
|
+
|
|
348
|
+
type EventInvocationResponseCustomType = CustomType<{
|
|
349
|
+
fields: {
|
|
350
|
+
success: ModelField<boolean, 'required', undefined>;
|
|
351
|
+
};
|
|
352
|
+
}>;
|
|
353
|
+
|
|
354
|
+
const eventInvocationResponse = {
|
|
355
|
+
data: {
|
|
356
|
+
type: 'ref',
|
|
357
|
+
link: 'EventInvocationResponse',
|
|
358
|
+
valueRequired: false,
|
|
359
|
+
array: false,
|
|
360
|
+
arrayRequired: false,
|
|
361
|
+
mutationOperations: [],
|
|
362
|
+
authorization: [],
|
|
363
|
+
},
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
function lastHandlerIsAsyncFunction(handlers: HandlerInputType): boolean {
|
|
367
|
+
const lastHandlerBrandSymbol = Array.isArray(handlers)
|
|
368
|
+
? handlers[handlers.length - 1][brandSymbol]
|
|
369
|
+
: handlers[brandSymbol];
|
|
370
|
+
return lastHandlerBrandSymbol === 'asyncFunctionHandler';
|
|
371
|
+
}
|
|
372
|
+
// #endregion async Lambda function related types
|
|
310
373
|
export interface GenerationInput {
|
|
311
374
|
aiModel: AiModel;
|
|
312
375
|
systemPrompt: string;
|
package/src/Handler.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { DefineFunction } from '@aws-amplify/data-schema-types';
|
|
2
2
|
import { Brand, brand } from './util';
|
|
3
|
+
import type { brandSymbol } from './util/Brand.js';
|
|
3
4
|
import { RefType } from './RefType';
|
|
4
5
|
|
|
5
6
|
export type HandlerType =
|
|
6
7
|
| InlineSqlHandler
|
|
7
8
|
| SqlReferenceHandler
|
|
8
9
|
| CustomHandler
|
|
9
|
-
| FunctionHandler
|
|
10
|
+
| FunctionHandler
|
|
11
|
+
| AsyncFunctionHandler;
|
|
10
12
|
|
|
11
13
|
const dataSymbol = Symbol('Data');
|
|
12
14
|
|
|
@@ -18,7 +20,8 @@ type AllHandlers =
|
|
|
18
20
|
| InlineSqlHandler
|
|
19
21
|
| SqlReferenceHandler
|
|
20
22
|
| CustomHandler
|
|
21
|
-
| FunctionHandler
|
|
23
|
+
| FunctionHandler
|
|
24
|
+
| AsyncFunctionHandler;
|
|
22
25
|
|
|
23
26
|
export function getHandlerData<H extends AllHandlers>(
|
|
24
27
|
handler: H,
|
|
@@ -30,9 +33,10 @@ export function getHandlerData<H extends AllHandlers>(
|
|
|
30
33
|
|
|
31
34
|
const inlineSqlBrand = 'inlineSql';
|
|
32
35
|
|
|
33
|
-
export type InlineSqlHandler = {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
export type InlineSqlHandler = {
|
|
37
|
+
[dataSymbol]: string;
|
|
38
|
+
[brandSymbol]: typeof inlineSqlBrand;
|
|
39
|
+
};
|
|
36
40
|
|
|
37
41
|
function inlineSql(sql: string): InlineSqlHandler {
|
|
38
42
|
return { [dataSymbol]: sql, ...buildHandler(inlineSqlBrand) };
|
|
@@ -51,7 +55,8 @@ export type SqlReferenceHandlerData = {
|
|
|
51
55
|
|
|
52
56
|
export type SqlReferenceHandler = {
|
|
53
57
|
[dataSymbol]: SqlReferenceHandlerData;
|
|
54
|
-
|
|
58
|
+
[brandSymbol]: typeof sqlReferenceBrand;
|
|
59
|
+
};
|
|
55
60
|
|
|
56
61
|
function sqlReference(sqlFilePath: string): SqlReferenceHandler {
|
|
57
62
|
// used to determine caller directory in order to resolve relative path downstream
|
|
@@ -88,12 +93,13 @@ export type CustomHandlerData = CustomHandlerInput & {
|
|
|
88
93
|
|
|
89
94
|
const customHandlerBrand = 'customHandler';
|
|
90
95
|
|
|
91
|
-
export type CustomHandler = {
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
export type CustomHandler = {
|
|
97
|
+
[dataSymbol]: CustomHandlerData;
|
|
98
|
+
[brandSymbol]: typeof customHandlerBrand;
|
|
99
|
+
};
|
|
94
100
|
|
|
95
101
|
/**
|
|
96
|
-
* Use a custom JavaScript resolver to handle a query, mutation, or subscription.
|
|
102
|
+
* Use a custom JavaScript resolver to handle a query, mutation, or subscription.
|
|
97
103
|
* @see {@link https://docs.amplify.aws/react/build-a-backend/data/custom-business-logic/#step-2---configure-custom-business-logic-handler-code}
|
|
98
104
|
* @param customHandler `{ entry: "path-to-javascript-resolver-file.js", dataSource: "Data Source name added via "backend.data.add*DataSoruce(...)"}`
|
|
99
105
|
* @returns A JavaScript resolver attached to the query, mutation, or subscription.
|
|
@@ -107,7 +113,7 @@ export type CustomHandler = { [dataSymbol]: CustomHandlerData } & Brand<
|
|
|
107
113
|
* allow.owner(),
|
|
108
114
|
* allow.authenticated().to(['read'])
|
|
109
115
|
* ]),
|
|
110
|
-
*
|
|
116
|
+
*
|
|
111
117
|
* likePost: a
|
|
112
118
|
* .mutation()
|
|
113
119
|
* .arguments({ postId: a.id() })
|
|
@@ -131,17 +137,63 @@ function custom(customHandler: CustomHandlerInput): CustomHandler {
|
|
|
131
137
|
//#endregion
|
|
132
138
|
|
|
133
139
|
//#region handler.function
|
|
134
|
-
|
|
135
|
-
export type FunctionHandlerData =
|
|
140
|
+
export type FunctionHandlerInput = DefineFunction | string;
|
|
141
|
+
export type FunctionHandlerData = {
|
|
142
|
+
handler: FunctionHandlerInput;
|
|
143
|
+
invocationType: 'RequestResponse' | 'Event';
|
|
144
|
+
};
|
|
136
145
|
|
|
137
146
|
const functionHandlerBrand = 'functionHandler';
|
|
138
147
|
|
|
139
148
|
export type FunctionHandler = {
|
|
140
149
|
[dataSymbol]: FunctionHandlerData;
|
|
141
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Use `async()` to have this function handler invoked asynchronously.
|
|
152
|
+
* If an `async()` function is only handler or the final handler in a pipeline, the return type of this
|
|
153
|
+
* custom query / mutation is `{ success: boolean }`.
|
|
154
|
+
* @example
|
|
155
|
+
* import {
|
|
156
|
+
* type ClientSchema,
|
|
157
|
+
* a,
|
|
158
|
+
* defineData,
|
|
159
|
+
* defineFunction // 1.Import "defineFunction" to create new functions
|
|
160
|
+
* } from '@aws-amplify/backend';
|
|
161
|
+
*
|
|
162
|
+
* // 2. define a function
|
|
163
|
+
* const echoHandler = defineFunction({
|
|
164
|
+
* entry: './echo-handler/handler.ts'
|
|
165
|
+
* })
|
|
166
|
+
*
|
|
167
|
+
* const schema = a.schema({
|
|
168
|
+
* EchoResponse: a.customType({
|
|
169
|
+
* content: a.string(),
|
|
170
|
+
* executionDuration: a.float()
|
|
171
|
+
* }),
|
|
172
|
+
*
|
|
173
|
+
* echo: a
|
|
174
|
+
* .query()
|
|
175
|
+
* .arguments({ content: a.string() })
|
|
176
|
+
* .returns(a.ref('EchoResponse'))
|
|
177
|
+
* .authorization(allow => [allow.publicApiKey()])
|
|
178
|
+
* // 3. set the function has the handler
|
|
179
|
+
* .handler(a.handler.function(echoHandler).async())
|
|
180
|
+
*
|
|
181
|
+
* @see {@link https://docs.amplify.aws/react/build-a-backend/data/INSERT_PATH_WHEN_READY}
|
|
182
|
+
* @returns A function handler for query / mutation that is asynchronously invoked.
|
|
183
|
+
*/
|
|
184
|
+
async(): AsyncFunctionHandler;
|
|
185
|
+
[brandSymbol]: typeof functionHandlerBrand;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const asyncFunctionHandlerBrand = 'asyncFunctionHandler';
|
|
189
|
+
|
|
190
|
+
export type AsyncFunctionHandler = {
|
|
191
|
+
[dataSymbol]: FunctionHandlerData;
|
|
192
|
+
[brandSymbol]: typeof asyncFunctionHandlerBrand;
|
|
193
|
+
};
|
|
142
194
|
|
|
143
195
|
/**
|
|
144
|
-
* Use a function created via `defineFunction` to handle the custom query/mutation/subscription. In your function handler,
|
|
196
|
+
* Use a function created via `defineFunction` to handle the custom query/mutation/subscription. In your function handler,
|
|
145
197
|
* you can use the `Schema["YOUR_QUERY_OR_MUTATION_NAME"]["functionHandler"]` utility type to type the handler function.
|
|
146
198
|
* @example
|
|
147
199
|
* import {
|
|
@@ -150,18 +202,18 @@ export type FunctionHandler = {
|
|
|
150
202
|
* defineData,
|
|
151
203
|
* defineFunction // 1.Import "defineFunction" to create new functions
|
|
152
204
|
* } from '@aws-amplify/backend';
|
|
153
|
-
*
|
|
205
|
+
*
|
|
154
206
|
* // 2. define a function
|
|
155
207
|
* const echoHandler = defineFunction({
|
|
156
208
|
* entry: './echo-handler/handler.ts'
|
|
157
209
|
* })
|
|
158
|
-
*
|
|
210
|
+
*
|
|
159
211
|
* const schema = a.schema({
|
|
160
212
|
* EchoResponse: a.customType({
|
|
161
213
|
* content: a.string(),
|
|
162
214
|
* executionDuration: a.float()
|
|
163
215
|
* }),
|
|
164
|
-
*
|
|
216
|
+
*
|
|
165
217
|
* echo: a
|
|
166
218
|
* .query()
|
|
167
219
|
* .arguments({ content: a.string() })
|
|
@@ -171,12 +223,31 @@ export type FunctionHandler = {
|
|
|
171
223
|
* .handler(a.handler.function(echoHandler))
|
|
172
224
|
* });
|
|
173
225
|
* @see {@link https://docs.amplify.aws/react/build-a-backend/data/custom-business-logic/}
|
|
174
|
-
* @param fn A function created via `defineFunction`. Alternatively, you can pass in a "string" of the function name and pass
|
|
226
|
+
* @param fn A function created via `defineFunction`. Alternatively, you can pass in a "string" of the function name and pass
|
|
175
227
|
* in a corresponding value into the `functionMap` property of defineData.
|
|
176
228
|
* @returns A handler for the query / mutation / subscription
|
|
177
229
|
*/
|
|
178
|
-
function fcn(fn:
|
|
179
|
-
return {
|
|
230
|
+
function fcn(fn: FunctionHandlerInput): FunctionHandler {
|
|
231
|
+
return {
|
|
232
|
+
[dataSymbol]: {
|
|
233
|
+
handler: fn,
|
|
234
|
+
invocationType: 'RequestResponse',
|
|
235
|
+
},
|
|
236
|
+
async() {
|
|
237
|
+
return _async(this);
|
|
238
|
+
},
|
|
239
|
+
...buildHandler(functionHandlerBrand),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function _async(fnHandler: FunctionHandler): AsyncFunctionHandler {
|
|
244
|
+
return {
|
|
245
|
+
[dataSymbol]: {
|
|
246
|
+
handler: fnHandler[dataSymbol].handler,
|
|
247
|
+
invocationType: 'Event',
|
|
248
|
+
},
|
|
249
|
+
...buildHandler(asyncFunctionHandlerBrand),
|
|
250
|
+
};
|
|
180
251
|
}
|
|
181
252
|
|
|
182
253
|
//#endregion
|
package/src/SchemaProcessor.ts
CHANGED
|
@@ -42,10 +42,12 @@ import {
|
|
|
42
42
|
type HandlerType,
|
|
43
43
|
type CustomHandler,
|
|
44
44
|
type SqlReferenceHandler,
|
|
45
|
-
|
|
45
|
+
FunctionHandler,
|
|
46
|
+
AsyncFunctionHandler
|
|
46
47
|
} from './Handler';
|
|
47
48
|
import * as os from 'os';
|
|
48
49
|
import * as path from 'path';
|
|
50
|
+
import { brandSymbol } from './util/Brand';
|
|
49
51
|
import {
|
|
50
52
|
brandName as conversationBrandName,
|
|
51
53
|
type InternalConversationType,
|
|
@@ -279,7 +281,7 @@ function enumFieldToGql(enumName: string, secondaryIndexes: string[] = []) {
|
|
|
279
281
|
}
|
|
280
282
|
|
|
281
283
|
function transformFunctionHandler(
|
|
282
|
-
handlers: FunctionHandler[],
|
|
284
|
+
handlers: (FunctionHandler | AsyncFunctionHandler)[],
|
|
283
285
|
functionFieldName: string,
|
|
284
286
|
): {
|
|
285
287
|
gqlHandlerContent: string;
|
|
@@ -291,21 +293,25 @@ function transformFunctionHandler(
|
|
|
291
293
|
handlers.forEach((handler, idx) => {
|
|
292
294
|
const handlerData = getHandlerData(handler);
|
|
293
295
|
|
|
294
|
-
if (typeof handlerData === 'string') {
|
|
295
|
-
gqlHandlerContent += `@function(name: "${handlerData}") `;
|
|
296
|
-
} else if (typeof handlerData.getInstance === 'function') {
|
|
296
|
+
if (typeof handlerData.handler === 'string') {
|
|
297
|
+
gqlHandlerContent += `@function(name: "${handlerData.handler}") `;
|
|
298
|
+
} else if (typeof handlerData.handler.getInstance === 'function') {
|
|
297
299
|
const fnName = `Fn${capitalize(functionFieldName)}${idx === 0 ? '' : `${idx + 1}`}`;
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
gqlHandlerContent += `@function(name: "${fnName}"
|
|
301
|
-
}
|
|
300
|
+
lambdaFunctionDefinition[fnName] = handlerData.handler;
|
|
301
|
+
const invocationTypeArg = handlerData.invocationType === 'Event' ? ', invocationType: Event)' : ')'
|
|
302
|
+
gqlHandlerContent += `@function(name: "${fnName}"${invocationTypeArg} `;
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
302
305
|
throw new Error(
|
|
303
306
|
`Invalid value specified for ${functionFieldName} handler.function(). Expected: defineFunction or string.`,
|
|
304
307
|
);
|
|
305
308
|
}
|
|
306
309
|
});
|
|
307
310
|
|
|
308
|
-
return {
|
|
311
|
+
return {
|
|
312
|
+
gqlHandlerContent,
|
|
313
|
+
lambdaFunctionDefinition,
|
|
314
|
+
};
|
|
309
315
|
}
|
|
310
316
|
|
|
311
317
|
type CustomTypeAuthRules =
|
|
@@ -1275,6 +1281,20 @@ const schemaPreprocessor = (
|
|
|
1275
1281
|
|
|
1276
1282
|
const staticSchema = databaseType === 'sql';
|
|
1277
1283
|
|
|
1284
|
+
// If the schema contains a custom operation with an async lambda handler,
|
|
1285
|
+
// we need to add the EventInvocationResponse custom type to the schema.
|
|
1286
|
+
// This is done here so that:
|
|
1287
|
+
// - it only happens once per schema
|
|
1288
|
+
// - downstream validation based on `getRefTypeForSchema` finds the EventInvocationResponse type
|
|
1289
|
+
const containsAsyncLambdaCustomOperation = Object.entries(schema.data.types).find(([_, typeDef]) => {
|
|
1290
|
+
return isCustomOperation(typeDef)
|
|
1291
|
+
&& finalHandlerIsAsyncFunctionHandler(typeDef.data.handlers);
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
if (containsAsyncLambdaCustomOperation) {
|
|
1295
|
+
schema.data.types['EventInvocationResponse'] = eventInvocationResponseCustomType;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1278
1298
|
const topLevelTypes = sortTopLevelTypes(Object.entries(schema.data.types));
|
|
1279
1299
|
|
|
1280
1300
|
const { schemaAuth, functionSchemaAccess } = extractFunctionSchemaAccess(
|
|
@@ -1575,12 +1595,16 @@ function validateCustomOperations(
|
|
|
1575
1595
|
}
|
|
1576
1596
|
|
|
1577
1597
|
if (configuredHandlers.size > 1) {
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
)
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1598
|
+
configuredHandlers.delete('asyncFunctionHandler');
|
|
1599
|
+
configuredHandlers.delete('functionHandler');
|
|
1600
|
+
if (configuredHandlers.size > 0) {
|
|
1601
|
+
const configuredHandlersStr = JSON.stringify(
|
|
1602
|
+
Array.from(configuredHandlers),
|
|
1603
|
+
);
|
|
1604
|
+
throw new Error(
|
|
1605
|
+
`Field handlers must be of the same type. ${typeName} has been configured with ${configuredHandlersStr}`,
|
|
1606
|
+
);
|
|
1607
|
+
}
|
|
1584
1608
|
}
|
|
1585
1609
|
}
|
|
1586
1610
|
|
|
@@ -1588,11 +1612,15 @@ function validateCustomOperations(
|
|
|
1588
1612
|
typeDef.data.returnType === null &&
|
|
1589
1613
|
(opType === 'Query' || opType === 'Mutation' || opType === 'Generation')
|
|
1590
1614
|
) {
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1615
|
+
// TODO: There should be a more elegant and readable way to handle this check.
|
|
1616
|
+
// Maybe it's not even necessary anymore since we're the setting returnType in the handler() method.
|
|
1617
|
+
if (!handlers || handlers.length === 0 || handlers[handlers.length - 1][brandSymbol] !== 'asyncFunctionHandler') {
|
|
1618
|
+
const typeDescription =
|
|
1619
|
+
opType === 'Generation' ? 'Generation Route' : `Custom ${opType}`;
|
|
1620
|
+
throw new Error(
|
|
1621
|
+
`Invalid ${typeDescription} definition. A ${typeDescription} must include a return type. ${typeName} has no return type specified.`,
|
|
1622
|
+
);
|
|
1623
|
+
}
|
|
1596
1624
|
}
|
|
1597
1625
|
|
|
1598
1626
|
if (opType !== 'Subscription' && subscriptionSource.length > 0) {
|
|
@@ -1677,10 +1705,16 @@ const isCustomHandler = (
|
|
|
1677
1705
|
|
|
1678
1706
|
const isFunctionHandler = (
|
|
1679
1707
|
handler: HandlerType[] | null,
|
|
1680
|
-
): handler is FunctionHandler[] => {
|
|
1681
|
-
return Array.isArray(handler) && getBrand(handler[0])
|
|
1708
|
+
): handler is (FunctionHandler | AsyncFunctionHandler)[] => {
|
|
1709
|
+
return Array.isArray(handler) && ['functionHandler', 'asyncFunctionHandler'].includes(getBrand(handler[0]));
|
|
1682
1710
|
};
|
|
1683
1711
|
|
|
1712
|
+
const finalHandlerIsAsyncFunctionHandler = (
|
|
1713
|
+
handler: HandlerType[] | null,
|
|
1714
|
+
): handler is AsyncFunctionHandler[] => {
|
|
1715
|
+
return Array.isArray(handler) && getBrand(handler[handler.length - 1]) === 'asyncFunctionHandler';
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1684
1718
|
const normalizeDataSourceName = (
|
|
1685
1719
|
dataSource: undefined | string | RefType<any, any, any>,
|
|
1686
1720
|
): string => {
|
|
@@ -1762,6 +1796,22 @@ const handleCustom = (
|
|
|
1762
1796
|
return jsFn;
|
|
1763
1797
|
};
|
|
1764
1798
|
|
|
1799
|
+
const eventInvocationResponseCustomType = {
|
|
1800
|
+
data: {
|
|
1801
|
+
fields: {
|
|
1802
|
+
success: {
|
|
1803
|
+
data: {
|
|
1804
|
+
fieldType: ModelFieldType.Boolean,
|
|
1805
|
+
required: true,
|
|
1806
|
+
array: false,
|
|
1807
|
+
arrayRequired: false,
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
},
|
|
1811
|
+
type: 'customType'
|
|
1812
|
+
}
|
|
1813
|
+
};
|
|
1814
|
+
|
|
1765
1815
|
function transformCustomOperations(
|
|
1766
1816
|
typeDef: InternalCustom,
|
|
1767
1817
|
typeName: string,
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
generateGraphQLDocument,
|
|
18
18
|
getCustomHeaders,
|
|
19
19
|
initializeModel,
|
|
20
|
+
flattenItems,
|
|
20
21
|
} from '../APIClient';
|
|
21
22
|
|
|
22
23
|
export function subscriptionFactory(
|
|
@@ -59,18 +60,25 @@ export function subscriptionFactory(
|
|
|
59
60
|
return observable.pipe(
|
|
60
61
|
map((value) => {
|
|
61
62
|
const [key] = Object.keys(value.data);
|
|
62
|
-
// Will need flattening here if/when custom selection set support is added:
|
|
63
63
|
const data = (value.data as any)[key];
|
|
64
|
-
const
|
|
65
|
-
client,
|
|
66
|
-
name,
|
|
67
|
-
[data],
|
|
68
|
-
modelIntrospection,
|
|
69
|
-
auth.authMode,
|
|
70
|
-
auth.authToken,
|
|
71
|
-
);
|
|
64
|
+
const flattenedResult = flattenItems(modelIntrospection, name, data);
|
|
72
65
|
|
|
73
|
-
|
|
66
|
+
if (flattenedResult === null) {
|
|
67
|
+
return null;
|
|
68
|
+
} else if (args?.selectionSet) {
|
|
69
|
+
return flattenedResult;
|
|
70
|
+
} else {
|
|
71
|
+
const [initialized] = initializeModel(
|
|
72
|
+
client,
|
|
73
|
+
name,
|
|
74
|
+
[flattenedResult],
|
|
75
|
+
modelIntrospection,
|
|
76
|
+
auth.authMode,
|
|
77
|
+
auth.authToken,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return initialized;
|
|
81
|
+
}
|
|
74
82
|
}),
|
|
75
83
|
);
|
|
76
84
|
};
|