bootstrap-schemas.mjs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. const parseUrl = process.env.PARSE_URL || 'http://127.0.0.1:3000/parse';
  2. const appId = process.env.PARSE_APP_ID;
  3. const masterKey = process.env.PARSE_MASTER_KEY;
  4. if (!appId || !masterKey) throw new Error('Missing Parse bootstrap environment variables');
  5. const headers = {
  6. 'Content-Type': 'application/json',
  7. 'X-Parse-Application-Id': appId,
  8. 'X-Parse-Master-Key': masterKey,
  9. };
  10. async function request(path, method = 'GET', body) {
  11. const response = await fetch(`${parseUrl}${path}`, {
  12. method,
  13. headers,
  14. body: body === undefined ? undefined : JSON.stringify(body),
  15. });
  16. const result = await response.json();
  17. if (!response.ok || result.error) throw new Error(`${method} ${path}: ${result.error || response.status}`);
  18. return result;
  19. }
  20. const StringField = { type: 'String' };
  21. const NumberField = { type: 'Number' };
  22. const BooleanField = { type: 'Boolean' };
  23. const DateField = { type: 'Date' };
  24. const ArrayField = { type: 'Array' };
  25. const ObjectField = { type: 'Object' };
  26. const pointer = targetClass => ({ type: 'Pointer', targetClass });
  27. const productFields = {
  28. recordKey: StringField, asin: StringField, Asin: StringField, parentAsin: StringField, sku: StringField,
  29. sellerSku: StringField, productSku: StringField, fnsku: StringField,
  30. title: StringField, itemName: StringField, imageUrl: StringField, photo: StringField,
  31. productType: StringField, platform: StringField, currency: StringField,
  32. brand: StringField, category: StringField, categoryName: StringField, nodeId: StringField,
  33. categoryNodeId: StringField, sellerId: StringField, marketplace: StringField,
  34. marketplaceId: StringField, domain: StringField, site: StringField, country: StringField,
  35. countryCode: StringField, storeName: StringField, shopName: StringField,
  36. shopId: StringField, storeId: StringField,
  37. userAccount: StringField, designerId: StringField, designerName: StringField,
  38. operatorId: StringField, operatorName: StringField,
  39. developerId: StringField, developerName: StringField,
  40. departCode: StringField, accessUserId: StringField, fulfillmentType: StringField,
  41. onlineDate: StringField, openDateSite: StringField, listingOpenDateSite: StringField,
  42. firstOrderDateSite: StringField, onlineDays: NumberField,
  43. listingColor: StringField, listingSize: StringField, listingVariation: StringField,
  44. color: StringField, size: StringField, variation: StringField,
  45. listingImageUrl: StringField, listingSmallImageUrl: StringField,
  46. source: StringField, status: StringField, itemStatus: NumberField,
  47. price: NumberField, salesPrice: NumberField, listingPrice: NumberField,
  48. regularPrice: NumberField, rating: NumberField, ratings: NumberField,
  49. ratingsCount: NumberField, reviewCount: NumberField, rank: NumberField,
  50. monthlySales: NumberField, listingSalesVolumeOfMonth: NumberField,
  51. ListingSalesVolumeOfMonth: NumberField, listingSalesOfMonth: NumberField,
  52. inventoryQuantity: NumberField, sellerCount: NumberField,
  53. bsrCategory: ArrayField, attributes: ObjectField, rawData: ObjectField,
  54. catalogData: ObjectField, pricingData: ObjectField, inventoryData: ObjectField,
  55. shop: pointer('Shop'),
  56. };
  57. const reviewFields = {
  58. recordKey: StringField, reviewId: StringField, asin: StringField,
  59. marketplaceId: StringField, domain: NumberField,
  60. asins: ArrayField, marketplaceIds: ArrayField, domains: ArrayField, shopIds: ArrayField,
  61. title: StringField, content: StringField,
  62. author: StringField, reviewDate: StringField, sentiment: StringField,
  63. date: StringField, createTime: StringField, source: StringField,
  64. reviewPublishedAt: DateField, capturedAt: DateField,
  65. translatedTitle: StringField, translatedContent: StringField,
  66. star: NumberField, rating: NumberField, verified: BooleanField,
  67. keywords: ArrayField, topics: ArrayField, rawData: ObjectField, shop: pointer('Shop'),
  68. };
  69. const schemaDefinitions = {
  70. Shop: {
  71. name: StringField, shopName: StringField, storeName: StringField, platform: StringField,
  72. region: StringField, marketplaceId: StringField, domain: StringField, nodeIds: ArrayField,
  73. sync_status: StringField, syncStatus: StringField, status: StringField,
  74. disable: BooleanField, deleted: BooleanField, deletedAt: DateField,
  75. last_sync_at: DateField, lastSyncTime: DateField, config: ObjectField,
  76. },
  77. Product: productFields,
  78. ProductDetail: productFields,
  79. SorftimeProduct: productFields,
  80. AmazonProduct: productFields,
  81. AsinSkuMapping: productFields,
  82. SorftimeReviews: reviewFields,
  83. SorftimeReview: reviewFields,
  84. Review: reviewFields,
  85. ProductReview: reviewFields,
  86. Listing: {
  87. ...productFields, createdDate: DateField, lastUpdatedDate: DateField,
  88. issues: ArrayField, mainImage: StringField, currency: StringField, parentSku: StringField,
  89. productType: StringField, quantity: NumberField, condition: StringField,
  90. },
  91. Order: {
  92. platformOrderId: StringField, orderId: StringField, orderDate: DateField,
  93. lastUpdateDate: DateField, status: StringField, marketplaceId: StringField,
  94. totalAmount: NumberField, currency: StringField, items: ArrayField,
  95. buyerInfo: ObjectField, shippingAddress: ObjectField, customerRegion: StringField,
  96. shippingCountry: StringField, shippingState: StringField, shippingCity: StringField,
  97. fulfillmentChannel: StringField, orderType: StringField, isPrime: BooleanField,
  98. isBusinessOrder: BooleanField, earliestShipDate: DateField, latestShipDate: DateField,
  99. itemCount: NumberField, itemsSyncedAt: DateField, shop: pointer('Shop'),
  100. },
  101. OrderMetrics: {
  102. date: StringField, orderCount: NumberField, unitCount: NumberField,
  103. revenue: NumberField, currency: StringField, shop: pointer('Shop'),
  104. },
  105. ReturnRecord: {
  106. asin: StringField, sku: StringField, sellerSku: StringField, productSku: StringField,
  107. fnsku: StringField, orderId: StringField, order_id: StringField,
  108. amazonOrderId: StringField, returnDate: DateField, return_date: DateField,
  109. reason: StringField, reasonCode: StringField, comment: StringField,
  110. customer_comments: StringField, disposition: StringField, detailed_disposition: StringField,
  111. productName: StringField, product_name: StringField, status: StringField, site: StringField,
  112. marketplaceId: StringField, storeName: StringField, quantity: NumberField,
  113. amount: NumberField, price: NumberField, licensePlateNumber: StringField,
  114. license_plate_number: StringField, fulfillmentCenterId: StringField,
  115. fulfillment_center_id: StringField, shop: pointer('Shop'), rawData: ObjectField,
  116. },
  117. CompetitorMonitorPrefs: {
  118. ownerId: StringField, selectedAsins: ArrayField, groupMap: ObjectField,
  119. customGroups: ArrayField, tagMap: ObjectField, allTags: ArrayField,
  120. hiddenAsins: ArrayField, socialBrands: ArrayField,
  121. },
  122. AmazonCategory: {
  123. categoryId: StringField, nodeId: StringField, parentId: StringField,
  124. name: StringField, cnName: StringField, url: StringField,
  125. domain: NumberField, isLeaf: BooleanField, level: NumberField,
  126. children: ArrayField, rawData: ObjectField,
  127. },
  128. SelfCategory: {
  129. categoryId: StringField, nodeId: StringField, parentId: StringField,
  130. name: StringField, cnName: StringField, url: StringField,
  131. domain: NumberField, isLeaf: BooleanField, level: NumberField,
  132. path: ArrayField, keywords: ArrayField,
  133. },
  134. SorftimeCategory: {
  135. categoryId: StringField, nodeId: StringField, parentId: StringField,
  136. name: StringField, domain: NumberField, isLeaf: BooleanField,
  137. sales: NumberField, satisfaction: NumberField, marketShare: NumberField,
  138. source: StringField, rawData: ObjectField,
  139. },
  140. MarketTrend: {
  141. nodeId: StringField, categoryId: StringField, domain: NumberField,
  142. trendIndex: NumberField, date: StringField, name: StringField,
  143. value: NumberField, data: ObjectField, rawData: ObjectField,
  144. },
  145. Reports: {
  146. recordKey: StringField, region: StringField, reportId: StringField,
  147. reportDocumentId: StringField, reportType: StringField,
  148. processingStatus: StringField, isParsed: BooleanField, marketplaceIds: ArrayField,
  149. dataStartTime: DateField, dataEndTime: DateField, shop: pointer('Shop'), rawData: ObjectField,
  150. createdTime: DateField, processingStartTime: DateField, processingEndTime: DateField,
  151. parsedAt: DateField, parsedRecords: NumberField,
  152. },
  153. TaskExecutionLog: {
  154. taskName: StringField, startTime: DateField, endTime: DateField, duration: NumberField,
  155. successCount: NumberField, errorCount: NumberField, errors: ArrayField,
  156. status: StringField, processed: NumberField,
  157. },
  158. ReviewSentiment: { asin: StringField, sentiment: StringField, score: NumberField, data: ObjectField },
  159. SocialMediaCache: { cacheKey: StringField, dimension: StringField, data: ObjectField, expiresAt: DateField },
  160. AiPromptConfig: { promptKey: StringField, name: StringField, content: StringField, config: ObjectField },
  161. TodoStatus: {
  162. todoId: StringField, sourceId: StringField, status: StringField,
  163. userId: StringField, updatedAt_ts: NumberField, data: ObjectField,
  164. },
  165. WorkOrder: {
  166. suggestionId: StringField, productId: StringField, title: StringField,
  167. description: StringField, status: StringField, priority: StringField,
  168. dueDate: DateField, assignee: StringField, issueType: StringField,
  169. issueId: StringField, sourceId: StringField, createdBy: StringField,
  170. actionSuggestionStatus: StringField, actionSuggestionType: StringField,
  171. actionSuggestionAssignee: StringField, priorityScore: NumberField,
  172. updatedAt_ts: NumberField, aiAnalysisSnapshot: ObjectField,
  173. actionSuggestionDetailPatch: ObjectField, workOrder: ObjectField,
  174. workOrderId: StringField, workOrderOwner: StringField, nextAction: StringField,
  175. slaStatus: StringField, acceptance: ObjectField, issueTaxonomy: ObjectField,
  176. rootCause: ObjectField, impact: ObjectField, actionPlan: ArrayField,
  177. designerNames: ArrayField, operatorNames: ArrayField,
  178. productSkus: ArrayField, fulfillmentTypes: ArrayField,
  179. verificationResult: ObjectField, data: ObjectField,
  180. },
  181. ActionSuggestionStatus: {
  182. suggestionId: StringField, status: StringField, assignee: StringField,
  183. updatedAt_ts: NumberField, verificationResult: ObjectField,
  184. detailPatch: ObjectField, data: ObjectField,
  185. },
  186. ActionSuggestionAudit: {
  187. suggestionId: StringField, action: StringField, workOrderId: StringField,
  188. eventType: StringField, status: StringField, title: StringField,
  189. assignee: StringField, summary: StringField, detailPatch: ObjectField,
  190. verificationResult: ObjectField, createdAt_ts: NumberField, data: ObjectField,
  191. },
  192. Notification: {
  193. type: StringField, targetType: StringField, sourceType: StringField,
  194. sourceId: StringField, suggestionId: StringField, actionStatus: StringField,
  195. eventType: StringField, channel: StringField, title: StringField,
  196. content: StringField, status: StringField, createdAt_ts: NumberField,
  197. data: ObjectField,
  198. },
  199. RequirementStrengthScore: { asin: StringField, score: NumberField, data: ObjectField },
  200. ReturnImprovementAction: {
  201. actionId: StringField, shopId: StringField, asin: StringField,
  202. reasonCategory: StringField, reasonDetail: StringField, action: StringField,
  203. owner: StringField, status: StringField, actionCreatedAt: StringField,
  204. completedAt: StringField, beforeRate: NumberField, afterRate: NumberField,
  205. improvement: NumberField, verificationNote: StringField, source: StringField,
  206. updatedAt_ts: NumberField, data: ObjectField,
  207. },
  208. SizeChartKnowledge: { name: StringField, note: StringField, category: StringField, rows: ArrayField, columns: ArrayField },
  209. Keyword: { keyword: StringField, searchVolume: NumberField, trend: NumberField, competition: NumberField, cpcAmount: NumberField, currency: StringField },
  210. Category: { categoryId: StringField, name: StringField, parentId: StringField, productCount: NumberField },
  211. SorftimeKeyword: {
  212. keyword: StringField, searchVolume: NumberField, trend: NumberField,
  213. growthRate: NumberField, source: StringField, rawData: ObjectField,
  214. },
  215. SorftimeMarketCapacity: {
  216. nodeId: StringField, domain: NumberField, category: StringField,
  217. totalMarketSize: NumberField, competitionLevel: StringField,
  218. source: StringField, data: ObjectField, rawData: ObjectField,
  219. },
  220. EmergingTrendSignal: {
  221. signalId: StringField, keyword: StringField, category: StringField,
  222. label: StringField, score: NumberField, trend: NumberField,
  223. source: StringField, fetchedAt: StringField, ttlMinutes: NumberField,
  224. data: ObjectField,
  225. },
  226. EmergingTrendKeyword: {
  227. keyword: StringField, text: StringField, normalizedText: StringField,
  228. score: NumberField, weight: NumberField, platform: StringField,
  229. growth: NumberField, rank: NumberField, fetchedAt: StringField,
  230. lastSeenAt: StringField, cacheAgeMinutes: NumberField,
  231. freshnessLevel: StringField, sourceStatus: StringField,
  232. sourceLatencyMs: NumberField, sampleCount: NumberField,
  233. confidence: NumberField, trendVelocity: NumberField,
  234. ttlMinutes: NumberField, source: StringField, data: ObjectField,
  235. },
  236. EmergingOpportunity: {
  237. opportunityId: StringField, title: StringField, score: NumberField,
  238. gapIndex: NumberField, description: StringField, tags: ArrayField,
  239. status: StringField, source: StringField, fetchedAt: StringField,
  240. data: ObjectField,
  241. },
  242. EmergingSocialTrendSnapshot: {
  243. snapshotId: StringField, status: StringField, fetchedAt: StringField,
  244. nextRefreshAt: StringField, cacheAgeMinutes: NumberField,
  245. ttlMinutes: NumberField, freshnessLevel: StringField,
  246. sourceStatus: StringField, sourceLatencyMs: NumberField,
  247. totalKeywords: NumberField, realtimeCount: NumberField,
  248. freshCount: NumberField, staleCount: NumberField,
  249. platformBreakdown: ObjectField, data: ObjectField,
  250. },
  251. KnowledgeCompetitor: {
  252. asin: StringField, title: StringField, name: StringField,
  253. brandName: StringField, category: StringField, price: NumberField,
  254. rating: NumberField, reviewCount: NumberField, monthlySales: NumberField,
  255. platform: StringField, source: StringField, data: ObjectField,
  256. },
  257. KnowledgeInfluencer: {
  258. uniqueId: StringField, platform: StringField, name: StringField,
  259. avatar: StringField, followers: NumberField, engagementRate: NumberField,
  260. tier: StringField, mainCategory: StringField, cooperationPrice: NumberField,
  261. source: StringField, data: ObjectField,
  262. },
  263. KnowledgeInfluencerProfile: {
  264. uniqueId: StringField, platform: StringField, nickname: StringField,
  265. avatar: StringField, signature: StringField, followerCount: NumberField,
  266. heartCount: NumberField, videoCount: NumberField, followingCount: NumberField,
  267. engagementRate: NumberField, cooperationScore: NumberField,
  268. tags: ArrayField, data: ObjectField,
  269. },
  270. KnowledgeInfluencerPost: {
  271. postId: StringField, uniqueId: StringField, authorUniqueId: StringField,
  272. platform: StringField, description: StringField, coverUrl: StringField,
  273. playCount: NumberField, likeCount: NumberField, commentCount: NumberField,
  274. shareCount: NumberField, createTime: StringField, data: ObjectField,
  275. },
  276. KnowledgeKeyword: {
  277. keyword: StringField, rank: NumberField, searchVolume: NumberField,
  278. competition: StringField, source: StringField, data: ObjectField,
  279. },
  280. KeywordSummary: { keyword: StringField, rank: NumberField, data: ObjectField },
  281. KnowledgePattern: {
  282. patternId: StringField, name: StringField, category: StringField,
  283. type: StringField, measurements: ObjectField, fitType: StringField,
  284. sizeRange: ArrayField, userFeedback: ObjectField,
  285. satisfactionScore: NumberField, problemRate: NumberField,
  286. optimizationSuggestions: ArrayField, applicableProducts: ArrayField,
  287. applicableScenarios: ArrayField, popularity: NumberField,
  288. season: StringField, referenceImages: ArrayField, source: StringField,
  289. updatedAt_custom: StringField, data: ObjectField,
  290. },
  291. KnowledgeHotProduct: {
  292. asin: StringField, title: StringField, productName: StringField,
  293. rank: NumberField, bsrRank: NumberField, category: StringField,
  294. price: NumberField, rating: NumberField, reviewCount: NumberField,
  295. monthlySales: NumberField, keyFeatures: ArrayField,
  296. successFactors: ArrayField, source: StringField, data: ObjectField,
  297. },
  298. CompetitorCampaign: { name: StringField, status: StringField, asins: ArrayField, data: ObjectField },
  299. TikTokSearchHistory: {
  300. keyword: StringField, platform: StringField, region: StringField,
  301. sortType: NumberField, publishTime: NumberField, videoCount: NumberField,
  302. totalViews: NumberField, totalLikes: NumberField, totalComments: NumberField,
  303. totalShares: NumberField, avgEngagement: NumberField,
  304. searchTime: StringField, videosData: ArrayField, data: ObjectField,
  305. },
  306. MonitoringStoreDetail: {
  307. shopId: StringField, name: StringField, totalSales: NumberField,
  308. totalAmount: NumberField, avgPrice: NumberField, healthScore: NumberField,
  309. rating: NumberField, returnRate: NumberField, positiveRate: NumberField,
  310. vocVolume: NumberField, newArrivalCount: NumberField, data: ObjectField,
  311. },
  312. VocReviewAnnotation: {
  313. annotationId: StringField, reviewKey: StringField, asin: StringField,
  314. star: NumberField, reviewTitle: StringField, reviewDate: StringField,
  315. annotationStatus: StringField, status: StringField, sentiment: StringField,
  316. level1Category: StringField, level2Category: StringField,
  317. severity: StringField, returnRelated: BooleanField, owner: StringField,
  318. source: StringField, annotatedAt: StringField, data: ObjectField,
  319. },
  320. RegionReturnData: { region: StringField, returnRate: NumberField, data: ObjectField },
  321. RealTimeVoc: { data: ObjectField },
  322. MerchantListing: productFields,
  323. SorftimeReviewProduct: {
  324. recordKey: StringField, reviewId: StringField, asin: StringField,
  325. marketplaceId: StringField, domain: NumberField, source: StringField,
  326. capturedAt: DateField, shop: pointer('Shop'),
  327. },
  328. SpApiSellerAccount: {
  329. recordKey: StringField, sellerId: StringField, marketplaceId: StringField,
  330. businessType: StringField, region: StringField, capturedAt: DateField,
  331. rawData: ObjectField, shop: pointer('Shop'),
  332. },
  333. SpApiMarketplaceParticipation: {
  334. recordKey: StringField, marketplaceId: StringField, name: StringField,
  335. countryCode: StringField, region: StringField, participation: ObjectField,
  336. capturedAt: DateField, rawData: ObjectField, shop: pointer('Shop'),
  337. },
  338. AmazonInventorySummary: {
  339. recordKey: StringField, marketplaceId: StringField, asin: StringField,
  340. sellerSku: StringField, fnSku: StringField, condition: StringField,
  341. productName: StringField, totalQuantity: NumberField, inventoryDetails: ObjectField,
  342. lastUpdatedTime: DateField, capturedAt: DateField, rawData: ObjectField,
  343. shop: pointer('Shop'),
  344. },
  345. AmazonCatalogItem: {
  346. recordKey: StringField, marketplaceId: StringField, asin: StringField,
  347. title: StringField, brand: StringField, productType: StringField,
  348. attributes: ObjectField, dimensions: ArrayField, identifiers: ArrayField,
  349. images: ArrayField, relationships: ArrayField, salesRanks: ArrayField,
  350. summaries: ArrayField, capturedAt: DateField, rawData: ObjectField,
  351. shop: pointer('Shop'),
  352. },
  353. AmazonProductPricing: {
  354. recordKey: StringField, marketplaceId: StringField, asin: StringField,
  355. sku: StringField, status: StringField, product: ObjectField,
  356. capturedAt: DateField, rawData: ObjectField, shop: pointer('Shop'),
  357. },
  358. AmazonSalesMetric: {
  359. recordKey: StringField, marketplaceId: StringField, interval: StringField,
  360. granularity: StringField, unitCount: NumberField, orderItemCount: NumberField,
  361. orderCount: NumberField, averageUnitPrice: ObjectField, totalSales: ObjectField,
  362. capturedAt: DateField, rawData: ObjectField, shop: pointer('Shop'),
  363. },
  364. AmazonOrderItem: {
  365. recordKey: StringField, marketplaceId: StringField, amazonOrderId: StringField,
  366. orderItemId: StringField, asin: StringField, sellerSku: StringField,
  367. title: StringField, quantityOrdered: NumberField, quantityShipped: NumberField,
  368. itemPrice: ObjectField, itemTax: ObjectField, promotionDiscount: ObjectField,
  369. promotionIds: ArrayField, capturedAt: DateField, rawData: ObjectField,
  370. shop: pointer('Shop'), order: pointer('Order'),
  371. },
  372. AmazonFinancialEvent: {
  373. recordKey: StringField, region: StringField, eventType: StringField,
  374. amazonOrderId: StringField, sellerOrderId: StringField, marketplaceName: StringField,
  375. postedDate: DateField, capturedAt: DateField, rawData: ObjectField,
  376. shop: pointer('Shop'),
  377. },
  378. AmazonCustomerFeedback: {
  379. recordKey: StringField, marketplaceId: StringField, feedbackType: StringField,
  380. asin: StringField, browseNodeId: StringField, capturedAt: DateField,
  381. rawData: ObjectField, shop: pointer('Shop'),
  382. },
  383. AmazonInboundShipment: {
  384. recordKey: StringField, region: StringField, marketplaceId: StringField, shipmentId: StringField,
  385. shipmentName: StringField, shipmentStatus: StringField, destinationFulfillmentCenterId: StringField,
  386. labelPrepType: StringField, capturedAt: DateField, rawData: ObjectField,
  387. shop: pointer('Shop'),
  388. },
  389. AmazonReportRow: {
  390. recordKey: StringField, rowHash: StringField, reportType: StringField,
  391. reportId: StringField, region: StringField, marketplaceIds: ArrayField,
  392. capturedAt: DateField, rowData: ObjectField, rawData: ObjectField,
  393. shop: pointer('Shop'),
  394. },
  395. SpApiSyncAudit: {
  396. runId: StringField, dataType: StringField, endpoint: StringField,
  397. status: StringField, region: StringField, marketplaceId: StringField,
  398. records: NumberField, pages: NumberField, error: StringField,
  399. startedAt: DateField, finishedAt: DateField, details: ObjectField,
  400. shop: pointer('Shop'),
  401. },
  402. };
  403. const classNames = Object.keys(schemaDefinitions);
  404. const current = await request('/schemas');
  405. const currentByName = new Map((current.results || []).map(item => [item.className, item]));
  406. const existing = new Set(currentByName.keys());
  407. const adminOnly = { 'role:admin': true };
  408. let created = 0;
  409. for (const className of classNames) {
  410. if (!existing.has(className)) {
  411. await request('/schemas', 'POST', { className, fields: {} });
  412. created += 1;
  413. }
  414. const knownFields = currentByName.get(className)?.fields || {};
  415. const fields = Object.fromEntries(
  416. Object.entries(schemaDefinitions[className] || {}).filter(([name]) => !knownFields[name])
  417. );
  418. await request(`/schemas/${encodeURIComponent(className)}`, 'PUT', {
  419. ...(Object.keys(fields).length ? { fields } : {}),
  420. classLevelPermissions: {
  421. find: adminOnly,
  422. count: adminOnly,
  423. get: adminOnly,
  424. create: adminOnly,
  425. update: adminOnly,
  426. delete: adminOnly,
  427. addField: {},
  428. },
  429. });
  430. }
  431. console.log(JSON.stringify({ created, secured: classNames.length }));