@stackbit/cms-contentstack 0.1.2-develop.1 → 0.1.2-staging.1

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.
Files changed (38) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contentstack-content-poller.js +1 -1
  3. package/dist/contentstack-content-poller.js.map +1 -1
  4. package/dist/contentstack-content-source.d.ts +7 -4
  5. package/dist/contentstack-content-source.d.ts.map +1 -1
  6. package/dist/contentstack-content-source.js +46 -45
  7. package/dist/contentstack-content-source.js.map +1 -1
  8. package/dist/contentstack-operation-converter.d.ts +8 -6
  9. package/dist/contentstack-operation-converter.d.ts.map +1 -1
  10. package/dist/contentstack-operation-converter.js.map +1 -1
  11. package/package.json +4 -4
  12. package/src/contentstack-content-poller.ts +1 -1
  13. package/src/contentstack-content-source.ts +56 -50
  14. package/src/contentstack-operation-converter.ts +6 -7
  15. package/dist/contentstack-conversion-utils.d.ts +0 -41
  16. package/dist/contentstack-conversion-utils.d.ts.map +0 -1
  17. package/dist/contentstack-conversion-utils.js +0 -504
  18. package/dist/contentstack-conversion-utils.js.map +0 -1
  19. package/dist/entries-converter.d.ts +0 -10
  20. package/dist/entries-converter.d.ts.map +0 -1
  21. package/dist/entries-converter.js +0 -245
  22. package/dist/entries-converter.js.map +0 -1
  23. package/dist/file-download.d.ts +0 -2
  24. package/dist/file-download.d.ts.map +0 -1
  25. package/dist/file-download.js +0 -33
  26. package/dist/file-download.js.map +0 -1
  27. package/dist/schema-converter.d.ts +0 -3
  28. package/dist/schema-converter.d.ts.map +0 -1
  29. package/dist/schema-converter.js +0 -169
  30. package/dist/schema-converter.js.map +0 -1
  31. package/dist/transformation-utils.d.ts +0 -41
  32. package/dist/transformation-utils.d.ts.map +0 -1
  33. package/dist/transformation-utils.js +0 -730
  34. package/dist/transformation-utils.js.map +0 -1
  35. package/dist/types.d.ts +0 -120
  36. package/dist/types.d.ts.map +0 -1
  37. package/dist/types.js +0 -3
  38. package/dist/types.js.map +0 -1
@@ -15,7 +15,7 @@ import {
15
15
  convertAssets,
16
16
  DocumentWithContext
17
17
  } from './contentstack-entries-converter';
18
- import { createEntryFromOperationFields, updateEntryFromUpdateOperations } from './contentstack-operation-converter';
18
+ import { createEntryFromOperationFields, updateEntryFromUpdateOperations, GetLeanDocumentById } from './contentstack-operation-converter';
19
19
  import { getContentstackDashboardUrl, resolvePublishEnvironment, downloadFile, createWebhookIfNeeded, userMissingInMap } from './contentstack-utils';
20
20
  import { ContentPoller, getLastUpdatedEntityDate, SyncContext, SyncResult } from './contentstack-content-poller';
21
21
  import type { User, Asset, Entry, Environment, WebhookPayload } from './contentstack-types';
