Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/apps/flash-courier/lib-mjs/update-tracking.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ export default () => {
'applications?limit=1&fields=hidden_data,data'
+ `&app_id=${config.get().apps.flashCourier.appId}`,
)).data.result;
// App not installed on this store, nothing to track
if (!app[0]) {
resolve(null);
return;
}

appData = {
...app[0].data,
Expand Down
13 changes: 7 additions & 6 deletions packages/apps/frenet/src/functions-lib/tracking-codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const getConfig = async () => {
`applications?app_id=${config.get().apps.frenet.appId}&fields=hidden_data`,
)).data.result;

return app[0].hidden_data;
// App not installed on this store, nothing to track
return app[0]?.hidden_data || null;
} catch (err) {
logger.error(err);
return null;
Expand Down Expand Up @@ -50,8 +51,9 @@ const handleTrackingCodes = async () => {
// no config for store bye
resolve(null);
} else {
// eslint-disable-next-line consistent-return
codes.forEach(async (code) => {
// Wait for every code before finishing, the function may be stopped
// right after resolving
await Promise.allSettled(codes.map(async (code) => {
const serviceCode = String(code.serviceCode).startsWith('FR')
? String(code.serviceCode.replace(/^FR/, '')).trim()
: code.serviceCode;
Expand Down Expand Up @@ -144,9 +146,8 @@ const handleTrackingCodes = async () => {
}
}
}

resolve(null);
});
}));
resolve(null);
}
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as logger from 'firebase-functions/logger';
import { logger } from '@cloudcommerce/firebase/lib/config';

const ignoreError = (response) => {
// check response status code
Expand Down Expand Up @@ -53,8 +53,8 @@ export default (err) => {
}
// debug unexpected response
error.configJSON = {
originalRequest: JSON.stringify(err.config),
logRequest: JSON.stringify(config),
originalRequest: JSON.stringify({ ...err.config, headers: undefined }),
logRequest: JSON.stringify({ ...config, headers: undefined }),
};
logger.error(error);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const getConfig = async () => {
`applications?app_id=${config.get().apps.melhorEnvio.appId}&fields=hidden_data`,
)).data.result;

return app[0].hidden_data;
// App not installed on this store, nothing to track
return app[0]?.hidden_data || null;
} catch (err) {
logger.error(err);
return null;
Expand Down
57 changes: 56 additions & 1 deletion packages/firebase/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,62 @@ export default config;

export const asyncLocalStorage = new AsyncLocalStorage<{ execId: string }>();

const log = (level: 'info' | 'warn' | 'error', msg: any, d?: Record<string, any>) => {
const secretHeaderRegex = /^(authorization|access-token|x-access-token|x-api-key|api-key|token|cookie)$/i;

const maskHeaders = (headers: any) => {
if (!headers || typeof headers !== 'object') return headers;
const masked = { ...(typeof headers.toJSON === 'function' ? headers.toJSON() : headers) };
Object.keys(masked).forEach((name) => {
if (secretHeaderRegex.test(name)) masked[name] = '***';
});
return masked;
};

// Keep credentials sent on request headers (e.g. Axios error config) out of logs
const maskSecrets = (value: any, depth = 0): any => {
if (!value || typeof value !== 'object' || depth > 3) return value;
if (value.isAxiosError) {
const err = Object.create(
Object.getPrototypeOf(value),
Object.getOwnPropertyDescriptors(value),
);
// Raw HTTP request has the headers on a plain string
delete err.request;
if (err.config) err.config = { ...err.config, headers: maskHeaders(err.config.headers) };
if (err.response) {
const {
status, statusText, headers, data,
} = err.response;
err.response = {
status, statusText, headers, data,
};
}
return err;
}
if (Array.isArray(value)) {
return value.map((item) => maskSecrets(item, depth + 1));
}
const proto = Object.getPrototypeOf(value);
// Plain objects only, Axios config may have no prototype
if (proto !== Object.prototype && proto !== null) return value;
const masked: Record<string, any> = {};
Object.keys(value).forEach((key) => {
masked[key] = key === 'headers'
? maskHeaders(value[key])
: maskSecrets(value[key], depth + 1);
});
return masked;
};

const log = (level: 'info' | 'warn' | 'error', _msg: any, _d?: Record<string, any>) => {
let msg = _msg;
let d = _d;
try {
msg = maskSecrets(_msg);
d = maskSecrets(_d);
} catch {
// Unexpected object (e.g. throwing getter), log as received
}
const execId = asyncLocalStorage.getStore()?.execId;
if (execId) {
if (d) {
Expand Down
33 changes: 25 additions & 8 deletions packages/ssr/src/lib/util/ssr-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type CachedRequestInit = RequestInit & {
timeout?: number,
};
const runtimeCache: Record<string, CacheValue | undefined> = {};
const runtimeFetchings: Record<string, Promise<Response> | undefined> = {};
const runtimeFetchings: Record<string, Promise<any> | undefined> = {};

// Also declared at `storefront/server.d.ts`
export const fetchAndCache = async (
Expand All @@ -61,7 +61,7 @@ export const fetchAndCache = async (
return runtimeCache[key].data;
}
const docRef = getFirestore().doc(`ssrFetchCache/${key}`);
const runFetch = async () => {
const doFetch = async () => {
let abortController: AbortController | undefined;
let timer: NodeJS.Timeout | undefined;
if (timeout) {
Expand All @@ -71,12 +71,17 @@ export const fetchAndCache = async (
}, timeout);
reqInit.signal = abortController.signal;
}
const fetching = runtimeFetchings[key] || fetch(url, reqInit);
runtimeFetchings[key] = fetching;
const response = await fetching;
delete runtimeFetchings[key];
if (timer) {
clearTimeout(timer);
let response: Response;
try {
response = await fetch(url, reqInit);
} catch (err: any) {
// Network error or aborted by timeout
err.url = url;
throw err;
} finally {
if (timer) {
clearTimeout(timer);
}
}
const error = (err: any) => {
err.url = url;
Expand Down Expand Up @@ -105,6 +110,18 @@ export const fetchAndCache = async (
}
return error(new Error(`Failed fetching ${url}`));
};
// Concurrent calls share the parsed data, a response body can be read only once,
// and a failed fetch must not stay cached for the next calls
const runFetch = () => {
let fetching = runtimeFetchings[key];
if (!fetching) {
fetching = doFetch().finally(() => {
delete runtimeFetchings[key];
});
runtimeFetchings[key] = fetching;
}
return fetching;
};
const docSnap = await docRef.get();
if (docSnap.exists) {
const cacheVal = docSnap.data() as CacheValue;
Expand Down
Loading