Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
Clarify data marshaling boundary
Browse files Browse the repository at this point in the history
- Use `_id` in Adapter instead of `id`
- Redefine Historical to include all the PouchDB properties
- Add concept of internal properties, to begin with $
- Implement marshal/unmarshal methods in AdapterWorkspace, to handle
  adding/removing properties, i.e. translating between
  Historical<Input<C, N>> and SavedInput<C, N>
- Move Historical generic into meta/pouch/types rather than alongside
  Workspace
  • Loading branch information
g. nicholas d'andrea committed May 19, 2021
1 parent efb7629 commit 6a03931
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 143 deletions.
12 changes: 1 addition & 11 deletions packages/db/src/meta/data.ts
Expand Up @@ -26,7 +26,7 @@ export interface Workspace<C extends Collections> {
get<N extends CollectionName<C>>(
collectionName: N,
id: string | undefined
): Promise<Historical<SavedInput<C, N>> | undefined>;
): Promise<SavedInput<C, N> | undefined>;

add<N extends CollectionName<C>>(
collectionName: N,
Expand All @@ -43,13 +43,3 @@ export interface Workspace<C extends Collections> {
input: MutationInput<C, M>
): Promise<void>;
}

export type History = Pick<PouchDB.Core.GetMeta, "_rev">;

export type Historical<T> = {
[K in keyof T | keyof History]: K extends keyof History
? History[K]
: K extends keyof T
? T[K]
: never;
};
150 changes: 68 additions & 82 deletions packages/db/src/meta/pouch/adapters/base.ts
Expand Up @@ -5,14 +5,11 @@ import PouchDB from "pouchdb";
import PouchDBDebug from "pouchdb-debug";
import PouchDBFind from "pouchdb-find";

import type {
CollectionName,
Collections,
} from "@truffle/db/meta/collections";
import type { CollectionName, Collections } from "@truffle/db/meta/collections";
import * as Id from "@truffle/db/meta/id";
import type { Historical } from "@truffle/db/meta/data";

import type {
Historical,
Adapter,
Definition,
Definitions
Expand Down Expand Up @@ -81,36 +78,36 @@ export abstract class Databases<C extends Collections> implements Adapter<C> {
}
}

