Skip to content

Commit 03eea2e

Browse files
committed
Update Translation History
1 parent b1fe8b1 commit 03eea2e

File tree

9 files changed

+306
-103
lines changed

9 files changed

+306
-103
lines changed

functions/src/contents.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { HttpsError, onCall } from 'firebase-functions/v2/https';
33
import { onDocumentDeleted, onDocumentUpdated, onDocumentWritten } from 'firebase-functions/v2/firestore';
44
import { FieldValue, UpdateData, WithFieldValue } from 'firebase-admin/firestore';
55
import { canPerform } from './utils/security-utils';
6-
import { bucket, firestoreService } from './config';
6+
import { BATCH_MAX, bucket, firestoreService } from './config';
77
import {
88
Content,
99
ContentDocument,
@@ -148,24 +148,42 @@ const onContentDelete = onDocumentDeleted('spaces/{spaceId}/contents/{contentId}
148148
const { spaceId, contentId } = event.params;
149149
// No Data
150150
if (!event.data) return;
151-
// TODO add 500 LIMIT
152-
const batch = firestoreService.batch();
151+
let batch = firestoreService.batch();
152+
let count = 0;
153+
logger.info(`[Content::onDelete] eventId='${event.id}' delete History`);
153154
const contentsHistorySnapshot = await findContentsHistory(spaceId, contentId).get();
154-
contentsHistorySnapshot.docs.forEach(it => batch.delete(it.ref));
155+
for (const item of contentsHistorySnapshot.docs) {
156+
batch.delete(item.ref);
157+
count++;
158+
if (count === BATCH_MAX) {
159+
await batch.commit();
160+
batch = firestoreService.batch();
161+
count = 0;
162+
}
163+
}
155164
const content = event.data.data() as Content;
156165
logger.info(`[Content::onDelete] eventId='${event.id}' id='${event.data.id}' fullSlug='${content.fullSlug}'`);
157166
// Logic related to delete, in case a folder is deleted it should be cascaded to all childs
158167
if (content.kind === ContentKind.DOCUMENT) {
159168
await bucket.deleteFiles({
160169
prefix: `spaces/${spaceId}/contents/${contentId}`,
161170
});
162-
return batch.commit();
163171
} else if (content.kind === ContentKind.FOLDER) {
164172
// cascade changes to all child's in case it is a FOLDER
165173
// It will create recursion
166174
const contentsSnapshot = await findContentByFullSlug(spaceId, content.fullSlug).get();
167-
contentsSnapshot.docs.filter(it => it.exists).forEach(it => batch.delete(it.ref));
168-
return batch.commit();
175+
for (const item of contentsSnapshot.docs) {
176+
batch.delete(item.ref);
177+
count++;
178+
if (count === BATCH_MAX) {
179+
await batch.commit();
180+
batch = firestoreService.batch();
181+
count = 0;
182+
}
183+
}
184+
}
185+
if (count > 0) {
186+
await batch.commit();
169187
}
170188
return;
171189
});
@@ -218,6 +236,9 @@ const onContentWrite = onDocumentWritten('spaces/{spaceId}/contents/{contentId}'
218236
cSlug: afterData.slug,
219237
createdAt: FieldValue.serverTimestamp(),
220238
};
239+
} else if (beforeData) {
240+
// delete : skip history
241+
return;
221242
}
222243
await findContentsHistory(spaceId, contentId).add(addHistory);
223244
return;

functions/src/plugins/stripe/utils.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import {
99
} from './model';
1010
import { v4 } from 'uuid';
1111

