@grantjs/schema 1.14.3 → 1.16.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/package.json +1 -1
- package/src/generated/graphql.ts +112 -0
- package/src/generated/resolvers.ts +32 -0
- package/src/generated/schema-types.ts +148 -5
- package/src/operations/projects/project-sync-job.graphql +4 -0
- package/src/operations/projects/request-project-sync-upload-url.graphql +16 -0
- package/src/schema/projects/inputs/request-project-sync-upload-url.graphql +9 -0
- package/src/schema/projects/inputs/sync-project-cdm.graphql +81 -5
- package/src/schema/projects/mutations/request-project-sync-upload-url.graphql +19 -0
- package/src/schema/projects/types/project-sync-upload-url.graphql +24 -0
- package/src/schema/projects/types/sync-project-result.graphql +20 -0
- package/src/sdl-contract.test.ts +2 -1
package/package.json
CHANGED
package/src/generated/graphql.ts
CHANGED
|
@@ -1465,6 +1465,10 @@ export type ProjectSyncJobQuery = {
|
|
|
1465
1465
|
usersCreated: number;
|
|
1466
1466
|
userRolesAssigned: number;
|
|
1467
1467
|
projectUserApiKeysCreated: number;
|
|
1468
|
+
passwordsImported: number;
|
|
1469
|
+
passwordsUpdated: number;
|
|
1470
|
+
passwordsUnchanged: number;
|
|
1471
|
+
passwordsSkippedExisting: number;
|
|
1468
1472
|
warnings: Array<string>;
|
|
1469
1473
|
} | null;
|
|
1470
1474
|
};
|
|
@@ -1505,6 +1509,22 @@ export type ProjectSyncJobsQuery = {
|
|
|
1505
1509
|
};
|
|
1506
1510
|
};
|
|
1507
1511
|
|
|
1512
|
+
export type RequestProjectSyncUploadUrlMutationVariables = Exact<{
|
|
1513
|
+
id: string | number;
|
|
1514
|
+
scope: Types.Scope;
|
|
1515
|
+
input: Types.RequestProjectSyncUploadUrlInput;
|
|
1516
|
+
}>;
|
|
1517
|
+
|
|
1518
|
+
export type RequestProjectSyncUploadUrlMutation = {
|
|
1519
|
+
requestProjectSyncUploadUrl: {
|
|
1520
|
+
uploadId: string;
|
|
1521
|
+
url: string;
|
|
1522
|
+
method: string;
|
|
1523
|
+
expiresAt: Date;
|
|
1524
|
+
headers: Array<{ name: string; value: string }>;
|
|
1525
|
+
};
|
|
1526
|
+
};
|
|
1527
|
+
|
|
1508
1528
|
export type StartProjectExportMutationVariables = Exact<{
|
|
1509
1529
|
id: string | number;
|
|
1510
1530
|
scope: Types.Scope;
|
|
@@ -8001,6 +8021,10 @@ export const ProjectSyncJobDocument = {
|
|
|
8001
8021
|
{ kind: 'Field', name: { kind: 'Name', value: 'usersCreated' } },
|
|
8002
8022
|
{ kind: 'Field', name: { kind: 'Name', value: 'userRolesAssigned' } },
|
|
8003
8023
|
{ kind: 'Field', name: { kind: 'Name', value: 'projectUserApiKeysCreated' } },
|
|
8024
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'passwordsImported' } },
|
|
8025
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'passwordsUpdated' } },
|
|
8026
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'passwordsUnchanged' } },
|
|
8027
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'passwordsSkippedExisting' } },
|
|
8004
8028
|
{ kind: 'Field', name: { kind: 'Name', value: 'warnings' } },
|
|
8005
8029
|
],
|
|
8006
8030
|
},
|
|
@@ -8153,6 +8177,94 @@ export const ProjectSyncJobsDocument = {
|
|
|
8153
8177
|
},
|
|
8154
8178
|
],
|
|
8155
8179
|
} as unknown as DocumentNode<ProjectSyncJobsQuery, ProjectSyncJobsQueryVariables>;
|
|
8180
|
+
export const RequestProjectSyncUploadUrlDocument = {
|
|
8181
|
+
kind: 'Document',
|
|
8182
|
+
definitions: [
|
|
8183
|
+
{
|
|
8184
|
+
kind: 'OperationDefinition',
|
|
8185
|
+
operation: 'mutation',
|
|
8186
|
+
name: { kind: 'Name', value: 'RequestProjectSyncUploadUrl' },
|
|
8187
|
+
variableDefinitions: [
|
|
8188
|
+
{
|
|
8189
|
+
kind: 'VariableDefinition',
|
|
8190
|
+
variable: { kind: 'Variable', name: { kind: 'Name', value: 'id' } },
|
|
8191
|
+
type: {
|
|
8192
|
+
kind: 'NonNullType',
|
|
8193
|
+
type: { kind: 'NamedType', name: { kind: 'Name', value: 'ID' } },
|
|
8194
|
+
},
|
|
8195
|
+
},
|
|
8196
|
+
{
|
|
8197
|
+
kind: 'VariableDefinition',
|
|
8198
|
+
variable: { kind: 'Variable', name: { kind: 'Name', value: 'scope' } },
|
|
8199
|
+
type: {
|
|
8200
|
+
kind: 'NonNullType',
|
|
8201
|
+
type: { kind: 'NamedType', name: { kind: 'Name', value: 'Scope' } },
|
|
8202
|
+
},
|
|
8203
|
+
},
|
|
8204
|
+
{
|
|
8205
|
+
kind: 'VariableDefinition',
|
|
8206
|
+
variable: { kind: 'Variable', name: { kind: 'Name', value: 'input' } },
|
|
8207
|
+
type: {
|
|
8208
|
+
kind: 'NonNullType',
|
|
8209
|
+
type: {
|
|
8210
|
+
kind: 'NamedType',
|
|
8211
|
+
name: { kind: 'Name', value: 'RequestProjectSyncUploadUrlInput' },
|
|
8212
|
+
},
|
|
8213
|
+
},
|
|
8214
|
+
},
|
|
8215
|
+
],
|
|
8216
|
+
selectionSet: {
|
|
8217
|
+
kind: 'SelectionSet',
|
|
8218
|
+
selections: [
|
|
8219
|
+
{
|
|
8220
|
+
kind: 'Field',
|
|
8221
|
+
name: { kind: 'Name', value: 'requestProjectSyncUploadUrl' },
|
|
8222
|
+
arguments: [
|
|
8223
|
+
{
|
|
8224
|
+
kind: 'Argument',
|
|
8225
|
+
name: { kind: 'Name', value: 'id' },
|
|
8226
|
+
value: { kind: 'Variable', name: { kind: 'Name', value: 'id' } },
|
|
8227
|
+
},
|
|
8228
|
+
{
|
|
8229
|
+
kind: 'Argument',
|
|
8230
|
+
name: { kind: 'Name', value: 'scope' },
|
|
8231
|
+
value: { kind: 'Variable', name: { kind: 'Name', value: 'scope' } },
|
|
8232
|
+
},
|
|
8233
|
+
{
|
|
8234
|
+
kind: 'Argument',
|
|
8235
|
+
name: { kind: 'Name', value: 'input' },
|
|
8236
|
+
value: { kind: 'Variable', name: { kind: 'Name', value: 'input' } },
|
|
8237
|
+
},
|
|
8238
|
+
],
|
|
8239
|
+
selectionSet: {
|
|
8240
|
+
kind: 'SelectionSet',
|
|
8241
|
+
selections: [
|
|
8242
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'uploadId' } },
|
|
8243
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'url' } },
|
|
8244
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'method' } },
|
|
8245
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'expiresAt' } },
|
|
8246
|
+
{
|
|
8247
|
+
kind: 'Field',
|
|
8248
|
+
name: { kind: 'Name', value: 'headers' },
|
|
8249
|
+
selectionSet: {
|
|
8250
|
+
kind: 'SelectionSet',
|
|
8251
|
+
selections: [
|
|
8252
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
|
|
8253
|
+
{ kind: 'Field', name: { kind: 'Name', value: 'value' } },
|
|
8254
|
+
],
|
|
8255
|
+
},
|
|
8256
|
+
},
|
|
8257
|
+
],
|
|
8258
|
+
},
|
|
8259
|
+
},
|
|
8260
|
+
],
|
|
8261
|
+
},
|
|
8262
|
+
},
|
|
8263
|
+
],
|
|
8264
|
+
} as unknown as DocumentNode<
|
|
8265
|
+
RequestProjectSyncUploadUrlMutation,
|
|
8266
|
+
RequestProjectSyncUploadUrlMutationVariables
|
|
8267
|
+
>;
|
|
8156
8268
|
export const StartProjectExportDocument = {
|
|
8157
8269
|
kind: 'Document',
|
|
8158
8270
|
definitions: [
|
|
@@ -237,6 +237,8 @@ export type ResolversTypes = ResolversObject<{
|
|
|
237
237
|
CdmModeInput: Types.CdmModeInput;
|
|
238
238
|
CdmModeStrategy: Types.CdmModeStrategy;
|
|
239
239
|
CdmOnConflict: Types.CdmOnConflict;
|
|
240
|
+
CdmPasswordAlgorithm: Types.CdmPasswordAlgorithm;
|
|
241
|
+
CdmPasswordInput: Types.CdmPasswordInput;
|
|
240
242
|
ChangeMyPasswordInput: Types.ChangeMyPasswordInput;
|
|
241
243
|
ChangeMyPasswordResult: ResolverTypeWrapper<Types.ChangeMyPasswordResult>;
|
|
242
244
|
ClearProjectAppPictureInput: Types.ClearProjectAppPictureInput;
|
|
@@ -378,6 +380,7 @@ export type ResolversTypes = ResolversObject<{
|
|
|
378
380
|
ProjectSyncJobSortInput: Types.ProjectSyncJobSortInput;
|
|
379
381
|
ProjectSyncJobSortableField: Types.ProjectSyncJobSortableField;
|
|
380
382
|
ProjectSyncJobStatus: Types.ProjectSyncJobStatus;
|
|
383
|
+
ProjectSyncUploadUrl: ResolverTypeWrapper<Types.ProjectSyncUploadUrl>;
|
|
381
384
|
ProjectTag: ResolverTypeWrapper<Types.ProjectTag>;
|
|
382
385
|
ProjectUser: ResolverTypeWrapper<Types.ProjectUser>;
|
|
383
386
|
ProjectUserApiKey: ResolverTypeWrapper<Types.ProjectUserApiKey>;
|
|
@@ -464,6 +467,7 @@ export type ResolversTypes = ResolversObject<{
|
|
|
464
467
|
RequestPasswordResetResponse: ResolverTypeWrapper<Types.RequestPasswordResetResponse>;
|
|
465
468
|
RequestProjectAppPictureUploadUrlInput: Types.RequestProjectAppPictureUploadUrlInput;
|
|
466
469
|
RequestProjectPictureUploadUrlInput: Types.RequestProjectPictureUploadUrlInput;
|
|
470
|
+
RequestProjectSyncUploadUrlInput: Types.RequestProjectSyncUploadUrlInput;
|
|
467
471
|
RequestUserPictureUploadUrlInput: Types.RequestUserPictureUploadUrlInput;
|
|
468
472
|
ResendVerificationInput: Types.ResendVerificationInput;
|
|
469
473
|
ResendVerificationResponse: ResolverTypeWrapper<Types.ResendVerificationResponse>;
|
|
@@ -502,6 +506,7 @@ export type ResolversTypes = ResolversObject<{
|
|
|
502
506
|
String: ResolverTypeWrapper<Types.Scalars['String']['output']>;
|
|
503
507
|
SyncProjectInput: Types.SyncProjectInput;
|
|
504
508
|
SyncProjectResult: ResolverTypeWrapper<Types.SyncProjectResult>;
|
|
509
|
+
SyncProjectSourceInput: Types.SyncProjectSourceInput;
|
|
505
510
|
Tag: ResolverTypeWrapper<Types.Tag>;
|
|
506
511
|
TagCdmInput: Types.TagCdmInput;
|
|
507
512
|
TagPage: ResolverTypeWrapper<Types.TagPage>;
|
|
@@ -643,6 +648,7 @@ export type ResolversParentTypes = ResolversObject<{
|
|
|
643
648
|
Boolean: Types.Scalars['Boolean']['output'];
|
|
644
649
|
CdmKeyResolverInput: Types.CdmKeyResolverInput;
|
|
645
650
|
CdmModeInput: Types.CdmModeInput;
|
|
651
|
+
CdmPasswordInput: Types.CdmPasswordInput;
|
|
646
652
|
ChangeMyPasswordInput: Types.ChangeMyPasswordInput;
|
|
647
653
|
ChangeMyPasswordResult: Types.ChangeMyPasswordResult;
|
|
648
654
|
ClearProjectAppPictureInput: Types.ClearProjectAppPictureInput;
|
|
@@ -759,6 +765,7 @@ export type ResolversParentTypes = ResolversObject<{
|
|
|
759
765
|
ProjectSyncJob: Types.ProjectSyncJob;
|
|
760
766
|
ProjectSyncJobPage: Types.ProjectSyncJobPage;
|
|
761
767
|
ProjectSyncJobSortInput: Types.ProjectSyncJobSortInput;
|
|
768
|
+
ProjectSyncUploadUrl: Types.ProjectSyncUploadUrl;
|
|
762
769
|
ProjectTag: Types.ProjectTag;
|
|
763
770
|
ProjectUser: Types.ProjectUser;
|
|
764
771
|
ProjectUserApiKey: Types.ProjectUserApiKey;
|
|
@@ -845,6 +852,7 @@ export type ResolversParentTypes = ResolversObject<{
|
|
|
845
852
|
RequestPasswordResetResponse: Types.RequestPasswordResetResponse;
|
|
846
853
|
RequestProjectAppPictureUploadUrlInput: Types.RequestProjectAppPictureUploadUrlInput;
|
|
847
854
|
RequestProjectPictureUploadUrlInput: Types.RequestProjectPictureUploadUrlInput;
|
|
855
|
+
RequestProjectSyncUploadUrlInput: Types.RequestProjectSyncUploadUrlInput;
|
|
848
856
|
RequestUserPictureUploadUrlInput: Types.RequestUserPictureUploadUrlInput;
|
|
849
857
|
ResendVerificationInput: Types.ResendVerificationInput;
|
|
850
858
|
ResendVerificationResponse: Types.ResendVerificationResponse;
|
|
@@ -878,6 +886,7 @@ export type ResolversParentTypes = ResolversObject<{
|
|
|
878
886
|
String: Types.Scalars['String']['output'];
|
|
879
887
|
SyncProjectInput: Types.SyncProjectInput;
|
|
880
888
|
SyncProjectResult: Types.SyncProjectResult;
|
|
889
|
+
SyncProjectSourceInput: Types.SyncProjectSourceInput;
|
|
881
890
|
Tag: Types.Tag;
|
|
882
891
|
TagCdmInput: Types.TagCdmInput;
|
|
883
892
|
TagPage: Types.TagPage;
|
|
@@ -1839,6 +1848,12 @@ export type MutationResolvers<
|
|
|
1839
1848
|
ContextType,
|
|
1840
1849
|
RequireFields<Types.MutationRequestProjectPictureUploadUrlArgs, 'input'>
|
|
1841
1850
|
>;
|
|
1851
|
+
requestProjectSyncUploadUrl?: Resolver<
|
|
1852
|
+
ResolversTypes['ProjectSyncUploadUrl'],
|
|
1853
|
+
ParentType,
|
|
1854
|
+
ContextType,
|
|
1855
|
+
RequireFields<Types.MutationRequestProjectSyncUploadUrlArgs, 'id' | 'input' | 'scope'>
|
|
1856
|
+
>;
|
|
1842
1857
|
requestUserPictureUploadUrl?: Resolver<
|
|
1843
1858
|
ResolversTypes['UploadUrl'],
|
|
1844
1859
|
ParentType,
|
|
@@ -2733,6 +2748,18 @@ export type ProjectSyncJobPageResolvers<
|
|
|
2733
2748
|
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
2734
2749
|
}>;
|
|
2735
2750
|
|
|
2751
|
+
export type ProjectSyncUploadUrlResolvers<
|
|
2752
|
+
ContextType = any,
|
|
2753
|
+
ParentType extends ResolversParentTypes['ProjectSyncUploadUrl'] =
|
|
2754
|
+
ResolversParentTypes['ProjectSyncUploadUrl'],
|
|
2755
|
+
> = ResolversObject<{
|
|
2756
|
+
expiresAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
|
2757
|
+
headers?: Resolver<Array<ResolversTypes['UploadUrlHeader']>, ParentType, ContextType>;
|
|
2758
|
+
method?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2759
|
+
uploadId?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
|
2760
|
+
url?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2761
|
+
}>;
|
|
2762
|
+
|
|
2736
2763
|
export type ProjectTagResolvers<
|
|
2737
2764
|
ContextType = any,
|
|
2738
2765
|
ParentType extends ResolversParentTypes['ProjectTag'] = ResolversParentTypes['ProjectTag'],
|
|
@@ -3264,6 +3291,10 @@ export type SyncProjectResultResolvers<
|
|
|
3264
3291
|
groupTagsLinked?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
|
3265
3292
|
groupsCreated?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
|
3266
3293
|
importId?: Resolver<Types.Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
3294
|
+
passwordsImported?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
|
3295
|
+
passwordsSkippedExisting?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
|
3296
|
+
passwordsUnchanged?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
|
3297
|
+
passwordsUpdated?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
|
3267
3298
|
permissionsCreated?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
|
3268
3299
|
projectGroupsLinked?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
|
3269
3300
|
projectId?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
|
@@ -3760,6 +3791,7 @@ export type Resolvers<ContextType = any> = ResolversObject<{
|
|
|
3760
3791
|
ProjectRolePermission?: ProjectRolePermissionResolvers<ContextType>;
|
|
3761
3792
|
ProjectSyncJob?: ProjectSyncJobResolvers<ContextType>;
|
|
3762
3793
|
ProjectSyncJobPage?: ProjectSyncJobPageResolvers<ContextType>;
|
|
3794
|
+
ProjectSyncUploadUrl?: ProjectSyncUploadUrlResolvers<ContextType>;
|
|
3763
3795
|
ProjectTag?: ProjectTagResolvers<ContextType>;
|
|
3764
3796
|
ProjectUser?: ProjectUserResolvers<ContextType>;
|
|
3765
3797
|
ProjectUserApiKey?: ProjectUserApiKeyResolvers<ContextType>;
|
|
@@ -448,8 +448,9 @@ export enum CdmIfMissing {
|
|
|
448
448
|
export type CdmKeyResolverInput = {
|
|
449
449
|
/**
|
|
450
450
|
* `id` references an existing Grant user id. `email` resolves or creates a
|
|
451
|
-
* global
|
|
452
|
-
* CDM-local external keys for project
|
|
451
|
+
* global email authentication method, passwordless unless the user carries a
|
|
452
|
+
* `password`. Other values are treated as CDM-local external keys for project
|
|
453
|
+
* import/export.
|
|
453
454
|
*/
|
|
454
455
|
findBy?: InputMaybe<CdmFindBy>;
|
|
455
456
|
ifMissing?: InputMaybe<CdmIfMissing>;
|
|
@@ -479,6 +480,31 @@ export enum CdmOnConflict {
|
|
|
479
480
|
Update = 'update',
|
|
480
481
|
}
|
|
481
482
|
|
|
483
|
+
/**
|
|
484
|
+
* Password hash algorithms a CDM import can carry. Only `bcrypt` is accepted; the
|
|
485
|
+
* enum leaves room for further algorithms without changing the input shape.
|
|
486
|
+
*/
|
|
487
|
+
export enum CdmPasswordAlgorithm {
|
|
488
|
+
Bcrypt = 'bcrypt',
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* A password hash the importer already holds, to be stored as the user's email
|
|
493
|
+
* password credential (ADR 0010). Never the plaintext password.
|
|
494
|
+
*/
|
|
495
|
+
export type CdmPasswordInput = {
|
|
496
|
+
algorithm: CdmPasswordAlgorithm;
|
|
497
|
+
/**
|
|
498
|
+
* A bcrypt hash in the modular-crypt form `$2a$`, `$2b$` or `$2y$`, a two-digit
|
|
499
|
+
* cost and 53 salt-and-hash characters (60 in all), such as the output of Go's
|
|
500
|
+
* `bcrypt.GenerateFromPassword`. The cost must be within the range the deployment
|
|
501
|
+
* accepts (4 to 15). It is written only to the credential store: it is not kept in
|
|
502
|
+
* metadata, searchable tokens, audit values, events or the stored job payload once
|
|
503
|
+
* the job finishes, and an invalid hash is refused without being echoed.
|
|
504
|
+
*/
|
|
505
|
+
hash: Scalars['String']['input'];
|
|
506
|
+
};
|
|
507
|
+
|
|
482
508
|
export type ChangeMyPasswordInput = {
|
|
483
509
|
confirmPassword: Scalars['String']['input'];
|
|
484
510
|
/** Required when a password already exists; omit when setting the first password. */
|
|
@@ -1191,6 +1217,19 @@ export type Mutation = {
|
|
|
1191
1217
|
* is recorded against the project.
|
|
1192
1218
|
*/
|
|
1193
1219
|
requestProjectPictureUploadUrl: UploadUrl;
|
|
1220
|
+
/**
|
|
1221
|
+
* Ask for a URL to upload a CDM import document directly to storage, so it can be
|
|
1222
|
+
* imported without passing through this API's request body (and its size limit).
|
|
1223
|
+
*
|
|
1224
|
+
* Requires Project sync job update permission in the given project scope, like
|
|
1225
|
+
* `startProjectSync`. The server derives the storage key from the project and a
|
|
1226
|
+
* server-minted `uploadId`; the client never supplies a path. The URL commits to exactly
|
|
1227
|
+
* `input.contentLength` bytes of `application/json`, up to `CDM_UPLOAD_MAX_BYTES`.
|
|
1228
|
+
*
|
|
1229
|
+
* PUT the file, then call `startProjectSync` with `input.source.uploadId`. An upload
|
|
1230
|
+
* that is never started is expired by the storage lifecycle rule.
|
|
1231
|
+
*/
|
|
1232
|
+
requestProjectSyncUploadUrl: ProjectSyncUploadUrl;
|
|
1194
1233
|
/**
|
|
1195
1234
|
* Ask for a URL to upload another user's picture directly to storage.
|
|
1196
1235
|
*
|
|
@@ -1519,6 +1558,12 @@ export type MutationRequestProjectPictureUploadUrlArgs = {
|
|
|
1519
1558
|
input: RequestProjectPictureUploadUrlInput;
|
|
1520
1559
|
};
|
|
1521
1560
|
|
|
1561
|
+
export type MutationRequestProjectSyncUploadUrlArgs = {
|
|
1562
|
+
id: Scalars['ID']['input'];
|
|
1563
|
+
input: RequestProjectSyncUploadUrlInput;
|
|
1564
|
+
scope: Scope;
|
|
1565
|
+
};
|
|
1566
|
+
|
|
1522
1567
|
export type MutationRequestUserPictureUploadUrlArgs = {
|
|
1523
1568
|
input: RequestUserPictureUploadUrlInput;
|
|
1524
1569
|
};
|
|
@@ -2382,6 +2427,28 @@ export enum ProjectSyncJobStatus {
|
|
|
2382
2427
|
Running = 'RUNNING',
|
|
2383
2428
|
}
|
|
2384
2429
|
|
|
2430
|
+
/**
|
|
2431
|
+
* A bounded, time-limited URL to PUT a CDM import document to, and the id to start the
|
|
2432
|
+
* import from. Same commitments as `UploadUrl`: exactly `contentLength` bytes of exactly
|
|
2433
|
+
* `contentType`, at a path derived on the server from the project, until `expiresAt`.
|
|
2434
|
+
*/
|
|
2435
|
+
export type ProjectSyncUploadUrl = {
|
|
2436
|
+
__typename?: 'ProjectSyncUploadUrl';
|
|
2437
|
+
/** Instant after which the store refuses the URL. */
|
|
2438
|
+
expiresAt: Scalars['Date']['output'];
|
|
2439
|
+
/** Headers the client must set verbatim. `Content-Length` is deliberately absent. */
|
|
2440
|
+
headers: Array<UploadUrlHeader>;
|
|
2441
|
+
/** HTTP method the client must use. Always PUT today. */
|
|
2442
|
+
method: Scalars['String']['output'];
|
|
2443
|
+
/** Pass as `source.uploadId` to `startProjectSync`. */
|
|
2444
|
+
uploadId: Scalars['ID']['output'];
|
|
2445
|
+
/**
|
|
2446
|
+
* Absolute for object stores; application-relative for targets this API serves
|
|
2447
|
+
* itself. Resolve it against the API origin before using it.
|
|
2448
|
+
*/
|
|
2449
|
+
url: Scalars['String']['output'];
|
|
2450
|
+
};
|
|
2451
|
+
|
|
2385
2452
|
export type ProjectTag = Auditable & {
|
|
2386
2453
|
__typename?: 'ProjectTag';
|
|
2387
2454
|
createdAt: Scalars['Date']['output'];
|
|
@@ -3144,6 +3211,16 @@ export type RequestProjectPictureUploadUrlInput = {
|
|
|
3144
3211
|
scope: Scope;
|
|
3145
3212
|
};
|
|
3146
3213
|
|
|
3214
|
+
export type RequestProjectSyncUploadUrlInput = {
|
|
3215
|
+
/**
|
|
3216
|
+
* Exact byte length of the CDM JSON file the client will PUT. The URL commits to it,
|
|
3217
|
+
* and it may not exceed `CDM_UPLOAD_MAX_BYTES` (default 64 MiB).
|
|
3218
|
+
*/
|
|
3219
|
+
contentLength: Scalars['Int']['input'];
|
|
3220
|
+
/** Defaults to `application/json`, the only type accepted. */
|
|
3221
|
+
contentType?: InputMaybe<Scalars['String']['input']>;
|
|
3222
|
+
};
|
|
3223
|
+
|
|
3147
3224
|
export type RequestUserPictureUploadUrlInput = {
|
|
3148
3225
|
/**
|
|
3149
3226
|
* Exact byte length the client will send. Validated against the upload policy
|
|
@@ -3499,6 +3576,13 @@ export type StartProjectExportInput = {
|
|
|
3499
3576
|
/**
|
|
3500
3577
|
* Versioned canonical project document (CDM) for export/import: roles, users,
|
|
3501
3578
|
* resources, permissions, groups, and tags scoped to one project.
|
|
3579
|
+
*
|
|
3580
|
+
* Two shapes. **Inline**: `version`, `roles` and `users` are required and the document is
|
|
3581
|
+
* the input. **By reference**: `source` is set, `mode` still says how to apply it, and
|
|
3582
|
+
* `version` is optional and the entity arrays (`roles`, `users`, `resources`,
|
|
3583
|
+
* `permissions`, `groups`, `tags`) must be absent: the document is the file uploaded
|
|
3584
|
+
* through `requestProjectSyncUploadUrl`, and the worker validates it, so a schema error or
|
|
3585
|
+
* an over-limit entity count surfaces as a `FAILED` job, not as an error on the mutation.
|
|
3502
3586
|
*/
|
|
3503
3587
|
export type SyncProjectInput = {
|
|
3504
3588
|
groups?: InputMaybe<Array<GroupCdmInput>>;
|
|
@@ -3506,10 +3590,19 @@ export type SyncProjectInput = {
|
|
|
3506
3590
|
mode: CdmModeInput;
|
|
3507
3591
|
permissions?: InputMaybe<Array<PermissionCdmInput>>;
|
|
3508
3592
|
resources?: InputMaybe<Array<ResourceCdmInput>>;
|
|
3509
|
-
|
|
3593
|
+
/** Required inline; must be absent with `source`. */
|
|
3594
|
+
roles?: InputMaybe<Array<RoleCdmInput>>;
|
|
3595
|
+
/**
|
|
3596
|
+
* Import the document uploaded through `requestProjectSyncUploadUrl` instead of sending
|
|
3597
|
+
* it inline. The entity arrays must then be absent, and `mode` (and `version`, if
|
|
3598
|
+
* given) must match the uploaded document.
|
|
3599
|
+
*/
|
|
3600
|
+
source?: InputMaybe<SyncProjectSourceInput>;
|
|
3510
3601
|
tags?: InputMaybe<Array<TagCdmInput>>;
|
|
3511
|
-
|
|
3512
|
-
|
|
3602
|
+
/** Required inline; must be absent with `source`. */
|
|
3603
|
+
users?: InputMaybe<Array<UserCdmInput>>;
|
|
3604
|
+
/** Required inline; optional with `source`, where the uploaded document carries it. */
|
|
3605
|
+
version?: InputMaybe<Scalars['Int']['input']>;
|
|
3513
3606
|
};
|
|
3514
3607
|
|
|
3515
3608
|
/** Counters and warnings from a completed CDM import. */
|
|
@@ -3519,6 +3612,22 @@ export type SyncProjectResult = {
|
|
|
3519
3612
|
groupTagsLinked: Scalars['Int']['output'];
|
|
3520
3613
|
groupsCreated: Scalars['Int']['output'];
|
|
3521
3614
|
importId?: Maybe<Scalars['String']['output']>;
|
|
3615
|
+
/** Users whose `password` hash was stored as their email credential for the first time. */
|
|
3616
|
+
passwordsImported: Scalars['Int']['output'];
|
|
3617
|
+
/**
|
|
3618
|
+
* Users whose `password` hash was not stored because the credential or the identity is
|
|
3619
|
+
* not the import's to write: a password set in Grant, an address the owner has verified,
|
|
3620
|
+
* or an identity that already existed and was not created by an earlier import of this
|
|
3621
|
+
* project.
|
|
3622
|
+
*/
|
|
3623
|
+
passwordsSkippedExisting: Scalars['Int']['output'];
|
|
3624
|
+
/** Users whose imported credential already equalled the hash in the document. */
|
|
3625
|
+
passwordsUnchanged: Scalars['Int']['output'];
|
|
3626
|
+
/**
|
|
3627
|
+
* Users whose imported credential was replaced because the document carried a different
|
|
3628
|
+
* hash for it.
|
|
3629
|
+
*/
|
|
3630
|
+
passwordsUpdated: Scalars['Int']['output'];
|
|
3522
3631
|
permissionsCreated: Scalars['Int']['output'];
|
|
3523
3632
|
projectGroupsLinked: Scalars['Int']['output'];
|
|
3524
3633
|
projectId: Scalars['ID']['output'];
|
|
@@ -3539,6 +3648,24 @@ export type SyncProjectResult = {
|
|
|
3539
3648
|
warnings: Array<Scalars['String']['output']>;
|
|
3540
3649
|
};
|
|
3541
3650
|
|
|
3651
|
+
/**
|
|
3652
|
+
* Where the CDM document of a by-reference import is: an upload made through
|
|
3653
|
+
* `requestProjectSyncUploadUrl`. See `SyncProjectInput.source`.
|
|
3654
|
+
*/
|
|
3655
|
+
export type SyncProjectSourceInput = {
|
|
3656
|
+
/**
|
|
3657
|
+
* How many entities (roles, users, resources, permissions, groups and tags) the
|
|
3658
|
+
* uploaded document holds. A hint, recorded for the job list and never trusted: the
|
|
3659
|
+
* worker counts the document itself.
|
|
3660
|
+
*/
|
|
3661
|
+
entityCount?: InputMaybe<Scalars['Int']['input']>;
|
|
3662
|
+
/**
|
|
3663
|
+
* The `uploadId` returned by `requestProjectSyncUploadUrl`. The server derives the
|
|
3664
|
+
* storage key from the project and this id; the client never supplies a path.
|
|
3665
|
+
*/
|
|
3666
|
+
uploadId: Scalars['ID']['input'];
|
|
3667
|
+
};
|
|
3668
|
+
|
|
3542
3669
|
export type Tag = Auditable & {
|
|
3543
3670
|
__typename?: 'Tag';
|
|
3544
3671
|
color: Scalars['String']['output'];
|
|
@@ -4004,6 +4131,22 @@ export type UserCdmInput = {
|
|
|
4004
4131
|
key: CdmKeyResolverInput;
|
|
4005
4132
|
metadata?: InputMaybe<Scalars['JSON']['input']>;
|
|
4006
4133
|
name: Scalars['String']['input'];
|
|
4134
|
+
/**
|
|
4135
|
+
* Existing password credential to import for this user. Only valid with
|
|
4136
|
+
* `key.findBy: email`, because the credential lives on the email authentication
|
|
4137
|
+
* method; any other resolver is a validation error.
|
|
4138
|
+
*
|
|
4139
|
+
* Ownership is forward-only. An imported credential is marked as imported and stays
|
|
4140
|
+
* owned by the importing system while the email is unverified: a later import of this
|
|
4141
|
+
* project with a different hash replaces it, an identical one changes nothing. Once the
|
|
4142
|
+
* owner verifies the address, or the password is set, changed or reset in Grant, Grant owns
|
|
4143
|
+
* it and imports never touch it again. A credential is written only for an identity the
|
|
4144
|
+
* import creates, or one an earlier import of this project created whose email is still
|
|
4145
|
+
* unverified; any other existing identity, including one found through GitHub or Google,
|
|
4146
|
+
* is left untouched. An absent `password` never removes a
|
|
4147
|
+
* stored credential. See `SyncProjectResult` for the counters.
|
|
4148
|
+
*/
|
|
4149
|
+
password?: InputMaybe<CdmPasswordInput>;
|
|
4007
4150
|
/** Direct user → permission grants (document keys or catalog `resourceSlug:action` refs). */
|
|
4008
4151
|
permissions?: InputMaybe<Array<Scalars['String']['input']>>;
|
|
4009
4152
|
primaryTag?: InputMaybe<Scalars['String']['input']>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
mutation RequestProjectSyncUploadUrl(
|
|
2
|
+
$id: ID!
|
|
3
|
+
$scope: Scope!
|
|
4
|
+
$input: RequestProjectSyncUploadUrlInput!
|
|
5
|
+
) {
|
|
6
|
+
requestProjectSyncUploadUrl(id: $id, scope: $scope, input: $input) {
|
|
7
|
+
uploadId
|
|
8
|
+
url
|
|
9
|
+
method
|
|
10
|
+
expiresAt
|
|
11
|
+
headers {
|
|
12
|
+
name
|
|
13
|
+
value
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
input RequestProjectSyncUploadUrlInput {
|
|
2
|
+
"""
|
|
3
|
+
Exact byte length of the CDM JSON file the client will PUT. The URL commits to it,
|
|
4
|
+
and it may not exceed `CDM_UPLOAD_MAX_BYTES` (default 64 MiB).
|
|
5
|
+
"""
|
|
6
|
+
contentLength: Int!
|
|
7
|
+
"Defaults to `application/json`, the only type accepted."
|
|
8
|
+
contentType: String
|
|
9
|
+
}
|
|
@@ -23,6 +23,14 @@ enum CdmIfMissing {
|
|
|
23
23
|
createNew
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
"""
|
|
27
|
+
Password hash algorithms a CDM import can carry. Only `bcrypt` is accepted; the
|
|
28
|
+
enum leaves room for further algorithms without changing the input shape.
|
|
29
|
+
"""
|
|
30
|
+
enum CdmPasswordAlgorithm {
|
|
31
|
+
bcrypt
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
input CdmModeInput {
|
|
27
35
|
strategy: CdmModeStrategy!
|
|
28
36
|
onConflict: CdmOnConflict
|
|
@@ -39,8 +47,9 @@ input CdmKeyResolverInput {
|
|
|
39
47
|
value: String!
|
|
40
48
|
"""
|
|
41
49
|
`id` references an existing Grant user id. `email` resolves or creates a
|
|
42
|
-
global
|
|
43
|
-
CDM-local external keys for project
|
|
50
|
+
global email authentication method, passwordless unless the user carries a
|
|
51
|
+
`password`. Other values are treated as CDM-local external keys for project
|
|
52
|
+
import/export.
|
|
44
53
|
"""
|
|
45
54
|
findBy: CdmFindBy
|
|
46
55
|
ifMissing: CdmIfMissing
|
|
@@ -128,6 +137,23 @@ input UserApiKeyCdmInput {
|
|
|
128
137
|
metadata: JSON
|
|
129
138
|
}
|
|
130
139
|
|
|
140
|
+
"""
|
|
141
|
+
A password hash the importer already holds, to be stored as the user's email
|
|
142
|
+
password credential (ADR 0010). Never the plaintext password.
|
|
143
|
+
"""
|
|
144
|
+
input CdmPasswordInput {
|
|
145
|
+
algorithm: CdmPasswordAlgorithm!
|
|
146
|
+
"""
|
|
147
|
+
A bcrypt hash in the modular-crypt form `$2a$`, `$2b$` or `$2y$`, a two-digit
|
|
148
|
+
cost and 53 salt-and-hash characters (60 in all), such as the output of Go's
|
|
149
|
+
`bcrypt.GenerateFromPassword`. The cost must be within the range the deployment
|
|
150
|
+
accepts (4 to 15). It is written only to the credential store: it is not kept in
|
|
151
|
+
metadata, searchable tokens, audit values, events or the stored job payload once
|
|
152
|
+
the job finishes, and an invalid hash is refused without being echoed.
|
|
153
|
+
"""
|
|
154
|
+
hash: String!
|
|
155
|
+
}
|
|
156
|
+
|
|
131
157
|
input UserCdmInput {
|
|
132
158
|
key: CdmKeyResolverInput!
|
|
133
159
|
name: String!
|
|
@@ -147,24 +173,74 @@ input UserCdmInput {
|
|
|
147
173
|
primaryTag: String
|
|
148
174
|
apiKeys: [UserApiKeyCdmInput!]
|
|
149
175
|
"""
|
|
176
|
+
Existing password credential to import for this user. Only valid with
|
|
177
|
+
`key.findBy: email`, because the credential lives on the email authentication
|
|
178
|
+
method; any other resolver is a validation error.
|
|
179
|
+
|
|
180
|
+
Ownership is forward-only. An imported credential is marked as imported and stays
|
|
181
|
+
owned by the importing system while the email is unverified: a later import of this
|
|
182
|
+
project with a different hash replaces it, an identical one changes nothing. Once the
|
|
183
|
+
owner verifies the address, or the password is set, changed or reset in Grant, Grant owns
|
|
184
|
+
it and imports never touch it again. A credential is written only for an identity the
|
|
185
|
+
import creates, or one an earlier import of this project created whose email is still
|
|
186
|
+
unverified; any other existing identity, including one found through GitHub or Google,
|
|
187
|
+
is left untouched. An absent `password` never removes a
|
|
188
|
+
stored credential. See `SyncProjectResult` for the counters.
|
|
189
|
+
"""
|
|
190
|
+
password: CdmPasswordInput
|
|
191
|
+
"""
|
|
150
192
|
Denormalized search tokens for project-scoped user list views.
|
|
151
193
|
"""
|
|
152
194
|
searchable: JSON
|
|
153
195
|
metadata: JSON
|
|
154
196
|
}
|
|
155
197
|
|
|
198
|
+
"""
|
|
199
|
+
Where the CDM document of a by-reference import is: an upload made through
|
|
200
|
+
`requestProjectSyncUploadUrl`. See `SyncProjectInput.source`.
|
|
201
|
+
"""
|
|
202
|
+
input SyncProjectSourceInput {
|
|
203
|
+
"""
|
|
204
|
+
The `uploadId` returned by `requestProjectSyncUploadUrl`. The server derives the
|
|
205
|
+
storage key from the project and this id; the client never supplies a path.
|
|
206
|
+
"""
|
|
207
|
+
uploadId: ID!
|
|
208
|
+
"""
|
|
209
|
+
How many entities (roles, users, resources, permissions, groups and tags) the
|
|
210
|
+
uploaded document holds. A hint, recorded for the job list and never trusted: the
|
|
211
|
+
worker counts the document itself.
|
|
212
|
+
"""
|
|
213
|
+
entityCount: Int
|
|
214
|
+
}
|
|
215
|
+
|
|
156
216
|
"""
|
|
157
217
|
Versioned canonical project document (CDM) for export/import: roles, users,
|
|
158
218
|
resources, permissions, groups, and tags scoped to one project.
|
|
219
|
+
|
|
220
|
+
Two shapes. **Inline**: `version`, `roles` and `users` are required and the document is
|
|
221
|
+
the input. **By reference**: `source` is set, `mode` still says how to apply it, and
|
|
222
|
+
`version` is optional and the entity arrays (`roles`, `users`, `resources`,
|
|
223
|
+
`permissions`, `groups`, `tags`) must be absent: the document is the file uploaded
|
|
224
|
+
through `requestProjectSyncUploadUrl`, and the worker validates it, so a schema error or
|
|
225
|
+
an over-limit entity count surfaces as a `FAILED` job, not as an error on the mutation.
|
|
159
226
|
"""
|
|
160
227
|
input SyncProjectInput {
|
|
161
|
-
|
|
228
|
+
"Required inline; optional with `source`, where the uploaded document carries it."
|
|
229
|
+
version: Int
|
|
162
230
|
id: String
|
|
163
231
|
mode: CdmModeInput!
|
|
164
|
-
|
|
165
|
-
|
|
232
|
+
"Required inline; must be absent with `source`."
|
|
233
|
+
roles: [RoleCdmInput!]
|
|
234
|
+
"Required inline; must be absent with `source`."
|
|
235
|
+
users: [UserCdmInput!]
|
|
166
236
|
resources: [ResourceCdmInput!]
|
|
167
237
|
permissions: [PermissionCdmInput!]
|
|
168
238
|
groups: [GroupCdmInput!]
|
|
169
239
|
tags: [TagCdmInput!]
|
|
240
|
+
"""
|
|
241
|
+
Import the document uploaded through `requestProjectSyncUploadUrl` instead of sending
|
|
242
|
+
it inline. The entity arrays must then be absent, and `mode` (and `version`, if
|
|
243
|
+
given) must match the uploaded document.
|
|
244
|
+
"""
|
|
245
|
+
source: SyncProjectSourceInput
|
|
170
246
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
extend type Mutation {
|
|
2
|
+
"""
|
|
3
|
+
Ask for a URL to upload a CDM import document directly to storage, so it can be
|
|
4
|
+
imported without passing through this API's request body (and its size limit).
|
|
5
|
+
|
|
6
|
+
Requires Project sync job update permission in the given project scope, like
|
|
7
|
+
`startProjectSync`. The server derives the storage key from the project and a
|
|
8
|
+
server-minted `uploadId`; the client never supplies a path. The URL commits to exactly
|
|
9
|
+
`input.contentLength` bytes of `application/json`, up to `CDM_UPLOAD_MAX_BYTES`.
|
|
10
|
+
|
|
11
|
+
PUT the file, then call `startProjectSync` with `input.source.uploadId`. An upload
|
|
12
|
+
that is never started is expired by the storage lifecycle rule.
|
|
13
|
+
"""
|
|
14
|
+
requestProjectSyncUploadUrl(
|
|
15
|
+
id: ID!
|
|
16
|
+
scope: Scope!
|
|
17
|
+
input: RequestProjectSyncUploadUrlInput!
|
|
18
|
+
): ProjectSyncUploadUrl!
|
|
19
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A bounded, time-limited URL to PUT a CDM import document to, and the id to start the
|
|
3
|
+
import from. Same commitments as `UploadUrl`: exactly `contentLength` bytes of exactly
|
|
4
|
+
`contentType`, at a path derived on the server from the project, until `expiresAt`.
|
|
5
|
+
"""
|
|
6
|
+
type ProjectSyncUploadUrl {
|
|
7
|
+
"Pass as `source.uploadId` to `startProjectSync`."
|
|
8
|
+
uploadId: ID!
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
Absolute for object stores; application-relative for targets this API serves
|
|
12
|
+
itself. Resolve it against the API origin before using it.
|
|
13
|
+
"""
|
|
14
|
+
url: String!
|
|
15
|
+
|
|
16
|
+
"HTTP method the client must use. Always PUT today."
|
|
17
|
+
method: String!
|
|
18
|
+
|
|
19
|
+
"Headers the client must set verbatim. `Content-Length` is deliberately absent."
|
|
20
|
+
headers: [UploadUrlHeader!]!
|
|
21
|
+
|
|
22
|
+
"Instant after which the store refuses the URL."
|
|
23
|
+
expiresAt: Date!
|
|
24
|
+
}
|
|
@@ -16,6 +16,26 @@ type SyncProjectResult {
|
|
|
16
16
|
usersCreated: Int!
|
|
17
17
|
userRolesAssigned: Int!
|
|
18
18
|
projectUserApiKeysCreated: Int!
|
|
19
|
+
"""
|
|
20
|
+
Users whose `password` hash was stored as their email credential for the first time.
|
|
21
|
+
"""
|
|
22
|
+
passwordsImported: Int!
|
|
23
|
+
"""
|
|
24
|
+
Users whose imported credential was replaced because the document carried a different
|
|
25
|
+
hash for it.
|
|
26
|
+
"""
|
|
27
|
+
passwordsUpdated: Int!
|
|
28
|
+
"""
|
|
29
|
+
Users whose imported credential already equalled the hash in the document.
|
|
30
|
+
"""
|
|
31
|
+
passwordsUnchanged: Int!
|
|
32
|
+
"""
|
|
33
|
+
Users whose `password` hash was not stored because the credential or the identity is
|
|
34
|
+
not the import's to write: a password set in Grant, an address the owner has verified,
|
|
35
|
+
or an identity that already existed and was not created by an earlier import of this
|
|
36
|
+
project.
|
|
37
|
+
"""
|
|
38
|
+
passwordsSkippedExisting: Int!
|
|
19
39
|
resourcesCreated: Int!
|
|
20
40
|
permissionsCreated: Int!
|
|
21
41
|
tagsCreated: Int!
|
package/src/sdl-contract.test.ts
CHANGED
|
@@ -50,10 +50,11 @@ describe('operation documents', () => {
|
|
|
50
50
|
// project-app pictures (eight operation documents).
|
|
51
51
|
// 135 after project OAuth connections: get/upsert/clear (three operation documents).
|
|
52
52
|
// 136 after closed-install: AuthSignupPolicyDocument (publicSignupEnabled, bootstrapOpen).
|
|
53
|
+
// 137 after CDM imports by reference: RequestProjectSyncUploadUrlDocument.
|
|
53
54
|
// Updating this number is the point of the pin, not a nuisance: every change
|
|
54
55
|
// to it should be a line in a commit message.
|
|
55
56
|
it('finds every document under src/operations', () => {
|
|
56
|
-
expect(files).toHaveLength(
|
|
57
|
+
expect(files).toHaveLength(137);
|
|
57
58
|
});
|
|
58
59
|
|
|
59
60
|
it.each(files.map((f) => [relative(process.cwd(), f), f]))('%s validates', (_label, file) => {
|