kysely-mapper
Version:
Flexible Kysely-based utility for mapping between tables and objects
425 lines • 22.4 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { TableMapper } from '../mappers/table-mapper';
import { createDB, resetDB, destroyDB } from './utils/test-setup';
import { createUserMapperReturningID, createUserMapperReturningIDAndHandleAsH, createUserMapperReturningNothing, } from './utils/test-mappers';
import { USERS } from './utils/test-objects';
import { ignore } from './utils/test-utils';
import { User } from './utils/test-types';
let db;
let userMapperReturningNothing;
let userMapperReturningID;
let userMapperReturningIDAndHandleAsH;
beforeAll(() => __awaiter(void 0, void 0, void 0, function* () {
db = yield createDB();
userMapperReturningNothing = createUserMapperReturningNothing(db);
userMapperReturningID = createUserMapperReturningID(db);
userMapperReturningIDAndHandleAsH =
createUserMapperReturningIDAndHandleAsH(db);
}));
beforeEach(() => resetDB(db));
afterAll(() => destroyDB(db));
describe('compiled updates', () => {
it('updates nothing returning zero update count', () => __awaiter(void 0, void 0, void 0, function* () {
const updateValues = { email: 'new.email@xyz.pdq' };
const compilation = userMapperReturningID
.update({ id: 1 })
.columns(['email'])
.compile();
const success2 = yield compilation.run({}, updateValues);
expect(success2).toBe(false);
const updateCount2 = yield compilation.returnCount({}, updateValues);
expect(updateCount2).toEqual(0);
const updates2 = yield compilation.returnAll({}, updateValues);
expect(updates2.length).toEqual(0);
const update2 = yield compilation.returnOne({}, updateValues);
expect(update2).toBeNull();
}));
it('compilations accept readonly updating objects', () => __awaiter(void 0, void 0, void 0, function* () {
const compilation = userMapperReturningNothing
.update('id', '=', 1)
.columns(['name', 'email'])
.compile();
const updateValues1 = {
name: 'Sue Rex',
email: 'srex@abc.def',
};
yield compilation.run({}, updateValues1);
}));
it('compiles a non-returning update query without transformation', () => __awaiter(void 0, void 0, void 0, function* () {
const insertReturns = yield userMapperReturningID.insert().returnAll(USERS);
const compilation = userMapperReturningNothing
.update('id', '=', insertReturns[0].id)
.columns(['name', 'email'])
.compile();
// test run()
const updateValues1 = {
name: 'Sue Rex',
email: 'srex@abc.def',
};
const updateReturns1 = yield compilation.run({}, updateValues1);
expect(updateReturns1).toBe(true);
const readUser1 = yield userMapperReturningID
.select({ id: insertReturns[0].id })
.returnOne();
expect(readUser1 === null || readUser1 === void 0 ? void 0 : readUser1.name).toEqual(updateValues1.name);
expect(readUser1 === null || readUser1 === void 0 ? void 0 : readUser1.email).toEqual(updateValues1.email);
// test returnOne()
const updateValues2 = {
name: 'Johnny Rex',
email: 'jrex@abc.def',
};
const updateReturns2 = yield compilation.returnOne({}, updateValues2);
expect(updateReturns2).toBeUndefined();
const readUser2 = yield userMapperReturningID
.select({ id: insertReturns[0].id })
.returnOne();
expect(readUser2 === null || readUser2 === void 0 ? void 0 : readUser2.name).toEqual(updateValues2.name);
expect(readUser2 === null || readUser2 === void 0 ? void 0 : readUser2.email).toEqual(updateValues2.email);
// test returnAll()
const updateReturns3 = yield compilation.returnAll({}, updateValues1);
expect(updateReturns3).toBeUndefined();
// test returnCount()
const updateReturns4 = yield compilation.returnCount({}, updateValues2);
expect(updateReturns4).toEqual(1);
}));
it('compiles a returning update query without transformation', () => __awaiter(void 0, void 0, void 0, function* () {
const insertReturns = yield userMapperReturningID.insert().returnAll(USERS);
const compilation = userMapperReturningIDAndHandleAsH
.update('id', '=', insertReturns[0].id)
.columns(['name', 'email'])
.compile();
// test run()
const updateValues1 = { name: 'Sue Rex', email: 'srex@abc.def' };
const updateReturns1 = yield compilation.run({}, updateValues1);
expect(updateReturns1).toBe(true);
const readUser1 = yield userMapperReturningID
.select({ id: insertReturns[0].id })
.returnOne();
expect(readUser1 === null || readUser1 === void 0 ? void 0 : readUser1.name).toEqual(updateValues1.name);
expect(readUser1 === null || readUser1 === void 0 ? void 0 : readUser1.email).toEqual(updateValues1.email);
// test returnOne()
const updateValues2 = { name: 'Johnny Rex', email: 'jrex@abc.def' };
const updateReturns2 = yield compilation.returnOne({}, updateValues2);
expect(updateReturns2 === null || updateReturns2 === void 0 ? void 0 : updateReturns2.id).toEqual(insertReturns[0].id);
expect(updateReturns2 === null || updateReturns2 === void 0 ? void 0 : updateReturns2.h).toEqual(USERS[0].handle);
const readUser2 = yield userMapperReturningID
.select({ id: insertReturns[0].id })
.returnOne();
expect(readUser2 === null || readUser2 === void 0 ? void 0 : readUser2.name).toEqual(updateValues2.name);
expect(readUser2 === null || readUser2 === void 0 ? void 0 : readUser2.email).toEqual(updateValues2.email);
// test returnAll()
const updateReturns3 = yield compilation.returnAll({}, updateValues1);
expect(updateReturns3[0].id).toEqual(insertReturns[0].id);
expect(updateReturns3[0].h).toEqual(USERS[0].handle);
// test returnCount()
const updateReturns4 = yield compilation.returnCount({}, updateValues2);
expect(updateReturns4).toEqual(1);
ignore('check compile-time types', () => {
compilation.returnOne({}, {
name: 'xyz',
handle: 'pdq',
email: 'abc@def.hij',
// @ts-expect-error - only insertable columns are allowed
notThere: 32,
});
// @ts-expect-error - only expected columns are returned
updateReturns2.handle;
// @ts-expect-error - only expected columns are returned
updateReturns3[0].handle;
});
}));
it('accepts readonly parameters and updating objects', () => __awaiter(void 0, void 0, void 0, function* () {
const parameterization = userMapperReturningIDAndHandleAsH.parameterize(({ mapper, param }) => mapper.update({ id: param('id') }).columns(['name']));
const params = { id: 1 };
const updateValues = {
name: 'Sue Rex',
email: 'srex@abc.def',
};
yield parameterization.run(params, updateValues);
yield parameterization.returnAll(params, updateValues);
yield parameterization.returnOne(params, updateValues);
yield parameterization.returnCount(params, updateValues);
}));
it('parameterizes a returning update query without transformation', () => __awaiter(void 0, void 0, void 0, function* () {
const insertReturns = yield userMapperReturningID.insert().returnAll(USERS);
const parameterization = userMapperReturningIDAndHandleAsH.parameterize(({ mapper, param }) => mapper.update({ id: param('id') }).columns(['name']));
// test run()
const updateValues1 = { name: 'Sue Rex' };
const updateReturns1 = yield parameterization.run({ id: insertReturns[0].id }, updateValues1);
expect(updateReturns1).toBe(true);
// test returnOne()
const updateValues2 = { name: 'Johnny Rex' };
const updateReturns2 = yield parameterization.returnOne({ id: insertReturns[1].id }, updateValues2);
expect(updateReturns2 === null || updateReturns2 === void 0 ? void 0 : updateReturns2.id).toEqual(insertReturns[1].id);
expect(updateReturns2 === null || updateReturns2 === void 0 ? void 0 : updateReturns2.h).toEqual(USERS[1].handle);
// test returnAll()
const updateReturns3 = yield parameterization.returnAll({ id: insertReturns[2].id }, updateValues1);
expect(updateReturns3[0].id).toEqual(insertReturns[2].id);
expect(updateReturns3[0].h).toEqual(USERS[2].handle);
// verify updates
const readUsers = yield userMapperReturningID.select().returnAll();
expect(readUsers[0].name).toEqual(updateValues1.name);
expect(readUsers[1].name).toEqual(updateValues2.name);
expect(readUsers[2].name).toEqual(updateValues1.name);
// test returnCount()
const updateReturns4 = yield parameterization.returnCount({ id: insertReturns[0].id }, updateValues2);
expect(updateReturns4).toEqual(1);
const readUser = yield userMapperReturningID
.select({ id: insertReturns[0].id })
.returnOne();
expect(readUser === null || readUser === void 0 ? void 0 : readUser.name).toEqual(updateValues2.name);
ignore('parameterization type errors', () => {
// @ts-expect-error - errors on invalid parameter names
parameterization.returnAll({ handle: 'foo' }, updateValues1);
// @ts-expect-error - errors on invalid column names
updateReturns2.handle;
// @ts-expect-error - errors on invalid column names
updateReturns3[0].handle;
userMapperReturningIDAndHandleAsH.parameterize(({ mapper, param }) =>
// @ts-expect-error - errors on invalid parameter name
mapper.update({ id: param('handle') }).columns(['name']));
userMapperReturningIDAndHandleAsH.parameterize(({ mapper, param }) =>
// @ts-expect-error - errors on invalid parameter type
mapper.update({ id: param('id') }).columns(['name']));
// @ts-expect-error - errors on invalid parameter value name
parameterization.returnOne({ handle: 'foo' }, updateValues1);
// @ts-expect-error - errors on invalid parameter value type
parameterization.returnOne({ id: 'foo' }, updateValues1);
parameterization.returnOne({ id: 1 }, {
name: 'xyz',
handle: 'pdq',
email: 'abc@def.hij',
// @ts-expect-error - only updateable columns are allowed
notThere: 32,
});
});
}));
it('compiles an update query with transformation', () => __awaiter(void 0, void 0, void 0, function* () {
expect.assertions(12);
const columnSubset = ['name'];
const transformMapper = new TableMapper(db, 'users', {
insertReturnColumns: ['*'],
updateReturnColumns: ['*'],
}).withTransforms({
selectTransform: (row) => {
const names = row.name.split(' ');
return new User(row.id, names[0], names[1], row.handle, row.email);
},
insertTransform: (source) => ({
name: `${source.firstName} ${source.lastName}`,
handle: source.handle,
email: source.email,
}),
insertReturnTransform: (_source, returns) => {
const names = returns.name.split(' ');
return new User(returns.id, names[0], names[1], returns.handle, returns.email);
},
updateTransform: (source, columns) => {
expect(columns).toEqual(columnSubset);
return {
name: `${source.firstName} ${source.lastName}`,
handle: source.handle,
email: source.email,
};
},
updateReturnTransform: (_source, returns) => {
const names = returns.name.split(' ');
return new User(returns.id, names[0], names[1], returns.handle, returns.email);
},
countTransform: (count) => Number(count),
});
const user1 = new User(0, 'John', 'Doe', 'johndoe', 'jdoe@abc.def');
const user2 = new User(0, 'Sam', 'Gamgee', 'sg', 'sg@abc.def');
const user3 = new User(0, 'Sue', 'Rex', 'srex', 'srex@abc.def');
const insertReturns = yield transformMapper
.insert()
.returnAll([user1, user2, user3]);
const compilation = transformMapper
.update({ id: insertReturns[2].id })
.columns(columnSubset)
.compile();
// test returnOne()
const updateReturn1 = yield compilation.returnOne({}, user1);
const expectedUser1 = User.create(insertReturns[2].id, {
firstName: user1.firstName,
lastName: user1.lastName,
handle: user3.handle,
email: user3.email,
});
expect(updateReturn1).toEqual(expectedUser1);
// Ensure that the provided columns are accessible
((_) => { })(updateReturn1.firstName);
const readUser1 = yield transformMapper
.select({ id: insertReturns[2].id })
.returnOne();
expect(readUser1).toEqual(expectedUser1);
// test returnAll()
const updateReturn2 = yield compilation.returnAll({}, user2);
const expectedUser2 = User.create(insertReturns[2].id, {
firstName: user2.firstName,
lastName: user2.lastName,
handle: user3.handle,
email: user3.email,
});
expect(updateReturn2[0]).toEqual(expectedUser2);
// Ensure that the provided columns are accessible
((_) => { })(updateReturn2[0].firstName);
const readUser2 = yield transformMapper
.select({ id: insertReturns[2].id })
.returnOne();
expect(readUser2).toEqual(expectedUser2);
// test run()
const success1 = yield compilation.run({}, user1);
expect(success1).toBe(true);
const readUser3 = yield transformMapper
.select({ id: insertReturns[2].id })
.returnOne();
expect(readUser3).toEqual(expectedUser1);
// test returnCount()
const count = yield compilation.returnCount({}, user2);
expect(count).toEqual(1);
const readUser4 = yield transformMapper
.select({ id: insertReturns[2].id })
.returnOne();
expect(readUser4).toEqual(expectedUser2);
ignore('check compile-time types', () => __awaiter(void 0, void 0, void 0, function* () {
// @ts-expect-error - only update objects are allowed
compilation.returnOne({}, USERS[0]);
// @ts-expect-error - only update objects are allowed
compilation.returnAll({}, USERS[0]);
// @ts-expect-error - only update objects are allowed
compilation.returnCount({}, USERS[0]);
// @ts-expect-error - only update objects are allowed
compilation.run({}, USERS[0]);
// @ts-expect-error - correct return is expected
(yield compilation.returnOne({}, user1)).name;
// @ts-expect-error - correct return is expected
(yield compilation.returnAll({}, user2))[0].name;
}));
}));
it('parameterizes an update query with transformation', () => __awaiter(void 0, void 0, void 0, function* () {
const transformMapper = new TableMapper(db, 'users', {
insertReturnColumns: ['id'],
updateReturnColumns: ['id'],
}).withTransforms({
selectTransform: (row) => {
const names = row.name.split(' ');
return new User(row.id, names[0], names[1], row.handle, row.email);
},
insertTransform: (source) => ({
name: `${source.firstName} ${source.lastName}`,
handle: source.handle,
email: source.email,
}),
insertReturnTransform: (source, returns) => ({
id: returns.id,
firstName: source.firstName,
lastName: source.lastName,
}),
updateTransform: (source) => ({
name: `${source.firstName} ${source.lastName}`,
handle: source.handle,
email: source.email,
}),
updateReturnTransform: (source, returns) => ({
id: returns.id,
firstName: source.firstName,
lastName: source.lastName,
}),
countTransform: (count) => Number(count),
});
const user1 = new User(0, 'John', 'Doe', 'johndoe', 'jdoe@abc.def');
const user2 = new User(0, 'Sam', 'Gamgee', 'sg', 'sg@abc.def');
const user3 = new User(0, 'Sue', 'Rex', 'srex', 'srex@abc.def');
const insertReturns = yield transformMapper
.insert()
.returnAll([user1, user2, user3]);
const parameterization = transformMapper.parameterize(({ mapper, param }) => mapper.update({ id: param('id') }).columns(['name']));
// test returnOne()
const updateReturn1 = yield parameterization.returnOne({ id: insertReturns[0].id }, user2);
const expectedReturn1 = {
id: insertReturns[0].id,
firstName: user2.firstName,
lastName: user2.lastName,
};
expect(updateReturn1).toEqual(expectedReturn1);
// Ensure that the provided columns are accessible
((_) => { })(updateReturn1.firstName);
// test returnAll()
const updateReturn2 = yield parameterization.returnAll({ id: insertReturns[1].id }, user3);
const expectedReturn2 = {
id: insertReturns[1].id,
firstName: user3.firstName,
lastName: user3.lastName,
};
expect(updateReturn2[0]).toEqual(expectedReturn2);
// Ensure that the provided columns are accessible
((_) => { })(updateReturn2[0].firstName);
// test run()
const success1 = yield parameterization.run({ id: insertReturns[2].id }, user1);
const expectedReturn3 = {
id: insertReturns[2].id,
firstName: user1.firstName,
lastName: user1.lastName,
};
expect(success1).toBe(true);
// verify updates
const readUsers = yield transformMapper.select().returnAll();
expect(readUsers).toEqual([
User.create(expectedReturn1.id, Object.assign(Object.assign({}, expectedReturn1), { handle: user1.handle, email: user1.email })),
User.create(expectedReturn2.id, Object.assign(Object.assign({}, expectedReturn2), { handle: user2.handle, email: user2.email })),
User.create(expectedReturn3.id, Object.assign(Object.assign({}, expectedReturn3), { handle: user3.handle, email: user3.email })),
]);
// test returnCount()
const count = yield parameterization.returnCount({ id: insertReturns[2].id }, user2);
expect(count).toEqual(1);
const readUser = yield transformMapper
.select({ id: insertReturns[2].id })
.returnOne();
expect(readUser).toEqual(User.create(insertReturns[2].id, Object.assign(Object.assign({}, expectedReturn1), { handle: user3.handle, email: user3.email })));
ignore('parameterization type errors', () => {
// @ts-expect-error - errors on invalid parameter names
parameterization.returnAll({ handle: 'foo' }, user1);
// @ts-expect-error - errors on invalid column names
updateReturn1.handle;
// @ts-expect-error - errors on invalid column names
updateReturn2[0].handle;
transformMapper.parameterize(({ mapper, param }) =>
// @ts-expect-error - errors on invalid parameter name
mapper.update({ id: param('handle') }).columns(['name']));
transformMapper.parameterize(({ mapper, param }) =>
// @ts-expect-error - errors on invalid parameter type
mapper.update({ id: param('id') }).columns(['name']));
// @ts-expect-error - errors on invalid parameter value name
parameterization.returnOne({ handle: 'foo' }, user1);
// @ts-expect-error - errors on invalid parameter value type
parameterization.returnOne({ id: 'foo' }, user1);
parameterization.returnOne({ id: 1 }, {
// @ts-expect-error - only updateable columns are allowed
name: 'xyz',
handle: 'pdq',
email: 'abc@def.hij',
});
});
}));
it('requires all indicated columns to be updated', () => __awaiter(void 0, void 0, void 0, function* () {
yield userMapperReturningID.insert().run(USERS);
const compilation = userMapperReturningID
.update()
.columns(['name', 'handle', 'email'])
.compile();
const updateValues = { name: 'John Doe', handle: 'johndoe' };
expect(() => compilation.returnOne({}, updateValues)).rejects.toThrow(`column 'email' missing`);
const success = yield compilation.run({}, Object.assign(Object.assign({}, updateValues), { email: null }));
expect(success).toBe(true);
}));
});
//# sourceMappingURL=update-compile.test.js.map