@@ -33,13 +33,13 @@ export interface ContentStackSourceOptions {
33
33
  * of your organization. If you don't pass the authtoken, some features
34
34
  * may not work in Stackbit properly.
35
35
  *
36
- * To get the authtoken, use the following command:
36
+ * To get the authtoken, use the following command (replace YOUR_EMAIL and
37
+ * YOUR_PASSWORD) with your Contentstack credentials:
37
38
  * ```
38
39
  * curl -X POST \
39
40
  * -H "Content-Type: application/json" \
40
- * -d '{"user":{"email":"simon@stackbit.com","password":"i7rdEiZUt!6tMEUkeFTo"}}' \
41
- * https://api.contentstack.io/v3/user-session \
42
- * | grep -ioE '"authtoken":"([^\"])+"'
41
+ * -d '{"user":{"email":"YOUR_EMAIL","password":"YOUR_PASSWORD"}}' \
42
+ * https://api.contentstack.io/v3/user-session | grep -ioE '"authtoken":"([^\"])+"'
43
43
  * ```
44
44
  */
45
45
  authtoken?: string;
@@ -85,6 +85,7 @@ export class ContentstackContentSource
85
85
  private publishEnvironmentName?: string;
86
86
  private publishEnvironment!: Environment;
87
87
  private usersById: Record<string, User> = {};
88
+ private newUnsyncedEntryMap: Record<string, string> = {};
88
89
 
89
90
  constructor({ apiKey, managementToken, branch, authtoken, masterLocale, publishEnvironmentName, skipFetchOnStartIfCache }: ContentStackSourceOptions) {
90
91
  this.apiKey = apiKey;
@@ -164,6 +165,8 @@ export class ContentstackContentSource
164
165
  }
165
166
 
166
167
  async reset(): Promise<void> {
168
+ this.logger.debug('reset');
169
+
167
170
  const environments = await this.contentStackClient.getEnvironments();
168
171
  this.publishEnvironment = resolvePublishEnvironment({
169
172
  environments,
@@ -172,6 +175,7 @@ export class ContentstackContentSource
172
175
  });
173
176
 
174
177
  if (this.authtoken) {
178
+ this.logger.debug('getStack');
175
179
  const stack = await this.contentStackClient.getStack(this.authtoken);
176
180
  this.masterLocale = stack.master_locale;
177
181
  await this.fetchUsers();
@@ -211,6 +215,11 @@ export class ContentstackContentSource
211
215
  } else {
212
216
  const result = await this.convertSyncResult(syncResult);
213
217
  await this.cache.updateContent(result);
218
+ for (const document of result.documents) {
219
+ if (this.newUnsyncedEntryMap[document.id]) {
220
+ delete this.newUnsyncedEntryMap[document.id];
221
+ }
222
+ }
214
223
  }
215
224
  }
216
225
  });
@@ -276,6 +285,7 @@ export class ContentstackContentSource
276
285
  }
277
286
 
278
287
  async fetchUsers() {
288
+ this.logger.debug('fetchUsers');
279
289
  if (this.authtoken) {
280
290
  const users = await this.contentStackClient.getUsers(this.authtoken);
281
291
  this.usersById = _.keyBy(users, 'uid');
@@ -435,11 +445,14 @@ export class ContentstackContentSource
435
445
  const entry = createEntryFromOperationFields({
436
446
  updateOperationFields: options.updateOperationFields,
437
447
  model: options.model,
438
- getDocumentById: this.cache.getDocumentById,
448
+ getDocumentById: this.getCachedDocumentById.bind(this),
439
449
  getModelByName: this.cache.getModelByName,
440
450
  logger: this.logger
441
451
  });
442
452
  const newEntry = await this.contentStackClient.createEntry(options.model.name, entry);
453
+ // cache the created entry's uid and model name until it is received from Contentstack
454
+ // via onWebhook or contentPoller's notificationCallback methods.
455
+ this.newUnsyncedEntryMap[newEntry.uid] = options.model.name;
443
456
  return { documentId: newEntry.uid };
444
457
  }
445
458
 
@@ -454,13 +467,28 @@ export class ContentstackContentSource
454
467
  const updatedEntry = updateEntryFromUpdateOperations({
455
468
  entry,
456
469
  updateOperations: operations,
457
- getDocumentById: this.cache.getDocumentById,
470
+ getDocumentById: this.getCachedDocumentById.bind(this),
458
471
  getModelByName: this.cache.getModelByName,
459
472
  logger: this.logger
460
473
  });
461
474
  await this.contentStackClient.updateEntry(updatedEntry);
462
475
  }
463
476
 
