@stamhoofd/backend 2.17.2 → 2.17.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stamhoofd/backend",
3
- "version": "2.17.2",
3
+ "version": "2.17.4",
4
4
  "main": "./dist/index.js",
5
5
  "exports": {
6
6
  ".": {
@@ -39,10 +39,10 @@
39
39
  "@stamhoofd/backend-i18n": "^2.17.0",
40
40
  "@stamhoofd/backend-middleware": "^2.17.0",
41
41
  "@stamhoofd/email": "^2.17.0",
42
- "@stamhoofd/models": "^2.17.0",
43
- "@stamhoofd/queues": "^2.17.0",
44
- "@stamhoofd/sql": "^2.17.0",
45
- "@stamhoofd/structures": "^2.17.0",
42
+ "@stamhoofd/models": "^2.17.3",
43
+ "@stamhoofd/queues": "^2.17.3",
44
+ "@stamhoofd/sql": "^2.17.3",
45
+ "@stamhoofd/structures": "^2.17.3",
46
46
  "@stamhoofd/utility": "^2.17.0",
47
47
  "archiver": "^7.0.1",
48
48
  "aws-sdk": "^2.885.0",
@@ -60,5 +60,5 @@
60
60
  "postmark": "4.0.2",
61
61
  "stripe": "^16.6.0"
62
62
  },
63
- "gitHead": "d43bd511d80deca4f467cd48fecf86a711c04565"
63
+ "gitHead": "56f46d516a4a18cffb011caf177b5de9a14607b6"
64
64
  }
@@ -10,6 +10,7 @@ import { sleep } from "@stamhoofd/utility";
10
10
  import { Context } from '../../../helpers/Context';
11
11
  import { fetchToAsyncIterator } from '../../../helpers/fetchToAsyncIterator';
12
12
  import { FileCache } from '../../../helpers/FileCache';
13
+ import { QueueHandler } from '@stamhoofd/queues';
13
14
 
14
15
  type Params = { type: string };
15
16
  type Query = undefined;
@@ -62,6 +63,15 @@ export class ExportToExcelEndpoint extends Endpoint<Params, Query, Body, Respons
62
63
  })
63
64
  }
64
65
 
66
+ if (QueueHandler.isRunning('user-export-to-excel-' + user.id)) {
67
+ throw new SimpleError({
68
+ code: "not_allowed",
69
+ message: "Export is pending",
70
+ human: 'Je hebt momenteel al een Excel export lopen. Wacht tot die klaar is voor je een nieuwe export start.',
71
+ statusCode: 403
72
+ })
73
+ }
74
+
65
75
  const loader = ExportToExcelEndpoint.loaders.get(request.params.type as ExcelExportType);
66
76
 
67
77
  if (!loader) {
@@ -137,27 +147,33 @@ export class ExportToExcelEndpoint extends Endpoint<Params, Query, Body, Respons
137
147
  }
138
148
 
139
149
  async job(loader: ExcelExporter<EncodableObject>, request: ExcelExportRequest, type: string): Promise<string> {
140
- // Estimate how long it will take.
141
- // If too long, we'll schedule it and write it to Digitalocean Spaces
142
- // Otherwise we'll just return the file directly
143
- const {file, stream} = await FileCache.getWriteStream('.xlsx');
144
-
145
- const zipWriterAdapter = new ArchiverWriterAdapter(stream);
146
- const writer = new XlsxWriter(zipWriterAdapter);
147
-
148
- // Limit to pages of 100
149
- request.filter.limit = 100;
150
-
151
- await exportToExcel({
152
- definitions: loader.sheets,
153
- writer,
154
- dataGenerator: fetchToAsyncIterator(request.filter, loader),
155
- filter: request.workbookFilter
156
- })
157
-
158
- console.log('Done writing excel file')
159
-
160
- const url = 'https://'+ STAMHOOFD.domains.api + '/v'+ Version +'/file-cache?file=' + encodeURIComponent(file) + '&name=' + encodeURIComponent(type)
161
- return url;
150
+ // Only run 1 export per user at the same time
151
+ return await QueueHandler.schedule('user-export-to-excel-' + Context.user!.id, async () => {
152
+ // Allow maximum 2 running Excel jobs at the same time for all users
153
+ return await QueueHandler.schedule('export-to-excel', async () => {
154
+ // Estimate how long it will take.
155
+ // If too long, we'll schedule it and write it to Digitalocean Spaces
156
+ // Otherwise we'll just return the file directly
157
+ const {file, stream} = await FileCache.getWriteStream('.xlsx');
158
+
159
+ const zipWriterAdapter = new ArchiverWriterAdapter(stream);
160
+ const writer = new XlsxWriter(zipWriterAdapter);
161
+
162
+ // Limit to pages of 100
163
+ request.filter.limit = STAMHOOFD.environment === 'development' ? 1 : 100; // in development, we need to check if total count matches and pagination is working correctly
164
+
165
+ await exportToExcel({
166
+ definitions: loader.sheets,
167
+ writer,
168
+ dataGenerator: fetchToAsyncIterator(request.filter, loader),
169
+ filter: request.workbookFilter
170
+ })
171
+
172
+ console.log('Done writing excel file')
173
+
174
+ const url = 'https://'+ STAMHOOFD.domains.api + '/v'+ Version +'/file-cache?file=' + encodeURIComponent(file) + '&name=' + encodeURIComponent(type)
175
+ return url;
176
+ }, 2)
177
+ });
162
178
  }
163
179
  }
