Skip to content

Commit

Permalink
Fix/autoupdating cdn downtime (#2467)
Browse files Browse the repository at this point in the history
  • Loading branch information
mimi-imtbl authored Dec 16, 2024
1 parent 0c2037d commit 13de51f
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 30 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ jobs:
with:
subject-path: './sdk'

- name: Generate last_updated.json
if: (env.DRY_RUN) == 'false'
run: |
echo "{\"last_updated\": \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\"}" > ./sdk/last_updated.json
cp ./sdk/last_updated.json ./sdk/dist/
- name: Authenticate NPM
if: contains(env.RELEASE_TYPE, 'release')
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.TS_IMMUTABLE_SDK_NPM_TOKEN }}
Expand Down
68 changes: 38 additions & 30 deletions packages/checkout/sdk/src/widgets/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,33 +84,6 @@ export async function getLatestVersionFromNpm(): Promise<string> {
}
}

/**
* Checks if the provided version is available on the CDN.
* @param {string} version - The version to check.
* @returns {Promise<boolean>} A promise resolving to a boolean indicating if the version is available on the CDN.
*/
async function isVersionAvailableOnCDN(version: string): Promise<boolean> {
const files = ['widgets-esm.js', 'widgets.js'];
const baseUrl = `https://cdn.jsdelivr.net/npm/@imtbl/sdk@${version}/dist/browser/checkout/`;

try {
const checks = files.map(async (file) => {
const response = await fetch(`${baseUrl}${file}`, { method: 'HEAD' });
if (!response.ok) {
return false;
}
return true;
});

const results = await Promise.all(checks);
const allFilesAvailable = results.every((isAvailable) => isAvailable);

return allFilesAvailable;
} catch {
return false;
}
}

/**
* Returns the latest compatible version based on the provided checkout version config.
* If no compatible version markers are provided, it returns 'latest'.
Expand All @@ -127,6 +100,40 @@ function latestCompatibleVersion(
return 'latest';
}

/**
* Checks if the last_updated.json file exists on the CDN and validates its timestamp.
* @param {string} version - The version to check.
* @returns {Promise<boolean>} A promise resolving to `true` if last_updated.json exists and is older than 15 minutes, `false` otherwise.
*/
async function checkLastUpdatedTimestamp(version: string): Promise<boolean> {
const WAIT_TIME_IN_MINUTES = 20;

const lastUpdatedJsonUrl = `https://cdn.jsdelivr.net/npm/@imtbl/sdk@${version}/dist/last_updated.json`;

try {
const response = await fetch(lastUpdatedJsonUrl);

if (!response.ok) {
return false;
}

const lastUpdatedData = await response.json();

if (lastUpdatedData.timestamp) {
const timestamp = new Date(lastUpdatedData.timestamp);
const now = new Date();
const diffInMs = now.getTime() - timestamp.getTime();
const diffInMinutes = diffInMs / (1000 * 60);

return diffInMinutes > WAIT_TIME_IN_MINUTES;
}
} catch (error) {
return false;
}

return false;
}

/**
* Determines the version of the widgets to use based on the provided validated build version and checkout version config.
* If a version is provided in the widget init parameters, it uses that version.
Expand Down Expand Up @@ -158,13 +165,14 @@ export async function determineWidgetsVersion(
versionConfig.compatibleVersionMarkers,
);

// If `latest` is returned, query NPM registry for the actual latest version and check if it's available on the CDN
// If `latest` is returned, query NPM registry for the actual latest version and check timestamp
if (compatibleVersion === 'latest') {
const latestVersion = await getLatestVersionFromNpm();
const isAvailable = await isVersionAvailableOnCDN(latestVersion);
if (isAvailable) {

if (await checkLastUpdatedTimestamp(latestVersion)) {
return latestVersion;
}

return 'latest';
}

Expand Down

0 comments on commit 13de51f

Please sign in to comment.