public async every<N extends CollectionName<C>, I extends { id: string }>(
collectionName: N
): Promise<Historical<I>[]> {
public async every<
N extends CollectionName<C>,
I extends PouchDB.Core.IdMeta
>(collectionName: N): Promise<Historical<I>[]> {
await this.ready;

const { rows }: any = await this.collections[collectionName].allDocs({
include_docs: true
});

const result = rows
// make sure we include `id` in the response as well
.map(({ doc }) => ({ ...doc, id: doc["_id"] }))
// but filter out any views
.map(({ doc }) => doc)
// filter out any views
.filter(({ views }) => !views);

return result;
}

public async retrieve<N extends CollectionName<C>, I extends { id: string }>(
collectionName: N,
references: (Pick<I, "id"> | undefined)[]
) {
public async retrieve<
N extends CollectionName<C>,
I extends PouchDB.Core.IdMeta
>(collectionName: N, references: (Pick<I, "_id"> | undefined)[]) {
await this.ready;

const unordered = await this.search<N, I>(collectionName, {
selector: {
id: {
_id: {
$in: references
.filter((obj): obj is Pick<I, "id"> => !!obj)
.map(({ id }) => id)
.filter((obj): obj is Pick<I, "_id"> => !!obj)
.map(({ _id }) => _id)
}
}
});
Expand All @@ -120,21 +117,21 @@ export abstract class Databases<C extends Collections> implements Adapter<C> {
savedInput
? {
...byId,
[savedInput.id as string]: savedInput
[savedInput._id as string]: savedInput
}
: byId,
{} as { [id: string]: Historical<I> }
);

return references.map(reference =>
reference ? byId[reference.id] : undefined
reference ? byId[reference._id] : undefined
);
}

public async search<N extends CollectionName<C>, I extends { id: string }>(
collectionName: N,
options: PouchDB.Find.FindRequest<{}>
) {
public async search<
N extends CollectionName<C>,
I extends PouchDB.Core.IdMeta
>(collectionName: N, options: PouchDB.Find.FindRequest<{}>) {
await this.ready;

// allows searching with `id` instead of pouch's internal `_id`,
Expand All @@ -153,25 +150,15 @@ export abstract class Databases<C extends Collections> implements Adapter<C> {
selector: fixIdSelector(options.selector)
});

// make sure we include `id` in the response as well
const result: Historical<I>[] = docs.map(doc => {
const {
_id,
_rev,
...retrievedInput
} = doc;

return {
...retrievedInput,
_rev,
id: _id
};
});
const result: Historical<I>[] = docs;

return result;
}

public async record<N extends CollectionName<C>, I extends { id: string }>(
public async record<
N extends CollectionName<C>,
I extends PouchDB.Core.IdMeta
>(
collectionName: N,
inputs: (I | undefined)[],
options: { overwrite?: boolean } = {}
Expand All @@ -181,68 +168,69 @@ export abstract class Databases<C extends Collections> implements Adapter<C> {
const { overwrite = false } = options;

const inputsById: {
[id: string]: I
[id: string]: I;
} = inputs
.filter((input): input is I => !!input)
.map((input) => ({
[input.id]: input
.map(input => ({
[input._id]: input
}))
.reduce((a, b) => ({ ...a, ...b }), {} as { [id: string]: I });

const currentInputsById: {
[id: string]: Historical<I>;
} = (await this.retrieve<N, I>(
collectionName,
Object.keys(inputsById).map(id => ({ id }))
))
} = (
await this.retrieve<N, I>(
collectionName,
Object.keys(inputsById).map(_id => ({ _id }))
)
)
.filter((currentInput): currentInput is Historical<I> => !!currentInput)
.map(currentInput => ({ [currentInput.id]: currentInput }))
.map(currentInput => ({ [currentInput._id]: currentInput }))
.reduce(
(a, b) => ({ ...a, ...b }),
{} as { [id: string]: Historical<I> }
);

const savedInputsById: {
[id: string]: Historical<I>
[id: string]: Historical<I>;
} = (
await Promise.all(
Object.entries(inputsById)
.map(async ([id, input]) => {
const currentInput = currentInputsById[id];
Object.entries(inputsById).map(async ([_id, input]) => {
const currentInput = currentInputsById[_id];

if (currentInput && !overwrite) {
return currentInput;
}
if (currentInput && !overwrite) {
return currentInput;
}

const { _rev = undefined } = currentInput || {};
const { _rev = undefined } = currentInput || {};

const { rev } = await this.collections[collectionName].put({
...input,
_rev,
_id: id
});
const { rev } = await this.collections[collectionName].put({
...input,
_rev,
_id
});

return {
...input,
_rev: rev,
id
} as Historical<I>;
})
return {
...input,
_rev: rev,
_id
} as Historical<I>;
})
)
)
.map(savedInput => ({ [savedInput.id]: savedInput }))
.map(savedInput => ({ [savedInput._id]: savedInput }))
.reduce(
(a, b) => ({ ...a, ...b }),
{} as { [id: string]: Historical<I> }
);

return inputs.map(input => input && savedInputsById[input.id]);
return inputs.map(input => input && savedInputsById[input._id]);
}

public async forget<N extends CollectionName<C>, I extends { id: string }>(
collectionName: N,
references: (Pick<I, "id"> | undefined)[]
) {
public async forget<
N extends CollectionName<C>,
I extends PouchDB.Core.IdMeta
>(collectionName: N, references: (Pick<I, "_id"> | undefined)[]) {
await this.ready;

const retrievedInputs = await this.retrieve<N, I>(
Expand All @@ -255,25 +243,23 @@ export abstract class Databases<C extends Collections> implements Adapter<C> {
(retrievedInput): retrievedInput is Historical<I> => !!retrievedInput
)
.map(retrievedInput => ({
[retrievedInput.id]: retrievedInput
[retrievedInput._id]: retrievedInput
}))
.reduce(
(a, b) => ({ ...a, ...b }),
{} as { [id: string]: Historical<I> }
);

await Promise.all(
Object.values(retrievedInputsById)
.map(async ({ id, _rev }) => {
await this.collections[collectionName].put({
_rev,
_id: id,
_deleted: true
});
})
Object.values(retrievedInputsById).map(async ({ _id, _rev }) => {
await this.collections[collectionName].put({
_rev,
_id,
_deleted: true
});
})
);
}

}

type CollectionDatabases<C extends Collections> = {
Expand Down
19 changes: 11 additions & 8 deletions packages/db/src/meta/pouch/types.ts
Expand Up @@ -4,33 +4,36 @@ const debug = logger("db:meta:pouch:types");
import PouchDB from "pouchdb";

import type { Collections, CollectionName } from "@truffle/db/meta/collections";
import type { Historical } from "@truffle/db/meta/data";
import type * as Id from "@truffle/db/meta/id";

export type History = PouchDB.Core.IdMeta & PouchDB.Core.GetMeta;

export type Historical<T> = T & History;

export interface Adapter<C extends Collections> {
every<N extends CollectionName<C>, I extends { id: string }>(
every<N extends CollectionName<C>, I extends PouchDB.Core.IdMeta>(
collectionName: N
): Promise<Historical<I>[]>;

retrieve<N extends CollectionName<C>, I extends { id: string }>(
retrieve<N extends CollectionName<C>, I extends PouchDB.Core.IdMeta>(
collectionName: N,
references: (Pick<I, "id"> | undefined)[]
references: (Pick<I, "_id"> | undefined)[]
): Promise<(Historical<I> | undefined)[]>;

search<N extends CollectionName<C>, I extends { id: string }>(
search<N extends CollectionName<C>, I extends PouchDB.Core.IdMeta>(
collectionName: N,
options: PouchDB.Find.FindRequest<{}>
): Promise<Historical<I>[]>;

record<N extends CollectionName<C>, I extends { id: string }>(
record<N extends CollectionName<C>, I extends PouchDB.Core.IdMeta>(
collectionName: N,
inputs: (I | undefined)[],
options: { overwrite?: boolean }
): Promise<(Historical<I> | undefined)[]>;

forget<N extends CollectionName<C>, I extends { id: string }>(
forget<N extends CollectionName<C>, I extends PouchDB.Core.IdMeta>(
collectionName: N,
references: (Pick<I, "id"> | undefined)[]
references: (Pick<I, "_id"> | undefined)[]
): Promise<void>;
}

Expand Down

0 comments on commit 6a03931

Please sign in to comment.