@stackbit/cms-sanity 0.2.23-develop.2 → 0.2.23-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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/sanity-api-client.d.ts +21 -2
- package/dist/sanity-api-client.d.ts.map +1 -1
- package/dist/sanity-api-client.js +33 -5
- package/dist/sanity-api-client.js.map +1 -1
- package/dist/sanity-content-source.d.ts +14 -1
- package/dist/sanity-content-source.d.ts.map +1 -1
- package/dist/sanity-content-source.js +54 -1
- package/dist/sanity-content-source.js.map +1 -1
- package/dist/sanity-schema-converter.d.ts.map +1 -1
- package/dist/sanity-schema-converter.js +7 -1
- package/dist/sanity-schema-converter.js.map +1 -1
- package/package.json +5 -5
- package/src/sanity-api-client.ts +65 -4
- package/src/sanity-content-source.ts +79 -4
- package/src/sanity-schema-converter.ts +6 -1
package/src/sanity-api-client.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import https from 'https';
|
|
2
2
|
import _ from 'lodash';
|
|
3
3
|
import { SanityClient as SanityClientType, SanityDocument } from '@sanity/client';
|
|
4
|
+
import { isDraftId } from './sanity-document-converter';
|
|
4
5
|
export { default as SanityClient } from '@sanity/client';
|
|
5
6
|
|
|
6
7
|
export interface SanityUser {
|
|
@@ -72,20 +73,35 @@ export async function testToken(apiToken: string): Promise<{
|
|
|
72
73
|
});
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
export async function fetchDocumentsHistory(
|
|
76
|
+
export async function fetchDocumentsHistory({
|
|
77
|
+
documentIds,
|
|
78
|
+
dataset,
|
|
79
|
+
client,
|
|
80
|
+
limitTime = true
|
|
81
|
+
}: {
|
|
82
|
+
documentIds: string[];
|
|
83
|
+
dataset: string;
|
|
84
|
+
client: SanityClientType;
|
|
85
|
+
limitTime?: boolean;
|
|
86
|
+
}): Promise<DocumentHistory[]> {
|
|
76
87
|
if (!documentIds.length) {
|
|
77
88
|
return [];
|
|
78
89
|
}
|
|
79
|
-
// get docs history from last 30 days
|
|
80
|
-
const fromTime = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();
|
|
81
90
|
// max size of one request to Sanity is 32Kb
|
|
82
91
|
// Sanity response: AssertionError [ERR_ASSERTION]: Your message must be < 32kb.
|
|
83
92
|
// it's undocumented, so 11Kb is query limit - https://www.sanity.io/docs/http-query, hence stick to that
|
|
84
93
|
const chunkedDocumentIds = chunkArray(documentIds, 11000);
|
|
85
94
|
const result: DocumentHistory[] = [];
|
|
86
95
|
for (const idsChunk of chunkedDocumentIds) {
|
|
96
|
+
const searchParams = new URLSearchParams();
|
|
97
|
+
searchParams.append('excludeContent', 'true');
|
|
98
|
+
searchParams.append('reverse', 'true');
|
|
99
|
+
if (limitTime) {
|
|
100
|
+
// get docs history from last 30 days
|
|
101
|
+
searchParams.append('fromTime', new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString());
|
|
102
|
+
}
|
|
87
103
|
const data = await client.request({
|
|
88
|
-
uri: `data/history/${dataset}/transactions/${idsChunk.join(',')}
|
|
104
|
+
uri: `data/history/${dataset}/transactions/${idsChunk.join(',')}?${searchParams.toString()}`
|
|
89
105
|
});
|
|
90
106
|
for (const line of data.split('\n')) {
|
|
91
107
|
if (line) {
|
|
@@ -96,6 +112,51 @@ export async function fetchDocumentsHistory(documentIds: string[], dataset: stri
|
|
|
96
112
|
return result;
|
|
97
113
|
}
|
|
98
114
|
|
|
115
|
+
export async function fetchDocumentRevision({
|
|
116
|
+
documentId,
|
|
117
|
+
draftDocumentId,
|
|
118
|
+
versionId,
|
|
119
|
+
dataset,
|
|
120
|
+
client
|
|
121
|
+
}: {
|
|
122
|
+
documentId: string;
|
|
123
|
+
draftDocumentId?: string;
|
|
124
|
+
versionId: string;
|
|
125
|
+
dataset: string;
|
|
126
|
+
client: SanityClientType;
|
|
127
|
+
}): Promise<DocumentHistory> {
|
|
128
|
+
const searchParams = new URLSearchParams();
|
|
129
|
+
searchParams.append('excludeContent', 'true');
|
|
130
|
+
searchParams.append('fromTransaction', versionId);
|
|
131
|
+
searchParams.append('toTransaction', versionId);
|
|
132
|
+
const documentIds = [documentId, draftDocumentId];
|
|
133
|
+
const data = await client.request({
|
|
134
|
+
uri: `data/history/${dataset}/transactions/${documentIds.join(',')}?${searchParams.toString()}`
|
|
135
|
+
});
|
|
136
|
+
return JSON.parse(data);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export async function fetchDocumentForRevision({
|
|
140
|
+
documentId,
|
|
141
|
+
draftDocumentId,
|
|
142
|
+
versionId,
|
|
143
|
+
dataset,
|
|
144
|
+
client
|
|
145
|
+
}: {
|
|
146
|
+
documentId: string;
|
|
147
|
+
draftDocumentId?: string;
|
|
148
|
+
versionId: string;
|
|
149
|
+
dataset: string;
|
|
150
|
+
client: SanityClientType;
|
|
151
|
+
}): Promise<SanityDocument> {
|
|
152
|
+
const documentIds = [documentId, draftDocumentId];
|
|
153
|
+
const data = await client.request({
|
|
154
|
+
uri: `data/history/${dataset}/documents/${documentIds.join(',')}?revision=${versionId}`
|
|
155
|
+
});
|
|
156
|
+
// take either latest draft revision object, or latest object
|
|
157
|
+
return data.documents.find((object: SanityDocument) => isDraftId(object._id)) ?? data.documents[data.documents.length - 1];
|
|
158
|
+
}
|
|
159
|
+
|
|
99
160
|
export async function fetchUsers(userIds: string[], client: SanityClientType): Promise<SanityUser[]> {
|
|
100
161
|
const chunkedDocumentIds = chunkArray(userIds, 11000);
|
|
101
162
|
const result = [];
|
|
@@ -6,7 +6,18 @@ import path from 'path';
|
|
|
6
6
|
import fse from 'fs-extra';
|
|
7
7
|
import { glob } from 'glob';
|
|
8
8
|
import { MutationEvent, PatchOperations, SanityAssetDocument, SanityClient as SanityClientType, SanityDocument, SanityDocumentStub } from '@sanity/client';
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
DocumentVersion,
|
|
11
|
+
DocumentVersionWithDocument,
|
|
12
|
+
Field,
|
|
13
|
+
FieldList,
|
|
14
|
+
FieldListItems,
|
|
15
|
+
FieldObjectProps,
|
|
16
|
+
FieldSpecificProps,
|
|
17
|
+
Model,
|
|
18
|
+
Schema,
|
|
19
|
+
UserCommandSpawner
|
|
20
|
+
} from '@stackbit/types';
|
|
10
21
|
import type * as ContentSourceTypes from '@stackbit/types';
|
|
11
22
|
import { getVersion as stackbitUtilsGetVersion } from '@stackbit/types';
|
|
12
23
|
|
|
@@ -16,10 +27,12 @@ import {
|
|
|
16
27
|
testToken,
|
|
17
28
|
DocumentHistory,
|
|
18
29
|
DocumentHistoryMap,
|
|
19
|
-
fetchDocumentsHistory,
|
|
20
30
|
fetchUsers,
|
|
21
31
|
SanityUser,
|
|
22
|
-
fetchScheduledActions
|
|
32
|
+
fetchScheduledActions,
|
|
33
|
+
fetchDocumentsHistory,
|
|
34
|
+
fetchDocumentForRevision,
|
|
35
|
+
fetchDocumentRevision
|
|
23
36
|
} from './sanity-api-client';
|
|
24
37
|
import { convertSchema } from './sanity-schema-converter';
|
|
25
38
|
import {
|
|
@@ -431,6 +444,30 @@ export class SanityContentSource implements ContentSourceTypes.ContentSourceInte
|
|
|
431
444
|
return this.cache.updateContent(result);
|
|
432
445
|
}
|
|
433
446
|
|
|
447
|
+
private convertVersionsFromDocumentHistory(versions: DocumentHistory[]): DocumentVersion[] {
|
|
448
|
+
return versions.map((version) => {
|
|
449
|
+
return {
|
|
450
|
+
id: version.id,
|
|
451
|
+
documentId: getPureObjectId(version.documentIDs[0]!),
|
|
452
|
+
srcType: this.getContentSourceType(),
|
|
453
|
+
srcProjectId: this.projectId,
|
|
454
|
+
createdAt: version.timestamp,
|
|
455
|
+
createdBy: this.userMap[version.author]?.email
|
|
456
|
+
};
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
private convertVersionForDocument(version: DocumentHistory, document: ContextualDocument): DocumentVersionWithDocument {
|
|
461
|
+
const [documentVersion] = this.convertVersionsFromDocumentHistory([version]);
|
|
462
|
+
if (!documentVersion) {
|
|
463
|
+
throw new Error('Document version could not be converted');
|
|
464
|
+
}
|
|
465
|
+
return {
|
|
466
|
+
...documentVersion,
|
|
467
|
+
document
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
434
471
|
async getSanitySchema(): Promise<any> {
|
|
435
472
|
let sanitySchema;
|
|
436
473
|
try {
|
|
@@ -461,7 +498,7 @@ export class SanityContentSource implements ContentSourceTypes.ContentSourceInte
|
|
|
461
498
|
this.logger.debug('getDocumentsHistory started', documentIds.length);
|
|
462
499
|
|
|
463
500
|
const draftDocumentIds = documentIds.filter((documentId) => isDraftId(documentId));
|
|
464
|
-
const historyData = await fetchDocumentsHistory(draftDocumentIds, this.dataset, this.client);
|
|
501
|
+
const historyData = await fetchDocumentsHistory({ documentIds: draftDocumentIds, dataset: this.dataset, client: this.client });
|
|
465
502
|
|
|
466
503
|
const notCachedDocAuthors = historyData.reduce((acc: string[], doc: DocumentHistory) => {
|
|
467
504
|
const user = this.userMap[doc.author];
|
|
@@ -899,6 +936,44 @@ export class SanityContentSource implements ContentSourceTypes.ContentSourceInte
|
|
|
899
936
|
apiVersion: SANITY_API_VERSION
|
|
900
937
|
});
|
|
901
938
|
}
|
|
939
|
+
|
|
940
|
+
async getDocumentVersions({ documentId }: { documentId: string }): Promise<{ versions: DocumentVersion[] }> {
|
|
941
|
+
this.logger.debug('getDocumentVersions', { documentId });
|
|
942
|
+
|
|
943
|
+
const documentHistory = await fetchDocumentsHistory({
|
|
944
|
+
documentIds: [documentId, getDraftObjectId(documentId)],
|
|
945
|
+
dataset: this.dataset,
|
|
946
|
+
client: this.client,
|
|
947
|
+
limitTime: false
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
if (!documentHistory) {
|
|
951
|
+
return { versions: [] };
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
return { versions: this.convertVersionsFromDocumentHistory(documentHistory) };
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
async getDocumentForVersion({ documentId, versionId }: { documentId: string; versionId: string }): Promise<{ version: DocumentVersionWithDocument }> {
|
|
958
|
+
this.logger.debug('getDocumentForVersion', { documentId, versionId });
|
|
959
|
+
|
|
960
|
+
const draftDocumentId = getDraftObjectId(documentId);
|
|
961
|
+
const [version, document] = await Promise.all([
|
|
962
|
+
fetchDocumentRevision({ documentId, draftDocumentId, versionId, dataset: this.dataset, client: this.client }),
|
|
963
|
+
fetchDocumentForRevision({ documentId, draftDocumentId, versionId, dataset: this.dataset, client: this.client })
|
|
964
|
+
]);
|
|
965
|
+
const [contextualDocument] = await this.convertDocuments({
|
|
966
|
+
documents: [document],
|
|
967
|
+
getModelByName: this.cache.getModelByName,
|
|
968
|
+
studioUrl: this.studioUrl
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
if (!contextualDocument) {
|
|
972
|
+
throw new Error(`Could not get document ${documentId} for revision ${versionId}`);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
return { version: this.convertVersionForDocument(version, contextualDocument) };
|
|
976
|
+
}
|
|
902
977
|
}
|
|
903
978
|
|
|
904
979
|
function mapUpdateOperationFieldToSanityValue(
|
|
@@ -5,7 +5,8 @@ import { deepMap, omitByNil } from '@stackbit/utils';
|
|
|
5
5
|
import { resolveLabelFieldForModel } from './utils';
|
|
6
6
|
|
|
7
7
|
// sanity cloudinary plugin adds these models, we are replacing them to our internal image model, so we removing them from schema response
|
|
8
|
-
const
|
|
8
|
+
const thirdPartyImageModels = ['cloudinary.asset', 'cloudinary.assetDerived', 'bynder.asset', 'aprimo.asset', 'aprimo.cdnasset'];
|
|
9
|
+
const skipModels = [...thirdPartyImageModels, 'media.tag'];
|
|
9
10
|
|
|
10
11
|
export function convertSchema(schema: any): { models: Model[] } {
|
|
11
12
|
// sanity schema allow arrays to be at the root level, we don't.
|
|
@@ -238,6 +239,10 @@ const fieldConverterMap = {
|
|
|
238
239
|
if (_.has(arrayModelsByName, type)) {
|
|
239
240
|
return fieldConverterMap.array(arrayModelsByName[type], arrayModelsByName);
|
|
240
241
|
}
|
|
242
|
+
if (thirdPartyImageModels.includes(type)) {
|
|
243
|
+
const fn = (fieldConverterMap[type as keyof typeof fieldConverterMap] as any) ?? fieldConverterMap.image;
|
|
244
|
+
return fn();
|
|
245
|
+
}
|
|
241
246
|
return {
|
|
242
247
|
type: 'model',
|
|
243
248
|
models: [type]
|