@symbo.ls/sdk 3.2.3 → 3.2.7
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/README.md +141 -0
- package/dist/cjs/config/environment.js +94 -10
- package/dist/cjs/index.js +152 -12
- package/dist/cjs/services/AdminService.js +351 -0
- package/dist/cjs/services/AuthService.js +738 -305
- package/dist/cjs/services/BaseService.js +158 -6
- package/dist/cjs/services/BranchService.js +484 -0
- package/dist/cjs/services/CollabService.js +439 -116
- package/dist/cjs/services/DnsService.js +340 -0
- package/dist/cjs/services/FeatureFlagService.js +175 -0
- package/dist/cjs/services/FileService.js +201 -0
- package/dist/cjs/services/IntegrationService.js +538 -0
- package/dist/cjs/services/MetricsService.js +62 -0
- package/dist/cjs/services/PaymentService.js +271 -0
- package/dist/cjs/services/PlanService.js +426 -0
- package/dist/cjs/services/ProjectService.js +1207 -0
- package/dist/cjs/services/PullRequestService.js +503 -0
- package/dist/cjs/services/ScreenshotService.js +304 -0
- package/dist/cjs/services/SubscriptionService.js +396 -0
- package/dist/cjs/services/TrackingService.js +661 -0
- package/dist/cjs/services/WaitlistService.js +148 -0
- package/dist/cjs/services/index.js +60 -4
- package/dist/cjs/state/RootStateManager.js +2 -23
- package/dist/cjs/state/rootEventBus.js +9 -0
- package/dist/cjs/utils/CollabClient.js +78 -12
- package/dist/cjs/utils/TokenManager.js +16 -3
- package/dist/cjs/utils/changePreprocessor.js +199 -0
- package/dist/cjs/utils/jsonDiff.js +46 -4
- package/dist/cjs/utils/ordering.js +309 -0
- package/dist/cjs/utils/services.js +285 -128
- package/dist/cjs/utils/validation.js +0 -3
- package/dist/esm/config/environment.js +94 -10
- package/dist/esm/index.js +47862 -18248
- package/dist/esm/services/AdminService.js +1132 -0
- package/dist/esm/services/AuthService.js +1493 -386
- package/dist/esm/services/BaseService.js +757 -6
- package/dist/esm/services/BranchService.js +1265 -0
- package/dist/esm/services/CollabService.js +24956 -16089
- package/dist/esm/services/DnsService.js +1121 -0
- package/dist/esm/services/FeatureFlagService.js +956 -0
- package/dist/esm/services/FileService.js +982 -0
- package/dist/esm/services/IntegrationService.js +1319 -0
- package/dist/esm/services/MetricsService.js +843 -0
- package/dist/esm/services/PaymentService.js +1052 -0
- package/dist/esm/services/PlanService.js +1207 -0
- package/dist/esm/services/ProjectService.js +2526 -0
- package/dist/esm/services/PullRequestService.js +1284 -0
- package/dist/esm/services/ScreenshotService.js +1085 -0
- package/dist/esm/services/SubscriptionService.js +1177 -0
- package/dist/esm/services/TrackingService.js +18454 -0
- package/dist/esm/services/WaitlistService.js +929 -0
- package/dist/esm/services/index.js +47373 -18027
- package/dist/esm/state/RootStateManager.js +11 -23
- package/dist/esm/state/rootEventBus.js +9 -0
- package/dist/esm/utils/CollabClient.js +17526 -16120
- package/dist/esm/utils/TokenManager.js +16 -3
- package/dist/esm/utils/changePreprocessor.js +542 -0
- package/dist/esm/utils/jsonDiff.js +958 -43
- package/dist/esm/utils/ordering.js +291 -0
- package/dist/esm/utils/services.js +285 -128
- package/dist/esm/utils/validation.js +116 -50
- package/dist/node/config/environment.js +94 -10
- package/dist/node/index.js +183 -16
- package/dist/node/services/AdminService.js +332 -0
- package/dist/node/services/AuthService.js +742 -310
- package/dist/node/services/BaseService.js +148 -6
- package/dist/node/services/BranchService.js +465 -0
- package/dist/node/services/CollabService.js +439 -116
- package/dist/node/services/DnsService.js +321 -0
- package/dist/node/services/FeatureFlagService.js +156 -0
- package/dist/node/services/FileService.js +182 -0
- package/dist/node/services/IntegrationService.js +519 -0
- package/dist/node/services/MetricsService.js +43 -0
- package/dist/node/services/PaymentService.js +252 -0
- package/dist/node/services/PlanService.js +407 -0
- package/dist/node/services/ProjectService.js +1188 -0
- package/dist/node/services/PullRequestService.js +484 -0
- package/dist/node/services/ScreenshotService.js +285 -0
- package/dist/node/services/SubscriptionService.js +377 -0
- package/dist/node/services/TrackingService.js +632 -0
- package/dist/node/services/WaitlistService.js +129 -0
- package/dist/node/services/index.js +60 -4
- package/dist/node/state/RootStateManager.js +2 -23
- package/dist/node/state/rootEventBus.js +9 -0
- package/dist/node/utils/CollabClient.js +77 -11
- package/dist/node/utils/TokenManager.js +16 -3
- package/dist/node/utils/changePreprocessor.js +180 -0
- package/dist/node/utils/jsonDiff.js +46 -4
- package/dist/node/utils/ordering.js +290 -0
- package/dist/node/utils/services.js +285 -128
- package/dist/node/utils/validation.js +0 -3
- package/package.json +30 -18
- package/src/config/environment.js +95 -10
- package/src/index.js +190 -23
- package/src/services/AdminService.js +374 -0
- package/src/services/AuthService.js +874 -328
- package/src/services/BaseService.js +166 -6
- package/src/services/BranchService.js +536 -0
- package/src/services/CollabService.js +557 -148
- package/src/services/DnsService.js +366 -0
- package/src/services/FeatureFlagService.js +174 -0
- package/src/services/FileService.js +213 -0
- package/src/services/IntegrationService.js +548 -0
- package/src/services/MetricsService.js +40 -0
- package/src/services/PaymentService.js +287 -0
- package/src/services/PlanService.js +468 -0
- package/src/services/ProjectService.js +1366 -0
- package/src/services/PullRequestService.js +537 -0
- package/src/services/ScreenshotService.js +258 -0
- package/src/services/SubscriptionService.js +425 -0
- package/src/services/TrackingService.js +853 -0
- package/src/services/WaitlistService.js +130 -0
- package/src/services/index.js +79 -5
- package/src/services/tests/BranchService/createBranch.test.js +153 -0
- package/src/services/tests/BranchService/deleteBranch.test.js +173 -0
- package/src/services/tests/BranchService/getBranchChanges.test.js +146 -0
- package/src/services/tests/BranchService/listBranches.test.js +87 -0
- package/src/services/tests/BranchService/mergeBranch.test.js +210 -0
- package/src/services/tests/BranchService/publishVersion.test.js +183 -0
- package/src/services/tests/BranchService/renameBranch.test.js +240 -0
- package/src/services/tests/BranchService/resetBranch.test.js +152 -0
- package/src/services/tests/FeatureFlagService/adminFeatureFlags.test.js +67 -0
- package/src/services/tests/FeatureFlagService/getFeatureFlags.test.js +75 -0
- package/src/services/tests/FileService/createFileFormData.test.js +74 -0
- package/src/services/tests/FileService/getFileUrl.test.js +69 -0
- package/src/services/tests/FileService/updateProjectIcon.test.js +109 -0
- package/src/services/tests/FileService/uploadDocument.test.js +36 -0
- package/src/services/tests/FileService/uploadFile.test.js +78 -0
- package/src/services/tests/FileService/uploadFileWithValidation.test.js +114 -0
- package/src/services/tests/FileService/uploadImage.test.js +36 -0
- package/src/services/tests/FileService/uploadMultipleFiles.test.js +111 -0
- package/src/services/tests/FileService/validateFile.test.js +63 -0
- package/src/services/tests/PlanService/createPlan.test.js +104 -0
- package/src/services/tests/PlanService/createPlanWithValidation.test.js +523 -0
- package/src/services/tests/PlanService/deletePlan.test.js +92 -0
- package/src/services/tests/PlanService/getActivePlans.test.js +123 -0
- package/src/services/tests/PlanService/getAdminPlans.test.js +84 -0
- package/src/services/tests/PlanService/getPlan.test.js +50 -0
- package/src/services/tests/PlanService/getPlanByKey.test.js +109 -0
- package/src/services/tests/PlanService/getPlanWithValidation.test.js +85 -0
- package/src/services/tests/PlanService/getPlans.test.js +53 -0
- package/src/services/tests/PlanService/getPlansByPriceRange.test.js +109 -0
- package/src/services/tests/PlanService/getPlansWithValidation.test.js +48 -0
- package/src/services/tests/PlanService/initializePlans.test.js +75 -0
- package/src/services/tests/PlanService/updatePlan.test.js +111 -0
- package/src/services/tests/PlanService/updatePlanWithValidation.test.js +556 -0
- package/src/state/RootStateManager.js +37 -32
- package/src/state/rootEventBus.js +19 -0
- package/src/utils/CollabClient.js +99 -12
- package/src/utils/TokenManager.js +20 -3
- package/src/utils/changePreprocessor.js +239 -0
- package/src/utils/jsonDiff.js +40 -5
- package/src/utils/ordering.js +271 -0
- package/src/utils/services.js +306 -139
- package/src/utils/validation.js +0 -3
- package/dist/cjs/services/AIService.js +0 -155
- package/dist/cjs/services/BasedService.js +0 -1185
- package/dist/cjs/services/CoreService.js +0 -2295
- package/dist/cjs/services/SocketService.js +0 -309
- package/dist/cjs/services/SymstoryService.js +0 -571
- package/dist/cjs/utils/basedQuerys.js +0 -181
- package/dist/cjs/utils/symstoryClient.js +0 -259
- package/dist/esm/services/AIService.js +0 -185
- package/dist/esm/services/BasedService.js +0 -5262
- package/dist/esm/services/CoreService.js +0 -2827
- package/dist/esm/services/SocketService.js +0 -456
- package/dist/esm/services/SymstoryService.js +0 -7025
- package/dist/esm/utils/basedQuerys.js +0 -163
- package/dist/esm/utils/symstoryClient.js +0 -354
- package/dist/node/services/AIService.js +0 -136
- package/dist/node/services/BasedService.js +0 -1156
- package/dist/node/services/CoreService.js +0 -2266
- package/dist/node/services/SocketService.js +0 -280
- package/dist/node/services/SymstoryService.js +0 -542
- package/dist/node/utils/basedQuerys.js +0 -162
- package/dist/node/utils/symstoryClient.js +0 -230
- package/src/services/AIService.js +0 -150
- package/src/services/BasedService.js +0 -1302
- package/src/services/CoreService.js +0 -2548
- package/src/services/SocketService.js +0 -336
- package/src/services/SymstoryService.js +0 -649
- package/src/utils/basedQuerys.js +0 -164
- package/src/utils/symstoryClient.js +0 -252
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
/* eslint-disable no-useless-escape */
|
|
2
|
+
/* eslint-disable no-undefined */
|
|
3
|
+
import test from 'tape'
|
|
4
|
+
import sinon from 'sinon'
|
|
5
|
+
import { PlanService } from '../../PlanService.js'
|
|
6
|
+
|
|
7
|
+
// #region Setup
|
|
8
|
+
const sandbox = sinon.createSandbox()
|
|
9
|
+
// #endregion
|
|
10
|
+
|
|
11
|
+
// #region Tests
|
|
12
|
+
test('updatePlanWithValidation should return response data', async t => {
|
|
13
|
+
t.plan(1)
|
|
14
|
+
const responseStub = [sandbox.stub()]
|
|
15
|
+
const testData = {
|
|
16
|
+
planId: 'testString',
|
|
17
|
+
planData: {
|
|
18
|
+
name: 'testName',
|
|
19
|
+
key: 'testkey'
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const planServiceStub = new PlanService()
|
|
23
|
+
sandbox.stub(planServiceStub, 'updatePlan').resolves(responseStub)
|
|
24
|
+
const response = await planServiceStub.updatePlanWithValidation(
|
|
25
|
+
testData.planId,
|
|
26
|
+
testData.planData
|
|
27
|
+
)
|
|
28
|
+
t.ok(response, 'Response returned successfully')
|
|
29
|
+
|
|
30
|
+
sandbox.restore()
|
|
31
|
+
t.end()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
function planIdEmptyOrNotAString () {
|
|
35
|
+
// Data test object
|
|
36
|
+
const badData = {
|
|
37
|
+
planIdUndefined: {
|
|
38
|
+
name: 'planId is undefined',
|
|
39
|
+
testValue: undefined
|
|
40
|
+
},
|
|
41
|
+
planIdIsNull: {
|
|
42
|
+
name: 'planId is null',
|
|
43
|
+
testValue: null
|
|
44
|
+
},
|
|
45
|
+
planIdIsAnObject: {
|
|
46
|
+
name: 'planId is an object',
|
|
47
|
+
testValue: {}
|
|
48
|
+
},
|
|
49
|
+
planIdIsANumber: {
|
|
50
|
+
name: 'planId is number',
|
|
51
|
+
testValue: 123
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
Object.keys(badData).forEach(key => {
|
|
56
|
+
test(`updatePlanWithValidation should return an error - ${badData[key].name}`, async t => {
|
|
57
|
+
t.plan(1)
|
|
58
|
+
const planData = {
|
|
59
|
+
name: 'testName',
|
|
60
|
+
key: 'testkey'
|
|
61
|
+
}
|
|
62
|
+
const planId = badData[key].testValue
|
|
63
|
+
const planServiceStub = new PlanService()
|
|
64
|
+
try {
|
|
65
|
+
await planServiceStub.updatePlanWithValidation(planId, planData)
|
|
66
|
+
t.fail('updatePlanWithValidation should have failed')
|
|
67
|
+
} catch (err) {
|
|
68
|
+
t.equal(
|
|
69
|
+
err.toString(),
|
|
70
|
+
'Error: Plan ID must be a valid string',
|
|
71
|
+
`${badData[key].name} successfully caused an error`
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
sandbox.restore()
|
|
75
|
+
t.end()
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function planDataEmptyOrNotAnObject () {
|
|
81
|
+
// Data test object
|
|
82
|
+
const badData = {
|
|
83
|
+
planDataUndefined: {
|
|
84
|
+
name: 'planData is undefined',
|
|
85
|
+
testValue: undefined
|
|
86
|
+
},
|
|
87
|
+
planDataIsNull: {
|
|
88
|
+
name: 'planData is null',
|
|
89
|
+
testValue: null
|
|
90
|
+
},
|
|
91
|
+
planDataIsAString: {
|
|
92
|
+
name: 'planData is a string',
|
|
93
|
+
testValue: 'test string'
|
|
94
|
+
},
|
|
95
|
+
planDataIsANumber: {
|
|
96
|
+
name: 'planData is a string',
|
|
97
|
+
testValue: 123
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
Object.keys(badData).forEach(key => {
|
|
102
|
+
test(`updatePlanWithValidation should return an error - ${badData[key].name}`, async t => {
|
|
103
|
+
t.plan(1)
|
|
104
|
+
const planId = 'testString'
|
|
105
|
+
const planData = badData[key].testValue
|
|
106
|
+
const planServiceStub = new PlanService()
|
|
107
|
+
try {
|
|
108
|
+
await planServiceStub.updatePlanWithValidation(planId, planData)
|
|
109
|
+
t.fail('updatePlanWithValidation should have failed')
|
|
110
|
+
} catch (err) {
|
|
111
|
+
t.equal(
|
|
112
|
+
err.toString(),
|
|
113
|
+
'Error: Plan data must be a valid object',
|
|
114
|
+
`${badData[key].name} successfully caused an error`
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
sandbox.restore()
|
|
118
|
+
t.end()
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
test('Price validation should throw an error when price field is present', async t => {
|
|
124
|
+
t.plan(1)
|
|
125
|
+
const planId = 'testString'
|
|
126
|
+
const planData = {
|
|
127
|
+
name: 'testName',
|
|
128
|
+
description: 'test description',
|
|
129
|
+
price: 1.0
|
|
130
|
+
}
|
|
131
|
+
const planServiceStub = new PlanService()
|
|
132
|
+
try {
|
|
133
|
+
await planServiceStub.updatePlanWithValidation(planId, planData)
|
|
134
|
+
t.fail('Price validation successfully threw an error')
|
|
135
|
+
} catch (err) {
|
|
136
|
+
t.equal(
|
|
137
|
+
err.toString(),
|
|
138
|
+
'Error: Field "price" is no longer supported. Use unified "pricingOptions" with "amount" instead.',
|
|
139
|
+
'Price validation detected unsupported price field'
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
sandbox.restore()
|
|
143
|
+
t.end()
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
function pricingOptionsValidation () {
|
|
147
|
+
// Data test object
|
|
148
|
+
const planData = {
|
|
149
|
+
name: 'testName',
|
|
150
|
+
description: 'test description'
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const badPricingOptions = {
|
|
154
|
+
stringValue: 'test string',
|
|
155
|
+
numberValue: 123,
|
|
156
|
+
falseBooleanValue: false,
|
|
157
|
+
trueBooleanValue: true,
|
|
158
|
+
emptyArray: []
|
|
159
|
+
}
|
|
160
|
+
Object.keys(badPricingOptions).forEach(field => {
|
|
161
|
+
test(`pricingOptions validation throws error for ${field}`, async t => {
|
|
162
|
+
t.plan(1)
|
|
163
|
+
const planId = 'testString'
|
|
164
|
+
const { ...testData } = planData
|
|
165
|
+
testData.pricingOptions = badPricingOptions[field]
|
|
166
|
+
const planServiceStub = new PlanService()
|
|
167
|
+
try {
|
|
168
|
+
await planServiceStub.updatePlanWithValidation(planId, testData)
|
|
169
|
+
t.fail('updatePlanWithValidation failed - bad pricingOptions detected')
|
|
170
|
+
} catch (err) {
|
|
171
|
+
t.equal(
|
|
172
|
+
err.toString(),
|
|
173
|
+
'Error: pricingOptions must be a non-empty array when provided',
|
|
174
|
+
`pricingOptions validation failed successfully on bad pricingOptions data: ${field}`
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
sandbox.restore()
|
|
178
|
+
t.end()
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function keyTypeValidation () {
|
|
184
|
+
const badKeyData = [
|
|
185
|
+
{ name: 'array value', key: [] },
|
|
186
|
+
{ name: 'number value', key: 123 },
|
|
187
|
+
{ name: 'null value', key: null },
|
|
188
|
+
{ name: 'undefined value', key: undefined },
|
|
189
|
+
{ name: 'false boolean value', key: false }
|
|
190
|
+
]
|
|
191
|
+
for (let ii = 0; ii < badKeyData.length; ii++) {
|
|
192
|
+
test(`Key type validation should throw an error checking for: ${badKeyData[ii].name}`, async t => {
|
|
193
|
+
t.plan(1)
|
|
194
|
+
const planId = 'testString'
|
|
195
|
+
const planData = {
|
|
196
|
+
name: 'testName',
|
|
197
|
+
description: 'test description',
|
|
198
|
+
pricingOptions: []
|
|
199
|
+
}
|
|
200
|
+
planData.pricingOptions.push(badKeyData[ii])
|
|
201
|
+
const responseStub = [sandbox.stub()]
|
|
202
|
+
const planServiceStub = new PlanService()
|
|
203
|
+
sandbox.stub(planServiceStub, 'updatePlan').resolves(responseStub)
|
|
204
|
+
try {
|
|
205
|
+
await planServiceStub.updatePlanWithValidation(planId, planData)
|
|
206
|
+
t.fail('Key type validation successfully threw an error')
|
|
207
|
+
} catch (err) {
|
|
208
|
+
t.ok(
|
|
209
|
+
err
|
|
210
|
+
.toString()
|
|
211
|
+
.includes(
|
|
212
|
+
"Error: Pricing option at index 0 is missing required field 'key'"
|
|
213
|
+
),
|
|
214
|
+
`Key type validation detected bad data: ${badKeyData[ii].name}`
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
sandbox.restore()
|
|
218
|
+
t.end()
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function keyFormatValidation () {
|
|
224
|
+
const badKeyData = [
|
|
225
|
+
{ name: 'capital letters', key: 'CAPITALLETTERS' },
|
|
226
|
+
{ name: 'special character', key: 'Special @ Character' },
|
|
227
|
+
{ name: 'under score', key: 'under_score' },
|
|
228
|
+
{ name: 'punctuation', key: 'syntax!' }
|
|
229
|
+
]
|
|
230
|
+
for (let ii = 0; ii < badKeyData.length; ii++) {
|
|
231
|
+
test(`Key format validation should throw an error checking for: ${badKeyData[ii].name}`, async t => {
|
|
232
|
+
t.plan(1)
|
|
233
|
+
const planId = 'testString'
|
|
234
|
+
const planData = {
|
|
235
|
+
name: 'testName',
|
|
236
|
+
description: 'test description',
|
|
237
|
+
pricingOptions: []
|
|
238
|
+
}
|
|
239
|
+
planData.pricingOptions.push(badKeyData[ii])
|
|
240
|
+
const responseStub = [sandbox.stub()]
|
|
241
|
+
const planServiceStub = new PlanService()
|
|
242
|
+
sandbox.stub(planServiceStub, 'updatePlan').resolves(responseStub)
|
|
243
|
+
try {
|
|
244
|
+
await planServiceStub.updatePlanWithValidation(planId, planData)
|
|
245
|
+
t.fail('Key type validation successfully threw an error')
|
|
246
|
+
} catch (err) {
|
|
247
|
+
t.equal(
|
|
248
|
+
err.toString(),
|
|
249
|
+
`Error: Pricing option key '${badKeyData[ii].key}' must contain only lowercase letters, numbers, and hyphens`,
|
|
250
|
+
`Key format validation detected bad data: ${badKeyData[ii].name}`
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
sandbox.restore()
|
|
254
|
+
t.end()
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function displayNameValidation () {
|
|
260
|
+
const badDisplayNameData = [
|
|
261
|
+
{
|
|
262
|
+
name: 'false boolean value',
|
|
263
|
+
displayName: false,
|
|
264
|
+
key: 'false-boolean-value'
|
|
265
|
+
},
|
|
266
|
+
{ name: 'number value', displayName: 123, key: 'number-value' },
|
|
267
|
+
{ name: 'undefined value', displayName: undefined, key: 'undefined-value' },
|
|
268
|
+
{ name: 'null value', displayName: null, key: 'null-value' }
|
|
269
|
+
]
|
|
270
|
+
for (let ii = 0; ii < badDisplayNameData.length; ii++) {
|
|
271
|
+
test(`displayName validation should throw an error checking for: ${badDisplayNameData[ii].name}`, async t => {
|
|
272
|
+
t.plan(1)
|
|
273
|
+
const planId = 'testString'
|
|
274
|
+
const planData = {
|
|
275
|
+
name: 'testName',
|
|
276
|
+
description: 'test description',
|
|
277
|
+
pricingOptions: []
|
|
278
|
+
}
|
|
279
|
+
planData.pricingOptions.push(badDisplayNameData[ii])
|
|
280
|
+
const responseStub = [sandbox.stub()]
|
|
281
|
+
const planServiceStub = new PlanService()
|
|
282
|
+
sandbox.stub(planServiceStub, 'updatePlan').resolves(responseStub)
|
|
283
|
+
try {
|
|
284
|
+
await planServiceStub.updatePlanWithValidation(planId, planData)
|
|
285
|
+
t.fail('Key type validation successfully threw an error')
|
|
286
|
+
} catch (err) {
|
|
287
|
+
t.equal(
|
|
288
|
+
err.toString(),
|
|
289
|
+
`Error: Pricing option \'${badDisplayNameData[ii].key}\' is missing required field \'displayName\'`,
|
|
290
|
+
`displayName validation detected bad data: ${badDisplayNameData[ii].name}`
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
sandbox.restore()
|
|
294
|
+
t.end()
|
|
295
|
+
})
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function amountValidation () {
|
|
300
|
+
const badAmountData = [
|
|
301
|
+
{
|
|
302
|
+
name: 'negative amount value',
|
|
303
|
+
displayName: 'test displayname',
|
|
304
|
+
key: 'negative-amount-value',
|
|
305
|
+
amount: -1
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
name: 'letter value',
|
|
309
|
+
displayName: 'test displayname',
|
|
310
|
+
key: 'letter-value',
|
|
311
|
+
amount: 'abc123'
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
name: 'undefined value',
|
|
315
|
+
displayName: 'test displayname',
|
|
316
|
+
key: 'undefined-value',
|
|
317
|
+
amount: undefined
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
name: 'null value',
|
|
321
|
+
displayName: 'test displayname',
|
|
322
|
+
key: 'null-value',
|
|
323
|
+
amount: null
|
|
324
|
+
}
|
|
325
|
+
]
|
|
326
|
+
for (let ii = 0; ii < badAmountData.length; ii++) {
|
|
327
|
+
test(`amount validation should throw an error checking for: ${badAmountData[ii].name}`, async t => {
|
|
328
|
+
t.plan(1)
|
|
329
|
+
const planId = 'testString'
|
|
330
|
+
const planData = {
|
|
331
|
+
name: 'testName',
|
|
332
|
+
description: 'test description',
|
|
333
|
+
pricingOptions: []
|
|
334
|
+
}
|
|
335
|
+
planData.pricingOptions.push(badAmountData[ii])
|
|
336
|
+
const responseStub = [sandbox.stub()]
|
|
337
|
+
const planServiceStub = new PlanService()
|
|
338
|
+
sandbox.stub(planServiceStub, 'updatePlan').resolves(responseStub)
|
|
339
|
+
try {
|
|
340
|
+
await planServiceStub.updatePlanWithValidation(planId, planData)
|
|
341
|
+
t.fail('Key type validation successfully threw an error')
|
|
342
|
+
} catch (err) {
|
|
343
|
+
t.equal(
|
|
344
|
+
err.toString(),
|
|
345
|
+
`Error: Pricing option \'${badAmountData[ii].key}\' must have a non-negative numeric \'amount\'`,
|
|
346
|
+
`amount validation detected bad data: ${badAmountData[ii].name}`
|
|
347
|
+
)
|
|
348
|
+
}
|
|
349
|
+
sandbox.restore()
|
|
350
|
+
t.end()
|
|
351
|
+
})
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function intervalValidation () {
|
|
356
|
+
const badAmountData = [
|
|
357
|
+
{
|
|
358
|
+
name: 'number value',
|
|
359
|
+
displayName: 'test displayname',
|
|
360
|
+
key: 'number-amount-value',
|
|
361
|
+
amount: 0,
|
|
362
|
+
interval: 123
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
name: 'unsupported string value',
|
|
366
|
+
displayName: 'test displayname',
|
|
367
|
+
key: 'letter-value',
|
|
368
|
+
amount: 0,
|
|
369
|
+
interval: 'abc123'
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
name: 'undefined value',
|
|
373
|
+
displayName: 'test displayname',
|
|
374
|
+
key: 'undefined-value',
|
|
375
|
+
amount: 0,
|
|
376
|
+
interval: undefined
|
|
377
|
+
}
|
|
378
|
+
]
|
|
379
|
+
for (let ii = 0; ii < badAmountData.length; ii++) {
|
|
380
|
+
test(`interval validation should throw an error checking for: ${badAmountData[ii].name}`, async t => {
|
|
381
|
+
t.plan(1)
|
|
382
|
+
const planId = 'testString'
|
|
383
|
+
const planData = {
|
|
384
|
+
name: 'testName',
|
|
385
|
+
description: 'test description',
|
|
386
|
+
pricingOptions: []
|
|
387
|
+
}
|
|
388
|
+
planData.pricingOptions.push(badAmountData[ii])
|
|
389
|
+
const responseStub = [sandbox.stub()]
|
|
390
|
+
const planServiceStub = new PlanService()
|
|
391
|
+
sandbox.stub(planServiceStub, 'updatePlan').resolves(responseStub)
|
|
392
|
+
try {
|
|
393
|
+
await planServiceStub.updatePlanWithValidation(planId, planData)
|
|
394
|
+
t.fail('Key type validation successfully threw an error')
|
|
395
|
+
} catch (err) {
|
|
396
|
+
t.equal(
|
|
397
|
+
err.toString(),
|
|
398
|
+
`Error: Pricing option \'${badAmountData[ii].key}\' has invalid interval \'${badAmountData[ii].interval}\'. Allowed: month, year, week, day or null`,
|
|
399
|
+
`interval validation detected bad data: ${badAmountData[ii].name}`
|
|
400
|
+
)
|
|
401
|
+
}
|
|
402
|
+
sandbox.restore()
|
|
403
|
+
t.end()
|
|
404
|
+
})
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function lookupKeyValidation () {
|
|
409
|
+
const badData = [
|
|
410
|
+
{
|
|
411
|
+
name: 'number value',
|
|
412
|
+
displayName: 'test displayname',
|
|
413
|
+
key: 'number-amount-value',
|
|
414
|
+
amount: 0,
|
|
415
|
+
interval: null,
|
|
416
|
+
lookupKey: 123
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
name: 'false boolean value',
|
|
420
|
+
displayName: 'test displayname',
|
|
421
|
+
key: 'letter-value',
|
|
422
|
+
amount: 0,
|
|
423
|
+
interval: null,
|
|
424
|
+
lookupKey: false
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
name: 'true boolean value',
|
|
428
|
+
displayName: 'test displayname',
|
|
429
|
+
key: 'letter-value',
|
|
430
|
+
amount: 0,
|
|
431
|
+
interval: null,
|
|
432
|
+
lookupKey: true
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
name: 'object value',
|
|
436
|
+
displayName: 'test displayname',
|
|
437
|
+
key: 'undefined-value',
|
|
438
|
+
amount: 0,
|
|
439
|
+
interval: null,
|
|
440
|
+
lookupKey: {}
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
name: 'undefined value',
|
|
444
|
+
displayName: 'test displayname',
|
|
445
|
+
key: 'undefined-value',
|
|
446
|
+
amount: 0,
|
|
447
|
+
interval: null,
|
|
448
|
+
lookupKey: undefined
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
name: 'null value',
|
|
452
|
+
displayName: 'test displayname',
|
|
453
|
+
key: 'undefined-value',
|
|
454
|
+
amount: 0,
|
|
455
|
+
interval: null,
|
|
456
|
+
lookupKey: null
|
|
457
|
+
}
|
|
458
|
+
]
|
|
459
|
+
for (let ii = 0; ii < badData.length; ii++) {
|
|
460
|
+
test(`lookup key validation should throw an error checking for: ${badData[ii].name}`, async t => {
|
|
461
|
+
t.plan(1)
|
|
462
|
+
const planId = 'testString'
|
|
463
|
+
const planData = {
|
|
464
|
+
name: 'testName',
|
|
465
|
+
description: 'test description',
|
|
466
|
+
pricingOptions: []
|
|
467
|
+
}
|
|
468
|
+
planData.pricingOptions.push(badData[ii])
|
|
469
|
+
const responseStub = [sandbox.stub()]
|
|
470
|
+
const planServiceStub = new PlanService()
|
|
471
|
+
sandbox.stub(planServiceStub, 'updatePlan').resolves(responseStub)
|
|
472
|
+
try {
|
|
473
|
+
await planServiceStub.updatePlanWithValidation(planId, planData)
|
|
474
|
+
t.fail('Key type validation successfully threw an error')
|
|
475
|
+
} catch (err) {
|
|
476
|
+
t.equal(
|
|
477
|
+
err.toString(),
|
|
478
|
+
`Error: Pricing option \'${badData[ii].key}\' is missing required field \'lookupKey\'`,
|
|
479
|
+
`lookup key validation detected bad data: ${badData[ii].name}`
|
|
480
|
+
)
|
|
481
|
+
}
|
|
482
|
+
sandbox.restore()
|
|
483
|
+
t.end()
|
|
484
|
+
})
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function topLevelKeyValidation () {
|
|
489
|
+
const planData = [
|
|
490
|
+
{
|
|
491
|
+
name: 'Uppercase letters',
|
|
492
|
+
description: 'test description',
|
|
493
|
+
key: 'ALLCAPS'
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
name: 'Underscore character',
|
|
497
|
+
description: 'test description',
|
|
498
|
+
key: '_'
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
name: 'Special characters',
|
|
502
|
+
description: 'test description',
|
|
503
|
+
key: '!@#$%'
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
name: 'Object type',
|
|
507
|
+
description: 'test description',
|
|
508
|
+
key: {}
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
name: 'Null value',
|
|
512
|
+
description: 'test description',
|
|
513
|
+
key: null
|
|
514
|
+
}
|
|
515
|
+
]
|
|
516
|
+
for (let ii = 0; ii < planData.length; ii++) {
|
|
517
|
+
test(`top-level key validation should throw an error checking for: ${planData[ii].name}`, async t => {
|
|
518
|
+
t.plan(1)
|
|
519
|
+
const planId = 'testString'
|
|
520
|
+
const responseStub = [sandbox.stub()]
|
|
521
|
+
const planServiceStub = new PlanService()
|
|
522
|
+
sandbox.stub(planServiceStub, 'updatePlan').resolves(responseStub)
|
|
523
|
+
try {
|
|
524
|
+
await planServiceStub.updatePlanWithValidation(planId, planData[ii])
|
|
525
|
+
t.fail('Key type validation successfully threw an error')
|
|
526
|
+
} catch (err) {
|
|
527
|
+
t.equal(
|
|
528
|
+
err.toString(),
|
|
529
|
+
'Error: Plan key must contain only lowercase letters, numbers, and hyphens',
|
|
530
|
+
`displayName validation detected bad data: ${planData[ii].name} with ${err}`
|
|
531
|
+
)
|
|
532
|
+
}
|
|
533
|
+
sandbox.restore()
|
|
534
|
+
t.end()
|
|
535
|
+
})
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
amountValidation()
|
|
540
|
+
displayNameValidation()
|
|
541
|
+
intervalValidation()
|
|
542
|
+
keyFormatValidation()
|
|
543
|
+
keyTypeValidation()
|
|
544
|
+
lookupKeyValidation()
|
|
545
|
+
planIdEmptyOrNotAString()
|
|
546
|
+
planDataEmptyOrNotAnObject()
|
|
547
|
+
pricingOptionsValidation()
|
|
548
|
+
topLevelKeyValidation()
|
|
549
|
+
// #endregion
|
|
550
|
+
|
|
551
|
+
// #region Cleanup
|
|
552
|
+
test('teardown', t => {
|
|
553
|
+
sandbox.restore()
|
|
554
|
+
t.end()
|
|
555
|
+
})
|
|
556
|
+
// #endregion
|
|
@@ -4,7 +4,7 @@ import { rootBus } from './rootEventBus.js'
|
|
|
4
4
|
const { isFunction } = utils.default || utils
|
|
5
5
|
|
|
6
6
|
export class RootStateManager {
|
|
7
|
-
constructor
|
|
7
|
+
constructor(rootState) {
|
|
8
8
|
this._rootState = rootState
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -15,57 +15,62 @@ export class RootStateManager {
|
|
|
15
15
|
* @param {Array} changes – eg. ['update', ['foo'], 'bar']
|
|
16
16
|
* @param {Object} opts – forwarded to setPathCollection
|
|
17
17
|
*/
|
|
18
|
-
applyChanges
|
|
18
|
+
async applyChanges(changes = [], opts = {}) {
|
|
19
19
|
if (!this._rootState || !isFunction(this._rootState.setPathCollection)) {
|
|
20
20
|
return
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const result = this._rootState.setPathCollection(changes, {
|
|
23
|
+
const result = await this._rootState.setPathCollection(changes, {
|
|
24
24
|
preventUpdate: true,
|
|
25
25
|
...opts
|
|
26
26
|
})
|
|
27
27
|
|
|
28
28
|
// Identify library component mutations and notify UI once per batch
|
|
29
|
-
try {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
// try {
|
|
30
|
+
// const changedKeys = new Set()
|
|
31
|
+
// changes.forEach(tuple => {
|
|
32
|
+
// const [, path = []] = tuple
|
|
33
|
+
// if (!Array.isArray(path) || !path.length) {
|
|
34
|
+
// return
|
|
35
|
+
// }
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
// // Direct component value: ['components', key]
|
|
38
|
+
// if (
|
|
39
|
+
// ['components', 'pages'].includes(path[0]) &&
|
|
40
|
+
// typeof path[1] === 'string'
|
|
41
|
+
// ) {
|
|
42
|
+
// changedKeys.add(path[1])
|
|
43
|
+
// }
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
// // Component schema: ['schema', 'components', key, ...]
|
|
46
|
+
// if (
|
|
47
|
+
// path[0] === 'schema' &&
|
|
48
|
+
// ['components', 'pages'].includes(path[1]) &&
|
|
49
|
+
// typeof path[2] === 'string'
|
|
50
|
+
// ) {
|
|
51
|
+
// changedKeys.add(path[2])
|
|
52
|
+
// }
|
|
53
|
+
// })
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
} catch (err) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
55
|
+
// if (changedKeys.size && !opts.skipComponentsChangedEvent) {
|
|
56
|
+
// console.log('emit components:changed', [...changedKeys])
|
|
57
|
+
// rootBus.emit('components:changed', [...changedKeys])
|
|
58
|
+
// }
|
|
59
|
+
// } catch (err) {
|
|
60
|
+
// // Do not interrupt the main apply flow if notification fails
|
|
61
|
+
// console.error('[RootStateManager] emit components:changed failed', err)
|
|
62
|
+
// }
|
|
58
63
|
|
|
59
64
|
return result
|
|
60
65
|
}
|
|
61
66
|
|
|
62
|
-
setVersion
|
|
67
|
+
setVersion(v) {
|
|
63
68
|
if (this._rootState) {
|
|
64
69
|
this._rootState.version = v
|
|
65
70
|
}
|
|
66
71
|
}
|
|
67
72
|
|
|
68
|
-
get root
|
|
73
|
+
get root() {
|
|
69
74
|
return this._rootState
|
|
70
75
|
}
|
|
71
|
-
}
|
|
76
|
+
}
|
|
@@ -5,11 +5,27 @@ const getGlobalBus = () => {
|
|
|
5
5
|
return globalThis.__SMBLS_ROOT_BUS__
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
// Stores arrays of handlers for each event
|
|
8
9
|
const events = {}
|
|
9
10
|
|
|
11
|
+
// Stores the last payload emitted for each event so that late subscribers
|
|
12
|
+
// can receive the most recent value immediately.
|
|
13
|
+
const lastPayloads = {}
|
|
14
|
+
|
|
10
15
|
const bus = {
|
|
11
16
|
on (event, handler) {
|
|
12
17
|
(events[event] ||= []).push(handler)
|
|
18
|
+
|
|
19
|
+
// If we already have a payload for this event (it was emitted before the
|
|
20
|
+
// listener subscribed), deliver it immediately so late subscribers do
|
|
21
|
+
// not miss any state.
|
|
22
|
+
if (Object.hasOwn(lastPayloads, event)) {
|
|
23
|
+
try {
|
|
24
|
+
handler(lastPayloads[event])
|
|
25
|
+
} catch (err) {
|
|
26
|
+
console.error('[rootBus] handler error for (replay)', event, err)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
13
29
|
},
|
|
14
30
|
|
|
15
31
|
off (event, handler) {
|
|
@@ -20,6 +36,9 @@ const getGlobalBus = () => {
|
|
|
20
36
|
},
|
|
21
37
|
|
|
22
38
|
emit (event, payload) {
|
|
39
|
+
// Remember the last payload for future late subscribers.
|
|
40
|
+
lastPayloads[event] = payload
|
|
41
|
+
|
|
23
42
|
const list = events[event]
|
|
24
43
|
if (!list || !list.length) {return}
|
|
25
44
|
// copy to avoid mutation during iteration
|