Skip to content

Commit

Permalink
store: support nested file groups with custom ordering (#393)
Browse files Browse the repository at this point in the history
Closes #319
  • Loading branch information
dirkdev98 committed Oct 18, 2020
1 parent 7890b72 commit c9b7628
Show file tree
Hide file tree
Showing 18 changed files with 1,629 additions and 15 deletions.
56 changes: 53 additions & 3 deletions gen/store.js
Expand Up @@ -20,7 +20,6 @@ export function applyStoreStructure(app) {
app.add(
T.object("file")
.keys({
id: T.uuid().primary(),
bucketName: T.string().searchable(),
contentLength: T.number(),
contentType: T.string(),
Expand All @@ -30,13 +29,64 @@ export function applyStoreStructure(app) {
.default("{}")
.docs("User definable, optional object to store whatever you want"),
})
.enableQueries({ withSoftDeletes: true }),
.enableQueries({ withSoftDeletes: true })
.relations(),
);

app.add(
T.object("fileGroup")
.keys({
name: T.string().optional(),
order: T.number()
.default("Math.floor(Date.now() / 1000000)")
.docs("Hack to get an increasing integer by default"),
meta: T.object("fileGroupMeta")
.keys({})
.default("{}")
.docs("User definable, optional object to store whatever you want"),
})
.enableQueries({ withSoftDeletes: true })
.relations(
T.oneToOne("file", T.reference("store", "file"), "group").optional(),
T.manyToOne(
"parent",
T.reference("store", "fileGroup"),
"children",
).optional(),
T.oneToMany("children", T.reference("store", "fileGroup")),
),
);

app.add(
T.object("fileGroupView")
.keys({
name: T.string().optional(),
order: T.number(),
meta: T.object("fileGroupMeta")
.keys({})
.default("{}")
.docs("User definable, optional object to store whatever you want"),
isDirectory: T.bool().searchable(),
})
.enableQueries({ withSoftDeletes: true, isView: true })
.relations(
T.oneToOne(
"file",
T.reference("store", "file"),
"groupView",
).optional(),
T.manyToOne(
"parent",
T.reference("store", "fileGroupView"),
"children",
).optional(),
T.oneToMany("children", T.reference("store", "fileGroupView")),
),
);

app.add(
T.object("session")
.keys({
id: T.uuid().primary(),
expires: T.date().searchable(),
data: T.any().default("{}"),
})
Expand Down
4 changes: 3 additions & 1 deletion migrations/001-user-post.sql
Expand Up @@ -22,7 +22,9 @@ CREATE TABLE "post"
"createdAt" timestamptz NOT NULL DEFAULT now(),
"updatedAt" timestamptz NOT NULL DEFAULT now(),
"deletedAt" timestamptz NULL,
constraint "postWriterFk" foreign key ("writer") references "user" ("id") ON DELETE CASCADE
constraint "postWriterFk" foreign key ("writer")
references "user" ("id")
ON DELETE CASCADE
);

CREATE INDEX "postWriterIdx" ON "post" ("writer");
Expand Down
12 changes: 9 additions & 3 deletions migrations/002-category.sql
Expand Up @@ -15,8 +15,12 @@ CREATE TABLE "postCategory"
"post" uuid NOT NULL,
"createdAt" timestamptz NOT NULL DEFAULT now(),
"updatedAt" timestamptz NOT NULL DEFAULT now(),
constraint "postCategoryPostFk" foreign key ("post") references "post" ("id") ON DELETE CASCADE,
constraint "postCategoryCategoryFk" foreign key ("category") references "category" ("id") ON DELETE CASCADE
constraint "postCategoryPostFk" foreign key ("post")
references "post" ("id")
ON DELETE CASCADE,
constraint "postCategoryCategoryFk" foreign key ("category")
references "category" ("id")
ON DELETE CASCADE
);

CREATE INDEX "postCategoryCategoryIdx" ON "postCategory" ("category");
Expand All @@ -29,7 +33,9 @@ CREATE TABLE "categoryMeta"
"postCount" int NOT NULL,
"category" uuid NOT NULL,
"isHighlighted" boolean NULL,
constraint "categoryMetaCategoryFk" foreign key ("category") references "category" ("id") ON DELETE CASCADE
constraint "categoryMetaCategoryFk" foreign key ("category")
references "category" ("id")
ON DELETE CASCADE
);

CREATE INDEX "categoryMetaCategoryIdx" ON "categoryMeta" ("category");
Expand Down
4 changes: 2 additions & 2 deletions packages/code-gen/index.d.ts
Expand Up @@ -266,9 +266,9 @@ export class TypeCreator {
oneToMany(ownKey: string, reference: ReferenceType): RelationType;

/**
* Create a manyToMany relation
* Create a manyOneMany relation
*/
manyToMany(
manyToOne(
ownKey: string,
reference: ReferenceType,
referencedKey: string,
Expand Down
67 changes: 67 additions & 0 deletions packages/store/index.d.ts
Expand Up @@ -215,6 +215,73 @@ export function getFileStream(
range?: { start?: number; end?: number },
): Promise<NodeJS.ReadStream>;

export interface FileGroup {
id: string;
name?: string;
order: number;
meta: {};
file?: string;
parent?: string;
createdAt: Date;
updatedAt: Date;
deletedAt: Date;
}

export interface NestedFileGroup {
id: string;
name?: string;
order: number;
meta: {};
parent?: string;
isDirectory: boolean;
file?:
| {
id: string;
bucketName?: string;
contentLength?: number;
contentType?: string;
name?: string;
createdAt?: string;
updatedAt?: string;
}
| undefined
| string;
createdAt: Date;
updatedAt: Date;
deletedAt: Date;
children?: NestedFileGroup[];
}

/**
* Assigns children of the provided fileGroup to the parent.
* Returns the affected children.
*/
export function hoistChildrenToParent(
sql: Postgres,
fileGroup: FileGroup,
): Promise<FileGroup[]>;

/**
* Update the order of the provided id's in relation to each other.
* This function does not check if all files are in the same group.
*/
export function updateFileGroupOrder(
sql: Postgres,
ids: string[],
): Promise<void>;

/**
* Return a result with nested file groups and files, sorted completely by the order id.
*/
export function getNestedFileGroups(
sql: Postgres,
where?: {
deletedAtIncludeNotNull?: boolean;
rootId?: string;
excludeFiles?: boolean;
},
): Promise<NestedFileGroup[]>;

export interface FileCacheOptions {
/**
* Maximum byte size of a file to be stored in memory
Expand Down
6 changes: 6 additions & 0 deletions packages/store/index.js
Expand Up @@ -32,6 +32,12 @@ export {
syncDeletedFiles,
} from "./src/files.js";

export {
hoistChildrenToParent,
updateFileGroupOrder,
getNestedFileGroups,
} from "./src/file-group.js";

export { FileCache } from "./src/file-cache.js";

export {
Expand Down
24 changes: 24 additions & 0 deletions packages/store/migrations/005-file-group.sql
@@ -0,0 +1,24 @@
CREATE TABLE "fileGroup"
(
"id" uuid PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
"order" int NOT NULL,
"file" uuid NULL,
"parent" uuid NULL,
"name" varchar NULL,
"meta" jsonb NOT NULL,
"createdAt" timestamptz NOT NULL DEFAULT now(),
"updatedAt" timestamptz NOT NULL DEFAULT now(),
"deletedAt" timestamptz NULL,
-- Bother file and parent fields are optional, since we expect either one of them to exists
-- However we still want to cascade hard deletes
constraint "fileGroupFileFk" foreign key ("file")
references "file" ("id")
ON DELETE CASCADE,
constraint "fileGroupParentFk" foreign key ("parent")
references "fileGroup" ("id")
ON DELETE CASCADE
);

CREATE INDEX "fileGroupFileIdx" ON "fileGroup" ("file");
CREATE INDEX "fileGroupParentIdx" ON "fileGroup" ("parent");
CREATE INDEX "fileGroupDeletedAtIdx" ON "fileGroup" ("deletedAt");
13 changes: 13 additions & 0 deletions packages/store/migrations/006-r-file-group-view.sql
@@ -0,0 +1,13 @@
CREATE OR REPLACE VIEW "fileGroupView" AS
SELECT
fg.*,
CASE WHEN (calc."childCount" > 0) THEN TRUE ELSE FALSE END as "isDirectory"
FROM
"fileGroup" fg
LEFT JOIN LATERAL ( SELECT
count(nfg."id") as "childCount"
FROM
"fileGroup" nfg
WHERE
nfg."parent" = fg."id" ) "calc"
ON TRUE;

0 comments on commit c9b7628

Please sign in to comment.