musora-content-services 1.2.5 → 1.3.1

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.
@@ -1,159 +1,177 @@
1
- import {fetchUserPermissions} from "./services/userPermissions.js";
2
- import {
3
- songAccessMembership
4
- } from "./contentTypeConfig.js";
1
+ import { fetchUserPermissions } from './services/userPermissions.js'
2
+ import { songAccessMembership } from './contentTypeConfig.js'
5
3
 
6
4
  export class FilterBuilder {
7
-
8
- STATUS_SCHEDULED = 'scheduled';
9
- STATUS_PUBLISHED = 'published';
10
- STATUS_DRAFT = 'draft';
11
- STATUS_ARCHIVED = 'archived';
12
- STATUS_UNLISTED = 'unlisted';
13
-
14
- constructor(
15
- filter = '',
16
- {
17
- availableContentStatuses = [],
18
- bypassPermissions = false,
19
- pullFutureContent = false,
20
- getFutureContentOnly = false,
21
- getFutureScheduledContentsOnly = false,
22
- bypassStatuses = false,
23
- bypassPublishedDateRestriction = false,
24
- isSingle = false,
25
- allowsPullSongsContent = true,
26
- isChildrenFilter = false
27
- } = {}) {
28
- this.availableContentStatuses = availableContentStatuses;
29
- this.bypassPermissions = bypassPermissions;
30
- this.bypassStatuses = bypassStatuses;
31
- this.bypassPublishedDateRestriction = bypassPublishedDateRestriction;
32
- this.pullFutureContent = pullFutureContent;
33
- this.getFutureContentOnly = getFutureContentOnly;
34
- this.getFutureScheduledContentsOnly = getFutureScheduledContentsOnly;
35
- this.isSingle = isSingle;
36
- this.allowsPullSongsContent = allowsPullSongsContent;
37
- this.filter = filter;
38
- // this.debug = process.env.DEBUG === 'true' || false;
39
- this.debug = false;
40
- this.prefix = isChildrenFilter ? '@->' : '';
41
- }
42
-
43
-
44
- static withOnlyFilterAvailableStatuses(filter, availableContentStatuses, bypassPermissions) {
45
- return new FilterBuilder(filter, {
46
- availableContentStatuses,
47
- bypassPermissions,
48
- });
49
- }
50
-
51
- async buildFilter() {
52
- this.userData = await fetchUserPermissions();
53
- if (this.debug) console.log('baseFilter', this.filter);
54
- const filter = this
55
- ._applyContentStatuses()
56
- ._applyPermissions()
57
- ._applyPublishingDateRestrictions()
58
- ._trimAmpersands() // just in case
59
- .filter;
60
- if (this.debug) console.log('finalFilter', filter);
61
- return filter;
62
- }
63
-
64
- _applyContentStatuses() {
65
- // This must be run before _applyPublishDateRestrictions()
66
- if(this.bypassStatuses) return this;
67
- if (this.availableContentStatuses.length === 0) {
68
- if (this.userData.isAdmin) {
69
- this.availableContentStatuses = [this.STATUS_DRAFT, this.STATUS_SCHEDULED, this.STATUS_PUBLISHED, this.STATUS_ARCHIVED, this.STATUS_UNLISTED];
70
- this.getFutureScheduledContentsOnly = true;
71
- } else if(this.isSingle){
72
- this.availableContentStatuses = [this.STATUS_SCHEDULED, this.STATUS_PUBLISHED, this.STATUS_UNLISTED, this.STATUS_ARCHIVED];
73
- } else{
74
- this.availableContentStatuses = [this.STATUS_SCHEDULED, this.STATUS_PUBLISHED];
75
- this.getFutureScheduledContentsOnly = true;
76
- }
77
- }
78
-
79
- // I'm not sure if I'm 100% on this logic, but this is my intepretation of the ContentRepository logic
80
- if (this.getFutureScheduledContentsOnly && this.availableContentStatuses.includes(this.STATUS_SCHEDULED)) {
81
- // we must pull in future content here, otherwise we'll restrict on content this is published in the past and remove any scheduled content
82
- this.pullFutureContent = true;
83
- const now = new Date().toISOString();
84
- let statuses = [...this.availableContentStatuses];
85
- statuses.splice(statuses.indexOf(this.STATUS_SCHEDULED), 1);
86
- this._andWhere(`(${this.prefix}status in ${arrayToStringRepresentation(statuses)} || (${this.prefix}status == '${this.STATUS_SCHEDULED}' && defined(${this.prefix}published_on) && ${this.prefix}published_on >= '${now}'))`)
87
-
88
- } else {
89
- this._andWhere(`${this.prefix}status in ${arrayToStringRepresentation(this.availableContentStatuses)}`);
90
- }
91
- return this;
92
- }
93
-
94
- _applyPermissions() {
95
- if (this.bypassPermissions || this.userData.isAdmin) return this;
96
- let requiredPermissions = this._getUserPermissions();
97
- if(this.userData.isABasicMember && this.allowsPullSongsContent){
98
- requiredPermissions = [...requiredPermissions, songAccessMembership];
99
- }
100
- this._andWhere(`(!defined(permission) || references(*[_type == 'permission' && railcontent_id in ${arrayToRawRepresentation(requiredPermissions)}]._id))`);
101
- return this;
5
+ STATUS_SCHEDULED = 'scheduled'
6
+ STATUS_PUBLISHED = 'published'
7
+ STATUS_DRAFT = 'draft'
8
+ STATUS_ARCHIVED = 'archived'
9
+ STATUS_UNLISTED = 'unlisted'
10
+
11
+ constructor(
12
+ filter = '',
13
+ {
14
+ availableContentStatuses = [],
15
+ bypassPermissions = false,
16
+ pullFutureContent = false,
17
+ getFutureContentOnly = false,
18
+ getFutureScheduledContentsOnly = false,
19
+ bypassStatuses = false,
20
+ bypassPublishedDateRestriction = false,
21
+ isSingle = false,
22
+ allowsPullSongsContent = true,
23
+ isChildrenFilter = false,
24
+ } = {}
25
+ ) {
26
+ this.availableContentStatuses = availableContentStatuses
27
+ this.bypassPermissions = bypassPermissions
28
+ this.bypassStatuses = bypassStatuses
29
+ this.bypassPublishedDateRestriction = bypassPublishedDateRestriction
30
+ this.pullFutureContent = pullFutureContent
31
+ this.getFutureContentOnly = getFutureContentOnly
32
+ this.getFutureScheduledContentsOnly = getFutureScheduledContentsOnly
33
+ this.isSingle = isSingle
34
+ this.allowsPullSongsContent = allowsPullSongsContent
35
+ this.filter = filter
36
+ // this.debug = process.env.DEBUG === 'true' || false;
37
+ this.debug = false
38
+ this.prefix = isChildrenFilter ? '@->' : ''
39
+ }
40
+
41
+ static withOnlyFilterAvailableStatuses(filter, availableContentStatuses, bypassPermissions) {
42
+ return new FilterBuilder(filter, {
43
+ availableContentStatuses,
44
+ bypassPermissions,
45
+ })
46
+ }
47
+
48
+ async buildFilter() {
49
+ this.userData = await fetchUserPermissions()
50
+ if (this.debug) console.log('baseFilter', this.filter)
51
+ const filter = this._applyContentStatuses()
52
+ ._applyPermissions()
53
+ ._applyPublishingDateRestrictions()
54
+ ._trimAmpersands().filter // just in case
55
+ if (this.debug) console.log('finalFilter', filter)
56
+ return filter
57
+ }
58
+
59
+ _applyContentStatuses() {
60
+ // This must be run before _applyPublishDateRestrictions()
61
+ if (this.bypassStatuses) return this
62
+ if (this.availableContentStatuses.length === 0) {
63
+ if (this.userData.isAdmin) {
64
+ this.availableContentStatuses = [
65
+ this.STATUS_DRAFT,
66
+ this.STATUS_SCHEDULED,
67
+ this.STATUS_PUBLISHED,
68
+ this.STATUS_ARCHIVED,
69
+ this.STATUS_UNLISTED,
70
+ ]
71
+ this.getFutureScheduledContentsOnly = true
72
+ } else if (this.isSingle) {
73
+ this.availableContentStatuses = [
74
+ this.STATUS_SCHEDULED,
75
+ this.STATUS_PUBLISHED,
76
+ this.STATUS_UNLISTED,
77
+ this.STATUS_ARCHIVED,
78
+ ]
79
+ } else {
80
+ this.availableContentStatuses = [this.STATUS_SCHEDULED, this.STATUS_PUBLISHED]
81
+ this.getFutureScheduledContentsOnly = true
82
+ }
102
83
  }
103
84
 
104
- _getUserPermissions() {
105
- return this.userData.permissions;
85
+ // I'm not sure if I'm 100% on this logic, but this is my intepretation of the ContentRepository logic
86
+ if (
87
+ this.getFutureScheduledContentsOnly &&
88
+ this.availableContentStatuses.includes(this.STATUS_SCHEDULED)
89
+ ) {
90
+ // we must pull in future content here, otherwise we'll restrict on content this is published in the past and remove any scheduled content
91
+ this.pullFutureContent = true
92
+ const now = new Date().toISOString()
93
+ let statuses = [...this.availableContentStatuses]
94
+ statuses.splice(statuses.indexOf(this.STATUS_SCHEDULED), 1)
95
+ this._andWhere(
96
+ `(${this.prefix}status in ${arrayToStringRepresentation(statuses)} || (${this.prefix}status == '${this.STATUS_SCHEDULED}' && defined(${this.prefix}published_on) && ${this.prefix}published_on >= '${now}'))`
97
+ )
98
+ } else {
99
+ this._andWhere(
100
+ `${this.prefix}status in ${arrayToStringRepresentation(this.availableContentStatuses)}`
101
+ )
106
102
  }
107
-
108
- _applyPublishingDateRestrictions() {
109
- if(this.bypassPublishedDateRestriction) return this;
110
- let now = new Date();
111
-
112
- // We need to set the published on filter date to be a round time so that it doesn't bypass the query cache
113
- // with every request by changing the filter date every second. I've set it to one minute past the current hour
114
- // because publishing usually publishes content on the hour exactly which means it should still skip the cache
115
- // when the new content is available.
116
- // Round to the start of the current hour
117
- const roundedDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours());
118
-
119
- now = roundedDate.toISOString();
120
-
121
- if (this.getFutureContentOnly) {
122
- this._andWhere(`${this.prefix}published_on >= '${now}'`);
123
- } else if (!this.pullFutureContent) {
124
- this._andWhere(`${this.prefix}published_on <= '${now}'`);
125
- } else {
126
- // const date = new Date();
127
- // const theFuture = new Date(date.setMonth(date.getMonth() + 18));
128
- // this._andWhere(`published_on <= '${theFuture}'`);
129
- }
130
- return this;
103
+ return this
104
+ }
105
+
106
+ _applyPermissions() {
107
+ if (this.bypassPermissions || this.userData.isAdmin) return this
108
+ let requiredPermissions = this._getUserPermissions()
109
+ if (this.userData.isABasicMember && this.allowsPullSongsContent) {
110
+ requiredPermissions = [...requiredPermissions, songAccessMembership]
131
111
  }
132
-
133
- _andWhere(query) {
134
- const leadingAmpersand = this.filter ? ' && ' : '';
135
- this.filter += leadingAmpersand + query;
136
- }
137
-
138
- _orWhere(query) {
139
- if (!this.filter) throw new Error("invalid query, _orWhere needs to be called on an existing query");
140
- this.filter += ` || (${query})`;
112
+ this._andWhere(
113
+ `(!defined(permission) || references(*[_type == 'permission' && railcontent_id in ${arrayToRawRepresentation(requiredPermissions)}]._id))`
114
+ )
115
+ return this
116
+ }
117
+
118
+ _getUserPermissions() {
119
+ return this.userData.permissions
120
+ }
121
+
122
+ _applyPublishingDateRestrictions() {
123
+ if (this.bypassPublishedDateRestriction) return this
124
+ let now = new Date()
125
+
126
+ // We need to set the published on filter date to be a round time so that it doesn't bypass the query cache
127
+ // with every request by changing the filter date every second. I've set it to one minute past the current hour
128
+ // because publishing usually publishes content on the hour exactly which means it should still skip the cache
129
+ // when the new content is available.
130
+ // Round to the start of the current hour
131
+ const roundedDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours())
132
+
133
+ now = roundedDate.toISOString()
134
+
135
+ if (this.getFutureContentOnly) {
136
+ this._andWhere(`${this.prefix}published_on >= '${now}'`)
137
+ } else if (!this.pullFutureContent) {
138
+ this._andWhere(`${this.prefix}published_on <= '${now}'`)
139
+ } else {
140
+ // const date = new Date();
141
+ // const theFuture = new Date(date.setMonth(date.getMonth() + 18));
142
+ // this._andWhere(`published_on <= '${theFuture}'`);
141
143
  }
142
-
143
- _trimAmpersands() {
144
- this.filter = this.filter.trim();
145
- while (this.filter.charAt(0) === '&' || this.filter.charAt(0) === ' ') this.filter = this.filter.substring(1);
146
- while (this.filter.charAt(this.filter.length) === '&' || this.filter.charAt(this.filter.length) === ' ') this.filter = this.filter.slice(-1);
147
- return this;
148
- }
149
-
150
-
144
+ return this
145
+ }
146
+
147
+ _andWhere(query) {
148
+ const leadingAmpersand = this.filter ? ' && ' : ''
149
+ this.filter += leadingAmpersand + query
150
+ }
151
+
152
+ _orWhere(query) {
153
+ if (!this.filter)
154
+ throw new Error('invalid query, _orWhere needs to be called on an existing query')
155
+ this.filter += ` || (${query})`
156
+ }
157
+
158
+ _trimAmpersands() {
159
+ this.filter = this.filter.trim()
160
+ while (this.filter.charAt(0) === '&' || this.filter.charAt(0) === ' ')
161
+ this.filter = this.filter.substring(1)
162
+ while (
163
+ this.filter.charAt(this.filter.length) === '&' ||
164
+ this.filter.charAt(this.filter.length) === ' '
165
+ )
166
+ this.filter = this.filter.slice(-1)
167
+ return this
168
+ }
151
169
  }
152
170
 
153
171
  export function arrayToStringRepresentation(arr) {
154
- return '[' + arr.map(item => `'${item}'`).join(',') + ']';
172
+ return '[' + arr.map((item) => `'${item}'`).join(',') + ']'
155
173
  }
156
174
 
157
175
  export function arrayToRawRepresentation(arr) {
158
- return '[' + arr.map(item => `${item}`).join(',') + ']';
176
+ return '[' + arr.map((item) => `${item}`).join(',') + ']'
159
177
  }