Skip to content

Commit 6f5b043

Browse files
committed
Implement Gel Product Repo
1 parent 50381dd commit 6f5b043

File tree

2 files changed

+234
-0
lines changed

2 files changed

+234
-0
lines changed

src/components/product/dto/product.dto.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,12 @@ declare module '../dto/producible.dto' {
266266
}
267267
}
268268

269+
export const ProductConcretes = {
270+
DirectScriptureProduct,
271+
DerivativeScriptureProduct,
272+
OtherProduct,
273+
};
274+
269275
declare module '~/core/resources/map' {
270276
interface ResourceMap {
271277
Product: typeof Product;
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
import { Injectable, type Type } from '@nestjs/common';
2+
import { ModuleRef } from '@nestjs/core';
3+
import { LazyGetter } from 'lazy-get-decorator';
4+
import { type ID, type PublicOf } from '../../common';
5+
import { grabInstances } from '../../common/instance-maps';
6+
import { e, RepoFor } from '../../core/gel';
7+
import {
8+
ProductConcretes as ConcreteTypes,
9+
type CreateDerivativeScriptureProduct,
10+
type CreateDirectScriptureProduct,
11+
type CreateOtherProduct,
12+
Product,
13+
} from './dto';
14+
import { type ProductRepository } from './product.repository';
15+
16+
// scriptureReferencesOverride, scriptureReferences
17+
18+
const baseHydrate = e.shape(e.Product, (product) => ({
19+
...product['*'],
20+
__typename: product.__type__.name,
21+
project: {
22+
id: true,
23+
status: true,
24+
type: true,
25+
},
26+
engagement: {
27+
id: true,
28+
status: true,
29+
},
30+
parent: e.select({
31+
identity: product.engagement.id,
32+
labels: e.array_agg(e.set(product.engagement.__type__.name.slice(9, null))),
33+
properties: e.select({
34+
id: product.engagement.id,
35+
createdAt: product.engagement.createdAt,
36+
}),
37+
}),
38+
pnpIndex: true,
39+
scriptureReferences: product.scripture,
40+
}));
41+
42+
const directScriptureExtraHydrate = {
43+
totalVerses: true,
44+
totalVerseEquivalents: true,
45+
} as const;
46+
47+
const derivativeScriptureExtraHydrate = {
48+
scripture: true,
49+
composite: true,
50+
totalVerses: true,
51+
totalVerseEquivalents: true,
52+
} as const;
53+
54+
const otherExtraHydrate = {
55+
title: true,
56+
description: true,
57+
} as const;
58+
59+
const directScriptureProductHydrate = e.shape(
60+
e.DirectScriptureProduct,
61+
(dsp) => ({
62+
...baseHydrate(dsp),
63+
__typename: dsp.__type__.name,
64+
unspecifiedScripture: {
65+
book: true,
66+
totalVerses: true,
67+
},
68+
//TODO - remove after migration
69+
unspecifiedScripturePortion: {
70+
book: true,
71+
totalVerses: true,
72+
},
73+
...directScriptureExtraHydrate,
74+
}),
75+
);
76+
77+
const derivativeScriptureProductHydrate = e.shape(
78+
e.DerivativeScriptureProduct,
79+
(dsp) => ({
80+
...baseHydrate(dsp),
81+
__typename: dsp.__type__.name,
82+
scriptureReferencesOverride: dsp.scriptureOverride,
83+
produces: {
84+
scriptureReferences: e.select([dsp.produces.scripture]),
85+
createdAt: dsp.produces.createdAt,
86+
id: dsp.produces.id,
87+
},
88+
...derivativeScriptureExtraHydrate,
89+
}),
90+
);
91+
92+
const otherProductHydrate = e.shape(e.OtherProduct, (op) => ({
93+
...baseHydrate(op),
94+
__typename: op.__type__.name,
95+
scriptureReferencesOverride: false, //TODO - remove after migration
96+
...otherExtraHydrate,
97+
}));
98+
99+
const hydrate = e.shape(e.Product, (product) => ({
100+
...baseHydrate(product),
101+
...e.is(e.DirectScriptureProduct, directScriptureExtraHydrate),
102+
...e.is(e.DerivativeScriptureProduct, derivativeScriptureExtraHydrate),
103+
...e.is(e.OtherProduct, otherExtraHydrate),
104+
}));
105+
106+
export const ConcreteRepos = {
107+
DirectScriptureProduct: class DirectScriptureProductRepository extends RepoFor(
108+
ConcreteTypes.DirectScriptureProduct,
109+
{
110+
hydrate: directScriptureProductHydrate,
111+
omit: ['create'],
112+
},
113+
) {
114+
async create(input: CreateDirectScriptureProduct) {
115+
const engagement = e.cast(
116+
e.LanguageEngagement,
117+
e.uuid(input.engagementId),
118+
);
119+
return await this.defaults.create({
120+
...input,
121+
projectContext: engagement.projectContext,
122+
});
123+
}
124+
},
125+
126+
DerivativeScriptureProduct: class DerivativeScriptureProductRepository extends RepoFor(
127+
ConcreteTypes.DerivativeScriptureProduct,
128+
{
129+
hydrate: derivativeScriptureProductHydrate,
130+
omit: ['create'],
131+
},
132+
) {
133+
async create(input: CreateDerivativeScriptureProduct) {
134+
const engagement = e.cast(
135+
e.LanguageEngagement,
136+
e.uuid(input.engagementId),
137+
);
138+
return await this.defaults.create({
139+
...input,
140+
projectContext: engagement.projectContext,
141+
});
142+
}
143+
},
144+
145+
OtherProduct: class OtherProductRepository extends RepoFor(
146+
ConcreteTypes.OtherProduct,
147+
{
148+
hydrate: otherProductHydrate,
149+
omit: ['create'],
150+
},
151+
) {
152+
async create(input: CreateOtherProduct) {
153+
const engagement = e.cast(
154+
e.LanguageEngagement,
155+
e.uuid(input.engagementId),
156+
);
157+
return await this.defaults.create({
158+
...input,
159+
projectContext: engagement.projectContext,
160+
});
161+
}
162+
},
163+
} satisfies Record<keyof typeof ConcreteTypes, Type>;
164+
165+
@Injectable()
166+
export class ProductGelRepository
167+
extends RepoFor(Product, {
168+
hydrate,
169+
omit: ['create'],
170+
})
171+
implements PublicOf<ProductRepository>
172+
{
173+
constructor(private readonly moduleRef: ModuleRef) {
174+
super();
175+
}
176+
177+
@LazyGetter() protected get concretes() {
178+
return grabInstances(this.moduleRef, ConcreteRepos);
179+
}
180+
181+
async createDerivative(
182+
input: CreateDerivativeScriptureProduct & {
183+
totalVerses: number;
184+
totalVerseEquivalents: number;
185+
},
186+
) {
187+
return await this.concretes.DerivativeScriptureProduct.create(input);
188+
}
189+
190+
async createDirect(
191+
input: CreateDirectScriptureProduct & {
192+
totalVerses: number;
193+
totalVerseEquivalents: number;
194+
},
195+
) {
196+
return await this.concretes.DirectScriptureProduct.create(input);
197+
}
198+
199+
async createOther(input: CreateOtherProduct) {
200+
return await this.concretes.OtherProduct.create(input);
201+
}
202+
203+
async listIdsAndScriptureRefs(engagementId: ID) {
204+
const engagement = e.cast(e.LanguageEngagement, e.uuid(engagementId));
205+
const query = e.select(e.DirectScriptureProduct, (dsp) => ({
206+
id: true,
207+
pnpIndex: true,
208+
scriptureRanges: dsp.scripture,
209+
unspecifiedScripture: dsp.unspecifiedScripture,
210+
filter: e.op(dsp.engagement, '=', engagement),
211+
}));
212+
213+
return await this.db.run(query);
214+
}
215+
216+
async listIdsWithPnpIndexes(engagementId: ID, _type?: string) {
217+
const engagement = e.cast(e.LanguageEngagement, e.uuid(engagementId));
218+
219+
const query = e.select(e.Product, (p) => ({
220+
id: true,
221+
pnpIndex: p.pnpIndex,
222+
...e.is(e.DirectScriptureProduct, {}),
223+
filter: e.op(p.engagement, '=', engagement),
224+
}));
225+
226+
return await this.db.run(query);
227+
}
228+
}

0 commit comments

Comments
 (0)