477
+ getCachedDocumentById(documentId: string): ReturnType<GetLeanDocumentById> {
478
+ const document = this.cache.getDocumentById(documentId);
479
+ if (document) {
480
+ return document;
481
+ }
482
+ if (this.newUnsyncedEntryMap[documentId]) {
483
+ this.logger.debug(`the created document ${documentId} was not yet fetched from Contentstack, using the cached document`);
484
+ return {
485
+ id: documentId,
486
+ modelName: this.newUnsyncedEntryMap[documentId]!
487
+ };
488
+ }
489
+ return;
490
+ }
491
+
464
492
  async deleteDocument(options: { document: StackbitTypes.Document<unknown>; userContext?: UserWithContext }): Promise<void> {
465
493
  this.logger.debug('deleteDocument');
466
494
  await this.contentStackClient.deleteEntry(options.document.modelName, options.document.id);
@@ -479,7 +507,7 @@ export class ContentstackContentSource
479
507
  let asset: Asset;
480
508
  try {
481
509
  if (options.base64) {
482
- // TODO: write to .stackbit/cache folder
510
+ // TODO: pass the path to .stackbit/cache folder in init() options and use it to write the temp file
483
511
  await writeFile(tempName, Buffer.from(options.base64, 'base64'));
484
512
  } else {
485
513
  await downloadFile(options.url!, tempName);
@@ -545,41 +573,31 @@ export class ContentstackContentSource
545
573
 
546
574
  this.logger.debug(`got webhook for ${webhookData.event} ${webhookData.module}`);
547
575
 
576
+ let unsyncedEntryUid: string | undefined;
577
+
548
578
  if (webhookData.module === 'entry') {
549
579
  switch (webhookData.event) {
550
580
  case 'create':
551
- case 'update': {
552
- if (userMissingInMap(this.usersById, webhookData.data.entry as Entry)) {
553
- await this.fetchUsers();
554
- }
555
- const document = convertEntry({
556
- entry: {
557
- ...webhookData.data.entry,
558
- content_type_uid: webhookData.data.content_type.uid,
559
- publish_details: [],
560
- _branch: webhookData.data.branch.uid
561
- } as unknown as Entry,
562
- status: webhookData.event === 'create' ? 'added' : 'modified',
563
- apiKey: this.apiKey,
564
- getModelByName: this.cache.getModelByName,
565
- usersById: this.usersById,
566
- publishEnvironment: this.publishEnvironment,
567
- logger: this.logger
568
- });
569
- updates.documents!.push(document);
570
- break;
571
- }
581
+ case 'update':
572
582
  case 'publish': {
573
- if (webhookData.data.environment.uid !== this.publishEnvironment.uid) {
583
+ // Generally, entry publish webhooks created by the content source are already configured to filter
584
+ // only the publishEnvironment. However, still check for the publish event environment in case the
585
+ // webhook was manually changed.
586
+ if (webhookData.event === 'publish' && webhookData.data.environment.uid !== this.publishEnvironment.uid) {
574
587
  this.logger.debug(
575
588
  `entry was published in an environment '${webhookData.data.environment.name}' ` +
576
589
  `different from the publish environment '${this.publishEnvironment.name}', ignoring the webhook'`
577
590
  );
578
591
  break;
579
592
  }
593
+ if (webhookData.event === 'create' && webhookData.data.entry.uid in this.newUnsyncedEntryMap) {
594
+ unsyncedEntryUid = webhookData.data.entry.uid;
595
+ }
580
596
  if (userMissingInMap(this.usersById, webhookData.data.entry as Entry)) {
581
597
  await this.fetchUsers();
582
598
  }
599
+ const status: StackbitTypes.DocumentStatus =
600
+ webhookData.event === 'publish' ? 'published' : webhookData.event === 'create' ? 'added' : 'modified';
583
601
  const document = convertEntry({
584
602
  entry: {
585
603
  ...webhookData.data.entry,
@@ -587,7 +605,7 @@ export class ContentstackContentSource
587
605
  publish_details: [],
588
606
  _branch: webhookData.data.branch.uid
589
607
  } as unknown as Entry,
590
- status: 'published',
608
+ status: status,
591
609
  apiKey: this.apiKey,
592
610
  getModelByName: this.cache.getModelByName,
593
611
  usersById: this.usersById,
@@ -614,25 +632,9 @@ export class ContentstackContentSource
614
632
  } else if (webhookData.module === 'asset') {
615
633
  switch (webhookData.event) {
616
634
  case 'create':
617
- case 'update': {
618
- if (userMissingInMap(this.usersById, webhookData.data.asset as Asset)) {
619
- await this.fetchUsers();
620
- }
621
- const asset = convertAsset({
622
- asset: {
623
- ...webhookData.data.asset,
624
- publish_details: [],
625
- _branch: webhookData.data.branch.uid
626
- } as unknown as Asset,
627
- apiKey: this.apiKey,
628
- usersById: this.usersById,
629
- publishEnvironment: this.publishEnvironment
630
- });
631
- updates.assets!.push(asset);
632
- break;
633
- }
635
+ case 'update':
634
636
  case 'publish': {
635
- if (webhookData.data.environment.uid !== this.publishEnvironment.uid) {
637
+ if (webhookData.event === 'publish' && webhookData.data.environment.uid !== this.publishEnvironment.uid) {
636
638
  this.logger.debug(
637
639
  `asset was published in an environment '${webhookData.data.environment.name}' ` +
638
640
  `different from the publish environment '${this.publishEnvironment.name}', ignoring the webhook'`
@@ -682,6 +684,10 @@ export class ContentstackContentSource
682
684
  `deleted documents: ${updates.deletedDocumentIds?.length}, ` +
683
685
  `deleted assets: ${updates.deletedAssetIds?.length}`
684
686
  );
685
- this.cache.updateContent(updates);
687
+ await this.cache.updateContent(updates);
688
+
689
+ if (unsyncedEntryUid) {
690
+ delete this.newUnsyncedEntryMap[unsyncedEntryUid];
691
+ }
686
692
  }
687
693
  }
@@ -4,10 +4,9 @@ import * as StackbitTypes from '@stackbit/types';
4
4
  import type { EntryData } from '@contentstack/management/types/stack/contentType/entry';
5
5
  import type { Entry, Asset } from './contentstack-types';
6
6
  import type { ModelWithContext } from './contentstack-schema-converter';
7
- import type { DocumentWithContext } from './contentstack-entries-converter';
8
7
 
9
8
  type GetModelByName = (modelName: string) => ModelWithContext | undefined;
10
- type GetDocumentById = (documentId: string) => DocumentWithContext | undefined;
9
+ export type GetLeanDocumentById = (documentId: string) => { id: string; modelName: string } | undefined;
11
10
 
12
11
  export function createEntryFromOperationFields({
13
12
  updateOperationFields,
@@ -18,7 +17,7 @@ export function createEntryFromOperationFields({
18
17
  }: {
19
18
  updateOperationFields: Record<string, StackbitTypes.UpdateOperationField>;
20
19
  model: ModelWithContext;
21
- getDocumentById: GetDocumentById;
20
+ getDocumentById: GetLeanDocumentById;
22
21
  getModelByName: GetModelByName;
23
22
  logger: StackbitTypes.Logger;
24
23
  }): EntryData {
@@ -55,7 +54,7 @@ export function updateEntryFromUpdateOperations({
55
54
  }: {
56
55
  entry: Entry;
57
56
  updateOperations: StackbitTypes.UpdateOperation[];
58
- getDocumentById: GetDocumentById;
57
+ getDocumentById: GetLeanDocumentById;
59
58
  getModelByName: GetModelByName;
60
59
  logger: StackbitTypes.Logger;
61
60
  }): Entry {
@@ -117,7 +116,7 @@ export function getUpdatedEntryAtFieldPathWithOperation({
117
116
  entry: Entry;
118
117
  updateOperation: StackbitTypes.UpdateOperation;
119
118
  model: ModelWithContext;
120
- getDocumentById: GetDocumentById;
119
+ getDocumentById: GetLeanDocumentById;
121
120
  getModelByName: GetModelByName;
122
121
  logger: StackbitTypes.Logger;
123
122
  }): Entry {
@@ -378,7 +377,7 @@ function getValueForOperation({
378
377
  rootModel: ModelWithContext;
379
378
  entryFieldPath: (string | number)[];
380
379
  modelFieldPath: string[];
381
- getDocumentById: GetDocumentById;
380
+ getDocumentById: GetLeanDocumentById;
382
381
  getModelByName: GetModelByName;
383
382
  errorPrefix: string;
384
383
  logger: StackbitTypes.Logger;
@@ -468,7 +467,7 @@ export function convertOperationFieldValue({
468
467
  isInList: boolean;
469
468
  entryFieldPath: (string | number)[];
470
469
  modelFieldPath: string[];
471
- getDocumentById: GetDocumentById;
470
+ getDocumentById: GetLeanDocumentById;
472
471
  getModelByName: GetModelByName;
473
472
  errorPrefix: string;
474
473
  }): any {
@@ -1,41 +0,0 @@
1
- import * as StackbitTypes from '@stackbit/types';
2
- import { EntryData } from '@contentstack/management/types/stack/contentType/entry';
3
- import { Entry } from './contentstack-types';
4
- import { ModelWithContext } from './contentstack-schema-converter';
5
- import { DocumentWithContext } from './contentstack-entries-converter';
6
- declare type GetModelByName = (modelName: string) => ModelWithContext | undefined;
7
- declare type GetDocumentById = (documentId: string) => DocumentWithContext | undefined;
8
- export declare function createEntryFromOperationFields({ updateOperationFields, model, getDocumentById, getModelByName, logger }: {
9
- updateOperationFields: Record<string, StackbitTypes.UpdateOperationField>;
10
- model: ModelWithContext;
11
- getDocumentById: GetDocumentById;
12
- getModelByName: GetModelByName;
13
- logger: StackbitTypes.Logger;
14
- }): EntryData;
15
- export declare function updateEntryFromUpdateOperations({ entry, updateOperations, getDocumentById, getModelByName, logger }: {
16
- entry: Entry;
17
- updateOperations: StackbitTypes.UpdateOperation[];
18
- getDocumentById: GetDocumentById;
19
- getModelByName: GetModelByName;
20
- logger: StackbitTypes.Logger;
21
- }): Entry;
22
- export declare function getUpdatedEntryAtFieldPathWithOperation({ entry, updateOperation, model, getDocumentById, getModelByName, logger }: {
23
- entry: Entry;
24
- updateOperation: StackbitTypes.UpdateOperation;
25
- model: ModelWithContext;
26
- getDocumentById: GetDocumentById;
27
- getModelByName: GetModelByName;
28
- logger: StackbitTypes.Logger;
29
- }): Entry;
30
- export declare function convertOperationFieldValue({ updateOperationField, rootModel, modelField, entryFieldPath, modelFieldPath, getDocumentById, getModelByName, errorPrefix }: {
31
- updateOperationField: StackbitTypes.UpdateOperationField;
32
- rootModel: ModelWithContext;
33
- modelField: StackbitTypes.FieldSpecificProps;
34
- entryFieldPath: (string | number)[];
35
- modelFieldPath: string[];
36
- getDocumentById: GetDocumentById;
37
- getModelByName: GetModelByName;
38
- errorPrefix: string;
39
- }): any;
40
- export {};
41
- //# sourceMappingURL=contentstack-conversion-utils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"contentstack-conversion-utils.d.ts","sourceRoot":"","sources":["../src/contentstack-conversion-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,aAAa,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,wDAAwD,CAAC;AAEnF,OAAO,EAAE,KAAK,EAAS,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAEvE,aAAK,cAAc,GAAG,CAAC,SAAS,EAAE,MAAM,KAAK,gBAAgB,GAAG,SAAS,CAAC;AAC1E,aAAK,eAAe,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,mBAAmB,GAAG,SAAS,CAAC;AAE/E,wBAAgB,8BAA8B,CAAC,EAC3C,qBAAqB,EACrB,KAAK,EACL,eAAe,EACf,cAAc,EACd,MAAM,EACT,EAAE;IACC,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IAC1E,KAAK,EAAE,gBAAgB,CAAC;IACxB,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC;CAChC,GAAG,SAAS,CAsBZ;AAED,wBAAgB,+BAA+B,CAAC,EAC5C,KAAK,EACL,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,MAAM,EACT,EAAE;IACC,KAAK,EAAE,KAAK,CAAC;IACb,gBAAgB,EAAE,aAAa,CAAC,eAAe,EAAE,CAAC;IAClD,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC;CAChC,GAAG,KAAK,CA8CR;AAED,wBAAgB,uCAAuC,CAAC,EACpD,KAAK,EACL,eAAe,EACf,KAAK,EACL,eAAe,EACf,cAAc,EACd,MAAM,EACT,EAAE;IACC,KAAK,EAAE,KAAK,CAAC;IACb,eAAe,EAAE,aAAa,CAAC,eAAe,CAAC;IAC/C,KAAK,EAAE,gBAAgB,CAAC;IACxB,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC;CAChC,GAAG,KAAK,CA+OR;AA2FD,wBAAgB,0BAA0B,CAAC,EACvC,oBAAoB,EACpB,SAAS,EACT,UAAU,EACV,cAAc,EACd,cAAc,EACd,eAAe,EACf,cAAc,EACd,WAAW,EACd,EAAE;IACC,oBAAoB,EAAE,aAAa,CAAC,oBAAoB,CAAC;IACzD,SAAS,EAAE,gBAAgB,CAAC;IAC5B,UAAU,EAAE,aAAa,CAAC,kBAAkB,CAAC;IAC7C,cAAc,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACpC,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;CACvB,GAAG,GAAG,CA+KN"}