Skip to content

Commit

Permalink
refactor(api): update after pr comments
Browse files Browse the repository at this point in the history
  • Loading branch information
djabarovgeorge committed Oct 22, 2024
1 parent 6fd09a6 commit 89606cc
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 52 deletions.
3 changes: 1 addition & 2 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"lint": "eslint src",
"lint:fix": "pnpm lint -- --fix",
"lint:openapi": "spectral lint http://127.0.0.1:${PORT:-3000}/openapi.yaml",
"test": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test E2E_RUNNER=true mocha --require ts-node/register --exit 'src/**/*.spec.ts'",
"test": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test E2E_RUNNER=true mocha --require ts-node/register --exit 'src/app/workflows-v2/pipes/parse-slug-Id.pipe spec.ts'",
"test:e2e": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test E2E_RUNNER=true mocha --require ts-node/register --exit --file e2e/setup.ts src/**/*.e2e.ts ",
"test:e2e:ee": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test E2E_RUNNER=true CI_EE_TEST=true CLERK_ENABLED=true mocha --grep @skip-in-ee --invert --require ts-node/register --exit --file e2e/setup.ts 'src/**/*.e2e{,-ee}.ts'",
"migration": "cross-env NODE_ENV=local MIGRATION=true ts-node --transpileOnly",
Expand Down Expand Up @@ -57,7 +57,6 @@
"@types/newrelic": "^9.14.0",
"@upstash/ratelimit": "^0.4.4",
"axios": "^1.6.8",
"base-x": "^5.0.0",
"bcrypt": "^5.0.0",
"body-parser": "^1.20.0",
"bull": "^4.2.1",
Expand Down
163 changes: 151 additions & 12 deletions apps/api/src/app/shared/helpers/base62.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,161 @@
import baseX from 'base-x';
// base-x encoding / decoding
// Copyright (c) 2018 base-x contributors
// Copyright (c) 2014-2018 The Bitcoin Core developers (base58.cpp)
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
// "version": "5.0.0",

function base(ALPHABET) {
if (ALPHABET.length >= 255) {
throw new TypeError('Alphabet too long');
}
const BASE_MAP = new Uint8Array(256);
for (let j = 0; j < BASE_MAP.length; j++) {
BASE_MAP[j] = 255;
}
for (let i = 0; i < ALPHABET.length; i++) {
const x = ALPHABET.charAt(i);
const xc = x.charCodeAt(0);
if (BASE_MAP[xc] !== 255) {
throw new TypeError(x + ' is ambiguous');
}
BASE_MAP[xc] = i;
}
const BASE = ALPHABET.length;
const LEADER = ALPHABET.charAt(0);
const FACTOR = Math.log(BASE) / Math.log(256); // log(BASE) / log(256), rounded up
const iFACTOR = Math.log(256) / Math.log(BASE); // log(256) / log(BASE), rounded up
function encode(source) {
// eslint-disable-next-line no-empty
if (source instanceof Uint8Array) {
} else if (ArrayBuffer.isView(source)) {
source = new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
} else if (Array.isArray(source)) {
source = Uint8Array.from(source);
}
if (!(source instanceof Uint8Array)) {
throw new TypeError('Expected Uint8Array');
}
if (source.length === 0) {
return '';
}
// Skip & count leading zeroes.
let zeroes = 0;
let length = 0;
let pbegin = 0;
const pend = source.length;
while (pbegin !== pend && source[pbegin] === 0) {
pbegin++;
zeroes++;
}
// Allocate enough space in big-endian base58 representation.
const size = ((pend - pbegin) * iFACTOR + 1) >>> 0;
const b58 = new Uint8Array(size);
// Process the bytes.
while (pbegin !== pend) {
let carry = source[pbegin];

Check warning on line 56 in apps/api/src/app/shared/helpers/base62.ts

View workflow job for this annotation

GitHub Actions / Spell check

Unknown word (pbegin)
// Apply "b58 = b58 * 256 + ch".
let i = 0;
for (let it1 = size - 1; (carry !== 0 || i < length) && it1 !== -1; it1--, i++) {
carry += (256 * b58[it1]) >>> 0;
b58[it1] = carry % BASE >>> 0;
carry = (carry / BASE) >>> 0;
}
if (carry !== 0) {
throw new Error('Non-zero carry');
}
length = i;
pbegin++;

Check warning on line 68 in apps/api/src/app/shared/helpers/base62.ts

View workflow job for this annotation

GitHub Actions / Spell check

Unknown word (pbegin)
}
// Skip leading zeroes in base58 result.
let it2 = size - length;
while (it2 !== size && b58[it2] === 0) {
it2++;
}
// Translate the result into a string.
let str = LEADER.repeat(zeroes);
for (; it2 < size; ++it2) {
str += ALPHABET.charAt(b58[it2]);
}
return str;
}
function decodeUnsafe(source) {
if (typeof source !== 'string') {
throw new TypeError('Expected String');
}
if (source.length === 0) {
return new Uint8Array();
}
let psz = 0;
// Skip and count leading '1's.
let zeroes = 0;
let length = 0;
while (source[psz] === LEADER) {
zeroes++;
psz++;
}
// Allocate enough space in big-endian base256 representation.
const size = ((source.length - psz) * FACTOR + 1) >>> 0; // log(58) / log(256), rounded up.
const b256 = new Uint8Array(size);
// Process the characters.
while (psz < source.length) {
// Decode character
let carry = BASE_MAP[source.charCodeAt(psz)];
// Invalid character
if (carry === 255) {
return;
}
let i = 0;
for (let it3 = size - 1; (carry !== 0 || i < length) && it3 !== -1; it3--, i++) {
carry += (BASE * b256[it3]) >>> 0;
b256[it3] = carry % 256 >>> 0;
carry = (carry / 256) >>> 0;
}
if (carry !== 0) {
throw new Error('Non-zero carry');
}
length = i;
psz++;
}
// Skip leading zeroes in b256.
let it4 = size - length;
while (it4 !== size && b256[it4] === 0) {
it4++;
}
const vch = new Uint8Array(zeroes + (size - it4));
let j = zeroes;
while (it4 !== size) {
vch[j++] = b256[it4++];
}
return vch;
}
function decode(string) {
const buffer = decodeUnsafe(string);
if (buffer) {
return buffer;
}
throw new Error('Non-base' + BASE + ' character');
}
return {
encode,
decode,
};
}

