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
62 changes: 62 additions & 0 deletions src/modules/tokenIntents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type {
CreateTokenIntent,
TokenIntent,
} from '@basis-theory/basis-theory-js/types/models';
import type {
BasisTheory as BasisTheoryType,
RequestOptions,
} from '@basis-theory/basis-theory-js/types/sdk';
import type {
BTRef,
InputBTRefWithDatepart,
PrimitiveType,
} from '../BaseElementTypes';
import { _elementErrors } from '../ElementValues';
import { replaceElementRefs } from '../utils/dataManipulationUtils';
import { isNilOrEmpty } from '../utils/shared';

export type CreateTokenIntentWithBtRef = Omit<CreateTokenIntent, 'data'> & {
data: Record<string, BTRef | InputBTRefWithDatepart | null | undefined>;
};

export type TokenIntentData = TokenIntent<
BTRef | InputBTRefWithDatepart | PrimitiveType
>;

export const TokenIntents = (bt: BasisTheoryType) => {
const create = async (
tokenIntentWithRef: CreateTokenIntentWithBtRef,
requestOptions?: RequestOptions
) => {
if (!isNilOrEmpty(_elementErrors)) {
throw new Error(
'Unable to create token. Payload contains invalid values. Review elements events for more details.'
);
}

try {
const _token = replaceElementRefs<CreateTokenIntent>(tokenIntentWithRef);

const token = await bt.tokenIntents.create(_token, requestOptions);

return token;
} catch (error) {
console.error(error);
}
};

const deleteTokenIntent = async (id: string) => {
try {
if (id) {
await bt.tokenIntents.delete(id);
}
} catch (error) {
console.error(error);
}
};

return {
create,
delete: deleteTokenIntent,
};
};
6 changes: 5 additions & 1 deletion src/useBasisTheory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Proxy } from './modules/proxy';
import { Sessions } from './modules/sessions';
import { Tokens } from './modules/tokens';
import { useBasisTheoryFromContext } from './BasisTheoryProvider';
import { TokenIntents } from './modules/tokenIntents';

const _BasisTheoryElements = async ({
apiKey,
Expand All @@ -25,10 +26,13 @@ const _BasisTheoryElements = async ({

const tokens = Tokens(bt);

const tokenIntents = TokenIntents(bt);

return {
proxy,
sessions,
tokenIntents,
tokens,
proxy,
};
};

Expand Down
115 changes: 115 additions & 0 deletions tests/modules/tokenIntents.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { _elementErrors, _elementValues } from '../../src/ElementValues';
import {
CreateTokenIntentWithBtRef,
TokenIntents,
TokenizeData,
} from '../../src/modules/tokenIntents';
import type { BasisTheory as BasisTheoryType } from '@basis-theory/basis-theory-js/types/sdk';

jest.mock('../../src/ElementValues', () => ({
_elementValues: {},
_elementErrors: {},
}));

describe('tokens', () => {
beforeEach(() => {
Object.assign(_elementValues, {
'123': 'my very sensitive value',
'456': 'my other very sensitive value',
firstArrayElement: 'first sensitive element in array',
secondArrayElement: 'second sensitive element in array',
expirationDate: '12/23',
});

Object.assign(_elementErrors, {});
});
afterAll(() => {
Object.keys(_elementValues).forEach((key) => delete _elementValues[key]);
Object.keys(_elementErrors).forEach((key) => delete _elementErrors[key]);
});

test('calls bt tokens create', async () => {
const mockCreate = jest.fn();
const tokenIntents = TokenIntents({
tokenIntents: { create: mockCreate },
} as unknown as BasisTheoryType);

const tokenWithRef = {
id: 'tokenID',
type: 'card',
data: {
number: { id: '123', format: jest.fn() },
nestedObject: {
test: { id: '456', format: jest.fn() },
},
myArray: [
{ id: 'firstArrayElement', format: jest.fn() },
{ id: 'secondArrayElement', format: jest.fn() },
],
},
} as unknown as CreateTokenIntentWithBtRef;

const expectedResult = {
id: 'tokenID',
type: 'card',
data: {
number: 'my very sensitive value',
nestedObject: {
test: 'my other very sensitive value',
},
myArray: [
'first sensitive element in array',
'second sensitive element in array',
],
},
};

await tokenIntents.create(tokenWithRef);

expect(mockCreate).toHaveBeenCalledWith(expectedResult, undefined);
});

test('calls bt tokens delete', async () => {
const mockDelete = jest.fn();
const tokenIntents = TokenIntents({
tokenIntents: { delete: mockDelete },
} as unknown as BasisTheoryType);

await tokenIntents.delete('tokenID');

expect(mockDelete).toHaveBeenCalledWith('tokenID');
});
});

describe('tokens - Validation', () => {
test('throws if there are any validation errors', () => {
Object.assign(_elementErrors, {
secondArrayElement: 'incomplete',
});

const tokenIntents = TokenIntents({} as BasisTheoryType);

const tokenWithRef = {
id: 'tokenID',
type: 'card',
data: {
number: { id: '123', format: jest.fn() },
nestedObject: {
test: { id: '456', format: jest.fn },
},
myArray: [
{ id: 'firstArrayElement', format: jest.fn() },
{ id: 'secondArrayElement', format: jest.fn() },
],
},
} as unknown as CreateTokenIntentWithBtRef;

const action = async () => {
await tokenIntents.create(tokenWithRef);
};

expect(() => action()).rejects.toThrow(
'Unable to create token. Payload contains invalid values. Review elements events for more details.'
);
});
});