@memberjunction/server 5.34.1 → 5.36.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/generated/generated.d.ts +49 -4
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +236 -14
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +1 -1
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +7 -4
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +4 -0
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +28 -0
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts +2 -0
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +42 -8
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/ListOperationsResolver.d.ts +175 -0
- package/dist/resolvers/ListOperationsResolver.d.ts.map +1 -0
- package/dist/resolvers/ListOperationsResolver.js +930 -0
- package/dist/resolvers/ListOperationsResolver.js.map +1 -0
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +111 -58
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/artifact-routing.d.ts +39 -0
- package/dist/resolvers/artifact-routing.d.ts.map +1 -0
- package/dist/resolvers/artifact-routing.js +40 -0
- package/dist/resolvers/artifact-routing.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +70 -68
- package/src/__tests__/ListOperationsResolver.test.ts +182 -0
- package/src/generated/generated.ts +173 -14
- package/src/generic/ResolverBase.ts +13 -4
- package/src/generic/RunViewResolver.ts +28 -0
- package/src/resolvers/AdhocQueryResolver.ts +41 -8
- package/src/resolvers/ListOperationsResolver.ts +607 -0
- package/src/resolvers/RunAIAgentResolver.ts +119 -61
- package/src/resolvers/__tests__/artifact-routing.test.ts +88 -0
- package/src/resolvers/artifact-routing.ts +80 -0
- package/src/types.ts +6 -0
|
@@ -7094,7 +7094,7 @@ export class MJAIAgentRunStep_ {
|
|
|
7094
7094
|
@Field(() => Int, {description: `Sequential number of this step within the agent run, starting from 1`})
|
|
7095
7095
|
StepNumber: number;
|
|
7096
7096
|
|
|
7097
|
-
@Field({description: `Type of execution step: Prompt, Actions, Sub-Agent, Decision, Chat, Validation`})
|
|
7097
|
+
@Field({description: `Type of execution step: Prompt, Actions, Sub-Agent, Decision, Chat, Validation, ForEach, While, Tool`})
|
|
7098
7098
|
@MaxLength(50)
|
|
7099
7099
|
StepType: string;
|
|
7100
7100
|
|
|
@@ -9453,6 +9453,9 @@ if this limit is exceeded.`})
|
|
|
9453
9453
|
@MaxLength(20)
|
|
9454
9454
|
SearchScopeAccess: string;
|
|
9455
9455
|
|
|
9456
|
+
@Field(() => Boolean, {description: `Per-agent opt-in to a Generic Binary fallback for file uploads whose MIME type does not match any registered Artifact Type. When false (default), unrecognized uploads are rejected at upload time with an actionable error. When true, unrecognized uploads resolve to the Generic Binary artifact type, exposing only get_full and get_metadata tools. Scoped per agent — there is no system-wide global flag.`})
|
|
9457
|
+
AcceptUnregisteredFiles: boolean;
|
|
9458
|
+
|
|
9456
9459
|
@Field({nullable: true})
|
|
9457
9460
|
@MaxLength(255)
|
|
9458
9461
|
Parent?: string;
|
|
@@ -9763,6 +9766,9 @@ export class CreateMJAIAgentInput {
|
|
|
9763
9766
|
@Field({ nullable: true })
|
|
9764
9767
|
SearchScopeAccess?: string;
|
|
9765
9768
|
|
|
9769
|
+
@Field(() => Boolean, { nullable: true })
|
|
9770
|
+
AcceptUnregisteredFiles?: boolean;
|
|
9771
|
+
|
|
9766
9772
|
@Field(() => RestoreContextInput, { nullable: true })
|
|
9767
9773
|
RestoreContext___?: RestoreContextInput;
|
|
9768
9774
|
}
|
|
@@ -9962,6 +9968,9 @@ export class UpdateMJAIAgentInput {
|
|
|
9962
9968
|
@Field({ nullable: true })
|
|
9963
9969
|
SearchScopeAccess?: string;
|
|
9964
9970
|
|
|
9971
|
+
@Field(() => Boolean, { nullable: true })
|
|
9972
|
+
AcceptUnregisteredFiles?: boolean;
|
|
9973
|
+
|
|
9965
9974
|
@Field(() => [KeyValuePairInput], { nullable: true })
|
|
9966
9975
|
OldValues___?: KeyValuePairInput[];
|
|
9967
9976
|
|
|
@@ -21569,6 +21578,16 @@ export class MJArtifactType_ {
|
|
|
21569
21578
|
@MaxLength(100)
|
|
21570
21579
|
ToolLibraryClass?: string;
|
|
21571
21580
|
|
|
21581
|
+
@Field(() => Int, {description: `Deterministic tiebreaker when multiple Artifact Types match the same MIME pattern. Higher values win. Within a specificity tier (exact > subtype-wildcard), the resolver sorts by Priority desc, then SystemSupplied = false beats SystemSupplied = true, then lowest ID wins.`})
|
|
21582
|
+
Priority: number;
|
|
21583
|
+
|
|
21584
|
+
@Field({description: `How artifacts of this type are delivered to the LLM by default. Inline: emitted as an inline content block (image_url, audio_url, small text, etc.) when the model supports the modality and the size is under the inline cap. ToolsOnly: never inlined; the agent reaches the bytes only through tool calls (get_full, library-specific tools). Per-instance override is one-way via ConversationArtifactVersion.ForceToolsOnly — an instance can opt out of inline but never opt in when the type default is ToolsOnly.`})
|
|
21585
|
+
@MaxLength(20)
|
|
21586
|
+
DefaultDeliveryMode: string;
|
|
21587
|
+
|
|
21588
|
+
@Field(() => Boolean, {description: `True for Artifact Types shipped as part of the MemberJunction default registry (JSON, PDF, Office variants, Image/Audio/Video, Generic Text, Generic Binary). False for user/org-supplied customizations. Used as a tiebreaker in MIME pattern resolution: user customizations win over shipped defaults at equal Priority.`})
|
|
21589
|
+
SystemSupplied: boolean;
|
|
21590
|
+
|
|
21572
21591
|
@Field({nullable: true})
|
|
21573
21592
|
@MaxLength(100)
|
|
21574
21593
|
Parent?: string;
|
|
@@ -21632,6 +21651,15 @@ export class CreateMJArtifactTypeInput {
|
|
|
21632
21651
|
@Field({ nullable: true })
|
|
21633
21652
|
ToolLibraryClass: string | null;
|
|
21634
21653
|
|
|
21654
|
+
@Field(() => Int, { nullable: true })
|
|
21655
|
+
Priority?: number;
|
|
21656
|
+
|
|
21657
|
+
@Field({ nullable: true })
|
|
21658
|
+
DefaultDeliveryMode?: string;
|
|
21659
|
+
|
|
21660
|
+
@Field(() => Boolean, { nullable: true })
|
|
21661
|
+
SystemSupplied?: boolean;
|
|
21662
|
+
|
|
21635
21663
|
@Field(() => RestoreContextInput, { nullable: true })
|
|
21636
21664
|
RestoreContext___?: RestoreContextInput;
|
|
21637
21665
|
}
|
|
@@ -21675,6 +21703,15 @@ export class UpdateMJArtifactTypeInput {
|
|
|
21675
21703
|
@Field({ nullable: true })
|
|
21676
21704
|
ToolLibraryClass?: string | null;
|
|
21677
21705
|
|
|
21706
|
+
@Field(() => Int, { nullable: true })
|
|
21707
|
+
Priority?: number;
|
|
21708
|
+
|
|
21709
|
+
@Field({ nullable: true })
|
|
21710
|
+
DefaultDeliveryMode?: string;
|
|
21711
|
+
|
|
21712
|
+
@Field(() => Boolean, { nullable: true })
|
|
21713
|
+
SystemSupplied?: boolean;
|
|
21714
|
+
|
|
21678
21715
|
@Field(() => [KeyValuePairInput], { nullable: true })
|
|
21679
21716
|
OldValues___?: KeyValuePairInput[];
|
|
21680
21717
|
|
|
@@ -21848,9 +21885,8 @@ export class MJArtifactUse_ {
|
|
|
21848
21885
|
@Field()
|
|
21849
21886
|
_mj__UpdatedAt: Date;
|
|
21850
21887
|
|
|
21851
|
-
@Field(
|
|
21852
|
-
|
|
21853
|
-
ArtifactVersion?: string;
|
|
21888
|
+
@Field(() => Int)
|
|
21889
|
+
ArtifactVersion: number;
|
|
21854
21890
|
|
|
21855
21891
|
@Field()
|
|
21856
21892
|
@MaxLength(100)
|
|
@@ -22030,9 +22066,8 @@ export class MJArtifactVersionAttribute_ {
|
|
|
22030
22066
|
@Field()
|
|
22031
22067
|
_mj__UpdatedAt: Date;
|
|
22032
22068
|
|
|
22033
|
-
@Field(
|
|
22034
|
-
|
|
22035
|
-
ArtifactVersion?: string;
|
|
22069
|
+
@Field(() => Int)
|
|
22070
|
+
ArtifactVersion: number;
|
|
22036
22071
|
|
|
22037
22072
|
}
|
|
22038
22073
|
|
|
@@ -22245,6 +22280,9 @@ export class MJArtifactVersion_ {
|
|
|
22245
22280
|
@Field(() => Int, {nullable: true, description: `Size of the stored file in bytes. Denormalized for display without loading the file. Only populated when ContentMode is 'File'.`})
|
|
22246
22281
|
ContentSizeBytes?: number;
|
|
22247
22282
|
|
|
22283
|
+
@Field(() => Boolean, {description: `One-way override that forces this artifact version to be delivered via tools regardless of the Artifact Type's DefaultDeliveryMode. When true, the resolver never emits an inline content block for this version. There is no inverse override — an instance cannot be widened from ToolsOnly to Inline. Default false.`})
|
|
22284
|
+
ForceToolsOnly: boolean;
|
|
22285
|
+
|
|
22248
22286
|
@Field()
|
|
22249
22287
|
@MaxLength(255)
|
|
22250
22288
|
Artifact: string;
|
|
@@ -22269,6 +22307,9 @@ export class MJArtifactVersion_ {
|
|
|
22269
22307
|
@Field(() => [MJConversationDetailArtifact_])
|
|
22270
22308
|
MJConversationDetailArtifacts_ArtifactVersionIDArray: MJConversationDetailArtifact_[]; // Link to MJConversationDetailArtifacts
|
|
22271
22309
|
|
|
22310
|
+
@Field(() => [MJConversationDetailAttachment_])
|
|
22311
|
+
MJConversationDetailAttachments_ArtifactVersionIDArray: MJConversationDetailAttachment_[]; // Link to MJConversationDetailAttachments
|
|
22312
|
+
|
|
22272
22313
|
}
|
|
22273
22314
|
|
|
22274
22315
|
//****************************************************************************
|
|
@@ -22321,6 +22362,9 @@ export class CreateMJArtifactVersionInput {
|
|
|
22321
22362
|
@Field(() => Int, { nullable: true })
|
|
22322
22363
|
ContentSizeBytes: number | null;
|
|
22323
22364
|
|
|
22365
|
+
@Field(() => Boolean, { nullable: true })
|
|
22366
|
+
ForceToolsOnly?: boolean;
|
|
22367
|
+
|
|
22324
22368
|
@Field(() => RestoreContextInput, { nullable: true })
|
|
22325
22369
|
RestoreContext___?: RestoreContextInput;
|
|
22326
22370
|
}
|
|
@@ -22376,6 +22420,9 @@ export class UpdateMJArtifactVersionInput {
|
|
|
22376
22420
|
@Field(() => Int, { nullable: true })
|
|
22377
22421
|
ContentSizeBytes?: number | null;
|
|
22378
22422
|
|
|
22423
|
+
@Field(() => Boolean, { nullable: true })
|
|
22424
|
+
ForceToolsOnly?: boolean;
|
|
22425
|
+
|
|
22379
22426
|
@Field(() => [KeyValuePairInput], { nullable: true })
|
|
22380
22427
|
OldValues___?: KeyValuePairInput[];
|
|
22381
22428
|
|
|
@@ -22480,6 +22527,16 @@ export class MJArtifactVersionResolver extends ResolverBase {
|
|
|
22480
22527
|
return result;
|
|
22481
22528
|
}
|
|
22482
22529
|
|
|
22530
|
+
@FieldResolver(() => [MJConversationDetailAttachment_])
|
|
22531
|
+
async MJConversationDetailAttachments_ArtifactVersionIDArray(@Root() mjartifactversion_: MJArtifactVersion_, @Ctx() { userPayload, providers }: AppContext, @PubSub() pubSub: PubSubEngine) {
|
|
22532
|
+
this.CheckUserReadPermissions('MJ: Conversation Detail Attachments', userPayload);
|
|
22533
|
+
const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
|
|
22534
|
+
const sSQL = `SELECT * FROM ${provider.QuoteSchemaAndView(Metadata.Provider.ConfigData.MJCoreSchemaName, 'vwConversationDetailAttachments')} WHERE ${provider.QuoteIdentifier('ArtifactVersionID')}='${mjartifactversion_.ID}' ` + this.getRowLevelSecurityWhereClause(provider, 'MJ: Conversation Detail Attachments', userPayload, EntityPermissionType.Read, 'AND');
|
|
22535
|
+
const rows = await provider.ExecuteSQL(sSQL, undefined, undefined, this.GetUserFromPayload(userPayload));
|
|
22536
|
+
const result = await this.ArrayMapFieldNamesToCodeNames('MJ: Conversation Detail Attachments', rows, this.GetUserFromPayload(userPayload));
|
|
22537
|
+
return result;
|
|
22538
|
+
}
|
|
22539
|
+
|
|
22483
22540
|
@Mutation(() => MJArtifactVersion_)
|
|
22484
22541
|
async CreateMJArtifactVersion(
|
|
22485
22542
|
@Arg('input', () => CreateMJArtifactVersionInput) input: CreateMJArtifactVersionInput,
|
|
@@ -23659,9 +23716,8 @@ export class MJCollectionArtifact_ {
|
|
|
23659
23716
|
@MaxLength(255)
|
|
23660
23717
|
Collection: string;
|
|
23661
23718
|
|
|
23662
|
-
@Field(
|
|
23663
|
-
|
|
23664
|
-
ArtifactVersion?: string;
|
|
23719
|
+
@Field(() => Int)
|
|
23720
|
+
ArtifactVersion: number;
|
|
23665
23721
|
|
|
23666
23722
|
}
|
|
23667
23723
|
|
|
@@ -32631,9 +32687,8 @@ export class MJConversationDetailArtifact_ {
|
|
|
32631
32687
|
@Field()
|
|
32632
32688
|
ConversationDetail: string;
|
|
32633
32689
|
|
|
32634
|
-
@Field(
|
|
32635
|
-
|
|
32636
|
-
ArtifactVersion?: string;
|
|
32690
|
+
@Field(() => Int)
|
|
32691
|
+
ArtifactVersion: number;
|
|
32637
32692
|
|
|
32638
32693
|
}
|
|
32639
32694
|
|
|
@@ -32828,6 +32883,10 @@ export class MJConversationDetailAttachment_ {
|
|
|
32828
32883
|
@Field({nullable: true, description: `Description of the attachment providing context about its content and purpose.`})
|
|
32829
32884
|
Description?: string;
|
|
32830
32885
|
|
|
32886
|
+
@Field({nullable: true, description: `Foreign key to the ArtifactVersion created alongside this attachment by the storage-unification path. When set, the agent resolver routes via the artifact path (manifest + tool dispatch) and skips inline embedding of the attachment to avoid double-processing. NULL for pre-v5.35 attachment rows authored before storage unification.`})
|
|
32887
|
+
@MaxLength(36)
|
|
32888
|
+
ArtifactVersionID?: string;
|
|
32889
|
+
|
|
32831
32890
|
@Field()
|
|
32832
32891
|
ConversationDetail: string;
|
|
32833
32892
|
|
|
@@ -32839,6 +32898,9 @@ export class MJConversationDetailAttachment_ {
|
|
|
32839
32898
|
@MaxLength(500)
|
|
32840
32899
|
File?: string;
|
|
32841
32900
|
|
|
32901
|
+
@Field(() => Int, {nullable: true})
|
|
32902
|
+
ArtifactVersion?: number;
|
|
32903
|
+
|
|
32842
32904
|
}
|
|
32843
32905
|
|
|
32844
32906
|
//****************************************************************************
|
|
@@ -32888,6 +32950,9 @@ export class CreateMJConversationDetailAttachmentInput {
|
|
|
32888
32950
|
@Field({ nullable: true })
|
|
32889
32951
|
Description: string | null;
|
|
32890
32952
|
|
|
32953
|
+
@Field({ nullable: true })
|
|
32954
|
+
ArtifactVersionID: string | null;
|
|
32955
|
+
|
|
32891
32956
|
@Field(() => RestoreContextInput, { nullable: true })
|
|
32892
32957
|
RestoreContext___?: RestoreContextInput;
|
|
32893
32958
|
}
|
|
@@ -32940,6 +33005,9 @@ export class UpdateMJConversationDetailAttachmentInput {
|
|
|
32940
33005
|
@Field({ nullable: true })
|
|
32941
33006
|
Description?: string | null;
|
|
32942
33007
|
|
|
33008
|
+
@Field({ nullable: true })
|
|
33009
|
+
ArtifactVersionID?: string | null;
|
|
33010
|
+
|
|
32943
33011
|
@Field(() => [KeyValuePairInput], { nullable: true })
|
|
32944
33012
|
OldValues___?: KeyValuePairInput[];
|
|
32945
33013
|
|
|
@@ -51872,6 +51940,27 @@ export class MJList_ {
|
|
|
51872
51940
|
@Field()
|
|
51873
51941
|
_mj__UpdatedAt: Date;
|
|
51874
51942
|
|
|
51943
|
+
@Field({nullable: true, description: `Optional ID of the User View this list was materialized from. NULL for hand-built lists. When set, the list can be refreshed against this view via ListOperations.RefreshFromSource.`})
|
|
51944
|
+
@MaxLength(36)
|
|
51945
|
+
SourceViewID?: string;
|
|
51946
|
+
|
|
51947
|
+
@Field({nullable: true, description: `JSON snapshot of the source filter at materialization time. When UseSnapshot=1, refreshes re-apply this snapshot rather than re-reading the live source view. Null when no snapshot was captured.`})
|
|
51948
|
+
SourceFilterSnapshot?: string;
|
|
51949
|
+
|
|
51950
|
+
@Field({nullable: true, description: `Timestamp (UTC) of the most recent successful RefreshFromSource. Null when the list has never been refreshed.`})
|
|
51951
|
+
LastRefreshedAt?: Date;
|
|
51952
|
+
|
|
51953
|
+
@Field({nullable: true, description: `User who triggered the most recent successful RefreshFromSource. Null when the list has never been refreshed.`})
|
|
51954
|
+
@MaxLength(36)
|
|
51955
|
+
LastRefreshedByUserID?: string;
|
|
51956
|
+
|
|
51957
|
+
@Field({description: `Default refresh mode for this list. Additive only adds new members; Sync reconciles in both directions (may remove members no longer in the source — requires explicit drop-confirmation).`})
|
|
51958
|
+
@MaxLength(20)
|
|
51959
|
+
RefreshMode: string;
|
|
51960
|
+
|
|
51961
|
+
@Field(() => Boolean, {description: `When 1, RefreshFromSource uses SourceFilterSnapshot as the source. When 0 (default), it re-reads the live SourceView.`})
|
|
51962
|
+
UseSnapshot: boolean;
|
|
51963
|
+
|
|
51875
51964
|
@Field()
|
|
51876
51965
|
@MaxLength(255)
|
|
51877
51966
|
Entity: string;
|
|
@@ -51888,6 +51977,14 @@ export class MJList_ {
|
|
|
51888
51977
|
@MaxLength(255)
|
|
51889
51978
|
CompanyIntegration?: string;
|
|
51890
51979
|
|
|
51980
|
+
@Field({nullable: true})
|
|
51981
|
+
@MaxLength(100)
|
|
51982
|
+
SourceView?: string;
|
|
51983
|
+
|
|
51984
|
+
@Field({nullable: true})
|
|
51985
|
+
@MaxLength(100)
|
|
51986
|
+
LastRefreshedByUser?: string;
|
|
51987
|
+
|
|
51891
51988
|
@Field(() => [MJDuplicateRun_])
|
|
51892
51989
|
MJDuplicateRuns_SourceListIDArray: MJDuplicateRun_[]; // Link to MJDuplicateRuns
|
|
51893
51990
|
|
|
@@ -51931,6 +52028,24 @@ export class CreateMJListInput {
|
|
|
51931
52028
|
@Field({ nullable: true })
|
|
51932
52029
|
CompanyIntegrationID: string | null;
|
|
51933
52030
|
|
|
52031
|
+
@Field({ nullable: true })
|
|
52032
|
+
SourceViewID: string | null;
|
|
52033
|
+
|
|
52034
|
+
@Field({ nullable: true })
|
|
52035
|
+
SourceFilterSnapshot: string | null;
|
|
52036
|
+
|
|
52037
|
+
@Field({ nullable: true })
|
|
52038
|
+
LastRefreshedAt: Date | null;
|
|
52039
|
+
|
|
52040
|
+
@Field({ nullable: true })
|
|
52041
|
+
LastRefreshedByUserID: string | null;
|
|
52042
|
+
|
|
52043
|
+
@Field({ nullable: true })
|
|
52044
|
+
RefreshMode?: string;
|
|
52045
|
+
|
|
52046
|
+
@Field(() => Boolean, { nullable: true })
|
|
52047
|
+
UseSnapshot?: boolean;
|
|
52048
|
+
|
|
51934
52049
|
@Field(() => RestoreContextInput, { nullable: true })
|
|
51935
52050
|
RestoreContext___?: RestoreContextInput;
|
|
51936
52051
|
}
|
|
@@ -51965,6 +52080,24 @@ export class UpdateMJListInput {
|
|
|
51965
52080
|
@Field({ nullable: true })
|
|
51966
52081
|
CompanyIntegrationID?: string | null;
|
|
51967
52082
|
|
|
52083
|
+
@Field({ nullable: true })
|
|
52084
|
+
SourceViewID?: string | null;
|
|
52085
|
+
|
|
52086
|
+
@Field({ nullable: true })
|
|
52087
|
+
SourceFilterSnapshot?: string | null;
|
|
52088
|
+
|
|
52089
|
+
@Field({ nullable: true })
|
|
52090
|
+
LastRefreshedAt?: Date | null;
|
|
52091
|
+
|
|
52092
|
+
@Field({ nullable: true })
|
|
52093
|
+
LastRefreshedByUserID?: string | null;
|
|
52094
|
+
|
|
52095
|
+
@Field({ nullable: true })
|
|
52096
|
+
RefreshMode?: string;
|
|
52097
|
+
|
|
52098
|
+
@Field(() => Boolean, { nullable: true })
|
|
52099
|
+
UseSnapshot?: boolean;
|
|
52100
|
+
|
|
51968
52101
|
@Field(() => [KeyValuePairInput], { nullable: true })
|
|
51969
52102
|
OldValues___?: KeyValuePairInput[];
|
|
51970
52103
|
|
|
@@ -60908,7 +61041,7 @@ export class MJRecordGeoCode_ {
|
|
|
60908
61041
|
@Field({nullable: true, description: `Timestamp of when geocoding was last attempted (success or failure).`})
|
|
60909
61042
|
GeocodedAt?: Date;
|
|
60910
61043
|
|
|
60911
|
-
@Field({nullable: true, description: `
|
|
61044
|
+
@Field({nullable: true, description: `Source that produced this geocode. One of: google, geocodio, here, reference_data, manual, ip_geolocation, native, reverse.`})
|
|
60912
61045
|
@MaxLength(30)
|
|
60913
61046
|
GeocodingSource?: string;
|
|
60914
61047
|
|
|
@@ -76853,6 +76986,9 @@ export class MJUserView_ {
|
|
|
76853
76986
|
@Field(() => [MJUserViewRun_])
|
|
76854
76987
|
MJUserViewRuns_UserViewIDArray: MJUserViewRun_[]; // Link to MJUserViewRuns
|
|
76855
76988
|
|
|
76989
|
+
@Field(() => [MJList_])
|
|
76990
|
+
MJLists_SourceViewIDArray: MJList_[]; // Link to MJLists
|
|
76991
|
+
|
|
76856
76992
|
}
|
|
76857
76993
|
|
|
76858
76994
|
//****************************************************************************
|
|
@@ -77100,6 +77236,16 @@ export class MJUserViewResolverBase extends ResolverBase {
|
|
|
77100
77236
|
return result;
|
|
77101
77237
|
}
|
|
77102
77238
|
|
|
77239
|
+
@FieldResolver(() => [MJList_])
|
|
77240
|
+
async MJLists_SourceViewIDArray(@Root() mjuserview_: MJUserView_, @Ctx() { userPayload, providers }: AppContext, @PubSub() pubSub: PubSubEngine) {
|
|
77241
|
+
this.CheckUserReadPermissions('MJ: Lists', userPayload);
|
|
77242
|
+
const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
|
|
77243
|
+
const sSQL = `SELECT * FROM ${provider.QuoteSchemaAndView(Metadata.Provider.ConfigData.MJCoreSchemaName, 'vwLists')} WHERE ${provider.QuoteIdentifier('SourceViewID')}='${mjuserview_.ID}' ` + this.getRowLevelSecurityWhereClause(provider, 'MJ: Lists', userPayload, EntityPermissionType.Read, 'AND');
|
|
77244
|
+
const rows = await provider.ExecuteSQL(sSQL, undefined, undefined, this.GetUserFromPayload(userPayload));
|
|
77245
|
+
const result = await this.ArrayMapFieldNamesToCodeNames('MJ: Lists', rows, this.GetUserFromPayload(userPayload));
|
|
77246
|
+
return result;
|
|
77247
|
+
}
|
|
77248
|
+
|
|
77103
77249
|
@Mutation(() => MJUserView_)
|
|
77104
77250
|
async CreateMJUserView(
|
|
77105
77251
|
@Arg('input', () => CreateMJUserViewInput) input: CreateMJUserViewInput,
|
|
@@ -77506,6 +77652,9 @@ export class MJUser_ {
|
|
|
77506
77652
|
@Field(() => [MJAIAgent_])
|
|
77507
77653
|
MJAIAgents_OwnerUserIDArray: MJAIAgent_[]; // Link to MJAIAgents
|
|
77508
77654
|
|
|
77655
|
+
@Field(() => [MJList_])
|
|
77656
|
+
MJLists_LastRefreshedByUserIDArray: MJList_[]; // Link to MJLists
|
|
77657
|
+
|
|
77509
77658
|
}
|
|
77510
77659
|
|
|
77511
77660
|
//****************************************************************************
|
|
@@ -78641,6 +78790,16 @@ export class MJUserResolverBase extends ResolverBase {
|
|
|
78641
78790
|
return result;
|
|
78642
78791
|
}
|
|
78643
78792
|
|
|
78793
|
+
@FieldResolver(() => [MJList_])
|
|
78794
|
+
async MJLists_LastRefreshedByUserIDArray(@Root() mjuser_: MJUser_, @Ctx() { userPayload, providers }: AppContext, @PubSub() pubSub: PubSubEngine) {
|
|
78795
|
+
this.CheckUserReadPermissions('MJ: Lists', userPayload);
|
|
78796
|
+
const provider = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
|
|
78797
|
+
const sSQL = `SELECT * FROM ${provider.QuoteSchemaAndView(Metadata.Provider.ConfigData.MJCoreSchemaName, 'vwLists')} WHERE ${provider.QuoteIdentifier('LastRefreshedByUserID')}='${mjuser_.ID}' ` + this.getRowLevelSecurityWhereClause(provider, 'MJ: Lists', userPayload, EntityPermissionType.Read, 'AND');
|
|
78798
|
+
const rows = await provider.ExecuteSQL(sSQL, undefined, undefined, this.GetUserFromPayload(userPayload));
|
|
78799
|
+
const result = await this.ArrayMapFieldNamesToCodeNames('MJ: Lists', rows, this.GetUserFromPayload(userPayload));
|
|
78800
|
+
return result;
|
|
78801
|
+
}
|
|
78802
|
+
|
|
78644
78803
|
@Mutation(() => MJUser_)
|
|
78645
78804
|
async CreateMJUser(
|
|
78646
78805
|
@Arg('input', () => CreateMJUserInput) input: CreateMJUserInput,
|
|
@@ -359,7 +359,8 @@ export class ResolverBase {
|
|
|
359
359
|
viewInput.Aggregates,
|
|
360
360
|
viewInput.AfterKey
|
|
361
361
|
? CompositeKey.FromKeyValuePairs((viewInput.AfterKey as { KeyValuePairs: { FieldName: string; Value: string }[] }).KeyValuePairs)
|
|
362
|
-
: undefined
|
|
362
|
+
: undefined,
|
|
363
|
+
viewInput.BypassCache
|
|
363
364
|
);
|
|
364
365
|
}
|
|
365
366
|
else {
|
|
@@ -400,7 +401,9 @@ export class ResolverBase {
|
|
|
400
401
|
userPayload,
|
|
401
402
|
viewInput.MaxRows,
|
|
402
403
|
viewInput.StartRow,
|
|
403
|
-
viewInput.Aggregates
|
|
404
|
+
viewInput.Aggregates,
|
|
405
|
+
undefined,
|
|
406
|
+
viewInput.BypassCache
|
|
404
407
|
);
|
|
405
408
|
} catch (err) {
|
|
406
409
|
console.log(err);
|
|
@@ -444,7 +447,9 @@ export class ResolverBase {
|
|
|
444
447
|
userPayload,
|
|
445
448
|
viewInput.MaxRows,
|
|
446
449
|
viewInput.StartRow,
|
|
447
|
-
viewInput.Aggregates
|
|
450
|
+
viewInput.Aggregates,
|
|
451
|
+
undefined,
|
|
452
|
+
viewInput.BypassCache
|
|
448
453
|
);
|
|
449
454
|
} catch (err) {
|
|
450
455
|
console.log(err);
|
|
@@ -517,6 +522,7 @@ export class ResolverBase {
|
|
|
517
522
|
resultType: viewInput.ResultType,
|
|
518
523
|
userPayload,
|
|
519
524
|
aggregates: viewInput.Aggregates,
|
|
525
|
+
bypassCache: viewInput.BypassCache,
|
|
520
526
|
});
|
|
521
527
|
} catch (err) {
|
|
522
528
|
LogError(err);
|
|
@@ -693,7 +699,8 @@ export class ResolverBase {
|
|
|
693
699
|
maxRows: number | undefined,
|
|
694
700
|
startRow: number | undefined,
|
|
695
701
|
aggregates?: AggregateExpression[],
|
|
696
|
-
afterKey?: CompositeKey
|
|
702
|
+
afterKey?: CompositeKey,
|
|
703
|
+
bypassCache?: boolean
|
|
697
704
|
) {
|
|
698
705
|
try {
|
|
699
706
|
if (!viewInfo || !userPayload) return null;
|
|
@@ -757,6 +764,7 @@ export class ResolverBase {
|
|
|
757
764
|
AuditLogDescription: auditLogDescription,
|
|
758
765
|
ResultType: rt,
|
|
759
766
|
Aggregates: aggregates,
|
|
767
|
+
BypassCache: bypassCache,
|
|
760
768
|
},
|
|
761
769
|
user
|
|
762
770
|
);
|
|
@@ -870,6 +878,7 @@ export class ResolverBase {
|
|
|
870
878
|
AuditLogDescription: param.auditLogDescription,
|
|
871
879
|
ResultType: rt,
|
|
872
880
|
Aggregates: param.aggregates,
|
|
881
|
+
BypassCache: param.bypassCache,
|
|
873
882
|
});
|
|
874
883
|
}
|
|
875
884
|
|
|
@@ -170,6 +170,13 @@ export class RunViewByIDInput {
|
|
|
170
170
|
description: 'Optional aggregate expressions to calculate on the full result set (e.g., SUM, COUNT, AVG). Results are returned in AggregateResults.',
|
|
171
171
|
})
|
|
172
172
|
Aggregates?: AggregateExpressionInput[];
|
|
173
|
+
|
|
174
|
+
@Field(() => Boolean, {
|
|
175
|
+
nullable: true,
|
|
176
|
+
description:
|
|
177
|
+
'Optional, when true bypasses ALL server-side caching for this view run — the pre-check cache lookup is skipped and the result is not stored in the cache. Use for maintenance/audit queries that must see true database state, or to force-refresh views whose filters reference rows the server cache invalidator cannot follow (e.g., cross-entity subqueries against vwListDetails).',
|
|
178
|
+
})
|
|
179
|
+
BypassCache?: boolean;
|
|
173
180
|
}
|
|
174
181
|
|
|
175
182
|
@InputType()
|
|
@@ -277,6 +284,13 @@ export class RunViewByNameInput {
|
|
|
277
284
|
description: 'Optional aggregate expressions to calculate on the full result set (e.g., SUM, COUNT, AVG). Results are returned in AggregateResults.',
|
|
278
285
|
})
|
|
279
286
|
Aggregates?: AggregateExpressionInput[];
|
|
287
|
+
|
|
288
|
+
@Field(() => Boolean, {
|
|
289
|
+
nullable: true,
|
|
290
|
+
description:
|
|
291
|
+
'Optional, when true bypasses ALL server-side caching for this view run — the pre-check cache lookup is skipped and the result is not stored in the cache. Use for maintenance/audit queries that must see true database state, or to force-refresh views whose filters reference rows the server cache invalidator cannot follow (e.g., cross-entity subqueries against vwListDetails).',
|
|
292
|
+
})
|
|
293
|
+
BypassCache?: boolean;
|
|
280
294
|
}
|
|
281
295
|
|
|
282
296
|
@InputType()
|
|
@@ -370,6 +384,13 @@ export class RunDynamicViewInput {
|
|
|
370
384
|
description: 'Optional aggregate expressions to calculate on the full result set (e.g., SUM, COUNT, AVG). Results are returned in AggregateResults.',
|
|
371
385
|
})
|
|
372
386
|
Aggregates?: AggregateExpressionInput[];
|
|
387
|
+
|
|
388
|
+
@Field(() => Boolean, {
|
|
389
|
+
nullable: true,
|
|
390
|
+
description:
|
|
391
|
+
'Optional, when true bypasses ALL server-side caching for this view run — the pre-check cache lookup is skipped and the result is not stored in the cache. Use for maintenance/audit queries that must see true database state, or to force-refresh views whose filters reference rows the server cache invalidator cannot follow (e.g., cross-entity subqueries against vwListDetails).',
|
|
392
|
+
})
|
|
393
|
+
BypassCache?: boolean;
|
|
373
394
|
}
|
|
374
395
|
|
|
375
396
|
@InputType()
|
|
@@ -492,6 +513,13 @@ export class RunViewGenericInput {
|
|
|
492
513
|
description: 'Optional aggregate expressions to calculate on the full result set (e.g., SUM, COUNT, AVG). Results are returned in AggregateResults.',
|
|
493
514
|
})
|
|
494
515
|
Aggregates?: AggregateExpressionInput[];
|
|
516
|
+
|
|
517
|
+
@Field(() => Boolean, {
|
|
518
|
+
nullable: true,
|
|
519
|
+
description:
|
|
520
|
+
'Optional, when true bypasses ALL server-side caching for this view run — the pre-check cache lookup is skipped and the result is not stored in the cache. Use for maintenance/audit queries that must see true database state, or to force-refresh views whose filters reference rows the server cache invalidator cannot follow (e.g., cross-entity subqueries against vwListDetails).',
|
|
521
|
+
})
|
|
522
|
+
BypassCache?: boolean;
|
|
495
523
|
}
|
|
496
524
|
|
|
497
525
|
//****************************************************************************
|
|
@@ -18,6 +18,12 @@ class AdhocQueryInput {
|
|
|
18
18
|
|
|
19
19
|
@Field(() => Int, { nullable: true, description: 'Query timeout in seconds. Defaults to 30.' })
|
|
20
20
|
TimeoutSeconds?: number;
|
|
21
|
+
|
|
22
|
+
@Field(() => Int, { nullable: true, description: 'Maximum number of rows to return. Applied in-memory after SQL execution; SQL still runs unbounded server-side.' })
|
|
23
|
+
MaxRows?: number;
|
|
24
|
+
|
|
25
|
+
@Field(() => Int, { nullable: true, description: 'Zero-based offset for pagination. Used in conjunction with MaxRows.' })
|
|
26
|
+
StartRow?: number;
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
/**
|
|
@@ -56,28 +62,55 @@ export class AdhocQueryResolver extends ResolverBase {
|
|
|
56
62
|
return this.buildErrorResult('No read-only data source available for ad-hoc query execution');
|
|
57
63
|
}
|
|
58
64
|
|
|
59
|
-
// 3.
|
|
65
|
+
// 3. Build executable SQL. When MaxRows is provided, wrap in a derived table
|
|
66
|
+
// with outer TOP so the engine can short-circuit at the source instead of
|
|
67
|
+
// scanning the full result. Skipped for SQL that begins with WITH/CTE — those
|
|
68
|
+
// can't be nested in a derived table on SQL Server and fall through to the
|
|
69
|
+
// in-memory slice below.
|
|
70
|
+
const startRow = input.StartRow ?? 0;
|
|
71
|
+
const maxRows = input.MaxRows;
|
|
72
|
+
const canWrap =
|
|
73
|
+
maxRows != null &&
|
|
74
|
+
Number.isInteger(maxRows) &&
|
|
75
|
+
maxRows > 0 &&
|
|
76
|
+
Number.isInteger(startRow) &&
|
|
77
|
+
startRow >= 0 &&
|
|
78
|
+
!/^\s*WITH\b/i.test(input.SQL);
|
|
79
|
+
const executableSql = canWrap
|
|
80
|
+
? `SELECT TOP ${startRow + maxRows} * FROM (\n${input.SQL}\n) AS _adhoc_capped`
|
|
81
|
+
: input.SQL;
|
|
82
|
+
|
|
83
|
+
// 4. Execute with timeout
|
|
60
84
|
const timeoutMs = (input.TimeoutSeconds ?? 30) * 1000;
|
|
61
85
|
const request = new sql.Request(readOnlyDS);
|
|
62
86
|
|
|
63
87
|
const result = await Promise.race([
|
|
64
|
-
request.query(
|
|
88
|
+
request.query(executableSql),
|
|
65
89
|
new Promise<never>((_, reject) =>
|
|
66
90
|
setTimeout(() => reject(new Error('Query timeout exceeded')), timeoutMs)
|
|
67
91
|
)
|
|
68
92
|
]);
|
|
69
93
|
const executionTimeMs = Date.now() - startTime;
|
|
70
94
|
|
|
71
|
-
//
|
|
95
|
+
// 5. Apply in-memory pagination. With the wrap applied this is a no-op for
|
|
96
|
+
// first-page reads; for StartRow > 0 (or CTE-headed SQL where the wrap was
|
|
97
|
+
// skipped) it carves out the requested page.
|
|
98
|
+
const fullRecordset = result.recordset ?? [];
|
|
99
|
+
const totalRowCount = fullRecordset.length;
|
|
100
|
+
let paginated = fullRecordset;
|
|
101
|
+
if (startRow > 0) paginated = paginated.slice(startRow);
|
|
102
|
+
if (maxRows != null && maxRows > 0) paginated = paginated.slice(0, maxRows);
|
|
103
|
+
|
|
104
|
+
// 6. Return as RunQueryResultType
|
|
72
105
|
return {
|
|
73
106
|
QueryID: '',
|
|
74
107
|
QueryName: 'Ad-Hoc Query',
|
|
75
108
|
Success: true,
|
|
76
|
-
Results: JSON.stringify(
|
|
77
|
-
RowCount:
|
|
78
|
-
TotalRowCount:
|
|
79
|
-
PageNumber: undefined,
|
|
80
|
-
PageSize: undefined,
|
|
109
|
+
Results: JSON.stringify(paginated),
|
|
110
|
+
RowCount: paginated.length,
|
|
111
|
+
TotalRowCount: totalRowCount,
|
|
112
|
+
PageNumber: maxRows != null && maxRows > 0 ? Math.floor(startRow / maxRows) + 1 : undefined,
|
|
113
|
+
PageSize: maxRows ?? undefined,
|
|
81
114
|
ExecutionTime: executionTimeMs,
|
|
82
115
|
ErrorMessage: ''
|
|
83
116
|
};
|