From b6d5642e1eeaf8e23fe5a6b749f1c66db2f4f46d Mon Sep 17 00:00:00 2001 From: arcataroger Date: Fri, 22 Mar 2024 10:32:41 -0700 Subject: [PATCH] Add function to convert Dato URL-safe Base64-encoded IDs back to valid UUID hex strings --- .../cma-client/__tests__/datoIdToUuid.test.ts | 21 +++++++++++ packages/cma-client/src/datoIdtoUuid.ts | 36 +++++++++++++++++++ packages/cma-client/src/index.ts | 1 + 3 files changed, 58 insertions(+) create mode 100644 packages/cma-client/__tests__/datoIdToUuid.test.ts create mode 100644 packages/cma-client/src/datoIdtoUuid.ts diff --git a/packages/cma-client/__tests__/datoIdToUuid.test.ts b/packages/cma-client/__tests__/datoIdToUuid.test.ts new file mode 100644 index 00000000..54ab5d07 --- /dev/null +++ b/packages/cma-client/__tests__/datoIdToUuid.test.ts @@ -0,0 +1,21 @@ +import { validate } from 'uuid'; +import { datoIdToUuid } from '../src/datoIdToUuid'; +import { generateId } from '../src/generateId'; + +describe('datoIdToUuid', () => { + it('converts an example URL-safe Base64 DatoID to a valid UUID hex string', () => { + const base64url = 'WTyssHtyTzu9_EbszSVhPw'; + const expectedUuid = '593cacb0-7b72-4f3b-bdfc-46eccd25613f'; + + const result = datoIdToUuid(base64url); + + expect(result).toBe(expectedUuid); + expect(validate(result)).toBe(true); + }); + + it('expects an ID from generateId() to validate as a v4 UUID', () => { + const base64Id = generateId(); + const uuid = datoIdToUuid(base64Id); + expect(validate(uuid)).toBe(true); + }); +}); diff --git a/packages/cma-client/src/datoIdtoUuid.ts b/packages/cma-client/src/datoIdtoUuid.ts new file mode 100644 index 00000000..4f262d9d --- /dev/null +++ b/packages/cma-client/src/datoIdtoUuid.ts @@ -0,0 +1,36 @@ +import { stringify } from 'uuid'; +function fromBase64toUint8Array(base64String: string) { + // Check if Buffer is available, indicating a Node.js environment + if (typeof Buffer !== 'undefined') { + // Use Buffer to convert base64 to a Uint8Array + return new Uint8Array(Buffer.from(base64String, 'base64')); + } else { + // Assuming a browser environment if Buffer is not available + // Use atob to decode base64 to a binary string + const binaryString = atob(base64String); + + // Create a Uint8Array of the same length as the binary string + const bytes = new Uint8Array(binaryString.length); + + // Convert each character to its byte value + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes; + } +} + +export const datoIdToUuid = (b64url: string): string => { + // Undo the URL encoding we did in generateId() + const reversedUrlEncoding: string = `${b64url + .replace(/-/g, '+') + .replace(/_/g, '/')}==`; + + // Decode the B64 back into a Uint8Array + const encodedAsBuffer = fromBase64toUint8Array(reversedUrlEncoding); + + // Stringify the UUID into a hex string + const stringified = stringify(encodedAsBuffer); + + return stringified; +}; diff --git a/packages/cma-client/src/index.ts b/packages/cma-client/src/index.ts index 03c637d1..f5059e10 100644 --- a/packages/cma-client/src/index.ts +++ b/packages/cma-client/src/index.ts @@ -5,5 +5,6 @@ export type { ClientConfigOptions } from './generated/Client'; export * from './buildClient'; export * from './buildBlockRecord'; export * from './generateId'; +export * from './datoIdtoUuid'; export * as SchemaTypes from './generated/SchemaTypes'; export * as SimpleSchemaTypes from './generated/SimpleSchemaTypes';