12+
/**
13+
* Convert Product to Content Data
14+
* @param {Stripe.Product} data Stripe Product
15+
* @return {ProductContentData} content data
16+
*/
1217
export function productToContentData(data: Stripe.Product): ProductContentData {
1318
const result: ProductContentData = {
1419
_id: data.id,
@@ -36,6 +41,11 @@ export function productToContentData(data: Stripe.Product): ProductContentData {
3641
return result;
3742
}
3843

44+
/**
45+
* Convert Price to Content Data
46+
* @param {Stripe.Price} data Stripe Price
47+
* @return {PriceContentData} content data
48+
*/
3949
export function priceToContentData(data: Stripe.Price): PriceContentData {
4050
const result: PriceContentData = {
4151
_id: data.id,
@@ -73,6 +83,11 @@ export function priceToContentData(data: Stripe.Price): PriceContentData {
7383
return result;
7484
}
7585

86+
/**
87+
* Convert Price Recurring to Content Data
88+
* @param {Stripe.Price.Recurring} data Stripe Price Recurring
89+
* @return {PriceRecurringContentData} content data
90+
*/
7691
export function recurringToContentData(data: Stripe.Price.Recurring): PriceRecurringContentData {
7792
const result: PriceRecurringContentData = {
7893
_id: v4(),
@@ -85,6 +100,11 @@ export function recurringToContentData(data: Stripe.Price.Recurring): PriceRecur
85100
return result;
86101
}
87102

103+
/**
104+
* Convert Price Tier to Content Data
105+
* @param {Stripe.Price.Tier} data Stripe Price Tier
106+
* @return {PriceTireContentData} content data
107+
*/
88108
export function tireToContentData(data: Stripe.Price.Tier): PriceTireContentData {
89109
const result: PriceTireContentData = {
90110
_id: v4(),
@@ -98,6 +118,11 @@ export function tireToContentData(data: Stripe.Price.Tier): PriceTireContentData
98118
return result;
99119
}
100120

121+
/**
122+
* Convert Price Product PackageDimensions to Content Data
123+
* @param {Stripe.Product.PackageDimensions} data Stripe Product PackageDimensions
124+
* @return {PackageDimensionRecurringContentData} content data
125+
*/
101126
export function packageDimensionsToContentData(data: Stripe.Product.PackageDimensions): PackageDimensionRecurringContentData {
102127
const result: PackageDimensionRecurringContentData = {
103128
_id: v4(),
@@ -110,6 +135,11 @@ export function packageDimensionsToContentData(data: Stripe.Product.PackageDimen
110135
return result;
111136
}
112137

138+
/**
139+
* Convert Price CustomUnitAmount to Content Data
140+
* @param {Stripe.Price.CustomUnitAmount} data Stripe Price CustomUnitAmount
141+
* @return {PriceCustomUnitAmountContentData} content data
142+
*/
113143
export function priceCustomUnitAmountToContentData(data: Stripe.Price.CustomUnitAmount): PriceCustomUnitAmountContentData {
114144
const result: PriceCustomUnitAmountContentData = {
115145
_id: v4(),
@@ -121,6 +151,12 @@ export function priceCustomUnitAmountToContentData(data: Stripe.Price.CustomUnit
121151
return result;
122152
}
123153

154+
/**
155+
* Convert Price CustomUnitAmount to Content Data
156+
* @param {Stripe} stripe Stripe
157+
* @param {Stripe.Product} product Stripe Product
158+
* @return {ProductContentData} content data
159+
*/
124160
export async function generateProductWithPricesData(stripe: Stripe, product: Stripe.Product): Promise<ProductContentData> {
125161
const productData: ProductContentData = productToContentData(product);
126162
const prices = await stripe.prices.list({ product: product.id, expand: ['data.tiers'] }).autoPagingToArray({ limit: 100 });

functions/src/services/space.service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { CollectionReference, DocumentReference } from 'firebase-admin/firestore
33

44
/**
55
* find Space by ID
6-
* @param {string} spaceId Space identifier
6+
* @param {string} id Space identifier
77
* @return {DocumentReference} document reference to the space
88
*/
9-
export function findSpaceById(spaceId: string): DocumentReference {
10-
return firestoreService.doc(`spaces/${spaceId}`);
9+
export function findSpaceById(id: string): DocumentReference {
10+
return firestoreService.doc(`spaces/${id}`);
1111
}
1212

1313
/**

functions/src/spaces.ts

Lines changed: 151 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,166 @@
11
import { logger } from 'firebase-functions/v2';
22
import { onDocumentDeleted } from 'firebase-functions/v2/firestore';
3-
import { bucket } from './config';
43
import { onCall } from 'firebase-functions/v2/https';
4+
import { BATCH_MAX, bucket, firestoreService } from './config';
55
import { AssetKind, ContentKind, Space, SpaceOverviewData } from './models';
6-
import { findAssets, findContents, findSchemas, findSpaceById, findTranslations } from './services';
6+
import {
7+
findAssets,
8+
findContents,
9+
findPlugins,
10+
findSchemas,
11+
findSpaceById,
12+
findTasks,
13+
findTokens,
14+
findTranslations,
15+
findTranslationsHistory,
16+
} from './services';
717
import { FieldValue, UpdateData } from 'firebase-admin/firestore';
818

919
// Firestore events
10-
const onSpaceDelete = onDocumentDeleted('spaces/{spaceId}', event => {
20+
const onSpaceDelete = onDocumentDeleted('spaces/{spaceId}', async event => {
1121
logger.info(`[Space::onDelete] eventId='${event.id}'`);
1222
logger.info(`[Space::onDelete] params='${JSON.stringify(event.params)}'`);
1323
const { spaceId } = event.params;
14-
return bucket.deleteFiles({
24+
await bucket.deleteFiles({
1525
prefix: `spaces/${spaceId}/`,
1626
});
17-
// TODO delete all sub collections
18-
// assets
19-
// contents
20-
// plugins
21-
// schemas
22-
// tasks
23-
// tokens
24-
// translations
25-
// translations_history
27+
let batch = firestoreService.batch();
28+
let count = 0;
29+
// Assets
30+
const assetsSnapshot = await findAssets(spaceId).get();
31+
logger.info(`[Space::onDelete] Assets size='${assetsSnapshot.docs.length}'`);
32+
for (const item of assetsSnapshot.docs) {
33+
batch.delete(item.ref);
34+
count++;
35+
if (count === BATCH_MAX) {
36+
await batch.commit();
37+
batch = firestoreService.batch();
38+
count = 0;
39+
}
40+
}
41+
if (count > 0) {
42+
await batch.commit();
43+
batch = firestoreService.batch();
44+
count = 0;
45+
}
46+
// Contents
47+
const contentsSnapshot = await findContents(spaceId).get();
48+
logger.info(`[Space::onDelete] Contents size='${contentsSnapshot.docs.length}'`);
49+
for (const item of contentsSnapshot.docs) {
50+
batch.delete(item.ref);
51+
count++;
52+
if (count === BATCH_MAX) {
53+
await batch.commit();
54+
batch = firestoreService.batch();
55+
count = 0;
56+
}
57+
}
58+
if (count > 0) {
59+
await batch.commit();
60+
batch = firestoreService.batch();
61+
count = 0;
62+
}
63+
// Plugins
64+
const pluginsSnapshot = await findPlugins(spaceId).get();
65+
logger.info(`[Space::onDelete] plugins size='${pluginsSnapshot.docs.length}'`);
66+
for (const item of pluginsSnapshot.docs) {
67+
batch.delete(item.ref);
68+
count++;
69+
if (count === BATCH_MAX) {
70+
await batch.commit();
71+
batch = firestoreService.batch();
72+
count = 0;
73+
}
74+
}
75+
if (count > 0) {
76+
await batch.commit();
77+
batch = firestoreService.batch();
78+
count = 0;
79+
}
80+
// Schemas
81+
const schemasSnapshot = await findSchemas(spaceId).get();
82+
logger.info(`[Space::onDelete] Schemas size='${schemasSnapshot.docs.length}'`);
83+
for (const item of schemasSnapshot.docs) {
84+
batch.delete(item.ref);
85+
count++;
86+
if (count === BATCH_MAX) {
87+
await batch.commit();
88+
batch = firestoreService.batch();
89+
count = 0;
90+
}
91+
}
92+
if (count > 0) {
93+
await batch.commit();
94+
batch = firestoreService.batch();
95+
count = 0;
96+
}
97+
// Tasks
98+
const tasksSnapshot = await findTasks(spaceId).get();
99+
logger.info(`[Space::onDelete] Tasks size='${tasksSnapshot.docs.length}'`);
100+
for (const item of tasksSnapshot.docs) {
101+
batch.delete(item.ref);
102+
count++;
103+
if (count === BATCH_MAX) {
104+
await batch.commit();
105+
batch = firestoreService.batch();
106+
count = 0;
107+
}
108+
}
109+
if (count > 0) {
110+
await batch.commit();
111+
batch = firestoreService.batch();
112+
count = 0;
113+
}
114+
// Tokens
115+
const tokensSnapshot = await findTokens(spaceId).get();
116+
logger.info(`[Space::onDelete] Tokens size='${tokensSnapshot.docs.length}'`);
117+
for (const item of tokensSnapshot.docs) {
118+
batch.delete(item.ref);
119+
count++;
120+
if (count === BATCH_MAX) {
121+
await batch.commit();
122+
batch = firestoreService.batch();
123+
count = 0;
124+
}
125+
}
126+
if (count > 0) {
127+
await batch.commit();
128+
batch = firestoreService.batch();
129+
count = 0;
130+
}
131+
// Translations
132+
const translationsSnapshot = await findTranslations(spaceId).get();
133+
logger.info(`[Space::onDelete] Translations size='${translationsSnapshot.docs.length}'`);
134+
for (const item of translationsSnapshot.docs) {
135+
batch.delete(item.ref);
136+
count++;
137+
if (count === BATCH_MAX) {
138+
await batch.commit();
139+
batch = firestoreService.batch();
140+
count = 0;
141+
}
142+
}
143+
if (count > 0) {
144+
await batch.commit();
145+
batch = firestoreService.batch();
146+
count = 0;
147+
}
148+
// Translations History
149+
const translationsHistorySnapshot = await findTranslationsHistory(spaceId).get();
150+
logger.info(`[Space::onDelete] Translations History size='${translationsHistorySnapshot.docs.length}'`);
151+
for (const item of translationsHistorySnapshot.docs) {
152+
batch.delete(item.ref);
153+
count++;
154+
if (count === BATCH_MAX) {
155+
await batch.commit();
156+
batch = firestoreService.batch();
157+
count = 0;
158+
}
159+
}
160+
if (count > 0) {
161+
await batch.commit();
162+
}
163+
return;
26164
});
27165

28166
const calculateOverview = onCall<SpaceOverviewData>(async request => {

functions/src/translations.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ const onWriteToHistory = onDocumentWritten('spaces/{spaceId}/translations/{trans
149149
key: beforeData.name,
150150
createdAt: FieldValue.serverTimestamp(),
151151
};
152+
const spaceSnapshot = await findSpaceById(spaceId).get();
153+
// Skip History in case Space is deleted
154+
if (!spaceSnapshot.exists) {
155+
return;
156+
}
152157
} else if (afterData) {
153158
// create
154159
addHistory = {

functions/src/users.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,22 +134,22 @@ const onUserUpdate = onDocumentUpdated('users/{userId}', async event => {
134134
logger.info(
135135
`[User::onUpdate::PermissionsChange] eventId='${event.id}' id='${userId}' from='${permissionsBefore}' to='${permissionsAfter}'`
136136
);
137-
const userRecord = await authService.getUser(userId);
137+
const { customClaims } = await authService.getUser(userId);
138138
// check if role update already in Auth
139-
if (userRecord.customClaims?.['role'] !== roleAfter || userRecord.customClaims?.['permissions'] !== permissionsAfter) {
139+
if (customClaims?.['role'] !== roleAfter || customClaims?.['permissions'] !== permissionsAfter) {
140140
logger.debug(
141-
`[User::onUpdate::RoleChange] eventId='${event.id}' id='${userId}' auth='${userRecord.customClaims?.['role']}' db='${roleAfter}', auth update required.`
141+
`[User::onUpdate::RoleChange] eventId='${event.id}' id='${userId}' auth='${customClaims?.['role']}' db='${roleAfter}', auth update required.`
142142
);
143143
logger.debug(
144-
`[User::onUpdate::PermissionsChange] eventId='${event.id}' id='${userId}' auth='${userRecord.customClaims?.['permissions']}' db='${permissionsAfter}', auth update required.`
144+
`[User::onUpdate::PermissionsChange] eventId='${event.id}' id='${userId}' auth='${customClaims?.['permissions']}' db='${permissionsAfter}', auth update required.`
145145
);
146146
return await authService.setCustomUserClaims(userId, { role: roleAfter, permissions: permissionsAfter });
147147
} else {
148148
logger.debug(
149-
`[User::onUpdate::RoleChange] eventId='${event.id}' id='${userId}' auth='${userRecord.customClaims?.['role']}' db='${roleAfter}', auth update not required.`
149+
`[User::onUpdate::RoleChange] eventId='${event.id}' id='${userId}' auth='${customClaims?.['role']}' db='${roleAfter}', auth update not required.`
150150
);
151151
logger.debug(
152-
`[User::onUpdate::PermissionsChange] eventId='${event.id}' id='${userId}' auth='${userRecord.customClaims?.['permissions']}' db='${permissionsAfter}', auth update not required.`
152+
`[User::onUpdate::PermissionsChange] eventId='${event.id}' id='${userId}' auth='${customClaims?.['permissions']}' db='${permissionsAfter}', auth update not required.`
153153
);
154154
}
155155
return true;

0 commit comments

Comments
 (0)