@@ -9,6 +9,8 @@ import { registrationFilterCompilers } from "./registrations";
9
9
  export const memberFilterCompilers: SQLFilterDefinitions = {
10
10
  ...baseSQLFilterCompilers,
11
11
  id: createSQLColumnFilterCompiler('id'),
12
+ firstName: createSQLColumnFilterCompiler('firstName'),
13
+ lastName: createSQLColumnFilterCompiler('lastName'),
12
14
  name: createSQLExpressionFilterCompiler(
13
15
  new SQLConcat(
14
16
  SQL.column('firstName'),
@@ -3,6 +3,14 @@ import { SQL, SQLOrderBy, SQLOrderByDirection, SQLSortDefinitions } from "@stamh
3
3
  import { Formatter } from "@stamhoofd/utility"
4
4
 
5
5
  export const memberSorters: SQLSortDefinitions<MemberWithRegistrations> = {
6
+ // WARNING! TEST NEW SORTERS THOROUGHLY!
7
+ // Try to avoid creating sorters on fields that er not 1:1 with the database, that often causes pagination issues if not thought through
8
+ // An example: sorting on 'name' is not a good idea, because it is a concatenation of two fields.
9
+ // You might be tempted to use ORDER BY firstName, lastName, but that will not work as expected and it needs to be ORDER BY CONCAT(firstName, ' ', lastName)
10
+ // Why? Because ORDER BY firstName, lastName produces a different order dan ORDER BY CONCAT(firstName, ' ', lastName) if there are multiple people with spaces in the first name
11
+ // And that again causes issues with pagination because the next query will append a filter of name > 'John Doe' - causing duplicate and/or skipped results
12
+ // What if you need mapping? simply map the sorters in the frontend: name -> firstname, lastname, age -> birthDay, etc.
13
+
6
14
  'id': {
7
15
  getValue(a) {
8
16
  return a.id
@@ -14,21 +22,26 @@ export const memberSorters: SQLSortDefinitions<MemberWithRegistrations> = {
14
22
  })
15
23
  }
16
24
  },
17
- 'name': {
25
+ 'firstName': {
26
+ getValue(a) {
27
+ return a.firstName
28
+ },
29
+ toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
30
+ return new SQLOrderBy({
31
+ column: SQL.column('firstName'),
32
+ direction
33
+ })
34
+ }
35
+ },
36
+ 'lastName': {
18
37
  getValue(a) {
19
- return a.details.name
38
+ return a.lastName
20
39
  },
21
40
  toSQL: (direction: SQLOrderByDirection): SQLOrderBy => {
22
- return SQLOrderBy.combine([
23
- new SQLOrderBy({
24
- column: SQL.column('firstName'),
25
- direction
26
- }),
27
- new SQLOrderBy({
28
- column: SQL.column('lastName'),
29
- direction
30
- })
31
- ])
41
+ return new SQLOrderBy({
42
+ column: SQL.column('lastName'),
43
+ direction
44
+ })
32
45
  }
33
46
  },
34
47
  'birthDay': {
@@ -42,5 +55,4 @@ export const memberSorters: SQLSortDefinitions<MemberWithRegistrations> = {
42
55
  })
43
56
  }
44
57
  }
45
- // Note: never add mapped sortings, that should happen in the frontend -> e.g. map age to birthDay
46
58
  }
@@ -2,6 +2,14 @@ import { Organization } from "@stamhoofd/models"
2
2
  import { SQL, SQLOrderBy, SQLOrderByDirection, SQLSortDefinitions } from "@stamhoofd/sql"
3
3
 
4
4
  export const organizationSorters: SQLSortDefinitions<Organization> = {
5
+ // WARNING! TEST NEW SORTERS THOROUGHLY!
6
+ // Try to avoid creating sorters on fields that er not 1:1 with the database, that often causes pagination issues if not thought through
7
+ // An example: sorting on 'name' is not a good idea, because it is a concatenation of two fields.
8
+ // You might be tempted to use ORDER BY firstName, lastName, but that will not work as expected and it needs to be ORDER BY CONCAT(firstName, ' ', lastName)
9
+ // Why? Because ORDER BY firstName, lastName produces a different order dan ORDER BY CONCAT(firstName, ' ', lastName) if there are multiple people with spaces in the first name
10
+ // And that again causes issues with pagination because the next query will append a filter of name > 'John Doe' - causing duplicate and/or skipped results
11
+ // What if you need mapping? simply map the sorters in the frontend: name -> firstname, lastname, age -> birthDay, etc.
12
+
5
13
  'id': {
6
14
  getValue(a) {
7
15
  return a.id
@@ -3,6 +3,14 @@ import { SQL, SQLOrderBy, SQLOrderByDirection, SQLSortDefinitions } from "@stamh
3
3
  import { Formatter } from "@stamhoofd/utility"
4
4
 
5
5
  export const paymentSorters: SQLSortDefinitions<Payment> = {
6
+ // WARNING! TEST NEW SORTERS THOROUGHLY!
7
+ // Try to avoid creating sorters on fields that er not 1:1 with the database, that often causes pagination issues if not thought through
8
+ // An example: sorting on 'name' is not a good idea, because it is a concatenation of two fields.
9
+ // You might be tempted to use ORDER BY firstName, lastName, but that will not work as expected and it needs to be ORDER BY CONCAT(firstName, ' ', lastName)
10
+ // Why? Because ORDER BY firstName, lastName produces a different order dan ORDER BY CONCAT(firstName, ' ', lastName) if there are multiple people with spaces in the first name
11
+ // And that again causes issues with pagination because the next query will append a filter of name > 'John Doe' - causing duplicate and/or skipped results
12
+ // What if you need mapping? simply map the sorters in the frontend: name -> firstname, lastname, age -> birthDay, etc.
13
+
6
14
  'id': {
7
15
  getValue(a) {
8
16
  return a.id