@getanthill/datastore
Version:
Event-Sourced Datastore
320 lines (278 loc) • 7.68 kB
text/typescript
import setup from '../../setup';
import Aggregator, { Aggregation } from './Aggregator';
import FHEClient from '../../services/fhe';
import { get } from 'lodash';
describe('sdk/Aggregator', () => {
let mongodb;
let instance;
let client;
let aggregator;
let uuid;
beforeAll(async () => {
[, mongodb, , , , client, , instance] = await setup.startApi({
features: {
api: {
admin: true,
},
},
});
client.config.debug = true;
uuid = setup.uuid();
});
beforeEach(() => {
const datastores = new Map([['datastore', client]]);
aggregator = new Aggregator(datastores);
});
afterEach(() => {
jest.restoreAllMocks();
});
afterAll(async () => {
await setup.teardownDb(mongodb);
await setup.stopApi(instance);
});
describe('retrieve nested objects', () => {
beforeAll(async () => {
await client.createModel({
is_enabled: true,
db: 'datastore',
name: 'accounts',
correlation_field: 'account_id',
schema: {
model: {
type: 'object',
properties: {
firstname: { type: 'string' },
data: {
type: 'string',
},
},
},
},
});
await client.createModel({
is_enabled: true,
db: 'datastore',
name: 'profiles',
correlation_field: 'profile_id',
schema: {
model: {
type: 'object',
properties: {
account_id: { type: 'string' },
firstname: { type: 'string' },
},
},
},
});
});
it('returns all entities at once', async () => {
const { data: account } = await client.create('accounts', {
firstname: `Alice ${uuid}`,
});
const { data: profile } = await client.create('profiles', {
account_id: account.account_id,
});
await client.update('accounts', account.account_id, {
firstname: `Bernard ${uuid}`,
});
const data = await aggregator.aggregate([
{
type: 'fetch',
datastore: 'datastore',
model: 'accounts',
destination: 'account',
as_entity: true,
query: {
firstname: `Bernard ${uuid}`,
},
},
{
type: 'fetch',
datastore: 'datastore',
model: 'accounts',
source: 'events',
destination: 'account_events',
map: [
{
from: 'account.account_id',
to: 'account_id',
},
],
},
{
type: 'fetch',
datastore: 'datastore',
model: 'profiles',
map: [
{
from: 'account.account_id',
to: 'account_id',
},
],
},
]);
expect(data).toMatchObject({
account: {
firstname: `Bernard ${uuid}`,
version: 1,
},
account_events: [
{
type: 'CREATED',
version: 0,
firstname: `Alice ${uuid}`,
},
{
type: 'UPDATED',
version: 1,
firstname: `Bernard ${uuid}`,
},
],
profiles: [
{
account_id: account.account_id,
version: 0,
},
],
});
});
it('returns timetravel entities from fetch', async () => {
const { data: account } = await client.create('accounts', {
firstname: `Alice ${uuid}`,
});
const { data: profile } = await client.create('profiles', {
account_id: account.account_id,
firstname: `Alice ${uuid}`,
});
await client.update('accounts', account.account_id, {
firstname: `Bernard ${uuid}`,
});
await client.update('profiles', profile.profile_id, {
firstname: `Bernard ${uuid}`,
});
const data = await aggregator.aggregate([
{
type: 'fetch',
datastore: 'datastore',
model: 'profiles',
source: 'events',
destination: 'entity',
as_entity: true,
query: {
type: 'CREATED',
firstname: `Alice ${uuid}`,
},
},
{
type: 'fetch',
datastore: 'datastore',
model: 'accounts',
source: 'entities',
destination: 'account',
as_entity: true,
// timetravel
timetravel: 'entity.created_at',
correlation_field: 'account_id',
// timetravel
map: [
{
from: 'entity.account_id',
to: 'account_id',
},
],
},
]);
expect(data).toMatchObject({
entity: {
version: 0,
firstname: `Alice ${uuid}`,
},
account: {
firstname: `Alice ${uuid}`,
version: 0,
},
});
});
it('performs FHE computations in aggregation', async () => {
aggregator = new Aggregator(new Map([['datastore', client]]));
const fhe = new FHEClient({});
await fhe.connect();
const fheStep = async (
step: { type: 'fhe'; script: string; args: Array<string> },
data: Aggregation = {},
) => {
const publicKey = data.public_key;
const another = new FHEClient({});
await another.connect();
const UploadedPublicKey = another.seal.PublicKey();
UploadedPublicKey.load(another.context!, publicKey);
another.keys!.public = UploadedPublicKey;
const asyncFn = another.compile(step.script);
const args = step.args.map((arg) => get(data, arg));
data.account.data = await another.compute(asyncFn, ...args);
return data;
};
const myStepDefinition = {
handler: fheStep,
};
aggregator.addStepType('fhe', myStepDefinition);
const value = fhe.createCypher([1]).save();
const publicKey = fhe.keys.public.save();
const { data: account } = await client.create('accounts', {
firstname: `Alice ${uuid}`,
data: value,
});
const data = await aggregator.aggregate(
[
{
type: 'fetch',
datastore: 'datastore',
model: 'accounts',
destination: 'account',
as_entity: true,
query: {
firstname: `Alice ${uuid}`,
},
},
{
type: 'fhe',
name: 'FHE increment example',
script: async function handler(a) {
return a + 1;
}.toString(),
args: ['account.data'],
},
{
type: 'persist',
datastore: 'datastore',
model: 'accounts',
correlation_field: 'account_id',
destination: 'account',
map: [
{
from: 'account.account_id',
to: 'account_id',
},
{
from: 'account.data',
to: 'data',
},
],
},
],
{
public_key: publicKey,
},
);
expect(data).toMatchObject({
account: {
firstname: `Alice ${uuid}`,
version: 1,
},
});
const cipher = fhe.seal.CipherText();
cipher.load(fhe.context, data.account.data);
expect(fhe.fromCypher(cipher).slice(0, 1)).toEqual([2]);
});
});
});