@memberjunction/server 5.13.0 → 5.15.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/agents/skip-sdk.d.ts +8 -0
- package/dist/agents/skip-sdk.d.ts.map +1 -1
- package/dist/agents/skip-sdk.js +19 -0
- package/dist/agents/skip-sdk.js.map +1 -1
- package/dist/config.d.ts +37 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/generated/generated.d.ts +169 -0
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +909 -1
- package/dist/generated/generated.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +2 -1
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/{CreateQueryResolver.d.ts → QuerySystemUserResolver.d.ts} +26 -82
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -0
- package/dist/resolvers/{CreateQueryResolver.js → QuerySystemUserResolver.js} +123 -486
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -0
- package/dist/resolvers/TestQuerySQLResolver.d.ts +54 -0
- package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -0
- package/dist/resolvers/TestQuerySQLResolver.js +189 -0
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -0
- package/package.json +59 -59
- package/src/agents/skip-sdk.ts +22 -0
- package/src/config.ts +8 -0
- package/src/generated/generated.ts +635 -2
- package/src/index.ts +2 -1
- package/src/resolvers/GetDataResolver.ts +2 -1
- package/src/resolvers/{CreateQueryResolver.ts → QuerySystemUserResolver.ts} +143 -413
- package/src/resolvers/TestQuerySQLResolver.ts +149 -0
- package/dist/resolvers/CreateQueryResolver.d.ts.map +0 -1
- package/dist/resolvers/CreateQueryResolver.js.map +0 -1
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
import { Arg, Ctx, Field, InputType, Mutation, ObjectType, registerEnumType, Resolver, PubSub, PubSubEngine } from 'type-graphql';
|
|
2
2
|
import { AppContext } from '../types.js';
|
|
3
|
-
import { LogError, RunView, UserInfo, CompositeKey, DatabaseProviderBase, LogStatus } from '@memberjunction/core';
|
|
3
|
+
import { LogError, RunView, UserInfo, CompositeKey, DatabaseProviderBase, LogStatus, QueryFieldInfo, QueryParameterInfo, QueryEntityInfo, QueryPermissionInfo } from '@memberjunction/core';
|
|
4
4
|
import { RequireSystemUser } from '../directives/RequireSystemUser.js';
|
|
5
5
|
import { MJQueryCategoryEntity, MJQueryPermissionEntity } from '@memberjunction/core-entities';
|
|
6
|
-
import { MJQueryResolver } from '../generated/generated.js';
|
|
7
|
-
import {
|
|
6
|
+
import { MJQueryResolver, MJQuery_, MJQueryField_, MJQueryParameter_, MJQueryEntity_, MJQueryPermission_ } from '../generated/generated.js';
|
|
7
|
+
import { GetReadWriteProvider } from '../util.js';
|
|
8
8
|
import { DeleteOptionsInput } from '../generic/DeleteOptionsInput.js';
|
|
9
9
|
import { MJQueryEntityServer } from '@memberjunction/core-entities-server';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Minimal shape of a query row returned by RunView lookups (plain object, not entity instance).
|
|
13
|
+
*/
|
|
14
|
+
interface QueryViewRow {
|
|
15
|
+
ID: string;
|
|
16
|
+
Name: string;
|
|
17
|
+
Description: string;
|
|
18
|
+
CategoryID: string;
|
|
19
|
+
Category: string;
|
|
20
|
+
SQL: string;
|
|
21
|
+
Status: string;
|
|
22
|
+
QualityRank: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
11
25
|
/**
|
|
12
26
|
* Query status enumeration for GraphQL
|
|
13
27
|
*/
|
|
@@ -146,227 +160,24 @@ export class UpdateQuerySystemUserInput {
|
|
|
146
160
|
Permissions?: QueryPermissionInputType[];
|
|
147
161
|
}
|
|
148
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Consolidated result type for Create and Update query mutations.
|
|
165
|
+
* Composes the CodeGen-generated MJQuery_ type so that new entity fields
|
|
166
|
+
* are automatically available in the GraphQL schema without manual sync.
|
|
167
|
+
*
|
|
168
|
+
* On success, Query contains the full query data (scalars + related entities).
|
|
169
|
+
* On failure, Query is null and ErrorMessage describes the problem.
|
|
170
|
+
*/
|
|
149
171
|
@ObjectType()
|
|
150
|
-
export class
|
|
151
|
-
@Field(() => String)
|
|
152
|
-
ID!: string;
|
|
153
|
-
|
|
154
|
-
@Field(() => String)
|
|
155
|
-
QueryID!: string;
|
|
156
|
-
|
|
157
|
-
@Field(() => String)
|
|
158
|
-
Name!: string;
|
|
159
|
-
|
|
160
|
-
@Field(() => String, { nullable: true })
|
|
161
|
-
Description?: string;
|
|
162
|
-
|
|
163
|
-
@Field(() => Number)
|
|
164
|
-
Sequence!: number;
|
|
165
|
-
|
|
166
|
-
@Field(() => String, { nullable: true })
|
|
167
|
-
SQLBaseType?: string;
|
|
168
|
-
|
|
169
|
-
@Field(() => String, { nullable: true })
|
|
170
|
-
SQLFullType?: string;
|
|
171
|
-
|
|
172
|
-
@Field(() => String, { nullable: true })
|
|
173
|
-
SourceEntityID?: string;
|
|
174
|
-
|
|
175
|
-
@Field(() => String, { nullable: true })
|
|
176
|
-
SourceEntity?: string;
|
|
177
|
-
|
|
178
|
-
@Field(() => String, { nullable: true })
|
|
179
|
-
SourceFieldName?: string;
|
|
180
|
-
|
|
181
|
-
@Field(() => Boolean)
|
|
182
|
-
IsComputed!: boolean;
|
|
183
|
-
|
|
184
|
-
@Field(() => String, { nullable: true })
|
|
185
|
-
ComputationDescription?: string;
|
|
186
|
-
|
|
187
|
-
@Field(() => Boolean, { nullable: true })
|
|
188
|
-
IsSummary?: boolean;
|
|
189
|
-
|
|
190
|
-
@Field(() => String, { nullable: true })
|
|
191
|
-
SummaryDescription?: string;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
@ObjectType()
|
|
195
|
-
export class QueryParameterType {
|
|
196
|
-
@Field(() => String)
|
|
197
|
-
ID!: string;
|
|
198
|
-
|
|
199
|
-
@Field(() => String)
|
|
200
|
-
QueryID!: string;
|
|
201
|
-
|
|
202
|
-
@Field(() => String)
|
|
203
|
-
Name!: string;
|
|
204
|
-
|
|
205
|
-
@Field(() => String, { nullable: true })
|
|
206
|
-
Description?: string;
|
|
207
|
-
|
|
208
|
-
@Field(() => String)
|
|
209
|
-
Type!: string;
|
|
210
|
-
|
|
211
|
-
@Field(() => Boolean)
|
|
212
|
-
IsRequired!: boolean;
|
|
213
|
-
|
|
214
|
-
@Field(() => String, { nullable: true })
|
|
215
|
-
DefaultValue?: string;
|
|
216
|
-
|
|
217
|
-
@Field(() => String, { nullable: true })
|
|
218
|
-
SampleValue?: string;
|
|
219
|
-
|
|
220
|
-
@Field(() => String, { nullable: true })
|
|
221
|
-
ValidationFilters?: string;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
@ObjectType()
|
|
225
|
-
export class MJQueryEntityType {
|
|
226
|
-
@Field(() => String)
|
|
227
|
-
ID!: string;
|
|
228
|
-
|
|
229
|
-
@Field(() => String)
|
|
230
|
-
QueryID!: string;
|
|
231
|
-
|
|
232
|
-
@Field(() => String)
|
|
233
|
-
EntityID!: string;
|
|
234
|
-
|
|
235
|
-
@Field(() => String, { nullable: true })
|
|
236
|
-
Entity?: string;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
@ObjectType()
|
|
240
|
-
export class QueryPermissionType {
|
|
241
|
-
@Field(() => String)
|
|
242
|
-
ID!: string;
|
|
243
|
-
|
|
244
|
-
@Field(() => String)
|
|
245
|
-
QueryID!: string;
|
|
246
|
-
|
|
247
|
-
@Field(() => String)
|
|
248
|
-
RoleID!: string;
|
|
249
|
-
|
|
250
|
-
@Field(() => String, { nullable: true })
|
|
251
|
-
Role?: string;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
@ObjectType()
|
|
255
|
-
export class CreateQueryResultType {
|
|
256
|
-
@Field(() => Boolean)
|
|
257
|
-
Success!: boolean;
|
|
258
|
-
|
|
259
|
-
@Field(() => String, { nullable: true })
|
|
260
|
-
ErrorMessage?: string;
|
|
261
|
-
|
|
262
|
-
// Core query properties
|
|
263
|
-
@Field(() => String, { nullable: true })
|
|
264
|
-
ID?: string;
|
|
265
|
-
|
|
266
|
-
@Field(() => String, { nullable: true })
|
|
267
|
-
Name?: string;
|
|
268
|
-
|
|
269
|
-
@Field(() => String, { nullable: true })
|
|
270
|
-
Description?: string;
|
|
271
|
-
|
|
272
|
-
@Field(() => String, { nullable: true })
|
|
273
|
-
CategoryID?: string;
|
|
274
|
-
|
|
275
|
-
@Field(() => String, { nullable: true })
|
|
276
|
-
Category?: string;
|
|
277
|
-
|
|
278
|
-
@Field(() => String, { nullable: true })
|
|
279
|
-
SQL?: string;
|
|
280
|
-
|
|
281
|
-
@Field(() => String, { nullable: true })
|
|
282
|
-
Status?: string;
|
|
283
|
-
|
|
284
|
-
@Field(() => Number, { nullable: true })
|
|
285
|
-
QualityRank?: number;
|
|
286
|
-
|
|
287
|
-
@Field(() => String, { nullable: true })
|
|
288
|
-
EmbeddingVector?: string;
|
|
289
|
-
|
|
290
|
-
@Field(() => String, { nullable: true })
|
|
291
|
-
EmbeddingModelID?: string;
|
|
292
|
-
|
|
293
|
-
@Field(() => String, { nullable: true })
|
|
294
|
-
EmbeddingModelName?: string;
|
|
295
|
-
|
|
296
|
-
@Field(() => String, { nullable: true })
|
|
297
|
-
TechnicalDescription?: string;
|
|
298
|
-
|
|
299
|
-
// Related collections
|
|
300
|
-
@Field(() => [QueryFieldType], { nullable: true })
|
|
301
|
-
Fields?: QueryFieldType[];
|
|
302
|
-
|
|
303
|
-
@Field(() => [QueryParameterType], { nullable: true })
|
|
304
|
-
Parameters?: QueryParameterType[];
|
|
305
|
-
|
|
306
|
-
@Field(() => [MJQueryEntityType], { nullable: true })
|
|
307
|
-
Entities?: MJQueryEntityType[];
|
|
308
|
-
|
|
309
|
-
@Field(() => [QueryPermissionType], { nullable: true })
|
|
310
|
-
Permissions?: QueryPermissionType[];
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
@ObjectType()
|
|
314
|
-
export class UpdateQueryResultType {
|
|
172
|
+
export class QueryMutationResultType {
|
|
315
173
|
@Field(() => Boolean)
|
|
316
174
|
Success!: boolean;
|
|
317
175
|
|
|
318
176
|
@Field(() => String, { nullable: true })
|
|
319
177
|
ErrorMessage?: string;
|
|
320
178
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
ID?: string;
|
|
324
|
-
|
|
325
|
-
@Field(() => String, { nullable: true })
|
|
326
|
-
Name?: string;
|
|
327
|
-
|
|
328
|
-
@Field(() => String, { nullable: true })
|
|
329
|
-
Description?: string;
|
|
330
|
-
|
|
331
|
-
@Field(() => String, { nullable: true })
|
|
332
|
-
CategoryID?: string;
|
|
333
|
-
|
|
334
|
-
@Field(() => String, { nullable: true })
|
|
335
|
-
Category?: string;
|
|
336
|
-
|
|
337
|
-
@Field(() => String, { nullable: true })
|
|
338
|
-
SQL?: string;
|
|
339
|
-
|
|
340
|
-
@Field(() => String, { nullable: true })
|
|
341
|
-
Status?: string;
|
|
342
|
-
|
|
343
|
-
@Field(() => Number, { nullable: true })
|
|
344
|
-
QualityRank?: number;
|
|
345
|
-
|
|
346
|
-
@Field(() => String, { nullable: true })
|
|
347
|
-
EmbeddingVector?: string;
|
|
348
|
-
|
|
349
|
-
@Field(() => String, { nullable: true })
|
|
350
|
-
EmbeddingModelID?: string;
|
|
351
|
-
|
|
352
|
-
@Field(() => String, { nullable: true })
|
|
353
|
-
EmbeddingModelName?: string;
|
|
354
|
-
|
|
355
|
-
@Field(() => String, { nullable: true })
|
|
356
|
-
TechnicalDescription?: string;
|
|
357
|
-
|
|
358
|
-
// Related collections
|
|
359
|
-
@Field(() => [QueryFieldType], { nullable: true })
|
|
360
|
-
Fields?: QueryFieldType[];
|
|
361
|
-
|
|
362
|
-
@Field(() => [QueryParameterType], { nullable: true })
|
|
363
|
-
Parameters?: QueryParameterType[];
|
|
364
|
-
|
|
365
|
-
@Field(() => [MJQueryEntityType], { nullable: true })
|
|
366
|
-
Entities?: MJQueryEntityType[];
|
|
367
|
-
|
|
368
|
-
@Field(() => [QueryPermissionType], { nullable: true })
|
|
369
|
-
Permissions?: QueryPermissionType[];
|
|
179
|
+
@Field(() => MJQuery_, { nullable: true })
|
|
180
|
+
Query?: MJQuery_;
|
|
370
181
|
}
|
|
371
182
|
|
|
372
183
|
@ObjectType()
|
|
@@ -406,12 +217,12 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
406
217
|
* @returns CreateQueryResultType with success status and query data
|
|
407
218
|
*/
|
|
408
219
|
@RequireSystemUser()
|
|
409
|
-
@Mutation(() =>
|
|
220
|
+
@Mutation(() => QueryMutationResultType)
|
|
410
221
|
async CreateQuerySystemUser(
|
|
411
222
|
@Arg('input', () => CreateQuerySystemUserInput) input: CreateQuerySystemUserInput,
|
|
412
223
|
@Ctx() context: AppContext,
|
|
413
224
|
@PubSub() pubSub: PubSubEngine
|
|
414
|
-
): Promise<
|
|
225
|
+
): Promise<QueryMutationResultType> {
|
|
415
226
|
try {
|
|
416
227
|
// Handle CategoryPath if provided
|
|
417
228
|
let finalCategoryID = input.CategoryID;
|
|
@@ -434,8 +245,8 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
434
245
|
// Use MJQueryEntityServer which handles AI processing
|
|
435
246
|
const record = await provider.GetEntityObject<MJQueryEntityServer>("MJ: Queries", context.userPayload.userRecord);
|
|
436
247
|
|
|
437
|
-
//
|
|
438
|
-
const fieldsToSet = {
|
|
248
|
+
// Destructure out non-database fields, keep only fields to persist
|
|
249
|
+
const { Permissions: _permissions, CategoryPath: _categoryPath, ...fieldsToSet } = {
|
|
439
250
|
...input,
|
|
440
251
|
CategoryID: finalCategoryID || input.CategoryID,
|
|
441
252
|
Status: input.Status || 'Approved',
|
|
@@ -446,9 +257,6 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
446
257
|
CacheTTLMinutes: input.CacheTTLMinutes || null,
|
|
447
258
|
CacheMaxSize: input.CacheMaxSize || null
|
|
448
259
|
};
|
|
449
|
-
// Remove non-database fields that we handle separately or are input-only
|
|
450
|
-
delete (fieldsToSet as any).Permissions; // Handled separately via createPermissions
|
|
451
|
-
delete (fieldsToSet as any).CategoryPath; // Input-only field, resolved to CategoryID
|
|
452
260
|
|
|
453
261
|
record.SetMany(fieldsToSet, true);
|
|
454
262
|
this.ListenForEntityMessages(record, pubSub, context.userPayload.userRecord);
|
|
@@ -470,60 +278,7 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
470
278
|
// This ensures subsequent operations can find the query without additional DB calls
|
|
471
279
|
await provider.Refresh();
|
|
472
280
|
|
|
473
|
-
return
|
|
474
|
-
Success: true,
|
|
475
|
-
ID: record.ID,
|
|
476
|
-
Name: record.Name,
|
|
477
|
-
Description: record.Description,
|
|
478
|
-
CategoryID: record.CategoryID,
|
|
479
|
-
Category: record.Category,
|
|
480
|
-
SQL: record.SQL,
|
|
481
|
-
Status: record.Status,
|
|
482
|
-
QualityRank: record.QualityRank,
|
|
483
|
-
EmbeddingVector: record.EmbeddingVector,
|
|
484
|
-
EmbeddingModelID: record.EmbeddingModelID,
|
|
485
|
-
EmbeddingModelName: record.EmbeddingModel,
|
|
486
|
-
TechnicalDescription: record.TechnicalDescription,
|
|
487
|
-
Fields: record.QueryFields.map(f => ({
|
|
488
|
-
ID: f.ID,
|
|
489
|
-
QueryID: f.QueryID,
|
|
490
|
-
Name: f.Name,
|
|
491
|
-
Description: f.Description,
|
|
492
|
-
Sequence: f.Sequence,
|
|
493
|
-
SQLBaseType: f.SQLBaseType,
|
|
494
|
-
SQLFullType: f.SQLFullType,
|
|
495
|
-
SourceEntityID: f.SourceEntityID,
|
|
496
|
-
SourceEntity: f.SourceEntity,
|
|
497
|
-
SourceFieldName: f.SourceFieldName,
|
|
498
|
-
IsComputed: f.IsComputed,
|
|
499
|
-
ComputationDescription: f.ComputationDescription,
|
|
500
|
-
IsSummary: f.IsSummary,
|
|
501
|
-
SummaryDescription: f.SummaryDescription
|
|
502
|
-
})),
|
|
503
|
-
Parameters: record.QueryParameters.map(p => ({
|
|
504
|
-
ID: p.ID,
|
|
505
|
-
QueryID: p.QueryID,
|
|
506
|
-
Name: p.Name,
|
|
507
|
-
Description: p.Description,
|
|
508
|
-
Type: p.Type,
|
|
509
|
-
IsRequired: p.IsRequired,
|
|
510
|
-
DefaultValue: p.DefaultValue,
|
|
511
|
-
SampleValue: p.SampleValue,
|
|
512
|
-
ValidationFilters: p.ValidationFilters
|
|
513
|
-
})),
|
|
514
|
-
Entities: record.QueryEntities.map(e => ({
|
|
515
|
-
ID: e.ID,
|
|
516
|
-
QueryID: e.QueryID,
|
|
517
|
-
EntityID: e.EntityID,
|
|
518
|
-
Entity: e.Entity
|
|
519
|
-
})),
|
|
520
|
-
Permissions: record.QueryPermissions.map(p => ({
|
|
521
|
-
ID: p.ID,
|
|
522
|
-
QueryID: p.QueryID,
|
|
523
|
-
RoleID: p.RoleID,
|
|
524
|
-
Role: p.Role
|
|
525
|
-
}))
|
|
526
|
-
};
|
|
281
|
+
return this.buildSuccessResult(record);
|
|
527
282
|
}
|
|
528
283
|
else {
|
|
529
284
|
// Save failed - check if another request created the same query (race condition)
|
|
@@ -531,62 +286,17 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
531
286
|
const existingQuery = await this.findExistingQuery(provider, input.Name, finalCategoryID, context.userPayload.userRecord);
|
|
532
287
|
|
|
533
288
|
if (existingQuery) {
|
|
534
|
-
// Found the query that was created by another request
|
|
535
|
-
//
|
|
289
|
+
// Found the query that was created by another request — load it as a full entity
|
|
290
|
+
// so that related metadata (Fields, Parameters, Entities, Permissions) is populated
|
|
536
291
|
LogStatus(`[CreateQuery] Unique constraint detected for query '${input.Name}'. Using existing query (ID: ${existingQuery.ID}) created by concurrent request.`);
|
|
292
|
+
const existingEntity = await provider.GetEntityObject<MJQueryEntityServer>('MJ: Queries', context.userPayload.userRecord);
|
|
293
|
+
if (await existingEntity.Load(existingQuery.ID)) {
|
|
294
|
+
return this.buildSuccessResult(existingEntity);
|
|
295
|
+
}
|
|
296
|
+
// Entity load failed after confirming the row exists — extremely rare
|
|
537
297
|
return {
|
|
538
|
-
Success:
|
|
539
|
-
ID: existingQuery.ID
|
|
540
|
-
Name: existingQuery.Name,
|
|
541
|
-
Description: existingQuery.Description,
|
|
542
|
-
CategoryID: existingQuery.CategoryID,
|
|
543
|
-
Category: existingQuery.Category,
|
|
544
|
-
SQL: existingQuery.SQL,
|
|
545
|
-
Status: existingQuery.Status,
|
|
546
|
-
QualityRank: existingQuery.QualityRank,
|
|
547
|
-
EmbeddingVector: existingQuery.EmbeddingVector,
|
|
548
|
-
EmbeddingModelID: existingQuery.EmbeddingModelID,
|
|
549
|
-
EmbeddingModelName: existingQuery.EmbeddingModel,
|
|
550
|
-
TechnicalDescription: existingQuery.TechnicalDescription,
|
|
551
|
-
Fields: existingQuery.Fields?.map((f: any) => ({
|
|
552
|
-
ID: f.ID,
|
|
553
|
-
QueryID: f.QueryID,
|
|
554
|
-
Name: f.Name,
|
|
555
|
-
Description: f.Description,
|
|
556
|
-
Sequence: f.Sequence,
|
|
557
|
-
SQLBaseType: f.SQLBaseType,
|
|
558
|
-
SQLFullType: f.SQLFullType,
|
|
559
|
-
SourceEntityID: f.SourceEntityID,
|
|
560
|
-
SourceEntity: f.SourceEntity,
|
|
561
|
-
SourceFieldName: f.SourceFieldName,
|
|
562
|
-
IsComputed: f.IsComputed,
|
|
563
|
-
ComputationDescription: f.ComputationDescription,
|
|
564
|
-
IsSummary: f.IsSummary,
|
|
565
|
-
SummaryDescription: f.SummaryDescription
|
|
566
|
-
})) || [],
|
|
567
|
-
Parameters: existingQuery.Parameters?.map((p: any) => ({
|
|
568
|
-
ID: p.ID,
|
|
569
|
-
QueryID: p.QueryID,
|
|
570
|
-
Name: p.Name,
|
|
571
|
-
Description: p.Description,
|
|
572
|
-
Type: p.Type,
|
|
573
|
-
IsRequired: p.IsRequired,
|
|
574
|
-
DefaultValue: p.DefaultValue,
|
|
575
|
-
SampleValue: p.SampleValue,
|
|
576
|
-
ValidationFilters: p.ValidationFilters
|
|
577
|
-
})) || [],
|
|
578
|
-
Entities: existingQuery.Entities?.map((e: any) => ({
|
|
579
|
-
ID: e.ID,
|
|
580
|
-
QueryID: e.QueryID,
|
|
581
|
-
EntityID: e.EntityID,
|
|
582
|
-
Entity: e.Entity
|
|
583
|
-
})) || [],
|
|
584
|
-
Permissions: existingQuery.Permissions?.map((p: any) => ({
|
|
585
|
-
ID: p.ID,
|
|
586
|
-
QueryID: p.QueryID,
|
|
587
|
-
RoleID: p.RoleID,
|
|
588
|
-
Role: p.Role
|
|
589
|
-
})) || []
|
|
298
|
+
Success: false,
|
|
299
|
+
ErrorMessage: `Found query '${input.Name}' created by concurrent request (ID: ${existingQuery.ID}) but failed to load it as entity`
|
|
590
300
|
};
|
|
591
301
|
}
|
|
592
302
|
|
|
@@ -607,29 +317,102 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
607
317
|
}
|
|
608
318
|
}
|
|
609
319
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
320
|
+
/**
|
|
321
|
+
* Maps an MJQueryEntityServer (with loaded related metadata) to a QueryMutationResultType.
|
|
322
|
+
* Uses entity.GetAll() for scalar fields so that new CodeGen-generated fields are included
|
|
323
|
+
* automatically without manual updates. Related entity arrays are mapped explicitly.
|
|
324
|
+
*/
|
|
325
|
+
private buildSuccessResult(entity: MJQueryEntityServer): QueryMutationResultType {
|
|
326
|
+
return {
|
|
327
|
+
Success: true,
|
|
328
|
+
Query: {
|
|
329
|
+
...entity.GetAll(),
|
|
330
|
+
MJQueryFields_QueryIDArray: this.mapFields(entity.QueryFields),
|
|
331
|
+
MJQueryParameters_QueryIDArray: this.mapParameters(entity.QueryParameters),
|
|
332
|
+
MJQueryEntities_QueryIDArray: this.mapEntities(entity.QueryEntities),
|
|
333
|
+
MJQueryPermissions_QueryIDArray: this.mapPermissions(entity.QueryPermissions),
|
|
334
|
+
} as MJQuery_
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
private mapFields(fields: QueryFieldInfo[]): MJQueryField_[] {
|
|
339
|
+
return fields.map(f => ({
|
|
340
|
+
ID: f.ID,
|
|
341
|
+
QueryID: f.QueryID,
|
|
342
|
+
Name: f.Name,
|
|
343
|
+
Description: f.Description,
|
|
344
|
+
Sequence: f.Sequence,
|
|
345
|
+
SQLBaseType: f.SQLBaseType,
|
|
346
|
+
SQLFullType: f.SQLFullType,
|
|
347
|
+
SourceEntityID: f.SourceEntityID,
|
|
348
|
+
SourceEntity: f.SourceEntity,
|
|
349
|
+
SourceFieldName: f.SourceFieldName,
|
|
350
|
+
IsComputed: f.IsComputed,
|
|
351
|
+
ComputationDescription: f.ComputationDescription,
|
|
352
|
+
IsSummary: f.IsSummary,
|
|
353
|
+
SummaryDescription: f.SummaryDescription,
|
|
354
|
+
_mj__CreatedAt: f.__mj_CreatedAt,
|
|
355
|
+
_mj__UpdatedAt: f.__mj_UpdatedAt,
|
|
356
|
+
DetectionMethod: f.DetectionMethod,
|
|
357
|
+
AutoDetectConfidenceScore: f.AutoDetectConfidenceScore,
|
|
358
|
+
Query: '',
|
|
359
|
+
}) as MJQueryField_);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private mapParameters(params: QueryParameterInfo[]): MJQueryParameter_[] {
|
|
363
|
+
return params.map(p => ({
|
|
364
|
+
ID: p.ID,
|
|
365
|
+
QueryID: p.QueryID,
|
|
366
|
+
Name: p.Name,
|
|
367
|
+
Description: p.Description,
|
|
368
|
+
Type: p.Type,
|
|
369
|
+
IsRequired: p.IsRequired,
|
|
370
|
+
DefaultValue: p.DefaultValue,
|
|
371
|
+
SampleValue: p.SampleValue,
|
|
372
|
+
ValidationFilters: p.ValidationFilters,
|
|
373
|
+
_mj__CreatedAt: p.__mj_CreatedAt,
|
|
374
|
+
_mj__UpdatedAt: p.__mj_UpdatedAt,
|
|
375
|
+
DetectionMethod: p.DetectionMethod,
|
|
376
|
+
AutoDetectConfidenceScore: p.AutoDetectConfidenceScore,
|
|
377
|
+
Query: p.Query ?? '',
|
|
378
|
+
}) as MJQueryParameter_);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
private mapEntities(entities: QueryEntityInfo[]): MJQueryEntity_[] {
|
|
382
|
+
return entities.map(e => ({
|
|
383
|
+
ID: e.ID,
|
|
384
|
+
QueryID: e.QueryID,
|
|
385
|
+
EntityID: e.EntityID,
|
|
386
|
+
Entity: e.Entity,
|
|
387
|
+
_mj__CreatedAt: e.__mj_CreatedAt,
|
|
388
|
+
_mj__UpdatedAt: e.__mj_UpdatedAt,
|
|
389
|
+
DetectionMethod: e.DetectionMethod,
|
|
390
|
+
AutoDetectConfidenceScore: e.AutoDetectConfidenceScore,
|
|
391
|
+
Query: e.Query ?? '',
|
|
392
|
+
}) as MJQueryEntity_);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
private mapPermissions(permissions: QueryPermissionInfo[]): MJQueryPermission_[] {
|
|
396
|
+
return permissions.map(p => ({
|
|
397
|
+
ID: p.ID,
|
|
398
|
+
QueryID: p.QueryID,
|
|
399
|
+
RoleID: p.RoleID,
|
|
400
|
+
Role: p.Role,
|
|
401
|
+
_mj__CreatedAt: new Date(),
|
|
402
|
+
_mj__UpdatedAt: new Date(),
|
|
403
|
+
Query: p.Query ?? '',
|
|
404
|
+
}) as MJQueryPermission_);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
protected async createPermissions(p: DatabaseProviderBase, permissions: QueryPermissionInputType[], queryID: string, contextUser: UserInfo): Promise<void> {
|
|
408
|
+
for (const perm of permissions) {
|
|
409
|
+
const permissionEntity = await p.GetEntityObject<MJQueryPermissionEntity>('MJ: Query Permissions', contextUser);
|
|
410
|
+
if (permissionEntity) {
|
|
411
|
+
permissionEntity.QueryID = queryID;
|
|
412
|
+
permissionEntity.RoleID = perm.RoleID;
|
|
413
|
+
await permissionEntity.Save();
|
|
630
414
|
}
|
|
631
415
|
}
|
|
632
|
-
return createdPermissions;
|
|
633
416
|
}
|
|
634
417
|
|
|
635
418
|
/**
|
|
@@ -639,12 +422,12 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
639
422
|
* @returns UpdateQueryResultType with success status and updated query data including related entities
|
|
640
423
|
*/
|
|
641
424
|
@RequireSystemUser()
|
|
642
|
-
@Mutation(() =>
|
|
425
|
+
@Mutation(() => QueryMutationResultType)
|
|
643
426
|
async UpdateQuerySystemUser(
|
|
644
427
|
@Arg('input', () => UpdateQuerySystemUserInput) input: UpdateQuerySystemUserInput,
|
|
645
428
|
@Ctx() context: AppContext,
|
|
646
429
|
@PubSub() pubSub: PubSubEngine
|
|
647
|
-
): Promise<
|
|
430
|
+
): Promise<QueryMutationResultType> {
|
|
648
431
|
try {
|
|
649
432
|
// Load the existing query using MJQueryEntityServer
|
|
650
433
|
const provider = GetReadWriteProvider(context.providers);
|
|
@@ -676,7 +459,7 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
676
459
|
}
|
|
677
460
|
|
|
678
461
|
// Update fields that were provided
|
|
679
|
-
const updateFields: Record<string,
|
|
462
|
+
const updateFields: Record<string, string | number | boolean | undefined> = {};
|
|
680
463
|
if (input.Name !== undefined) updateFields.Name = input.Name;
|
|
681
464
|
if (finalCategoryID !== undefined) updateFields.CategoryID = finalCategoryID;
|
|
682
465
|
if (input.UserQuestion !== undefined) updateFields.UserQuestion = input.UserQuestion;
|
|
@@ -731,60 +514,7 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
731
514
|
await queryEntity.RefreshRelatedMetadata(true);
|
|
732
515
|
}
|
|
733
516
|
|
|
734
|
-
return
|
|
735
|
-
Success: true,
|
|
736
|
-
ID: queryEntity.ID,
|
|
737
|
-
Name: queryEntity.Name,
|
|
738
|
-
Description: queryEntity.Description,
|
|
739
|
-
CategoryID: queryEntity.CategoryID,
|
|
740
|
-
Category: queryEntity.Category,
|
|
741
|
-
SQL: queryEntity.SQL,
|
|
742
|
-
Status: queryEntity.Status,
|
|
743
|
-
QualityRank: queryEntity.QualityRank,
|
|
744
|
-
EmbeddingVector: queryEntity.EmbeddingVector,
|
|
745
|
-
EmbeddingModelID: queryEntity.EmbeddingModelID,
|
|
746
|
-
EmbeddingModelName: queryEntity.EmbeddingModel,
|
|
747
|
-
TechnicalDescription: queryEntity.TechnicalDescription,
|
|
748
|
-
Fields: queryEntity.QueryFields.map(f => ({
|
|
749
|
-
ID: f.ID,
|
|
750
|
-
QueryID: f.QueryID,
|
|
751
|
-
Name: f.Name,
|
|
752
|
-
Description: f.Description,
|
|
753
|
-
Sequence: f.Sequence,
|
|
754
|
-
SQLBaseType: f.SQLBaseType,
|
|
755
|
-
SQLFullType: f.SQLFullType,
|
|
756
|
-
SourceEntityID: f.SourceEntityID,
|
|
757
|
-
SourceEntity: f.SourceEntity,
|
|
758
|
-
SourceFieldName: f.SourceFieldName,
|
|
759
|
-
IsComputed: f.IsComputed,
|
|
760
|
-
ComputationDescription: f.ComputationDescription,
|
|
761
|
-
IsSummary: f.IsSummary,
|
|
762
|
-
SummaryDescription: f.SummaryDescription
|
|
763
|
-
})),
|
|
764
|
-
Parameters: queryEntity.QueryParameters.map(p => ({
|
|
765
|
-
ID: p.ID,
|
|
766
|
-
QueryID: p.QueryID,
|
|
767
|
-
Name: p.Name,
|
|
768
|
-
Description: p.Description,
|
|
769
|
-
Type: p.Type,
|
|
770
|
-
IsRequired: p.IsRequired,
|
|
771
|
-
DefaultValue: p.DefaultValue,
|
|
772
|
-
SampleValue: p.SampleValue,
|
|
773
|
-
ValidationFilters: p.ValidationFilters
|
|
774
|
-
})),
|
|
775
|
-
Entities: queryEntity.QueryEntities.map(e => ({
|
|
776
|
-
ID: e.ID,
|
|
777
|
-
QueryID: e.QueryID,
|
|
778
|
-
EntityID: e.EntityID,
|
|
779
|
-
Entity: e.Entity
|
|
780
|
-
})),
|
|
781
|
-
Permissions: queryEntity.QueryPermissions.map(p => ({
|
|
782
|
-
ID: p.ID,
|
|
783
|
-
QueryID: p.QueryID,
|
|
784
|
-
RoleID: p.RoleID,
|
|
785
|
-
Role: p.Role
|
|
786
|
-
}))
|
|
787
|
-
};
|
|
517
|
+
return this.buildSuccessResult(queryEntity);
|
|
788
518
|
|
|
789
519
|
} catch (err) {
|
|
790
520
|
LogError(err);
|
|
@@ -949,20 +679,20 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
949
679
|
* @param queryName - Name of the query to find
|
|
950
680
|
* @param categoryID - Category ID (can be null)
|
|
951
681
|
* @param contextUser - User context for database operations
|
|
952
|
-
* @returns The matching query
|
|
682
|
+
* @returns The matching query row or null if not found
|
|
953
683
|
*/
|
|
954
684
|
private async findExistingQuery(
|
|
955
685
|
provider: DatabaseProviderBase,
|
|
956
686
|
queryName: string,
|
|
957
687
|
categoryID: string | null,
|
|
958
688
|
contextUser: UserInfo
|
|
959
|
-
): Promise<
|
|
689
|
+
): Promise<QueryViewRow | null> {
|
|
960
690
|
try {
|
|
961
691
|
// Query database directly to avoid cache staleness issues
|
|
962
692
|
const categoryFilter = categoryID ? `CategoryID='${categoryID}'` : 'CategoryID IS NULL';
|
|
963
693
|
const nameFilter = `LOWER(Name) = LOWER('${queryName.replace(/'/g, "''")}')`;
|
|
964
694
|
|
|
965
|
-
const result = await provider.RunView({
|
|
695
|
+
const result = await provider.RunView<QueryViewRow>({
|
|
966
696
|
EntityName: 'MJ: Queries',
|
|
967
697
|
ExtraFilter: `${nameFilter} AND ${categoryFilter}`
|
|
968
698
|
}, contextUser);
|