@stackbit/cms-contentstack 0.1.1-staging.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.
Files changed (72) hide show
  1. package/README.md +1 -0
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/contentstack-api-client.d.ts +63 -0
  4. package/dist/contentstack-api-client.d.ts.map +1 -0
  5. package/dist/contentstack-api-client.js +295 -0
  6. package/dist/contentstack-api-client.js.map +1 -0
  7. package/dist/contentstack-content-poller.d.ts +46 -0
  8. package/dist/contentstack-content-poller.d.ts.map +1 -0
  9. package/dist/contentstack-content-poller.js +111 -0
  10. package/dist/contentstack-content-poller.js.map +1 -0
  11. package/dist/contentstack-content-source.d.ts +138 -0
  12. package/dist/contentstack-content-source.d.ts.map +1 -0
  13. package/dist/contentstack-content-source.js +544 -0
  14. package/dist/contentstack-content-source.js.map +1 -0
  15. package/dist/contentstack-conversion-utils.d.ts +41 -0
  16. package/dist/contentstack-conversion-utils.d.ts.map +1 -0
  17. package/dist/contentstack-conversion-utils.js +504 -0
  18. package/dist/contentstack-conversion-utils.js.map +1 -0
  19. package/dist/contentstack-entries-converter.d.ts +39 -0
  20. package/dist/contentstack-entries-converter.d.ts.map +1 -0
  21. package/dist/contentstack-entries-converter.js +333 -0
  22. package/dist/contentstack-entries-converter.js.map +1 -0
  23. package/dist/contentstack-operation-converter.d.ts +42 -0
  24. package/dist/contentstack-operation-converter.d.ts.map +1 -0
  25. package/dist/contentstack-operation-converter.js +535 -0
  26. package/dist/contentstack-operation-converter.js.map +1 -0
  27. package/dist/contentstack-schema-converter.d.ts +26 -0
  28. package/dist/contentstack-schema-converter.d.ts.map +1 -0
  29. package/dist/contentstack-schema-converter.js +379 -0
  30. package/dist/contentstack-schema-converter.js.map +1 -0
  31. package/dist/contentstack-types.d.ts +429 -0
  32. package/dist/contentstack-types.d.ts.map +1 -0
  33. package/dist/contentstack-types.js +3 -0
  34. package/dist/contentstack-types.js.map +1 -0
  35. package/dist/contentstack-utils.d.ts +31 -0
  36. package/dist/contentstack-utils.d.ts.map +1 -0
  37. package/dist/contentstack-utils.js +144 -0
  38. package/dist/contentstack-utils.js.map +1 -0
  39. package/dist/entries-converter.d.ts +10 -0
  40. package/dist/entries-converter.d.ts.map +1 -0
  41. package/dist/entries-converter.js +245 -0
  42. package/dist/entries-converter.js.map +1 -0
  43. package/dist/file-download.d.ts +2 -0
  44. package/dist/file-download.d.ts.map +1 -0
  45. package/dist/file-download.js +33 -0
  46. package/dist/file-download.js.map +1 -0
  47. package/dist/index.d.ts +4 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +14 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/schema-converter.d.ts +3 -0
  52. package/dist/schema-converter.d.ts.map +1 -0
  53. package/dist/schema-converter.js +169 -0
  54. package/dist/schema-converter.js.map +1 -0
  55. package/dist/transformation-utils.d.ts +41 -0
  56. package/dist/transformation-utils.d.ts.map +1 -0
  57. package/dist/transformation-utils.js +730 -0
  58. package/dist/transformation-utils.js.map +1 -0
  59. package/dist/types.d.ts +120 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/types.js +3 -0
  62. package/dist/types.js.map +1 -0
  63. package/package.json +44 -0
  64. package/src/contentstack-api-client.ts +330 -0
  65. package/src/contentstack-content-poller.ts +157 -0
  66. package/src/contentstack-content-source.ts +687 -0
  67. package/src/contentstack-entries-converter.ts +438 -0
  68. package/src/contentstack-operation-converter.ts +703 -0
  69. package/src/contentstack-schema-converter.ts +486 -0
  70. package/src/contentstack-types.ts +527 -0
  71. package/src/contentstack-utils.ts +174 -0
  72. package/src/index.ts +3 -0
