@griddo/cx 1.75.222 → 1.75.224

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/src/utils/api.ts CHANGED
@@ -12,7 +12,7 @@ import axios from "axios";
12
12
  import chalk from "chalk";
13
13
  import dotenv from "dotenv";
14
14
 
15
- import { getCache, saveCache } from "./cache";
15
+ import { searchCacheData, saveCache } from "./cache";
16
16
  import { delay, getSafeSiteId, logInfo, msToSec } from "./shared";
17
17
  import { AuthService } from "../services/auth";
18
18
 
@@ -32,26 +32,35 @@ const {
32
32
  * @example
33
33
  * const response = await requestAPI<Site>({
34
34
  * endpoint: "...",
35
- * cached: true,
35
+ * cacheKey: "..."",
36
36
  * }, { method:"get" });
37
37
  */
38
38
  async function requestAPI<T extends APIResponses>(
39
- props: APIRequest & { method: Method }
39
+ props: APIRequest & { method: Method },
40
+ appendToLog = ""
40
41
  ): Promise<T> {
41
- const { endpoint, body, cached, attempt = 1, method, headers } = props;
42
- const cacheOptions = { endpoint, body, cached };
42
+ const { endpoint, body, cacheKey = "", attempt = 1, method, headers } = props;
43
+ const cacheOptions = { endpoint, body, headers, cacheKey };
43
44
 
44
45
  // Cache
45
- if (cached) {
46
- const cacheData = getCache<T>(cacheOptions);
46
+ if (cacheKey) {
47
+ const start = new Date();
48
+ const cacheData = searchCacheData<T>(cacheOptions);
47
49
 
48
50
  if (cacheData) {
51
+ const siteId = getSafeSiteId(cacheData);
52
+ const siteIdMsg = siteId ? `site: ${siteId}` : "";
53
+ const duration = msToSec(new Date().getTime() - start.getTime());
54
+ logInfo(
55
+ `${method} (cache) ${siteIdMsg} ${endpoint} ${duration}s ${appendToLog}`
56
+ );
49
57
  return cacheData;
50
58
  }
51
59
  }
52
60
 
53
61
  // Network
54
62
  try {
63
+ const start = new Date();
55
64
  const { data }: { data: T } = await axios({
56
65
  url: endpoint,
57
66
  method,
@@ -59,6 +68,13 @@ async function requestAPI<T extends APIResponses>(
59
68
  data: body,
60
69
  });
61
70
 
71
+ const siteId = getSafeSiteId(data);
72
+ const siteIdMsg = siteId ? `site: ${siteId}` : "";
73
+ const duration = msToSec(new Date().getTime() - start.getTime());
74
+ logInfo(
75
+ `${method} (fetch) ${siteIdMsg} ${endpoint} ${duration}s ${appendToLog}`
76
+ );
77
+
62
78
  saveCache(cacheOptions, data);
63
79
 
64
80
  return data;
@@ -80,13 +96,16 @@ async function requestAPI<T extends APIResponses>(
80
96
 
81
97
  await delay(parseInt(RETRY_WAIT_SECONDS) * 1000);
82
98
 
83
- return requestAPI({
84
- endpoint,
85
- body,
86
- cached,
87
- method,
88
- attempt: attempt + 1,
89
- });
99
+ return requestAPI<T>(
100
+ {
101
+ endpoint,
102
+ body,
103
+ cacheKey,
104
+ method,
105
+ attempt: attempt + 1,
106
+ },
107
+ appendToLog
108
+ );
90
109
  }
91
110
  }
92
111
 
@@ -97,22 +116,7 @@ async function requestAPI<T extends APIResponses>(
97
116
  * @returns A promise that is resolved with the data from the API response.
98
117
  */
99
118
  async function getApi<T extends APIResponses>(props: GetAPI) {
100
- // Starts
101
- const start = new Date();
102
- const { endpoint } = props;
103
-
104
- // Fetching...
105
- const response = await requestAPI<T>({ ...props, method: "get" });
106
-
107
- // Ends
108
- const cacheMsg = props.cached ? "(cached)" : "(fetch)";
109
- const siteId = getSafeSiteId(response);
110
- const siteIdMsg = siteId ? `site: ${siteId} ` : "";
111
- const duration = msToSec(new Date().getTime() - start.getTime());
112
-
113
- logInfo(`GET ${cacheMsg} ${siteIdMsg} ${endpoint} - ${duration}s`);
114
-
115
- return response;
119
+ return await requestAPI<T>({ ...props, method: "get" });
116
120
  }
117
121
 
118
122
  /**
@@ -122,20 +126,7 @@ async function getApi<T extends APIResponses>(props: GetAPI) {
122
126
  * @returns A promise that is resolved with the data from the API response.
123
127
  */
124
128
  async function putApi<T extends APIResponses>(props: PutAPI) {
125
- // Starts
126
- const start = new Date();
127
- const { endpoint } = props;
128
-
129
- // Fetching...
130
- const response = await requestAPI<T>({ ...props, method: "put" });
131
-
132
- // Ends
133
- const cacheMsg = props.cached ? "(cached)" : "(fetch)";
134
- const duration = msToSec(new Date().getTime() - start.getTime());
135
-
136
- logInfo(`PUT ${cacheMsg} ${endpoint} - ${duration}s`);
137
-
138
- return response;
129
+ return await requestAPI<T>({ ...props, method: "put" });
139
130
  }
140
131
 
141
132
  /**
@@ -145,27 +136,17 @@ async function putApi<T extends APIResponses>(props: PutAPI) {
145
136
  * @returns A promise that is resolved with the data from the API response.
146
137
  */
147
138
  async function postApi<T extends APIResponses>(props: PostAPI) {
148
- // Starts
149
- const start = new Date();
150
139
  const { endpoint, body, headers } = props;
151
- const distributorBodyParams = endpoint.endsWith("/distributor")
152
- ? `# Distributor body: ${JSON.stringify(body)} - lang: ${JSON.stringify(
153
- headers?.lang
154
- )}`
155
- : "";
156
-
157
- // Fetching...
158
- const response = await requestAPI<T>({ ...props, method: "post" });
159
-
160
- // Ends
161
- const cacheMsg = props.cached ? "(cached)" : "(fetch)";
162
- const duration = msToSec(new Date().getTime() - start.getTime());
163
-
164
- logInfo(
165
- `POST ${cacheMsg} ${endpoint} - ${duration}s ${distributorBodyParams}`
140
+ const distributorBodyParams =
141
+ endpoint.endsWith("/distributor") &&
142
+ `# Distributor body: ${JSON.stringify(body)} lang: ${JSON.stringify(
143
+ headers?.lang
144
+ )}`;
145
+
146
+ return await requestAPI<T>(
147
+ { ...props, method: "post" },
148
+ distributorBodyParams || ""
166
149
  );
167
-
168
- return response;
169
150
  }
170
151
 
171
152
  /**
@@ -60,16 +60,17 @@ function saveCache<T>(petition: Petition, content: T) {
60
60
  }
61
61
 
62
62
  /**
63
- * Read a cache file and return it as an object
63
+ * Search in the `apiCache` folder for a file using the petition as hash generator.
64
+ * Return the file content if found or null if not.
64
65
  *
65
66
  * @param petition An object
66
67
  */
67
- function getCache<T>(petition: Petition) {
68
+ function searchCacheData<T>(petition: Petition) {
68
69
  try {
69
- const content = fs.readFileSync(generateFilenameWithHash(petition), {
70
+ const file = generateFilenameWithHash(petition);
71
+ const content = fs.readFileSync(file, {
70
72
  encoding: "utf-8",
71
73
  });
72
-
73
74
  return JSON.parse(content) as T;
74
75
  } catch {
75
76
  return null;
@@ -107,7 +108,7 @@ function updatedSiteHash(siteId: number, siteHash: SiteHash) {
107
108
  });
108
109
  }
109
110
 
110
- return currentHash;
111
+ return currentHash.toString();
111
112
  }
112
113
 
113
- export { createSha256, updatedSiteHash, getCache, saveCache, initCache };
114
+ export { createSha256, updatedSiteHash, searchCacheData, saveCache, initCache };
@@ -22,8 +22,8 @@ async function deleteSites(updatedSites: Array<Site>) {
22
22
  `../../assets/page-data${mappedDomain}`
23
23
  );
24
24
 
25
- logInfo("Site dir", dir);
26
- logInfo("Page data dir", pageDataDir);
25
+ logInfo(`Site dir ${dir}`);
26
+ logInfo(`Page data dir ${pageDataDir}`);
27
27
 
28
28
  // delete directory recursively
29
29
  if (!fs.existsSync(dir)) return;
@@ -5,6 +5,7 @@ import gradient from "gradient-string";
5
5
 
6
6
  import { version } from "../../package.json";
7
7
  import { APIResponses } from "../types/api";
8
+ import { APIPageObject } from "../types/pages";
8
9
  import { Site } from "../types/sites";
9
10
 
10
11
  dotenv.config();
@@ -49,12 +50,13 @@ function logBox(str: string) {
49
50
 
50
51
  /**
51
52
  * Custom basic logging function controlled by a environment variable.
53
+ * Strip double spaces.
52
54
  *
53
- * @param str The string or strings separated by commans to be logged.
55
+ * @param str The string to be logged.
54
56
  */
55
- function logInfo(...str: Array<unknown>) {
57
+ function logInfo(str: string) {
56
58
  if (GRIDDO_BUILD_LOGS) {
57
- console.info(...str);
59
+ console.info(str.replace(/(\s)\s+/g, "$1"));
58
60
  }
59
61
  }
60
62
 
@@ -187,6 +189,70 @@ function exporterLogo() {
187
189
  console.log(gradient.cristal(logo));
188
190
  }
189
191
 
192
+ /**
193
+ * Remove unused files (old) inside the `apiCache` folder
194
+ *
195
+ * @param folderPath The path for the `apiCache` folder
196
+ * @todo remove other file types: sites, socials, etc..
197
+ */
198
+ function sanitizeApiCache(folderPath: string) {
199
+ // Read all `apiCache` file paths
200
+ const allCachedFiles = fs.readdirSync(folderPath);
201
+
202
+ // Object to store the the more rencent file names
203
+ // Record<string, string> = { "234856872634", "3268746238747238.json"};
204
+ const filesByIdMap: Record<string, string> = {};
205
+ // ^id ^path
206
+
207
+ // Page files.
208
+ // We only need files that describes a page object.
209
+ const pageFilePaths = allCachedFiles.filter((fileName) => {
210
+ const filePath = `${folderPath}/${fileName}`;
211
+ const fileObject = fs.readJSONSync(filePath, "utf-8") as APIPageObject;
212
+ const { id, entity, fullUrl } = fileObject;
213
+
214
+ // Is a page file if has id, entity and fullUrl
215
+ return !!(id && entity && fullUrl);
216
+ });
217
+
218
+ // Fill the filesById object
219
+ for (const fileName of pageFilePaths) {
220
+ const filePath = `${folderPath}/${fileName}`;
221
+ const fileObject = fs.readJSONSync(filePath, "utf-8") as APIPageObject;
222
+ const fileCreationDate = fs.statSync(filePath).mtimeMs;
223
+
224
+ const { id } = fileObject;
225
+
226
+ // Is a valid page if doesn't exists in the store object or is newer
227
+ // that the stored one.
228
+ const validPageFile =
229
+ !filesByIdMap[id] ||
230
+ fileCreationDate >
231
+ fs.statSync(`${folderPath}/${filesByIdMap[id]}`).mtimeMs;
232
+
233
+ if (validPageFile) filesByIdMap[id] = fileName;
234
+ }
235
+
236
+ // TODO: Remove this counter for production
237
+ let counter = 0;
238
+
239
+ // Delete files using the store object filesById as reference map.
240
+ for (const fileName of pageFilePaths) {
241
+ const filePath = `${folderPath}/${fileName}`;
242
+ const fileObject = fs.readJSONSync(filePath, "utf-8") as APIPageObject;
243
+
244
+ const { id } = fileObject;
245
+
246
+ // If the filename is not present in the map, remove it!
247
+ if (fileName !== filesByIdMap[id]) {
248
+ fs.unlinkSync(filePath);
249
+ counter++;
250
+ }
251
+ }
252
+
253
+ console.log(`>>> Sanitize apiCache folder for ${counter} files <<<`);
254
+ }
255
+
190
256
  export {
191
257
  delay,
192
258
  exporterLogo,
@@ -195,6 +261,7 @@ export {
195
261
  logPageSize,
196
262
  measureFunctions,
197
263
  removeProperties,
264
+ sanitizeApiCache,
198
265
  siteList,
199
266
  splash,
200
267
  walk,
@@ -109,7 +109,7 @@ async function unpublishSites(sites: Array<Site>) {
109
109
  unpublishHashes: [],
110
110
  };
111
111
 
112
- logInfo("Unpublish site starts", buildInfo);
112
+ logInfo(`Unpublish site starts ${buildInfo}`);
113
113
 
114
114
  await SitesService.endSiteRender(site.id, body);
115
115
  }
@@ -121,20 +121,20 @@ async function unpublishSites(sites: Array<Site>) {
121
121
  * Return a single site generic data.
122
122
  *
123
123
  * @param siteID The site id.
124
- * @param cached Boolean that indicates if we want to get cache version.
124
+ * @param cacheKey Boolean that indicates if we want to get cache version.
125
125
  *
126
126
  * @see SiteData
127
127
  */
128
- async function getSiteData(siteID: number, cached: boolean) {
128
+ async function getSiteData(siteID: number) {
129
129
  const buildData = await SitesService.startSiteRender(siteID);
130
- const siteInfo = await SitesService.getInfo(siteID, cached);
131
- const siteLangs = await SitesService.getLanguages(siteID, cached);
132
- const socials = await SitesService.getSocials(siteID, cached);
130
+ const siteInfo = await SitesService.getInfo(siteID);
131
+ const siteLangs = await SitesService.getLanguages(siteID);
132
+ const socials = await SitesService.getSocials(siteID);
133
133
  const siteLangsInfo = siteLangs.items;
134
134
  const defaultLang = siteLangsInfo.find((lang) => lang.isDefault);
135
135
  // TODO: Eliminado para validar que efectivamente no rompe nada si el cambio
136
136
  // es correcto, hay que eliminar las 7 líneas que contienen "sitePages":
137
- // const sitePages = await SitesService.getPages(siteID, cached);
137
+ // const sitePages = await SitesService.getPages(siteID, cacheKey);
138
138
  const sitePages: AllPagesResponse = [];
139
139
 
140
140
  const { siteHash, unpublishHashes, publishIds } = buildData;