/**
* Base62 alphabet
* Modifying this alphabet is prohibited as it would invalidate existing encoded data
*/
const BASE62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
const { encode, decode } = baseX(BASE62);
const { encode, decode } = base(BASE62);
const ENCODING = 'hex' satisfies BufferEncoding;

export function encodeBase62(value: string): string {
const buffer = Buffer.from(value, 'hex');

const buffer = Buffer.from(value, ENCODING);
return encode(buffer);
}

export function decodeBase62(encoded: string): string {
const uint8Array = decode(encoded);

return Buffer.from(uint8Array).toString('hex');
}

export function isBase62(input: string): boolean {
const base62Regex = /^[0-9A-Za-z]+$/;

return base62Regex.test(input);
return Buffer.from(uint8Array).toString(ENCODING);
}
23 changes: 7 additions & 16 deletions apps/api/src/app/workflows-v2/pipes/parse-slug-Id.pipe spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,11 @@ describe('ParseSlugIdPipe', () => {
expect(pipe.transform(internalId, {} as ArgumentMetadata)).to.equal(internalId);
});

it.skip('should trim prefix and decode base62 encoded internalId', () => {
const internalId = '6615943e7ace93b0540ae377';
const encodedId = encodeBase62(`wf_${internalId}`);
expect(pipe.transform(`wf_${encodedId}`, {} as ArgumentMetadata)).to.equal(internalId);
});

it('should not trim or decode simple workflow identifier', () => {
const identifier = 'my-workflow';
expect(pipe.transform(identifier, {} as ArgumentMetadata)).to.equal(identifier);
});

it.skip('should trim, decode, and remove prefix for a valid slug ID', () => {
const internalId = '6615943e7ace93b0540ae377';
const encodedId = encodeBase62(`wf_${internalId}`);
expect(pipe.transform(`my-workflow_${encodedId}`, {} as ArgumentMetadata)).to.equal(internalId);
});

it('should return original value for invalid encoded ID', () => {
const invalidSlug = 'my-workflow_invalid';
expect(pipe.transform(invalidSlug, {} as ArgumentMetadata)).to.equal(invalidSlug);
Expand All @@ -57,9 +45,12 @@ describe('ParseSlugIdPipe', () => {
expect(pipe.transform(`my-workflow_${encodedId}`, {} as ArgumentMetadata)).to.equal(internalId);
});

it.skip('should handle slug IDs with multiple underscores', () => {
const internalId = '6615943e7ace93b0540ae377';
const encodedId = encodeBase62(`wf_${internalId}`);
expect(pipe.transform(`my_complex_workflow_${encodedId}`, {} as ArgumentMetadata)).to.equal(internalId);
it('should handle internalIds with leading zeros', () => {
const internalIds = ['6615943e7ace93b0540ae377', '0615943e7ace93b0540ae377', '0015943e7ace93b0540ae377'];

internalIds.forEach((internalId) => {
const encodedId = encodeBase62(internalId);
expect(pipe.transform(`my-workflow_${encodedId}`, {} as ArgumentMetadata)).to.equal(internalId);
});
});
});
2 changes: 1 addition & 1 deletion apps/api/src/app/workflows-v2/pipes/parse-slug-Id.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function parseSlugId(value: string): InternalId {
return validId;
}

const encodedValue = value.slice(-16);
const encodedValue = value.slice(-ENCODED_ID_LENGTH);
let decodedValue: string;
try {
decodedValue = decodeBase62(encodedValue);
Expand Down
34 changes: 13 additions & 21 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 89606cc

Please sign in to comment.