@cravery/firebase 0.0.9 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/converter/equipment.d.ts.map +1 -1
- package/dist/converter/equipment.js.map +1 -1
- package/dist/converter/ingredient.d.ts.map +1 -1
- package/dist/converter/ingredient.js.map +1 -1
- package/dist/converter/recipe.d.ts.map +1 -1
- package/dist/converter/recipe.js +1 -0
- package/dist/converter/recipe.js.map +1 -1
- package/dist/converter/submission.d.ts.map +1 -1
- package/dist/converter/submission.js.map +1 -1
- package/dist/repository/base.d.ts.map +1 -1
- package/dist/repository/base.js.map +1 -1
- package/dist/repository/equipment.d.ts.map +1 -1
- package/dist/repository/equipment.js +1 -1
- package/dist/repository/equipment.js.map +1 -1
- package/dist/repository/ingredient.d.ts.map +1 -1
- package/dist/repository/ingredient.js +1 -1
- package/dist/repository/ingredient.js.map +1 -1
- package/dist/repository/recipe.d.ts.map +1 -1
- package/dist/repository/recipe.js.map +1 -1
- package/dist/repository/user_recipe.d.ts.map +1 -1
- package/dist/repository/user_recipe.js +15 -5
- package/dist/repository/user_recipe.js.map +1 -1
- package/package.json +2 -2
- package/src/converter/comment.ts +41 -41
- package/src/converter/equipment.ts +63 -58
- package/src/converter/ingredient.ts +63 -58
- package/src/converter/recipe.ts +1 -0
- package/src/converter/report.ts +52 -52
- package/src/converter/settings.ts +49 -49
- package/src/converter/submission.ts +48 -47
- package/src/repository/base.ts +280 -274
- package/src/repository/equipment.ts +17 -2
- package/src/repository/ingredient.ts +77 -62
- package/src/repository/recipe.ts +149 -147
- package/src/repository/user_recipe.ts +249 -225
package/src/repository/base.ts
CHANGED
|
@@ -1,274 +1,280 @@
|
|
|
1
|
-
import {
|
|
2
|
-
FieldPath,
|
|
3
|
-
Firestore,
|
|
4
|
-
Timestamp,
|
|
5
|
-
FirestoreDataConverter,
|
|
6
|
-
Query,
|
|
7
|
-
} from "firebase-admin/firestore";
|
|
8
|
-
import {
|
|
9
|
-
type Entity,
|
|
10
|
-
type Locale,
|
|
11
|
-
type CursorPaginatedResult,
|
|
12
|
-
type PaginationDirection,
|
|
13
|
-
COLLECTIONS,
|
|
14
|
-
} from "@cravery/core";
|
|
15
|
-
import { encodeCursor, cursorToFirestoreValues } from "../utils/cursor";
|
|
16
|
-
|
|
17
|
-
export interface SplitResult<TMeta, TContent> {
|
|
18
|
-
meta: Omit<TMeta, "id">;
|
|
19
|
-
content: TContent;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export abstract class BaseRepository<
|
|
23
|
-
TEntity extends TMeta & TContent,
|
|
24
|
-
TMeta extends Entity,
|
|
25
|
-
TContent,
|
|
26
|
-
> {
|
|
27
|
-
protected abstract readonly collectionName: string;
|
|
28
|
-
protected abstract readonly entityName: string;
|
|
29
|
-
protected abstract readonly metaConverter: FirestoreDataConverter<TMeta>;
|
|
30
|
-
protected abstract readonly contentConverter: FirestoreDataConverter<TContent>;
|
|
31
|
-
|
|
32
|
-
constructor(protected db: Firestore) {}
|
|
33
|
-
|
|
34
|
-
protected abstract merge(meta: TMeta, content: TContent): TEntity;
|
|
35
|
-
protected abstract split(entity: TEntity): SplitResult<TMeta, TContent>;
|
|
36
|
-
|
|
37
|
-
protected get metaCollection() {
|
|
38
|
-
return this.db
|
|
39
|
-
.collection(this.collectionName)
|
|
40
|
-
.withConverter(this.metaConverter);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
protected contentCollection(entityId: string) {
|
|
44
|
-
return this.db
|
|
45
|
-
.collection(this.collectionName)
|
|
46
|
-
.doc(entityId)
|
|
47
|
-
.collection(COLLECTIONS.Content)
|
|
48
|
-
.withConverter(this.contentConverter);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async create(
|
|
52
|
-
entity: Omit<TEntity, "id">,
|
|
53
|
-
locale: Locale,
|
|
54
|
-
id?: string,
|
|
55
|
-
): Promise<TEntity> {
|
|
56
|
-
const { meta, content } = this.split(entity as TEntity);
|
|
57
|
-
|
|
58
|
-
let entityId: string;
|
|
59
|
-
if (id) {
|
|
60
|
-
await this.metaCollection.doc(id).set(meta as TMeta);
|
|
61
|
-
entityId = id;
|
|
62
|
-
} else {
|
|
63
|
-
const metaRef = await this.metaCollection.add(meta as TMeta);
|
|
64
|
-
entityId = metaRef.id;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
await this.contentCollection(entityId).doc(locale).set(content);
|
|
68
|
-
return { id: entityId, ...entity } as TEntity;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async getById(entityId: string, locale: Locale): Promise<TEntity> {
|
|
72
|
-
const [metaDoc, contentDoc] = await Promise.all([
|
|
73
|
-
this.metaCollection.doc(entityId).get(),
|
|
74
|
-
this.contentCollection(entityId).doc(locale).get(),
|
|
75
|
-
]);
|
|
76
|
-
|
|
77
|
-
if (!metaDoc.exists) {
|
|
78
|
-
throw new Error(`${this.entityName} ${entityId} not found`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const meta = metaDoc.data()!;
|
|
82
|
-
|
|
83
|
-
if (!contentDoc.exists) {
|
|
84
|
-
throw new Error(
|
|
85
|
-
`${this.entityName} ${entityId} has no content for locale ${locale}`,
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const content = contentDoc.data()!;
|
|
90
|
-
return this.merge(meta, content);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async getMeta(entityId: string): Promise<TMeta> {
|
|
94
|
-
const metaDoc = await this.metaCollection.doc(entityId).get();
|
|
95
|
-
|
|
96
|
-
if (!metaDoc.exists) {
|
|
97
|
-
throw new Error(`${this.entityName} ${entityId} not found`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return metaDoc.data()!;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
async getContent(entityId: string, locale: Locale): Promise<TContent> {
|
|
104
|
-
const contentDoc = await this.contentCollection(entityId).doc(locale).get();
|
|
105
|
-
|
|
106
|
-
if (!contentDoc.exists) {
|
|
107
|
-
throw new Error(
|
|
108
|
-
`${this.entityName} ${entityId} has no content for locale ${locale}`,
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return contentDoc.data()!;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async updateMeta(
|
|
116
|
-
entityId: string,
|
|
117
|
-
updates: Partial<Omit<TMeta, "id" | "createdAt">>,
|
|
118
|
-
): Promise<void> {
|
|
119
|
-
await this.metaCollection.doc(entityId).update({
|
|
120
|
-
...updates,
|
|
121
|
-
updatedAt: Timestamp.now(),
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
async updateContent(
|
|
126
|
-
entityId: string,
|
|
127
|
-
locale: Locale,
|
|
128
|
-
content: Partial<TContent>,
|
|
129
|
-
): Promise<void> {
|
|
130
|
-
await this.contentCollection(entityId)
|
|
131
|
-
.doc(locale)
|
|
132
|
-
.set(content, { merge: true });
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async createContent(
|
|
136
|
-
entityId: string,
|
|
137
|
-
locale: Locale,
|
|
138
|
-
content: TContent,
|
|
139
|
-
): Promise<void> {
|
|
140
|
-
const metaDoc = await this.metaCollection.doc(entityId).get();
|
|
141
|
-
if (!metaDoc.exists) {
|
|
142
|
-
throw new Error(`${this.entityName} ${entityId} not found`);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const existingContent = await this.contentCollection(entityId)
|
|
146
|
-
.doc(locale)
|
|
147
|
-
.get();
|
|
148
|
-
|
|
149
|
-
if (existingContent.exists) {
|
|
150
|
-
throw new Error(
|
|
151
|
-
`Content for ${this.entityName.toLowerCase()} ${entityId} in locale ${locale} already exists. Use updateContent() instead.`,
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
await this.contentCollection(entityId).doc(locale).set(content);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async getAllContent(entityId: string): Promise<TContent[]> {
|
|
159
|
-
const metaDoc = await this.metaCollection.doc(entityId).get();
|
|
160
|
-
if (!metaDoc.exists) {
|
|
161
|
-
throw new Error(`${this.entityName} ${entityId} not found`);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const snapshot = await this.contentCollection(entityId).get();
|
|
165
|
-
|
|
166
|
-
if (snapshot.empty) {
|
|
167
|
-
throw new Error(`${this.entityName} ${entityId} has no content`);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return snapshot.docs.map((doc) => doc.data());
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
async delete(entityId: string): Promise<void> {
|
|
174
|
-
await this.metaCollection.doc(entityId).update({
|
|
175
|
-
status: "deleted",
|
|
176
|
-
deletedAt: Timestamp.now(),
|
|
177
|
-
updatedAt: Timestamp.now(),
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
async getPaginated(
|
|
182
|
-
locale: Locale,
|
|
183
|
-
limit = 20,
|
|
184
|
-
cursor?: string,
|
|
185
|
-
direction: PaginationDirection = "forward",
|
|
186
|
-
): Promise<CursorPaginatedResult<TEntity>> {
|
|
187
|
-
const baseQuery = this.metaCollection.where("status", "!=", "deleted");
|
|
188
|
-
return this.executePaginatedQuery(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
.map((metaDoc
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
1
|
+
import {
|
|
2
|
+
FieldPath,
|
|
3
|
+
Firestore,
|
|
4
|
+
Timestamp,
|
|
5
|
+
FirestoreDataConverter,
|
|
6
|
+
Query,
|
|
7
|
+
} from "firebase-admin/firestore";
|
|
8
|
+
import {
|
|
9
|
+
type Entity,
|
|
10
|
+
type Locale,
|
|
11
|
+
type CursorPaginatedResult,
|
|
12
|
+
type PaginationDirection,
|
|
13
|
+
COLLECTIONS,
|
|
14
|
+
} from "@cravery/core";
|
|
15
|
+
import { encodeCursor, cursorToFirestoreValues } from "../utils/cursor";
|
|
16
|
+
|
|
17
|
+
export interface SplitResult<TMeta, TContent> {
|
|
18
|
+
meta: Omit<TMeta, "id">;
|
|
19
|
+
content: TContent;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export abstract class BaseRepository<
|
|
23
|
+
TEntity extends TMeta & TContent,
|
|
24
|
+
TMeta extends Entity,
|
|
25
|
+
TContent,
|
|
26
|
+
> {
|
|
27
|
+
protected abstract readonly collectionName: string;
|
|
28
|
+
protected abstract readonly entityName: string;
|
|
29
|
+
protected abstract readonly metaConverter: FirestoreDataConverter<TMeta>;
|
|
30
|
+
protected abstract readonly contentConverter: FirestoreDataConverter<TContent>;
|
|
31
|
+
|
|
32
|
+
constructor(protected db: Firestore) {}
|
|
33
|
+
|
|
34
|
+
protected abstract merge(meta: TMeta, content: TContent): TEntity;
|
|
35
|
+
protected abstract split(entity: TEntity): SplitResult<TMeta, TContent>;
|
|
36
|
+
|
|
37
|
+
protected get metaCollection() {
|
|
38
|
+
return this.db
|
|
39
|
+
.collection(this.collectionName)
|
|
40
|
+
.withConverter(this.metaConverter);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected contentCollection(entityId: string) {
|
|
44
|
+
return this.db
|
|
45
|
+
.collection(this.collectionName)
|
|
46
|
+
.doc(entityId)
|
|
47
|
+
.collection(COLLECTIONS.Content)
|
|
48
|
+
.withConverter(this.contentConverter);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async create(
|
|
52
|
+
entity: Omit<TEntity, "id">,
|
|
53
|
+
locale: Locale,
|
|
54
|
+
id?: string,
|
|
55
|
+
): Promise<TEntity> {
|
|
56
|
+
const { meta, content } = this.split(entity as TEntity);
|
|
57
|
+
|
|
58
|
+
let entityId: string;
|
|
59
|
+
if (id) {
|
|
60
|
+
await this.metaCollection.doc(id).set(meta as TMeta);
|
|
61
|
+
entityId = id;
|
|
62
|
+
} else {
|
|
63
|
+
const metaRef = await this.metaCollection.add(meta as TMeta);
|
|
64
|
+
entityId = metaRef.id;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
await this.contentCollection(entityId).doc(locale).set(content);
|
|
68
|
+
return { id: entityId, ...entity } as TEntity;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async getById(entityId: string, locale: Locale): Promise<TEntity> {
|
|
72
|
+
const [metaDoc, contentDoc] = await Promise.all([
|
|
73
|
+
this.metaCollection.doc(entityId).get(),
|
|
74
|
+
this.contentCollection(entityId).doc(locale).get(),
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
if (!metaDoc.exists) {
|
|
78
|
+
throw new Error(`${this.entityName} ${entityId} not found`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const meta = metaDoc.data()!;
|
|
82
|
+
|
|
83
|
+
if (!contentDoc.exists) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`${this.entityName} ${entityId} has no content for locale ${locale}`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const content = contentDoc.data()!;
|
|
90
|
+
return this.merge(meta, content);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async getMeta(entityId: string): Promise<TMeta> {
|
|
94
|
+
const metaDoc = await this.metaCollection.doc(entityId).get();
|
|
95
|
+
|
|
96
|
+
if (!metaDoc.exists) {
|
|
97
|
+
throw new Error(`${this.entityName} ${entityId} not found`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return metaDoc.data()!;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async getContent(entityId: string, locale: Locale): Promise<TContent> {
|
|
104
|
+
const contentDoc = await this.contentCollection(entityId).doc(locale).get();
|
|
105
|
+
|
|
106
|
+
if (!contentDoc.exists) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`${this.entityName} ${entityId} has no content for locale ${locale}`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return contentDoc.data()!;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async updateMeta(
|
|
116
|
+
entityId: string,
|
|
117
|
+
updates: Partial<Omit<TMeta, "id" | "createdAt">>,
|
|
118
|
+
): Promise<void> {
|
|
119
|
+
await this.metaCollection.doc(entityId).update({
|
|
120
|
+
...updates,
|
|
121
|
+
updatedAt: Timestamp.now(),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async updateContent(
|
|
126
|
+
entityId: string,
|
|
127
|
+
locale: Locale,
|
|
128
|
+
content: Partial<TContent>,
|
|
129
|
+
): Promise<void> {
|
|
130
|
+
await this.contentCollection(entityId)
|
|
131
|
+
.doc(locale)
|
|
132
|
+
.set(content, { merge: true });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async createContent(
|
|
136
|
+
entityId: string,
|
|
137
|
+
locale: Locale,
|
|
138
|
+
content: TContent,
|
|
139
|
+
): Promise<void> {
|
|
140
|
+
const metaDoc = await this.metaCollection.doc(entityId).get();
|
|
141
|
+
if (!metaDoc.exists) {
|
|
142
|
+
throw new Error(`${this.entityName} ${entityId} not found`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const existingContent = await this.contentCollection(entityId)
|
|
146
|
+
.doc(locale)
|
|
147
|
+
.get();
|
|
148
|
+
|
|
149
|
+
if (existingContent.exists) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`Content for ${this.entityName.toLowerCase()} ${entityId} in locale ${locale} already exists. Use updateContent() instead.`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
await this.contentCollection(entityId).doc(locale).set(content);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async getAllContent(entityId: string): Promise<TContent[]> {
|
|
159
|
+
const metaDoc = await this.metaCollection.doc(entityId).get();
|
|
160
|
+
if (!metaDoc.exists) {
|
|
161
|
+
throw new Error(`${this.entityName} ${entityId} not found`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const snapshot = await this.contentCollection(entityId).get();
|
|
165
|
+
|
|
166
|
+
if (snapshot.empty) {
|
|
167
|
+
throw new Error(`${this.entityName} ${entityId} has no content`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return snapshot.docs.map((doc) => doc.data());
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async delete(entityId: string): Promise<void> {
|
|
174
|
+
await this.metaCollection.doc(entityId).update({
|
|
175
|
+
status: "deleted",
|
|
176
|
+
deletedAt: Timestamp.now(),
|
|
177
|
+
updatedAt: Timestamp.now(),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async getPaginated(
|
|
182
|
+
locale: Locale,
|
|
183
|
+
limit = 20,
|
|
184
|
+
cursor?: string,
|
|
185
|
+
direction: PaginationDirection = "forward",
|
|
186
|
+
): Promise<CursorPaginatedResult<TEntity>> {
|
|
187
|
+
const baseQuery = this.metaCollection.where("status", "!=", "deleted");
|
|
188
|
+
return this.executePaginatedQuery(
|
|
189
|
+
baseQuery,
|
|
190
|
+
locale,
|
|
191
|
+
limit,
|
|
192
|
+
cursor,
|
|
193
|
+
direction,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async exists(entityId: string): Promise<boolean> {
|
|
198
|
+
const doc = await this.metaCollection.doc(entityId).get();
|
|
199
|
+
return doc.exists;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
protected async executePaginatedQuery(
|
|
203
|
+
baseQuery: Query<TMeta>,
|
|
204
|
+
locale: Locale,
|
|
205
|
+
limit: number,
|
|
206
|
+
cursor?: string,
|
|
207
|
+
direction: PaginationDirection = "forward",
|
|
208
|
+
): Promise<CursorPaginatedResult<TEntity>> {
|
|
209
|
+
const isForward = direction === "forward";
|
|
210
|
+
const sortDir = isForward ? "desc" : "asc";
|
|
211
|
+
|
|
212
|
+
let query = baseQuery
|
|
213
|
+
.orderBy("createdAt", sortDir)
|
|
214
|
+
.orderBy(FieldPath.documentId(), sortDir);
|
|
215
|
+
|
|
216
|
+
if (cursor) {
|
|
217
|
+
const [timestamp, id] = cursorToFirestoreValues(cursor);
|
|
218
|
+
query = query.startAfter(timestamp, id);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const snapshot = await query.limit(limit + 1).get();
|
|
222
|
+
const hasMore = snapshot.docs.length > limit;
|
|
223
|
+
let docs = hasMore ? snapshot.docs.slice(0, -1) : snapshot.docs;
|
|
224
|
+
|
|
225
|
+
if (!isForward) {
|
|
226
|
+
docs = docs.reverse();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (docs.length === 0) {
|
|
230
|
+
return {
|
|
231
|
+
data: [],
|
|
232
|
+
startCursor: null,
|
|
233
|
+
endCursor: null,
|
|
234
|
+
hasNextPage: false,
|
|
235
|
+
hasPreviousPage: false,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const entities = await this.hydrate(docs, locale);
|
|
240
|
+
|
|
241
|
+
const firstDoc = docs[0];
|
|
242
|
+
const lastDoc = docs[docs.length - 1];
|
|
243
|
+
|
|
244
|
+
const firstCreatedAt = firstDoc.get("createdAt") as Timestamp;
|
|
245
|
+
const lastCreatedAt = lastDoc.get("createdAt") as Timestamp;
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
data: entities,
|
|
249
|
+
startCursor: encodeCursor(firstCreatedAt, firstDoc.id),
|
|
250
|
+
endCursor: encodeCursor(lastCreatedAt, lastDoc.id),
|
|
251
|
+
hasNextPage: isForward ? hasMore : !!cursor,
|
|
252
|
+
hasPreviousPage: isForward ? !!cursor : hasMore,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
protected async hydrate(
|
|
257
|
+
docs: FirebaseFirestore.QueryDocumentSnapshot<TMeta>[],
|
|
258
|
+
locale: Locale,
|
|
259
|
+
): Promise<TEntity[]> {
|
|
260
|
+
const contentDocs = await Promise.all(
|
|
261
|
+
docs.map((metaDoc) =>
|
|
262
|
+
this.contentCollection(metaDoc.id).doc(locale).get(),
|
|
263
|
+
),
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
return docs
|
|
267
|
+
.map((metaDoc, index) => {
|
|
268
|
+
const meta = metaDoc.data();
|
|
269
|
+
const contentDoc = contentDocs[index];
|
|
270
|
+
|
|
271
|
+
if (!contentDoc.exists) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const content = contentDoc.data()!;
|
|
276
|
+
return this.merge(meta, content);
|
|
277
|
+
})
|
|
278
|
+
.filter((item): item is TEntity => item !== null);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -39,7 +39,16 @@ export class EquipmentRepository extends BaseRepository<
|
|
|
39
39
|
protected split(
|
|
40
40
|
equipment: EquipmentEntity,
|
|
41
41
|
): SplitResult<EquipmentEntityMeta, EquipmentEntityContent> {
|
|
42
|
-
const {
|
|
42
|
+
const {
|
|
43
|
+
id: _id,
|
|
44
|
+
createdAt,
|
|
45
|
+
updatedAt,
|
|
46
|
+
deletedAt,
|
|
47
|
+
status,
|
|
48
|
+
category,
|
|
49
|
+
name,
|
|
50
|
+
slug,
|
|
51
|
+
} = equipment;
|
|
43
52
|
return {
|
|
44
53
|
meta: { category, slug, status, createdAt, updatedAt, deletedAt },
|
|
45
54
|
content: { name, slug },
|
|
@@ -57,6 +66,12 @@ export class EquipmentRepository extends BaseRepository<
|
|
|
57
66
|
.where("category", "==", category)
|
|
58
67
|
.where("status", "!=", "deleted");
|
|
59
68
|
|
|
60
|
-
return this.executePaginatedQuery(
|
|
69
|
+
return this.executePaginatedQuery(
|
|
70
|
+
baseQuery,
|
|
71
|
+
locale,
|
|
72
|
+
limit,
|
|
73
|
+
cursor,
|
|
74
|
+
direction,
|
|
75
|
+
);
|
|
61
76
|
}
|
|
62
77
|
}
|