Skip to content

Commit

Permalink
feat: add DatasetRequests
Browse files Browse the repository at this point in the history
  • Loading branch information
mikaelvesavuori committed May 14, 2024
1 parent 5b3f60c commit d2d3687
Show file tree
Hide file tree
Showing 8 changed files with 323 additions and 10 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@getpopcorn/utils",
"version": "0.0.20",
"version": "0.0.21",
"description": "Various common utilities and tools",
"author": "Popcorn Cloud",
"license": "SEE LICENSE IN LICENSE",
Expand Down
96 changes: 96 additions & 0 deletions src/DatasetRequests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Utils } from './Utils.js';
import {
datasetAddRequestObject,
datasetDeleteRequestObject,
datasetGetRequestObject,
datasetUpdateRequestObject
} from './RequestObjects.js';

import {
DatasetCreateUpdateOptions,
DatasetDeleteOptions,
DatasetGetOptions
} from './interfaces/DatasetRequests.js';

/**
* @description TODO
*/
export class DatasetRequests {
utils: Utils;

constructor() {
this.utils = new Utils();
}

/**
* @description Get the metadata, headers, and first items from the specified Dataset.
* @todo Support queries
*
* @example
* dataset.get();
*/
public async get(options: DatasetGetOptions) {
const { datasetApiBaseUrl, datasetId } = options;

return await this.utils.request(datasetGetRequestObject(datasetApiBaseUrl, datasetId));
}

/**
* @description Delete an item from the specified Dataset.
*
* @example
* dataset.delete();
*/
public async delete(options: DatasetDeleteOptions) {
const { datasetApiBaseUrl, datasetId, id } = options;

await this.utils.request(datasetDeleteRequestObject(datasetApiBaseUrl, datasetId, 'item', id));

return true;
}

/**
* @description Create an item in the Dataset.
*
* @example
* dataset.create(input);
*/
public async create(options: DatasetCreateUpdateOptions) {
const { datasetApiBaseUrl, datasetId, id, input, properties } = options;

const resourcePath = id ? `item/${id}` : `item`;

const { success, errors } = this.utils.inputMatchesDatasetConfig(input, properties);
if (!success) return { success, errors };

const payload = this.utils.inputToDatasetPayload(input, properties);

await this.utils.request(
datasetAddRequestObject(datasetApiBaseUrl, datasetId, resourcePath, payload)
);

return true;
}

/**
* @description Update an item in the Dataset.
* The deletion is based on the `itemId` set in the settings.
*
* @example
* dataset.update(input);
*/
public async update(options: DatasetCreateUpdateOptions) {
const { datasetApiBaseUrl, datasetId, id, input, properties } = options;

const { success, errors } = this.utils.inputMatchesDatasetConfig(input, properties);
if (!success) return { success, errors };

const payload = this.utils.inputToDatasetPayload(input, properties);

await this.utils.request(
datasetUpdateRequestObject(datasetApiBaseUrl, datasetId, 'item', id, payload)
);

return true;
}
}
21 changes: 14 additions & 7 deletions src/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ export class Utils {
/**
* @description Check if an input object matches a Dataset configuration.
*
* Note that it does not fail on extraneous properties, as it picks only
* what the configuration expects (as references) and/or provides as directly assigned values.
*
* @example
* const input = {
* name: 'Sam Person',
Expand Down Expand Up @@ -226,7 +229,7 @@ export class Utils {
const value = this.getReferencedValue(item.value, input);

const exists = value !== '__KEY_NOT_FOUND__';
if (!exists && item.isRequired) errors.push(`Missing value for "${value}"`);
if (!exists && item.isRequired) errors.push(`Missing value for "${item.value}"`);

const isValidType = validateType(item.headerType, value);
if (!isValidType) errors.push(`Invalid type for "${value}"`);
Expand Down Expand Up @@ -268,12 +271,16 @@ export class Utils {
/**
* @description Get the referenced value, either literally or if used within a variable-type format.
*/
private getReferencedValue(value: string, input: Record<string, any>) {
const isReferenceValue = value.startsWith('{input.');
return this.getNestedValue(
isReferenceValue ? value.replace('{input.', '').replace('}', '') : value,
input
);
private getReferencedValue(value: unknown, input: Record<string, any>) {
const isString = typeof value === 'string';
const isReferenceValue = isString ? value.startsWith('{input.') : false;
const fixedValue =
isString && isReferenceValue ? value.replace('{input.', '').replace('}', '') : value;

if (typeof fixedValue === 'string' && isString && isReferenceValue)
return this.getNestedValue(fixedValue, input);

return fixedValue;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//export * from './Compressor.js'; // TODO
export * from './DatasetRequests.js';
export * from './ItemOptimizer.js';
export * from './RequestObjects.js';
export * from './Utils.js';
16 changes: 16 additions & 0 deletions src/interfaces/DatasetRequests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
type DatasetOptionsBase = {
datasetApiBaseUrl: string;
datasetId: string;
};

export type DatasetGetOptions = DatasetOptionsBase;

export type DatasetDeleteOptions = DatasetOptionsBase & {
id: string;
};

export type DatasetCreateUpdateOptions = DatasetOptionsBase &
DatasetDeleteOptions & {
input: Record<string, any>;
properties: Record<string, any>[];
};
55 changes: 55 additions & 0 deletions testdata/dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,58 @@ export const validDatasetConfig = [
isRequired: true
}
];

export const validDatasetConfigDirectAssignment = [
{
headerRef: 'j2d8y22d',
headerType: 'short_text',
value: 'Sam Person',
isRequired: true
},
{
headerRef: 'kjhf298y',
headerType: 'short_text',
value: '10:00',
isRequired: true
},
{
headerRef: 'f2oifh9q',
headerType: 'short_text',
value: 'Central',
isRequired: true
},
{
headerRef: 'fb1891g2',
headerType: 'number',
value: 2,
isRequired: false
},
{
headerRef: 'mbhwf8ax',
headerType: 'number',
value: 46,
isRequired: true
}
];

export const datasetGetResponse = {
metadata: { deletedAt: '' },
headers: {
u: '100000000',
h: [
{ i: 'k392dg', t: 'short_text', r: true, n: 'First Header', p: 0, l: 'user123' },
{ i: 'o2ufj2', t: 'short_text', r: false, n: 'Second Header', p: 1, l: 'user123' }
]
},
items: [
{ i: 'aj2831', f: [{ h: 'k392dg', v: 'something here' }] },
{ i: 'vjc923', f: [{ h: 'k392dg', v: 'whoops' }] },
{
i: 'k473nd',
f: [
{ h: 'k392dg', v: 'more there' },
{ h: 'o2ufj2', v: 'whoa' }
]
}
]
};
117 changes: 117 additions & 0 deletions tests/DatasetRequests.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { test, expect } from 'vitest';

import { DatasetRequests } from '../src/DatasetRequests.js';

import { datasetGetResponse } from '../testdata/dataset.js';

const datasetApiBaseUrl = 'https://www.mockachino.com/6e5778ca-638a-4c';
const datasetId = 'asdf1234';
const id = 'abc123';
const input = {
something: 'my value here'
};
const properties = [
{
headerRef: 'abc123',
headerType: 'short_text',
value: '{input.something}',
isRequired: true
}
];

/**
* POSITIVE TESTS
*/
test('It should make a Dataset create request', async () => {
const expected = true;

const result = await new DatasetRequests().create({
datasetApiBaseUrl,
datasetId,
id,
input,
properties
});

expect(result).toBe(expected);
});

test('It should make a Dataset create request for a specific ID', async () => {
const expected = true;

const result = await new DatasetRequests().create({
datasetApiBaseUrl,
datasetId,
id: '',
input,
properties
});

expect(result).toBe(expected);
});

test('It should make a Dataset update request', async () => {
const expected = true;

const result = await new DatasetRequests().update({
datasetApiBaseUrl,
datasetId,
id,
input,
properties
});

expect(result).toBe(expected);
});

test('It should make a Dataset delete request', async () => {
const expected = true;

const result = await new DatasetRequests().delete({
datasetApiBaseUrl,
datasetId,
id
});

expect(result).toBe(expected);
});

test('It should make a Dataset get request', async () => {
const result = await new DatasetRequests().get({
datasetApiBaseUrl,
datasetId
});

expect(result).toMatchObject(datasetGetResponse);
});

/**
* NEGATIVE TESTS
*/
test('It should not make a Dataset create request if the input does not match the configuration', async () => {
const expected = { success: false, errors: ['Missing value for "{input.something}"'] };

const result = await new DatasetRequests().create({
datasetApiBaseUrl,
datasetId,
id,
input: { x: 1 },
properties
});

expect(result).toMatchObject(expected);
});

test('It should not make a Dataset update request if the input does not match the configuration', async () => {
const expected = { success: false, errors: ['Missing value for "{input.something}"'] };

const result = await new DatasetRequests().update({
datasetApiBaseUrl,
datasetId,
id,
input: { x: 1 },
properties
});

expect(result).toMatchObject(expected);
});
25 changes: 23 additions & 2 deletions tests/Utils.dataset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import { test, expect } from 'vitest';

import { Utils } from '../src/index.js';

import { validDatasetInput, validDatasetConfig } from '../testdata/dataset.js';
import {
validDatasetInput,
validDatasetConfig,
validDatasetConfigDirectAssignment
} from '../testdata/dataset.js';

/**
* POSITIVE TESTS
Expand All @@ -26,7 +30,7 @@ test('It should return true for an input that is missing optional properties, bu
expect(success).toBe(expected);
});

test('It should return a Dataset payload (create/update) from a valid input and Dataset configuration', () => {
test('It should return a Dataset payload (create/update) from a valid input (using a reference value) and Dataset configuration', () => {
const expected = [
{ headerRef: 'j2d8y22d', value: 'Sam Person' },
{ headerRef: 'kjhf298y', value: '10:00' },
Expand All @@ -40,6 +44,23 @@ test('It should return a Dataset payload (create/update) from a valid input and
expect(result).toMatchObject(expected);
});

test('It should return a Dataset payload (create/update) from a valid input (using a direct-assigned value) and Dataset configuration', () => {
const expected = [
{ headerRef: 'j2d8y22d', value: 'Sam Person' },
{ headerRef: 'kjhf298y', value: '10:00' },
{ headerRef: 'f2oifh9q', value: 'Central' },
{ headerRef: 'fb1891g2', value: 2 },
{ headerRef: 'mbhwf8ax', value: 46 }
];

const result = new Utils().inputToDatasetPayload(
validDatasetInput,
validDatasetConfigDirectAssignment
);

expect(result).toMatchObject(expected);
});

test('It should not handle incomplete configs', () => {
const expected = [{ headerRef: 'kjhf298y', value: '10:00' }];

Expand Down

0 comments on commit d2d3687

Please sign in to comment.