@@ -0,0 +1,157 @@
1
+ import _ from 'lodash';
2
+ import type { Logger } from '@stackbit/types';
3
+ import type { ContentStackClient } from './contentstack-api-client';
4
+ import type { ContentType, GlobalField, Entry, Asset } from './contentstack-types';
5
+
6
+ export type SyncContext = {
7
+ lastUpdatedEntryDate?: string;
8
+ lastUpdatedAssetDate?: string;
9
+ lastUpdatedContentTypeDate?: string;
10
+ lastUpdatedGlobalFieldDate?: string;
11
+ };
12
+
13
+ export interface SyncResult {
14
+ entries: Entry[];
15
+ assets: Asset[];
16
+ contentTypes: ContentType[];
17
+ globalFields: GlobalField[];
18
+ deletedEntries: Entry[];
19
+ deletedAssets: Asset[];
20
+ }
21
+
22
+ export type SyncCallback = (result: SyncResult) => void;
23
+
24
+ export class ContentPoller {
25
+ private readonly logger: Logger;
26
+ private readonly contentStackClient: ContentStackClient;
27
+ private readonly notificationCallback: SyncCallback;
28
+ private readonly pollingIntervalMs: number;
29
+ private readonly modelNames: string[];
30
+ private running: boolean;
31
+ private pollTimeout: NodeJS.Timeout | null;
32
+ private syncContext!: Required<SyncContext>;
33
+
34
+ constructor({
35
+ modelNames,
36
+ contentStackClient,
37
+ syncContext,
38
+ pollingIntervalMs = 2000,
39
+ notificationCallback,
40
+ logger
41
+ }: {
42
+ modelNames: string[];
43
+ contentStackClient: ContentStackClient;
44
+ syncContext?: SyncContext;
45
+ pollingIntervalMs?: number;
46
+ notificationCallback: SyncCallback;
47
+ logger: Logger;
48
+ }) {
49
+ this.logger = logger;
50
+ this.modelNames = modelNames;
51
+ this.running = false;
52
+ this.pollTimeout = null;
53
+ this.pollingIntervalMs = pollingIntervalMs;
54
+ this.contentStackClient = contentStackClient;
55
+ this.notificationCallback = notificationCallback;
56
+ this.setSyncContext(syncContext);
57
+ this.handleTimeout = this.handleTimeout.bind(this);
58
+ }
59
+
60
+ start() {
61
+ this.logger.debug(`start polling Contentstack for content changes`);
62
+ if (!this.running) {
63
+ this.running = true;
64
+ this.setPollTimeout();
65
+ }
66
+ }
67
+
68
+ stop() {
69
+ this.logger.debug(`stop polling Contentstack for content changes`);
70
+ if (this.pollTimeout) {
71
+ clearTimeout(this.pollTimeout);
72
+ this.pollTimeout = null;
73
+ }
74
+ this.running = false;
75
+ }
76
+
77
+ setSyncContext(syncContext?: SyncContext) {
78
+ const now = new Date().toISOString();
79
+ this.syncContext = {
80
+ lastUpdatedEntryDate: syncContext?.lastUpdatedEntryDate ?? now,
81
+ lastUpdatedAssetDate: syncContext?.lastUpdatedAssetDate ?? now,
82
+ lastUpdatedContentTypeDate: syncContext?.lastUpdatedContentTypeDate ?? now,
83
+ lastUpdatedGlobalFieldDate: syncContext?.lastUpdatedGlobalFieldDate ?? now
84
+ };
85
+ }
86
+
87
+ private setPollTimeout() {
88
+ if (this.pollTimeout) {
89
+ clearTimeout(this.pollTimeout);
90
+ }
91
+ this.pollTimeout = setTimeout(this.handleTimeout, this.pollingIntervalMs);
92
+ }
93
+
94
+ async handleTimeout() {
95
+ this.pollTimeout = null;
96
+ try {
97
+ await this.pollContent();
98
+ } catch (error: any) {
99
+ this.logger.warn('error polling', { error: error.message });
100
+ }
101
+ if (this.running) {
102
+ this.setPollTimeout();
103
+ }
104
+ }
105
+
106
+ async pollContent(): Promise<any> {
107
+ const prevSyncContext = this.syncContext;
108
+ const [entries, assets, contentTypes, globalFields] = await Promise.all([
109
+ this.contentStackClient.getAllEntriesUpdatedAfter(this.modelNames, this.syncContext.lastUpdatedEntryDate),
110
+ this.contentStackClient.getAssetsUpdatedAfter(this.syncContext.lastUpdatedAssetDate),
111
+ this.contentStackClient.getContentTypesUpdatedAfter(this.syncContext.lastUpdatedContentTypeDate),
112
+ this.contentStackClient.getGlobalFieldsUpdatedAfter(this.syncContext.lastUpdatedGlobalFieldDate)
113
+ ]);
114
+ // Check that syncContext wasn't updated externally by calling
115
+ // setLastUpdatedAt() while the content is being fetched. If it was
116
+ // updated, then ignore the dates from the fetched content and use the
117
+ // syncContext that was set externally in the next poll.
118
+ if (_.isEqual(prevSyncContext, this.syncContext)) {
119
+ this.syncContext = _.defaults(
120
+ {
121
+ lastUpdatedEntryDate: getLastUpdatedEntityDate(entries),
122
+ lastUpdatedAssetDate: getLastUpdatedEntityDate(assets),
123
+ lastUpdatedContentTypeDate: getLastUpdatedEntityDate(contentTypes),
124
+ lastUpdatedGlobalFieldDate: getLastUpdatedEntityDate(globalFields)
125
+ },
126
+ this.syncContext
127
+ );
128
+ }
129
+ if (contentTypes.length || globalFields.length || entries.length || assets.length) {
130
+ this.logger.info(
131
+ `poll date response: got ` +
132
+ `${entries.length} changed entries, ` +
133
+ `${assets.length} changed assets, ` +
134
+ `${contentTypes.length} changed content types` +
135
+ `${globalFields.length} changed global fields`
136
+ );
137
+ this.notificationCallback({
138
+ contentTypes,
139
+ globalFields,
140
+ entries,
141
+ assets,
142
+ deletedEntries: [],
143
+ deletedAssets: []
144
+ });
145
+ }
146
+ }
147
+ }
148
+
149
+ export function getLastUpdatedEntityDate(entities: { updated_at?: string }[]): string | undefined {
150
+ let lastUpdatedDate: string = '';
151
+ for (const entity of entities) {
152
+ if (entity.updated_at! > lastUpdatedDate) {
153
+ lastUpdatedDate = entity.updated_at!;
154
+ }
155
+ }
156
+ return lastUpdatedDate || undefined;
157
+ }