@openstax/ts-utils 1.34.0 → 1.34.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.
Files changed (503) hide show
  1. package/README.md +90 -62
  2. package/dist/cjs/assertions/index.d.ts +89 -0
  3. package/dist/cjs/assertions/index.js +157 -0
  4. package/dist/cjs/aws/ssmService.d.ts +5 -0
  5. package/dist/cjs/aws/ssmService.js +9 -0
  6. package/dist/cjs/config/awsParameterConfig.d.ts +10 -0
  7. package/dist/cjs/config/awsParameterConfig.js +26 -0
  8. package/dist/cjs/config/envConfig.d.ts +24 -0
  9. package/dist/cjs/config/envConfig.js +57 -0
  10. package/{packages/utils/src/config/index.ts → dist/cjs/config/index.d.ts} +13 -29
  11. package/dist/cjs/config/index.js +35 -0
  12. package/dist/cjs/config/lambdaParameterConfig.d.ts +12 -0
  13. package/dist/cjs/config/lambdaParameterConfig.js +45 -0
  14. package/dist/cjs/config/replaceConfig.d.ts +14 -0
  15. package/dist/cjs/config/replaceConfig.js +22 -0
  16. package/dist/cjs/config/resolveConfigValue.d.ts +5 -0
  17. package/dist/cjs/config/resolveConfigValue.js +12 -0
  18. package/dist/cjs/errors/index.d.ts +88 -0
  19. package/dist/cjs/errors/index.js +123 -0
  20. package/dist/cjs/fetch/fetchStatusRetry.d.ts +8 -0
  21. package/dist/cjs/fetch/fetchStatusRetry.js +27 -0
  22. package/dist/cjs/fetch/index.d.ts +64 -0
  23. package/dist/cjs/fetch/index.js +55 -0
  24. package/{packages/utils/src/guards/index.ts → dist/cjs/guards/index.d.ts} +7 -10
  25. package/dist/cjs/guards/index.js +44 -0
  26. package/dist/cjs/index.js +20 -0
  27. package/dist/cjs/middleware/apiErrorHandler.d.ts +24 -0
  28. package/dist/cjs/middleware/apiErrorHandler.js +42 -0
  29. package/dist/cjs/middleware/apiSlowResponseMiddleware.d.ts +23 -0
  30. package/dist/cjs/middleware/apiSlowResponseMiddleware.js +54 -0
  31. package/{packages/utils/src/middleware/index.ts → dist/cjs/middleware/index.d.ts} +5 -53
  32. package/dist/cjs/middleware/index.js +48 -0
  33. package/dist/cjs/middleware/lambdaCorsResponseMiddleware.d.ts +20 -0
  34. package/dist/cjs/middleware/lambdaCorsResponseMiddleware.js +44 -0
  35. package/dist/cjs/middleware/throwNotFoundMiddleware.d.ts +4 -0
  36. package/dist/cjs/middleware/throwNotFoundMiddleware.js +14 -0
  37. package/dist/cjs/misc/hashValue.d.ts +10 -0
  38. package/dist/cjs/misc/hashValue.js +17 -0
  39. package/dist/cjs/misc/helpers.d.ts +124 -0
  40. package/dist/cjs/misc/helpers.js +214 -0
  41. package/dist/cjs/misc/merge.d.ts +21 -0
  42. package/dist/cjs/misc/merge.js +45 -0
  43. package/dist/cjs/misc/partitionSequence.d.ts +35 -0
  44. package/dist/cjs/misc/partitionSequence.js +55 -0
  45. package/dist/cjs/pagination/index.d.ts +91 -0
  46. package/dist/cjs/pagination/index.js +83 -0
  47. package/dist/cjs/routing/helpers.d.ts +57 -0
  48. package/dist/cjs/routing/helpers.js +90 -0
  49. package/dist/cjs/routing/index.d.ts +290 -0
  50. package/dist/cjs/routing/index.js +295 -0
  51. package/dist/cjs/routing/validators/zod.d.ts +4 -0
  52. package/dist/cjs/routing/validators/zod.js +14 -0
  53. package/dist/cjs/services/accountsGateway/index.d.ts +92 -0
  54. package/dist/cjs/services/accountsGateway/index.js +138 -0
  55. package/dist/cjs/services/apiGateway/index.d.ts +68 -0
  56. package/dist/cjs/services/apiGateway/index.js +118 -0
  57. package/dist/cjs/services/authProvider/browser.d.ts +40 -0
  58. package/dist/cjs/services/authProvider/browser.js +155 -0
  59. package/dist/cjs/services/authProvider/decryption.d.ts +19 -0
  60. package/dist/cjs/services/authProvider/decryption.js +73 -0
  61. package/dist/cjs/services/authProvider/index.d.ts +63 -0
  62. package/dist/cjs/services/authProvider/index.js +34 -0
  63. package/dist/cjs/services/authProvider/subrequest.d.ts +13 -0
  64. package/dist/cjs/services/authProvider/subrequest.js +49 -0
  65. package/dist/cjs/services/authProvider/utils/decryptAndVerify.d.ts +28 -0
  66. package/dist/cjs/services/authProvider/utils/decryptAndVerify.js +91 -0
  67. package/dist/cjs/services/authProvider/utils/embeddedAuthProvider.d.ts +26 -0
  68. package/dist/cjs/services/authProvider/utils/embeddedAuthProvider.js +47 -0
  69. package/dist/cjs/services/authProvider/utils/userRoleValidator.d.ts +13 -0
  70. package/dist/cjs/services/authProvider/utils/userRoleValidator.js +37 -0
  71. package/dist/cjs/services/authProvider/utils/userSubrequest.d.ts +3 -0
  72. package/dist/cjs/services/authProvider/utils/userSubrequest.js +13 -0
  73. package/dist/cjs/services/documentStore/dynamoEncoding.d.ts +10 -0
  74. package/dist/cjs/services/documentStore/dynamoEncoding.js +52 -0
  75. package/dist/cjs/services/documentStore/fileSystemAssert.d.ts +1 -0
  76. package/dist/cjs/services/documentStore/fileSystemAssert.js +14 -0
  77. package/{packages/utils/src/services/documentStore/index.ts → dist/cjs/services/documentStore/index.d.ts} +8 -8
  78. package/dist/cjs/services/documentStore/index.js +2 -0
  79. package/dist/cjs/services/documentStore/unversioned/dynamodb.d.ts +31 -0
  80. package/dist/cjs/services/documentStore/unversioned/dynamodb.js +233 -0
  81. package/dist/cjs/services/documentStore/unversioned/file-system.d.ts +32 -0
  82. package/dist/cjs/services/documentStore/unversioned/file-system.js +214 -0
  83. package/{packages/utils/src/services/documentStore/unversioned/index.ts → dist/cjs/services/documentStore/unversioned/index.d.ts} +0 -2
  84. package/dist/cjs/services/documentStore/unversioned/index.js +2 -0
  85. package/dist/cjs/services/documentStore/versioned/dynamodb.d.ts +25 -0
  86. package/dist/cjs/services/documentStore/versioned/dynamodb.js +143 -0
  87. package/dist/cjs/services/documentStore/versioned/file-system.d.ts +25 -0
  88. package/dist/cjs/services/documentStore/versioned/file-system.js +73 -0
  89. package/dist/cjs/services/documentStore/versioned/index.d.ts +17 -0
  90. package/dist/cjs/services/documentStore/versioned/index.js +2 -0
  91. package/dist/cjs/services/exercisesGateway/index.d.ts +67 -0
  92. package/dist/cjs/services/exercisesGateway/index.js +107 -0
  93. package/dist/cjs/services/fileServer/index.d.ts +30 -0
  94. package/dist/cjs/services/fileServer/index.js +19 -0
  95. package/dist/cjs/services/fileServer/localFileServer.d.ts +13 -0
  96. package/dist/cjs/services/fileServer/localFileServer.js +132 -0
  97. package/dist/cjs/services/fileServer/s3FileServer.d.ts +14 -0
  98. package/dist/cjs/services/fileServer/s3FileServer.js +131 -0
  99. package/dist/cjs/services/launchParams/index.js +7 -0
  100. package/dist/cjs/services/launchParams/signer.d.ts +23 -0
  101. package/dist/cjs/services/launchParams/signer.js +58 -0
  102. package/dist/cjs/services/launchParams/verifier.d.ts +21 -0
  103. package/dist/cjs/services/launchParams/verifier.js +129 -0
  104. package/dist/cjs/services/logger/console.d.ts +4 -0
  105. package/dist/cjs/services/logger/console.js +12 -0
  106. package/{packages/utils/src/services/logger/index.ts → dist/cjs/services/logger/index.d.ts} +9 -23
  107. package/dist/cjs/services/logger/index.js +31 -0
  108. package/dist/cjs/services/lrsGateway/addStatementDefaultFields.d.ts +5 -0
  109. package/dist/cjs/services/lrsGateway/addStatementDefaultFields.js +21 -0
  110. package/dist/cjs/services/lrsGateway/attempt-utils.d.ts +72 -0
  111. package/dist/cjs/services/lrsGateway/attempt-utils.js +283 -0
  112. package/dist/cjs/services/lrsGateway/file-system.d.ts +15 -0
  113. package/dist/cjs/services/lrsGateway/file-system.js +150 -0
  114. package/dist/cjs/services/lrsGateway/index.d.ts +122 -0
  115. package/dist/cjs/services/lrsGateway/index.js +148 -0
  116. package/dist/cjs/services/lrsGateway/xapiUtils.d.ts +71 -0
  117. package/dist/cjs/services/lrsGateway/xapiUtils.js +145 -0
  118. package/dist/cjs/services/postgresConnection/index.d.ts +28 -0
  119. package/dist/cjs/services/postgresConnection/index.js +65 -0
  120. package/dist/cjs/services/searchProvider/index.d.ts +67 -0
  121. package/dist/cjs/services/searchProvider/index.js +2 -0
  122. package/dist/cjs/services/searchProvider/memorySearchTheBadWay.d.ts +20 -0
  123. package/dist/cjs/services/searchProvider/memorySearchTheBadWay.js +191 -0
  124. package/dist/cjs/services/searchProvider/openSearch.d.ts +28 -0
  125. package/dist/cjs/services/searchProvider/openSearch.js +154 -0
  126. package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -0
  127. package/{packages/utils/src/types.ts → dist/cjs/types.d.ts} +6 -34
  128. package/dist/cjs/types.js +2 -0
  129. package/dist/esm/assertions/index.d.ts +89 -0
  130. package/{packages/utils/src/assertions/index.ts → dist/esm/assertions/index.js} +49 -64
  131. package/dist/esm/aws/ssmService.d.ts +5 -0
  132. package/dist/esm/aws/ssmService.js +6 -0
  133. package/dist/esm/config/awsParameterConfig.d.ts +10 -0
  134. package/dist/esm/config/awsParameterConfig.js +22 -0
  135. package/dist/esm/config/envConfig.d.ts +24 -0
  136. package/dist/esm/config/envConfig.js +53 -0
  137. package/dist/esm/config/index.d.ts +48 -0
  138. package/dist/esm/config/index.js +17 -0
  139. package/dist/esm/config/lambdaParameterConfig.d.ts +12 -0
  140. package/dist/esm/config/lambdaParameterConfig.js +38 -0
  141. package/dist/esm/config/replaceConfig.d.ts +14 -0
  142. package/{packages/utils/src/config/replaceConfig.ts → dist/esm/config/replaceConfig.js} +6 -16
  143. package/dist/esm/config/resolveConfigValue.d.ts +5 -0
  144. package/dist/esm/config/resolveConfigValue.js +8 -0
  145. package/dist/esm/errors/index.d.ts +88 -0
  146. package/{packages/utils/src/errors/index.ts → dist/esm/errors/index.js} +41 -57
  147. package/dist/esm/fetch/fetchStatusRetry.d.ts +8 -0
  148. package/dist/esm/fetch/fetchStatusRetry.js +23 -0
  149. package/dist/esm/fetch/index.d.ts +64 -0
  150. package/dist/esm/fetch/index.js +46 -0
  151. package/dist/esm/guards/index.d.ts +38 -0
  152. package/dist/esm/guards/index.js +36 -0
  153. package/dist/esm/index.d.ts +4 -0
  154. package/dist/esm/index.js +4 -0
  155. package/dist/esm/middleware/apiErrorHandler.d.ts +24 -0
  156. package/dist/esm/middleware/apiErrorHandler.js +38 -0
  157. package/dist/esm/middleware/apiSlowResponseMiddleware.d.ts +23 -0
  158. package/dist/esm/middleware/apiSlowResponseMiddleware.js +50 -0
  159. package/dist/esm/middleware/index.d.ts +47 -0
  160. package/dist/esm/middleware/index.js +44 -0
  161. package/dist/esm/middleware/lambdaCorsResponseMiddleware.d.ts +20 -0
  162. package/dist/esm/middleware/lambdaCorsResponseMiddleware.js +40 -0
  163. package/dist/esm/middleware/throwNotFoundMiddleware.d.ts +4 -0
  164. package/dist/esm/middleware/throwNotFoundMiddleware.js +10 -0
  165. package/dist/esm/misc/hashValue.d.ts +10 -0
  166. package/dist/esm/misc/hashValue.js +13 -0
  167. package/dist/esm/misc/helpers.d.ts +124 -0
  168. package/dist/esm/misc/helpers.js +199 -0
  169. package/dist/esm/misc/merge.d.ts +21 -0
  170. package/dist/esm/misc/merge.js +40 -0
  171. package/dist/esm/misc/partitionSequence.d.ts +35 -0
  172. package/{packages/utils/src/misc/partitionSequence.ts → dist/esm/misc/partitionSequence.js} +15 -23
  173. package/dist/esm/pagination/index.d.ts +91 -0
  174. package/dist/esm/pagination/index.js +77 -0
  175. package/dist/esm/routing/helpers.d.ts +57 -0
  176. package/{packages/utils/src/routing/helpers.ts → dist/esm/routing/helpers.js} +30 -42
  177. package/dist/esm/routing/index.d.ts +290 -0
  178. package/dist/esm/routing/index.js +246 -0
  179. package/dist/esm/routing/validators/zod.d.ts +4 -0
  180. package/dist/esm/routing/validators/zod.js +10 -0
  181. package/dist/esm/services/accountsGateway/index.d.ts +92 -0
  182. package/dist/esm/services/accountsGateway/index.js +131 -0
  183. package/dist/esm/services/apiGateway/index.d.ts +68 -0
  184. package/dist/esm/services/apiGateway/index.js +77 -0
  185. package/dist/esm/services/authProvider/browser.d.ts +40 -0
  186. package/dist/esm/services/authProvider/browser.js +151 -0
  187. package/dist/esm/services/authProvider/decryption.d.ts +19 -0
  188. package/dist/esm/services/authProvider/decryption.js +69 -0
  189. package/dist/esm/services/authProvider/index.d.ts +63 -0
  190. package/dist/esm/services/authProvider/index.js +26 -0
  191. package/dist/esm/services/authProvider/subrequest.d.ts +13 -0
  192. package/dist/esm/services/authProvider/subrequest.js +45 -0
  193. package/dist/esm/services/authProvider/utils/decryptAndVerify.d.ts +28 -0
  194. package/dist/esm/services/authProvider/utils/decryptAndVerify.js +85 -0
  195. package/dist/esm/services/authProvider/utils/embeddedAuthProvider.d.ts +26 -0
  196. package/dist/esm/services/authProvider/utils/embeddedAuthProvider.js +40 -0
  197. package/dist/esm/services/authProvider/utils/userRoleValidator.d.ts +13 -0
  198. package/dist/esm/services/authProvider/utils/userRoleValidator.js +33 -0
  199. package/dist/esm/services/authProvider/utils/userSubrequest.d.ts +3 -0
  200. package/dist/esm/services/authProvider/utils/userSubrequest.js +6 -0
  201. package/dist/esm/services/documentStore/dynamoEncoding.d.ts +10 -0
  202. package/dist/esm/services/documentStore/dynamoEncoding.js +45 -0
  203. package/dist/esm/services/documentStore/fileSystemAssert.d.ts +1 -0
  204. package/dist/esm/services/documentStore/fileSystemAssert.js +10 -0
  205. package/dist/esm/services/documentStore/index.d.ts +14 -0
  206. package/dist/esm/services/documentStore/index.js +1 -0
  207. package/dist/esm/services/documentStore/unversioned/dynamodb.d.ts +31 -0
  208. package/dist/esm/services/documentStore/unversioned/dynamodb.js +226 -0
  209. package/dist/esm/services/documentStore/unversioned/file-system.d.ts +32 -0
  210. package/dist/esm/services/documentStore/unversioned/file-system.js +174 -0
  211. package/dist/esm/services/documentStore/unversioned/index.d.ts +2 -0
  212. package/dist/esm/services/documentStore/unversioned/index.js +1 -0
  213. package/dist/esm/services/documentStore/versioned/dynamodb.d.ts +25 -0
  214. package/dist/esm/services/documentStore/versioned/dynamodb.js +139 -0
  215. package/dist/esm/services/documentStore/versioned/file-system.d.ts +25 -0
  216. package/dist/esm/services/documentStore/versioned/file-system.js +69 -0
  217. package/dist/esm/services/documentStore/versioned/index.d.ts +17 -0
  218. package/dist/esm/services/documentStore/versioned/index.js +1 -0
  219. package/dist/esm/services/exercisesGateway/index.d.ts +67 -0
  220. package/dist/esm/services/exercisesGateway/index.js +70 -0
  221. package/dist/esm/services/fileServer/index.d.ts +30 -0
  222. package/dist/esm/services/fileServer/index.js +13 -0
  223. package/dist/esm/services/fileServer/localFileServer.d.ts +13 -0
  224. package/dist/esm/services/fileServer/localFileServer.js +125 -0
  225. package/dist/esm/services/fileServer/s3FileServer.d.ts +14 -0
  226. package/dist/esm/services/fileServer/s3FileServer.js +124 -0
  227. package/dist/esm/services/launchParams/index.d.ts +2 -0
  228. package/dist/esm/services/launchParams/index.js +2 -0
  229. package/dist/esm/services/launchParams/signer.d.ts +23 -0
  230. package/dist/esm/services/launchParams/signer.js +51 -0
  231. package/dist/esm/services/launchParams/verifier.d.ts +21 -0
  232. package/dist/esm/services/launchParams/verifier.js +92 -0
  233. package/dist/esm/services/logger/console.d.ts +4 -0
  234. package/{packages/utils/src/services/logger/console.ts → dist/esm/services/logger/console.js} +2 -5
  235. package/dist/esm/services/logger/index.d.ts +39 -0
  236. package/dist/esm/services/logger/index.js +27 -0
  237. package/dist/esm/services/lrsGateway/addStatementDefaultFields.d.ts +5 -0
  238. package/dist/esm/services/lrsGateway/addStatementDefaultFields.js +14 -0
  239. package/dist/esm/services/lrsGateway/attempt-utils.d.ts +72 -0
  240. package/dist/esm/services/lrsGateway/attempt-utils.js +261 -0
  241. package/dist/esm/services/lrsGateway/file-system.d.ts +15 -0
  242. package/dist/esm/services/lrsGateway/file-system.js +110 -0
  243. package/dist/esm/services/lrsGateway/index.d.ts +122 -0
  244. package/dist/esm/services/lrsGateway/index.js +111 -0
  245. package/dist/esm/services/lrsGateway/xapiUtils.d.ts +71 -0
  246. package/dist/esm/services/lrsGateway/xapiUtils.js +134 -0
  247. package/dist/esm/services/postgresConnection/index.d.ts +28 -0
  248. package/dist/esm/services/postgresConnection/index.js +58 -0
  249. package/dist/esm/services/searchProvider/index.d.ts +67 -0
  250. package/dist/esm/services/searchProvider/index.js +1 -0
  251. package/dist/esm/services/searchProvider/memorySearchTheBadWay.d.ts +20 -0
  252. package/dist/esm/services/searchProvider/memorySearchTheBadWay.js +187 -0
  253. package/dist/esm/services/searchProvider/openSearch.d.ts +28 -0
  254. package/dist/esm/services/searchProvider/openSearch.js +150 -0
  255. package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -0
  256. package/dist/esm/types.d.ts +31 -0
  257. package/dist/esm/types.js +1 -0
  258. package/package.json +228 -12
  259. package/.cfnlintrc +0 -2
  260. package/.github/CODEOWNERS +0 -1
  261. package/.github/workflows/ci.yml +0 -36
  262. package/.github/workflows/lint.yml +0 -55
  263. package/.nvmrc +0 -1
  264. package/.syncignore +0 -4
  265. package/.syncpackrc +0 -18
  266. package/CONTRIBUTING.md +0 -96
  267. package/LICENSE +0 -661
  268. package/Procfile +0 -1
  269. package/app.json +0 -23
  270. package/cspell.json +0 -32
  271. package/deploy/constants.env +0 -21
  272. package/deploy/deploy.bash +0 -157
  273. package/deploy/deployment-alt-region.cfn.yml +0 -70
  274. package/deploy/deployment.cfn.yml +0 -650
  275. package/deploy/destroy-deployment.bash +0 -23
  276. package/deploy/shared.cfn.yml +0 -94
  277. package/docs/lambda-build.md +0 -35
  278. package/packages/frontend/README.md +0 -46
  279. package/packages/frontend/package.json +0 -101
  280. package/packages/frontend/public/favicon.ico +0 -0
  281. package/packages/frontend/public/index.html +0 -107
  282. package/packages/frontend/public/maintenance.html +0 -59
  283. package/packages/frontend/public/manifest.json +0 -15
  284. package/packages/frontend/public/robots.txt +0 -3
  285. package/packages/frontend/script/make-certificate.bash +0 -49
  286. package/packages/frontend/script/server/cli.js +0 -11
  287. package/packages/frontend/script/server/index.js +0 -47
  288. package/packages/frontend/script/start.bash +0 -22
  289. package/packages/frontend/script/trust-localhost.bash +0 -7
  290. package/packages/frontend/src/auth/authProvider.ts +0 -10
  291. package/packages/frontend/src/auth/useAuth.ts +0 -33
  292. package/packages/frontend/src/components/Pagination.tsx +0 -26
  293. package/packages/frontend/src/configProvider/index.ts +0 -53
  294. package/packages/frontend/src/configProvider/use.ts +0 -41
  295. package/packages/frontend/src/core/context/services.spec.tsx +0 -39
  296. package/packages/frontend/src/core/context/services.tsx +0 -16
  297. package/packages/frontend/src/core/index.spec.ts +0 -7
  298. package/packages/frontend/src/core/index.ts +0 -20
  299. package/packages/frontend/src/core/services.tsx +0 -14
  300. package/packages/frontend/src/core/types.ts +0 -3
  301. package/packages/frontend/src/example/api.ts +0 -28
  302. package/packages/frontend/src/example/components/Layout.tsx +0 -23
  303. package/packages/frontend/src/example/screens/Home.spec.tsx +0 -68
  304. package/packages/frontend/src/example/screens/Home.tsx +0 -78
  305. package/packages/frontend/src/example/screens/ThingList.spec.tsx +0 -60
  306. package/packages/frontend/src/example/screens/ThingList.tsx +0 -75
  307. package/packages/frontend/src/example/screens/ThingView.spec.tsx +0 -71
  308. package/packages/frontend/src/example/screens/ThingView.tsx +0 -47
  309. package/packages/frontend/src/example/screens/index.ts +0 -9
  310. package/packages/frontend/src/index.css +0 -159
  311. package/packages/frontend/src/index.tsx +0 -67
  312. package/packages/frontend/src/react-app-env.d.ts +0 -1
  313. package/packages/frontend/src/routing/components/RouteLink.spec.tsx +0 -55
  314. package/packages/frontend/src/routing/components/RouteLink.tsx +0 -35
  315. package/packages/frontend/src/routing/middleware.ts +0 -6
  316. package/packages/frontend/src/routing/useQuery.ts +0 -14
  317. package/packages/frontend/src/setupProxy.js +0 -19
  318. package/packages/frontend/src/setupTests.ts +0 -9
  319. package/packages/frontend/src/tests/testServices.tsx +0 -23
  320. package/packages/frontend/tsconfig.json +0 -27
  321. package/packages/lambda/.eslintrc.js +0 -64
  322. package/packages/lambda/jest-global-setup.js +0 -3
  323. package/packages/lambda/jest-setup-after-env.js +0 -1
  324. package/packages/lambda/jest.config.js +0 -31
  325. package/packages/lambda/jest.resolver.js +0 -17
  326. package/packages/lambda/package.json +0 -68
  327. package/packages/lambda/script/build.bash +0 -19
  328. package/packages/lambda/script/bundle-functions.bash +0 -10
  329. package/packages/lambda/script/lambdaLocalProxy.js +0 -16
  330. package/packages/lambda/script/lambdaLocalProxy.spec.ts +0 -147
  331. package/packages/lambda/script/utils/getRouteData.ts +0 -7
  332. package/packages/lambda/script/utils/routeDataLoader.js +0 -8
  333. package/packages/lambda/script/utils/routeDataLoader.spec.ts +0 -8
  334. package/packages/lambda/src/functions/serviceApi/core/index.ts +0 -7
  335. package/packages/lambda/src/functions/serviceApi/core/request.spec.ts +0 -38
  336. package/packages/lambda/src/functions/serviceApi/core/request.ts +0 -42
  337. package/packages/lambda/src/functions/serviceApi/core/routes.spec.ts +0 -7
  338. package/packages/lambda/src/functions/serviceApi/core/routes.ts +0 -10
  339. package/packages/lambda/src/functions/serviceApi/core/services.ts +0 -9
  340. package/packages/lambda/src/functions/serviceApi/core/types.ts +0 -13
  341. package/packages/lambda/src/functions/serviceApi/entry/lambda/https-xray.ts +0 -4
  342. package/packages/lambda/src/functions/serviceApi/entry/lambda/index.spec.ts +0 -48
  343. package/packages/lambda/src/functions/serviceApi/entry/lambda/index.ts +0 -58
  344. package/packages/lambda/src/functions/serviceApi/entry/lambda/services.ts +0 -36
  345. package/packages/lambda/src/functions/serviceApi/entry/local.ts +0 -71
  346. package/packages/lambda/src/functions/serviceApi/versions/v0/example/documentSearchMiddleware.spec.ts +0 -16
  347. package/packages/lambda/src/functions/serviceApi/versions/v0/example/documentSearchMiddleware.ts +0 -41
  348. package/packages/lambda/src/functions/serviceApi/versions/v0/example/documentStoreMiddleware.spec.ts +0 -78
  349. package/packages/lambda/src/functions/serviceApi/versions/v0/example/documentStoreMiddleware.ts +0 -70
  350. package/packages/lambda/src/functions/serviceApi/versions/v0/example/routes.spec.ts +0 -306
  351. package/packages/lambda/src/functions/serviceApi/versions/v0/example/routes.ts +0 -176
  352. package/packages/lambda/src/functions/serviceApi/versions/v0/index.spec.ts +0 -263
  353. package/packages/lambda/src/functions/serviceApi/versions/v0/index.ts +0 -134
  354. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/authMiddleware.spec.ts +0 -23
  355. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/authMiddleware.ts +0 -32
  356. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/configMiddleware.spec.ts +0 -10
  357. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/configMiddleware.ts +0 -7
  358. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/frontendFileServerMiddleware.spec.ts +0 -13
  359. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/frontendFileServerMiddleware.ts +0 -23
  360. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/paginationMiddleware.spec.ts +0 -9
  361. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/paginationMiddleware.ts +0 -9
  362. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/searchMiddleware.spec.ts +0 -12
  363. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/searchMiddleware.ts +0 -21
  364. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/userRoleValidatorMiddleware.spec.ts +0 -21
  365. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/userRoleValidatorMiddleware.ts +0 -18
  366. package/packages/lambda/tsconfig.json +0 -30
  367. package/packages/lambda/webpack.config.js +0 -97
  368. package/packages/utils/.eslintrc.js +0 -64
  369. package/packages/utils/README.md +0 -118
  370. package/packages/utils/jest-global-setup.js +0 -3
  371. package/packages/utils/jest.config.js +0 -25
  372. package/packages/utils/jest.resolver.js +0 -17
  373. package/packages/utils/package.json +0 -238
  374. package/packages/utils/src/assertions/index.spec.ts +0 -126
  375. package/packages/utils/src/aws/ssmService.ts +0 -7
  376. package/packages/utils/src/config/awsParameterConfig.ts +0 -24
  377. package/packages/utils/src/config/envConfig.ts +0 -58
  378. package/packages/utils/src/config/index.spec.ts +0 -165
  379. package/packages/utils/src/config/lambdaParameterConfig.ts +0 -49
  380. package/packages/utils/src/config/resolveConfigValue.ts +0 -10
  381. package/packages/utils/src/errors/index.spec.ts +0 -35
  382. package/packages/utils/src/fetch/fetchStatusRetry.spec.ts +0 -197
  383. package/packages/utils/src/fetch/fetchStatusRetry.ts +0 -33
  384. package/packages/utils/src/fetch/index.spec.ts +0 -34
  385. package/packages/utils/src/fetch/index.ts +0 -87
  386. package/packages/utils/src/guards/index.spec.ts +0 -58
  387. package/packages/utils/src/index.spec.ts +0 -471
  388. package/packages/utils/src/middleware/apiErrorHandler.spec.ts +0 -65
  389. package/packages/utils/src/middleware/apiErrorHandler.ts +0 -67
  390. package/packages/utils/src/middleware/apiSlowResponseMiddleware.spec.ts +0 -184
  391. package/packages/utils/src/middleware/apiSlowResponseMiddleware.ts +0 -71
  392. package/packages/utils/src/middleware/index.spec.ts +0 -99
  393. package/packages/utils/src/middleware/lambdaCorsResponseMiddleware.spec.ts +0 -103
  394. package/packages/utils/src/middleware/lambdaCorsResponseMiddleware.ts +0 -52
  395. package/packages/utils/src/middleware/throwNotFoundMiddleware.spec.ts +0 -20
  396. package/packages/utils/src/middleware/throwNotFoundMiddleware.ts +0 -11
  397. package/packages/utils/src/misc/hashValue.ts +0 -18
  398. package/packages/utils/src/misc/helpers.ts +0 -259
  399. package/packages/utils/src/misc/merge.ts +0 -48
  400. package/packages/utils/src/pagination/index.spec.ts +0 -150
  401. package/packages/utils/src/pagination/index.ts +0 -117
  402. package/packages/utils/src/routing/index.spec.ts +0 -553
  403. package/packages/utils/src/routing/index.ts +0 -424
  404. package/packages/utils/src/routing/validators/zod.spec.ts +0 -16
  405. package/packages/utils/src/routing/validators/zod.ts +0 -14
  406. package/packages/utils/src/services/accountsGateway/README.md +0 -3
  407. package/packages/utils/src/services/accountsGateway/index.spec.ts +0 -518
  408. package/packages/utils/src/services/accountsGateway/index.ts +0 -251
  409. package/packages/utils/src/services/apiGateway/README.md +0 -93
  410. package/packages/utils/src/services/apiGateway/index.spec.ts +0 -254
  411. package/packages/utils/src/services/apiGateway/index.ts +0 -189
  412. package/packages/utils/src/services/authProvider/README.md +0 -21
  413. package/packages/utils/src/services/authProvider/browser.spec.ts +0 -391
  414. package/packages/utils/src/services/authProvider/browser.ts +0 -209
  415. package/packages/utils/src/services/authProvider/decryption.spec.ts +0 -337
  416. package/packages/utils/src/services/authProvider/decryption.ts +0 -98
  417. package/packages/utils/src/services/authProvider/index.ts +0 -93
  418. package/packages/utils/src/services/authProvider/stub.spec.ts +0 -29
  419. package/packages/utils/src/services/authProvider/subrequest.spec.ts +0 -105
  420. package/packages/utils/src/services/authProvider/subrequest.ts +0 -68
  421. package/packages/utils/src/services/authProvider/utils/decryptAndVerify.spec.ts +0 -128
  422. package/packages/utils/src/services/authProvider/utils/decryptAndVerify.ts +0 -106
  423. package/packages/utils/src/services/authProvider/utils/embeddedAuthProvider.spec.ts +0 -26
  424. package/packages/utils/src/services/authProvider/utils/embeddedAuthProvider.ts +0 -57
  425. package/packages/utils/src/services/authProvider/utils/userRoleValidator.spec.ts +0 -135
  426. package/packages/utils/src/services/authProvider/utils/userRoleValidator.ts +0 -49
  427. package/packages/utils/src/services/authProvider/utils/userSubrequest.spec.ts +0 -26
  428. package/packages/utils/src/services/authProvider/utils/userSubrequest.ts +0 -10
  429. package/packages/utils/src/services/documentStore/dynamoEncoding.ts +0 -57
  430. package/packages/utils/src/services/documentStore/fileSystemAssert.spec.ts +0 -43
  431. package/packages/utils/src/services/documentStore/fileSystemAssert.ts +0 -10
  432. package/packages/utils/src/services/documentStore/unversioned/README.md +0 -13
  433. package/packages/utils/src/services/documentStore/unversioned/dynamodb.spec.ts +0 -859
  434. package/packages/utils/src/services/documentStore/unversioned/dynamodb.ts +0 -243
  435. package/packages/utils/src/services/documentStore/unversioned/file-system.spec.ts +0 -629
  436. package/packages/utils/src/services/documentStore/unversioned/file-system.ts +0 -194
  437. package/packages/utils/src/services/documentStore/versioned/README.md +0 -13
  438. package/packages/utils/src/services/documentStore/versioned/dynamodb.spec.ts +0 -376
  439. package/packages/utils/src/services/documentStore/versioned/dynamodb.ts +0 -167
  440. package/packages/utils/src/services/documentStore/versioned/file-system.spec.ts +0 -262
  441. package/packages/utils/src/services/documentStore/versioned/file-system.ts +0 -90
  442. package/packages/utils/src/services/documentStore/versioned/index.ts +0 -25
  443. package/packages/utils/src/services/exercisesGateway/README.md +0 -5
  444. package/packages/utils/src/services/exercisesGateway/index.spec.ts +0 -326
  445. package/packages/utils/src/services/exercisesGateway/index.ts +0 -163
  446. package/packages/utils/src/services/fileServer/index.spec.ts +0 -88
  447. package/packages/utils/src/services/fileServer/index.ts +0 -43
  448. package/packages/utils/src/services/fileServer/localFileServer.spec.ts +0 -182
  449. package/packages/utils/src/services/fileServer/localFileServer.ts +0 -159
  450. package/packages/utils/src/services/fileServer/s3FileServer.spec.ts +0 -266
  451. package/packages/utils/src/services/fileServer/s3FileServer.ts +0 -155
  452. package/packages/utils/src/services/launchParams/index.spec.ts +0 -366
  453. package/packages/utils/src/services/launchParams/signer.ts +0 -73
  454. package/packages/utils/src/services/launchParams/verifier.ts +0 -120
  455. package/packages/utils/src/services/logger/console.spec.ts +0 -29
  456. package/packages/utils/src/services/logger/index.spec.ts +0 -65
  457. package/packages/utils/src/services/lrsGateway/README.md +0 -5
  458. package/packages/utils/src/services/lrsGateway/addStatementDefaultFields.ts +0 -22
  459. package/packages/utils/src/services/lrsGateway/attempt-utils.spec.ts +0 -847
  460. package/packages/utils/src/services/lrsGateway/attempt-utils.ts +0 -358
  461. package/packages/utils/src/services/lrsGateway/file-system.spec.ts +0 -363
  462. package/packages/utils/src/services/lrsGateway/file-system.ts +0 -165
  463. package/packages/utils/src/services/lrsGateway/index.spec.ts +0 -194
  464. package/packages/utils/src/services/lrsGateway/index.ts +0 -257
  465. package/packages/utils/src/services/lrsGateway/xapiUtils.spec.ts +0 -887
  466. package/packages/utils/src/services/lrsGateway/xapiUtils.ts +0 -262
  467. package/packages/utils/src/services/postgresConnection/index.spec.ts +0 -170
  468. package/packages/utils/src/services/postgresConnection/index.ts +0 -84
  469. package/packages/utils/src/services/searchProvider/README.md +0 -3
  470. package/packages/utils/src/services/searchProvider/index.ts +0 -59
  471. package/packages/utils/src/services/searchProvider/memorySearchTheBadWay.spec.ts +0 -526
  472. package/packages/utils/src/services/searchProvider/memorySearchTheBadWay.ts +0 -223
  473. package/packages/utils/src/services/searchProvider/openSearch.spec.ts +0 -926
  474. package/packages/utils/src/services/searchProvider/openSearch.ts +0 -195
  475. package/packages/utils/tsconfig.json +0 -31
  476. package/packages/utils/tsconfig.without-specs.cjs.json +0 -7
  477. package/packages/utils/tsconfig.without-specs.esm.json +0 -7
  478. package/packages/utils/tsconfig.without-specs.json +0 -6
  479. package/scripts/build.bash +0 -24
  480. package/scripts/ci.bash +0 -10
  481. package/scripts/start.bash +0 -29
  482. /package/{packages/utils/src/index.ts → dist/cjs/index.d.ts} +0 -0
  483. /package/{packages/utils/src/services/launchParams/index.ts → dist/cjs/services/launchParams/index.d.ts} +0 -0
  484. /package/{packages/utils/script → script}/bin/copy-from-template.bash +0 -0
  485. /package/{packages/utils/script → script}/bin/delete-stack.bash +0 -0
  486. /package/{packages/utils/script → script}/bin/deploy.bash +0 -0
  487. /package/{packages/utils/script → script}/bin/destroy-deployment.bash +0 -0
  488. /package/{packages/utils/script → script}/bin/empty-bucket.bash +0 -0
  489. /package/{packages/utils/script → script}/bin/get-arg.bash +0 -0
  490. /package/{packages/utils/script → script}/bin/get-deployed-environments.bash +0 -0
  491. /package/{packages/utils/script → script}/bin/get-env-param.bash +0 -0
  492. /package/{packages/utils/script → script}/bin/get-kwarg.bash +0 -0
  493. /package/{packages/utils/script → script}/bin/get-stack-param.bash +0 -0
  494. /package/{packages/utils/script → script}/bin/has-flag.bash +0 -0
  495. /package/{packages/utils/script → script}/bin/init-constants-script.bash +0 -0
  496. /package/{packages/utils/script → script}/bin/init-params-script.bash +0 -0
  497. /package/{packages/utils/script → script}/bin/stack-exists.bash +0 -0
  498. /package/{packages/utils/script → script}/bin/update-utils.bash +0 -0
  499. /package/{packages/utils/script → script}/bin/upload-pager-duty-endpoints.bash +0 -0
  500. /package/{packages/utils/script → script}/bin/upload-params.bash +0 -0
  501. /package/{packages/utils/script → script}/bin/which.bash +0 -0
  502. /package/{packages/utils/script → script}/bin-entry.bash +0 -0
  503. /package/{packages/utils/script → script}/build.bash +0 -0
@@ -1,887 +0,0 @@
1
- import { v4 as uuid, v5 as uuidV5 } from 'uuid';
2
- import { AccountsGateway } from '../accountsGateway';
3
- import { AuthProvider, User } from '../authProvider';
4
- import { createAttemptStatement, createCompletedStatement } from './attempt-utils';
5
- import { getAssignmentGrades, getCurrentGrade, getRegistrationAttemptInfo } from './xapiUtils';
6
- import { LrsGateway } from '.';
7
-
8
- describe('getRegistrationAttemptInfo', () => {
9
- let mockGetStatements: jest.SpyInstance;
10
- let lrs: LrsGateway;
11
- const mockRegistration = uuidV5('1', uuidV5('platform id', uuidV5.URL));
12
- const accountUuid = '8cf86c14-20c6-474c-ba44-f4ac932a0a2d';
13
-
14
- const defaultStatementFields = () => ({
15
- id: uuid(),
16
- timestamp: new Date().toISOString(),
17
- actor: {
18
- account: {
19
- homePage: 'https://openstax.org' as const,
20
- name: accountUuid,
21
- },
22
- objectType: 'Agent' as const,
23
- },
24
- });
25
-
26
- beforeEach(() => {
27
- mockGetStatements = jest.fn();
28
- lrs = {getAllXapiStatements: mockGetStatements} as unknown as LrsGateway;
29
- });
30
-
31
- it('returns empty object when there are no statements', async () => {
32
- mockGetStatements.mockReturnValue(Promise.resolve([]));
33
- const infoPerUser = await getRegistrationAttemptInfo(lrs, mockRegistration);
34
- expect(infoPerUser).toEqual({});
35
- });
36
-
37
- it('returns user info when there are statements', async () => {
38
- const attempt = {
39
- ...defaultStatementFields(),
40
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
41
- };
42
- const attemptCompleted = {
43
- ...defaultStatementFields(),
44
- ...createCompletedStatement(attempt, {score: {raw: 4, max: 5, min: 0, scaled: 0.8}})
45
- };
46
- mockGetStatements.mockReturnValue(Promise.resolve([
47
- attempt,
48
- attemptCompleted
49
- ]));
50
- const infoPerUser = await getRegistrationAttemptInfo(lrs, mockRegistration);
51
- const userInfo = infoPerUser[accountUuid];
52
-
53
- expect(userInfo.attempts).toBe(1);
54
- expect(userInfo.completedAttempts).toBe(1);
55
- expect(userInfo.currentAttempt).toBe(attempt);
56
- expect(userInfo.currentAttemptCompleted).toBe(attemptCompleted);
57
- });
58
-
59
- it('gets only the oldest attempt', async () => {
60
- const attempt = {
61
- ...defaultStatementFields(),
62
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
63
- };
64
- const attemptCompleted = {
65
- ...defaultStatementFields(),
66
- ...createCompletedStatement(attempt, {score: {raw: 4, max: 5, min: 0, scaled: 0.8}})
67
- };
68
-
69
- await new Promise((resolve) => setTimeout(resolve, 10));
70
-
71
- const attempt2 = {
72
- ...defaultStatementFields(),
73
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
74
- };
75
- mockGetStatements.mockReturnValue(Promise.resolve([
76
- attempt,
77
- attemptCompleted,
78
- attempt2
79
- ]));
80
- const infoPerUser = await getRegistrationAttemptInfo(lrs, mockRegistration, { currentPreference: 'oldest' });
81
- const userInfo = infoPerUser[accountUuid];
82
-
83
- expect(userInfo.attempts).toBe(2);
84
- expect(userInfo.completedAttempts).toBe(1);
85
- expect(userInfo.currentAttempt).toBe(attempt);
86
- expect(userInfo.currentAttemptCompleted).toBe(attemptCompleted);
87
- });
88
- });
89
-
90
- describe('getCurrentGrade', () => {
91
- let mockGetStatements: jest.SpyInstance;
92
- let mockGetUser: jest.SpyInstance;
93
- let services: {
94
- ltiAuthProvider: AuthProvider;
95
- lrs: LrsGateway;
96
- };
97
- const mockRegistration = uuidV5('1', uuidV5('platform id', uuidV5.URL));
98
- const accountsUuid = '8cf86c14-20c6-474c-ba44-f4ac932a0a2d';
99
- const user = { uuid: accountsUuid } as User;
100
-
101
- const defaultStatementFields = () => ({
102
- id: uuid(),
103
- timestamp: new Date().toISOString(),
104
- actor: {
105
- account: {
106
- homePage: 'https://openstax.org' as const,
107
- name: accountsUuid,
108
- },
109
- objectType: 'Agent' as const,
110
- },
111
- });
112
-
113
- beforeEach(() => {
114
- mockGetStatements = jest.fn();
115
- mockGetUser = jest.fn();
116
- services = {
117
- ltiAuthProvider: {getUser: mockGetUser},
118
- lrs: {getAllXapiStatements: mockGetStatements},
119
- } as any;
120
- });
121
-
122
- it('returns null when there is no user', async () => {
123
- mockGetUser.mockReturnValue(Promise.resolve(undefined));
124
- const grade = await getCurrentGrade(services, mockRegistration);
125
-
126
- expect(grade).toBeNull();
127
- });
128
-
129
- it('gets zero grade when there are no statements', async () => {
130
- mockGetStatements.mockReturnValue(Promise.resolve([]));
131
- mockGetUser.mockReturnValue(Promise.resolve(user));
132
- const grade = await getCurrentGrade(services, mockRegistration);
133
-
134
- expect(grade).toMatchInlineSnapshot(`
135
- {
136
- "grade": {
137
- "activityProgress": "Started",
138
- "gradingProgress": "FullyGraded",
139
- "scoreGiven": 0,
140
- "scoreMaximum": 100,
141
- "submission": {},
142
- "userId": "8cf86c14-20c6-474c-ba44-f4ac932a0a2d",
143
- },
144
- "name": undefined,
145
- "progress": {
146
- "scaled": 0,
147
- },
148
- }
149
- `);
150
- });
151
-
152
- it('gets grade when there are statements', async () => {
153
- const attempt = {
154
- ...defaultStatementFields(),
155
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
156
- };
157
- mockGetStatements.mockReturnValue(Promise.resolve([
158
- attempt,
159
- {
160
- ...defaultStatementFields(),
161
- ...createCompletedStatement(attempt, {score: {raw: 4, max: 5, min: 0, scaled: 0.8}})
162
- }
163
- ]));
164
- mockGetUser.mockReturnValue(Promise.resolve(user));
165
- const grade = await getCurrentGrade(services, mockRegistration, { scoreMaximum: 10 });
166
-
167
- expect(grade).toStrictEqual({
168
- grade: {
169
- activityProgress: 'Completed',
170
- gradingProgress: 'FullyGraded',
171
- scoreGiven: 8,
172
- scoreMaximum: 10,
173
- submission: {
174
- startedAt: expect.any(String),
175
- submittedAt: expect.any(String),
176
- },
177
- userId: '8cf86c14-20c6-474c-ba44-f4ac932a0a2d'
178
- },
179
- name: undefined,
180
- progress: {
181
- scaled: 1
182
- }
183
- });
184
- });
185
-
186
- it('gets default grade when there are no completed statements and no incompleteAttemptCallback', async () => {
187
- const attempt = {
188
- ...defaultStatementFields(),
189
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
190
- };
191
- mockGetStatements.mockReturnValue(Promise.resolve([
192
- attempt,
193
- ]));
194
- mockGetUser.mockReturnValue(Promise.resolve(user));
195
- const grade = await getCurrentGrade(services, mockRegistration);
196
-
197
- expect(grade).toStrictEqual({
198
- grade: {
199
- activityProgress: 'Started',
200
- gradingProgress: 'FullyGraded',
201
- scoreGiven: 0,
202
- scoreMaximum: 100,
203
- submission: {
204
- startedAt: expect.any(String),
205
- },
206
- userId: '8cf86c14-20c6-474c-ba44-f4ac932a0a2d'
207
- },
208
- name: undefined,
209
- progress: {
210
- scaled: 0
211
- }
212
- });
213
- });
214
-
215
- it('can accept a callback to get incomplete assignments', async () => {
216
- const userId = 'a3ee0232-06c4-46cd-8d2f-0d7a12788b4a';
217
- const attempt = {
218
- ...defaultStatementFields(),
219
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
220
- };
221
- mockGetStatements.mockReturnValue(Promise.resolve([
222
- attempt,
223
- ]));
224
- mockGetUser.mockReturnValue(Promise.resolve(user));
225
-
226
- const incompleteAttemptCallback = jest.fn().mockReturnValue(Promise.resolve({
227
- activityProgress: 'Started',
228
- gradingProgress: 'FullyGraded',
229
- scoreGiven: 1,
230
- scoreMaximum: 5,
231
- userId,
232
- }));
233
- const grade = await getCurrentGrade(services, mockRegistration, { incompleteAttemptCallback, userId });
234
-
235
- expect(grade).toMatchInlineSnapshot(`
236
- {
237
- "activityProgress": "Started",
238
- "gradingProgress": "FullyGraded",
239
- "scoreGiven": 1,
240
- "scoreMaximum": 5,
241
- "userId": "a3ee0232-06c4-46cd-8d2f-0d7a12788b4a",
242
- }
243
- `);
244
-
245
- expect(incompleteAttemptCallback).toHaveBeenCalledTimes(1);
246
-
247
- const args = incompleteAttemptCallback.mock.calls[0];
248
- expect(args).toHaveLength(1);
249
-
250
- const info = args[0];
251
-
252
- expect(info.attempts).toBe(1);
253
- expect(info.completedAttempts).toBe(0);
254
- });
255
-
256
- it('gets grade when grade has no min/max/raw values', async () => {
257
- const attempt = {
258
- ...defaultStatementFields(),
259
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
260
- };
261
- mockGetStatements.mockReturnValue(Promise.resolve([
262
- attempt,
263
- {
264
- ...defaultStatementFields(),
265
- ...createCompletedStatement(attempt, {score: {scaled: 0.8}})
266
- }
267
- ]));
268
- mockGetUser.mockReturnValue(Promise.resolve(user));
269
- const grade = await getCurrentGrade(services, mockRegistration, { userId: 'user id' });
270
-
271
- expect(grade).toStrictEqual({
272
- grade: {
273
- activityProgress: 'Completed',
274
- gradingProgress: 'FullyGraded',
275
- scoreGiven: 80,
276
- scoreMaximum: 100,
277
- submission: {
278
- startedAt: expect.any(String),
279
- submittedAt: expect.any(String),
280
- },
281
- userId: 'user id'
282
- },
283
- name: undefined,
284
- progress: {
285
- scaled: 1
286
- }
287
- });
288
- });
289
-
290
- it('gets only the oldest attempt', async () => {
291
- const attempt = {
292
- ...defaultStatementFields(),
293
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
294
- };
295
- const attemptCompleted = {
296
- ...defaultStatementFields(),
297
- ...createCompletedStatement(attempt, {score: {raw: 4, max: 5, min: 0, scaled: 0.8}})
298
- };
299
-
300
- await new Promise((resolve) => setTimeout(resolve, 10));
301
-
302
- const attempt2 = {
303
- ...defaultStatementFields(),
304
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
305
- };
306
- mockGetStatements.mockReturnValue(Promise.resolve([
307
- attempt,
308
- attemptCompleted,
309
- attempt2
310
- ]));
311
- mockGetUser.mockReturnValue(Promise.resolve(user));
312
- const grade = await getCurrentGrade(
313
- services, mockRegistration, { currentPreference: 'oldest', userId: 'user id' }
314
- );
315
-
316
- expect(grade).toStrictEqual({
317
- grade: {
318
- activityProgress: 'Completed',
319
- gradingProgress: 'FullyGraded',
320
- scoreGiven: 80,
321
- scoreMaximum: 100,
322
- submission: {
323
- startedAt: expect.any(String),
324
- submittedAt: expect.any(String),
325
- },
326
- userId: 'user id'
327
- },
328
- name: undefined,
329
- progress: {
330
- scaled: 1
331
- }
332
- });
333
- });
334
- });
335
-
336
- describe('getAssignmentGrades', () => {
337
- let mockMapUserUuids: jest.SpyInstance;
338
- let mockLogEvent: jest.SpyInstance;
339
- let mockGetStatements: jest.SpyInstance;
340
- let services: {
341
- accountsGateway: AccountsGateway;
342
- lrs: LrsGateway;
343
- };
344
- const platformId = uuid();
345
- const assignmentIRI = 'https://openstax.org/orn/assignment/e2c1e160-4913-4397-b295-b31b0b4367ec';
346
- const mockRegistration = uuidV5(uuid(), uuidV5(platformId, uuidV5.URL));
347
- const studentId1 = 'a3ee0232-06c4-46cd-8d2f-0d7a12788b4a';
348
- const studentId2 = '74f3254c-cbc6-417f-9442-dc76d877f744';
349
- const studentId3 = 'e9accd58-f8ad-4132-a8fe-69486f81ae6e';
350
- const studentUuid1 = '3beb5a53-2628-4c82-8853-5892577cb672';
351
- const studentUuid2 = 'efe70ccb-9712-424a-912a-51486b2ae138';
352
- const studentUuid3 = '6aaebe17-d1e2-432a-8506-f6ce6f809bf0';
353
- const userUuidToPlatformUserIdMap: { [uuid: string]: string } = {
354
- [studentUuid1]: studentId1,
355
- [studentUuid2]: studentId2,
356
- [studentUuid3]: studentId3,
357
- };
358
- const userUuidToNameMap: { [uuid: string]: string } = {
359
- [studentUuid1]: 'Alice',
360
- [studentUuid2]: 'Bob',
361
- [studentUuid3]: 'Carol',
362
- };
363
-
364
- const defaultStatementFields = (accountName: string) => ({
365
- id: uuid(),
366
- timestamp: new Date().toISOString(),
367
- actor: {
368
- account: {
369
- homePage: 'https://openstax.org' as const,
370
- name: accountName,
371
- },
372
- objectType: 'Agent' as const,
373
- },
374
- });
375
-
376
- beforeEach(() => {
377
- mockMapUserUuids = jest.fn().mockImplementation(<T>(
378
- userUuidsMap: { [uuid: string]: T }, platformId: string
379
- ) => Object.entries(userUuidsMap).map(
380
- ([uuid, data]) => ({
381
- data,
382
- fullName: userUuidToNameMap[uuid],
383
- platformUserId: platformId ? userUuidToPlatformUserIdMap[uuid] : undefined,
384
- uuid,
385
- })
386
- ));
387
- mockLogEvent = jest.fn();
388
- mockGetStatements = jest.fn();
389
- services = {
390
- accountsGateway: {mapUserUuids: mockMapUserUuids},
391
- lrs: {getAllXapiStatements: mockGetStatements},
392
- } as any;
393
- });
394
-
395
- it('gets no grades when there are no statements', async () => {
396
- mockGetStatements.mockReturnValue(Promise.resolve([]));
397
- const grades = await getAssignmentGrades(services, assignmentIRI, mockRegistration);
398
-
399
- expect(grades).toEqual([]);
400
-
401
- expect(mockLogEvent).not.toHaveBeenCalled();
402
- });
403
-
404
- it('gets grade when there are statements', async () => {
405
- const attempt1 = {
406
- ...defaultStatementFields(studentUuid1),
407
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
408
- };
409
- const attempt2 = {
410
- ...defaultStatementFields(studentUuid2),
411
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
412
- };
413
- const attempt3 = {
414
- ...defaultStatementFields(studentUuid3),
415
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
416
- };
417
- mockGetStatements.mockReturnValue(Promise.resolve([
418
- attempt1,
419
- {
420
- ...defaultStatementFields(studentUuid1),
421
- ...createCompletedStatement(attempt1, {score: {raw: 4, max: 5, min: 0, scaled: 0.8}})
422
- },
423
- attempt2,
424
- {
425
- ...defaultStatementFields(studentUuid2),
426
- ...createCompletedStatement(attempt2, {score: {raw: 3, max: 5, min: 0, scaled: 0.6}})
427
- },
428
- attempt3,
429
- ]));
430
- const grades = await getAssignmentGrades(services, assignmentIRI, mockRegistration);
431
-
432
- expect(grades).toStrictEqual([
433
- {
434
- grade: {
435
- activityProgress: 'Completed',
436
- gradingProgress: 'FullyGraded',
437
- scoreGiven: 80,
438
- scoreMaximum: 100,
439
- submission: {
440
- startedAt: expect.any(String),
441
- submittedAt: expect.any(String),
442
- },
443
- userId: undefined
444
- },
445
- name: 'Alice',
446
- progress: {
447
- scaled: 1
448
- }
449
- },
450
- {
451
- grade: {
452
- activityProgress: 'Completed',
453
- gradingProgress: 'FullyGraded',
454
- scoreGiven: 60,
455
- scoreMaximum: 100,
456
- submission: {
457
- startedAt: expect.any(String),
458
- submittedAt: expect.any(String),
459
- },
460
- userId: undefined
461
- },
462
- name: 'Bob',
463
- progress: {
464
- scaled: 1
465
- }
466
- },
467
- {
468
- grade: {
469
- activityProgress: 'Started',
470
- gradingProgress: 'FullyGraded',
471
- scoreGiven: 0,
472
- scoreMaximum: 100,
473
- submission: {
474
- startedAt: expect.any(String),
475
- },
476
- userId: undefined
477
- },
478
- name: 'Carol',
479
- progress: {
480
- scaled: 0
481
- }
482
- },
483
- ]);
484
-
485
- expect(mockLogEvent).not.toHaveBeenCalled();
486
- });
487
-
488
- it('can accept a callback to get incomplete assignments', async () => {
489
- const attempt1 = {
490
- ...defaultStatementFields(studentUuid1),
491
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
492
- };
493
- const attempt2 = {
494
- ...defaultStatementFields(studentUuid2),
495
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
496
- };
497
- const attempt3 = {
498
- ...defaultStatementFields(studentUuid3),
499
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
500
- };
501
- mockGetStatements.mockReturnValue(Promise.resolve([
502
- attempt1,
503
- {
504
- ...defaultStatementFields(studentUuid1),
505
- ...createCompletedStatement(attempt1, {score: {raw: 4, max: 5, min: 0, scaled: 0.8}})
506
- },
507
- attempt2,
508
- {
509
- ...defaultStatementFields(studentUuid2),
510
- ...createCompletedStatement(attempt2, {score: {raw: 3, max: 5, min: 0, scaled: 0.6}})
511
- },
512
- attempt3,
513
- ]));
514
-
515
- const incompleteAttemptsCallback = jest.fn().mockReturnValue(Promise.resolve([]));
516
- const grades = await getAssignmentGrades(
517
- services, assignmentIRI, mockRegistration, { incompleteAttemptsCallback }
518
- );
519
-
520
- expect(grades).toStrictEqual([
521
- {
522
- grade: {
523
- activityProgress: 'Completed',
524
- gradingProgress: 'FullyGraded',
525
- scoreGiven: 80,
526
- scoreMaximum: 100,
527
- submission: {
528
- startedAt: expect.any(String),
529
- submittedAt: expect.any(String),
530
- },
531
- userId: undefined
532
- },
533
- name: 'Alice',
534
- progress: {
535
- scaled: 1
536
- }
537
- },
538
- {
539
- grade: {
540
- activityProgress: 'Completed',
541
- gradingProgress: 'FullyGraded',
542
- scoreGiven: 60,
543
- scoreMaximum: 100,
544
- submission: {
545
- startedAt: expect.any(String),
546
- submittedAt: expect.any(String),
547
- },
548
- userId: undefined
549
- },
550
- name: 'Bob',
551
- progress: {
552
- scaled: 1
553
- }
554
- }
555
- ]);
556
-
557
- expect(incompleteAttemptsCallback).toHaveBeenCalledTimes(1);
558
-
559
- const args = incompleteAttemptsCallback.mock.calls[0];
560
- expect(args).toHaveLength(1);
561
-
562
- const mappedInfo = args[0];
563
- expect(mappedInfo).toHaveLength(1);
564
-
565
- const { data, fullName, platformUserId, uuid } = mappedInfo[0];
566
- expect(data.attempts).toBe(1);
567
- expect(data.completedAttempts).toBe(0);
568
- expect(fullName).toBe('Carol');
569
- expect(platformUserId).toBeUndefined();
570
- expect(uuid).toEqual(studentUuid3);
571
-
572
- expect(mockLogEvent).not.toHaveBeenCalled();
573
- });
574
-
575
- it('gets grade when grade has no min/max/raw values', async () => {
576
- const attempt1 = {
577
- ...defaultStatementFields(studentUuid1),
578
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
579
- };
580
- const attempt2 = {
581
- ...defaultStatementFields(studentUuid2),
582
- ...createAttemptStatement({iri: 'my cool activity', type: 'cool type', name: 'the activity name'})
583
- };
584
- mockGetStatements.mockReturnValue(Promise.resolve([
585
- attempt1,
586
- {
587
- ...defaultStatementFields(studentUuid1),
588
- ...createCompletedStatement(attempt1, {score: {scaled: 0.8}})
589
- },
590
- attempt2,
591
- {
592
- ...defaultStatementFields(studentUuid2),
593
- ...createCompletedStatement(attempt2, {score: {scaled: 0.6}})
594
- },
595
- ]));
596
- const grades = await getAssignmentGrades(
597
- services, assignmentIRI, mockRegistration, { platformId, scoreMaximum: 10 }
598
- );
599
-
600
- expect(grades).toStrictEqual([
601
- {
602
- grade: {
603
- activityProgress: 'Completed',
604
- gradingProgress: 'FullyGraded',
605
- scoreGiven: 8,
606
- scoreMaximum: 10,
607
- submission: {
608
- startedAt: expect.any(String),
609
- submittedAt: expect.any(String),
610
- },
611
- userId: 'a3ee0232-06c4-46cd-8d2f-0d7a12788b4a'
612
- },
613
- name: 'Alice',
614
- progress: {
615
- scaled: 1
616
- }
617
- },
618
- {
619
- grade: {
620
- activityProgress: 'Completed',
621
- gradingProgress: 'FullyGraded',
622
- scoreGiven: 6,
623
- scoreMaximum: 10,
624
- submission: {
625
- startedAt: expect.any(String),
626
- submittedAt: expect.any(String),
627
- },
628
- userId: '74f3254c-cbc6-417f-9442-dc76d877f744'
629
- },
630
- name: 'Bob',
631
- progress: {
632
- scaled: 1
633
- }
634
- }
635
- ]);
636
-
637
- expect(mockLogEvent).not.toHaveBeenCalled();
638
- });
639
-
640
- it('uses best completed attempt when gradePreference is "best" and partitions accordingly', async () => {
641
- // Alice has two completed attempts. Best is the older
642
- const aliceOlderAttempt = {
643
- ...defaultStatementFields(studentUuid1),
644
- ...createAttemptStatement({ iri: 'my cool activity', type: 'cool type', name: 'the activity name' }),
645
- };
646
- const aliceOlderCompleted = {
647
- ...defaultStatementFields(studentUuid1),
648
- ...createCompletedStatement(aliceOlderAttempt, { score: { raw: 9, max: 10, min: 0, scaled: 0.9 } }),
649
- };
650
- const aliceNewerAttempt = {
651
- ...defaultStatementFields(studentUuid1),
652
- ...createAttemptStatement({ iri: 'my cool activity', type: 'cool type', name: 'the activity name' }),
653
- };
654
- const aliceNewerCompleted = {
655
- ...defaultStatementFields(studentUuid1),
656
- ...createCompletedStatement(aliceNewerAttempt, { score: { raw: 4, max: 10, min: 0, scaled: 0.4 } }),
657
- };
658
-
659
- // Bob has an uncompleted attempt
660
- const bobAttemptOnly = {
661
- ...defaultStatementFields(studentUuid2),
662
- ...createAttemptStatement({ iri: 'my cool activity', type: 'cool type', name: 'the activity name' }),
663
- };
664
-
665
- // Carol has two completed attempts. The newer one is the best.
666
- const carolOlderAttempt = {
667
- ...defaultStatementFields(studentUuid3),
668
- ...createAttemptStatement({ iri: 'my cool activity', type: 'cool type', name: 'the activity name' }),
669
- };
670
- const carolOlderCompleted = {
671
- ...defaultStatementFields(studentUuid3),
672
- ...createCompletedStatement(carolOlderAttempt, { score: { scaled: 0.6 } }),
673
- };
674
- const carolNewerAttempt = {
675
- ...defaultStatementFields(studentUuid3),
676
- ...createAttemptStatement({ iri: 'my cool activity', type: 'cool type', name: 'the activity name' }),
677
- };
678
- const carolNewerCompleted = {
679
- ...defaultStatementFields(studentUuid3),
680
- ...createCompletedStatement(carolNewerAttempt, { score: { scaled: 0.7 } }),
681
- };
682
-
683
- mockGetStatements.mockReturnValue(Promise.resolve([
684
- aliceOlderAttempt, aliceOlderCompleted,
685
- bobAttemptOnly,
686
- carolOlderAttempt, carolOlderCompleted,
687
- carolNewerAttempt, carolNewerCompleted,
688
- aliceNewerAttempt, aliceNewerCompleted,
689
- ]));
690
-
691
- const incompleteAttemptsCallback = jest.fn().mockResolvedValue([]);
692
-
693
- const grades = await getAssignmentGrades(
694
- services,
695
- assignmentIRI,
696
- mockRegistration,
697
- {
698
- gradePreference: 'best',
699
- platformId,
700
- incompleteAttemptsCallback,
701
- }
702
- );
703
-
704
- // Should grade Alice's best 0.9 and Carol's best 0.7
705
- expect(grades).toStrictEqual([
706
- {
707
- grade: {
708
- activityProgress: 'Completed',
709
- gradingProgress: 'FullyGraded',
710
- scoreGiven: 90,
711
- scoreMaximum: 100,
712
- submission: {
713
- startedAt: expect.any(String),
714
- submittedAt: expect.any(String),
715
- },
716
- userId: studentId1,
717
- },
718
- name: 'Alice',
719
- progress: { scaled: 1 },
720
- },
721
- {
722
- grade: {
723
- activityProgress: 'Completed',
724
- gradingProgress: 'FullyGraded',
725
- scoreGiven: 70,
726
- scoreMaximum: 100,
727
- submission: {
728
- startedAt: expect.any(String),
729
- submittedAt: expect.any(String),
730
- },
731
- userId: studentId3,
732
- },
733
- name: 'Carol',
734
- progress: { scaled: 1 },
735
- },
736
- ]);
737
-
738
- // incompleteAttemptsCallback should receive only Bob because he has an incomplete attempt.
739
- expect(incompleteAttemptsCallback).toHaveBeenCalledTimes(1);
740
- const [incompleteList] = incompleteAttemptsCallback.mock.calls[0];
741
- expect(Array.isArray(incompleteList)).toBe(true);
742
- expect(incompleteList).toHaveLength(1);
743
- expect(incompleteList[0].uuid).toBe(studentUuid2);
744
- expect(incompleteList[0].fullName).toBe('Bob');
745
- });
746
-
747
- it('uses current attempt when gradePreference is "current" and partitions accordingly', async () => {
748
- // Alice has two completed attempts. Current is the newer. 0.4 score
749
- const aliceOlderAttempt = {
750
- ...defaultStatementFields(studentUuid1),
751
- ...createAttemptStatement({ iri: 'my cool activity', type: 'cool type', name: 'the activity name' }),
752
- };
753
- const aliceOlderCompleted = {
754
- ...defaultStatementFields(studentUuid1),
755
- ...createCompletedStatement(aliceOlderAttempt, { score: { raw: 9, max: 10, min: 0, scaled: 0.9 } }),
756
- };
757
- const aliceNewerAttempt = {
758
- ...defaultStatementFields(studentUuid1),
759
- ...createAttemptStatement({ iri: 'my cool activity', type: 'cool type', name: 'the activity name' }),
760
- };
761
- const aliceNewerCompleted = {
762
- ...defaultStatementFields(studentUuid1),
763
- ...createCompletedStatement(aliceNewerAttempt, { score: { raw: 4, max: 10, min: 0, scaled: 0.4 } }),
764
- };
765
-
766
- // Bob only an uncompleted attempt
767
- const bobAttemptOnly = {
768
- ...defaultStatementFields(studentUuid2),
769
- ...createAttemptStatement({ iri: 'my cool activity', type: 'cool type', name: 'the activity name' }),
770
- };
771
-
772
- // Carol has two completed attempts. Current is the newer. 0.7 score
773
- const carolOlderAttempt = {
774
- ...defaultStatementFields(studentUuid3),
775
- ...createAttemptStatement({ iri: 'my cool activity', type: 'cool type', name: 'the activity name' }),
776
- };
777
- const carolOlderCompleted = {
778
- ...defaultStatementFields(studentUuid3),
779
- ...createCompletedStatement(carolOlderAttempt, { score: { scaled: 0.6 } }),
780
- };
781
- const carolNewerAttempt = {
782
- ...defaultStatementFields(studentUuid3),
783
- ...createAttemptStatement({ iri: 'my cool activity', type: 'cool type', name: 'the activity name' }),
784
- };
785
- const carolNewerCompleted = {
786
- ...defaultStatementFields(studentUuid3),
787
- ...createCompletedStatement(carolNewerAttempt, { score: { scaled: 0.7 } }),
788
- };
789
-
790
- mockGetStatements.mockReturnValue(Promise.resolve([
791
- aliceOlderAttempt, aliceOlderCompleted,
792
- bobAttemptOnly,
793
- carolOlderAttempt, carolOlderCompleted,
794
- carolNewerAttempt, carolNewerCompleted,
795
- aliceNewerAttempt, aliceNewerCompleted,
796
- ]));
797
-
798
- const incompleteAttemptsCallback = jest.fn().mockResolvedValue([]);
799
-
800
- const grades = await getAssignmentGrades(
801
- services,
802
- assignmentIRI,
803
- mockRegistration,
804
- {
805
- platformId,
806
- incompleteAttemptsCallback,
807
- gradePreference: 'current',
808
- }
809
- );
810
-
811
- // Alice's current should have 0.4. Carol's current should have 0.7
812
- expect(grades).toStrictEqual([
813
- {
814
- grade: {
815
- activityProgress: 'Completed',
816
- gradingProgress: 'FullyGraded',
817
- scoreGiven: 40,
818
- scoreMaximum: 100,
819
- submission: {
820
- startedAt: expect.any(String),
821
- submittedAt: expect.any(String),
822
- },
823
- userId: studentId1,
824
- },
825
- name: 'Alice',
826
- progress: { scaled: 1 },
827
- },
828
- {
829
- grade: {
830
- activityProgress: 'Completed',
831
- gradingProgress: 'FullyGraded',
832
- scoreGiven: 70,
833
- scoreMaximum: 100,
834
- submission: {
835
- startedAt: expect.any(String),
836
- submittedAt: expect.any(String),
837
- },
838
- userId: studentId3,
839
- },
840
- name: 'Carol',
841
- progress: { scaled: 1 },
842
- },
843
- ]);
844
-
845
- // Bob is the only incomplete attempt
846
- expect(incompleteAttemptsCallback).toHaveBeenCalledTimes(1);
847
- const [incompleteList] = incompleteAttemptsCallback.mock.calls[0];
848
- expect(Array.isArray(incompleteList)).toBe(true);
849
- expect(incompleteList).toHaveLength(1);
850
- expect(incompleteList[0].uuid).toBe(studentUuid2);
851
- expect(incompleteList[0].fullName).toBe('Bob');
852
- });
853
-
854
- it('uses current if best attempt and completed does not exist when gradePreference is "best"', async () => {
855
- // Alice has one incomplete attempt but gradePreference is best
856
-
857
- const aliceAttempt = {
858
- ...defaultStatementFields(studentUuid1),
859
- ...createAttemptStatement({ iri: 'my cool activity', type: 'cool type', name: 'the activity name' }),
860
- };
861
-
862
- mockGetStatements.mockReturnValue(Promise.resolve([
863
- aliceAttempt,
864
- ]));
865
-
866
- const incompleteAttemptsCallback = jest.fn().mockResolvedValue([]);
867
-
868
- const grades = await getAssignmentGrades(
869
- services,
870
- assignmentIRI,
871
- mockRegistration,
872
- {
873
- gradePreference: 'best',
874
- platformId,
875
- incompleteAttemptsCallback,
876
- }
877
- );
878
- expect(grades).toStrictEqual([]);
879
-
880
- expect(incompleteAttemptsCallback).toHaveBeenCalledTimes(1);
881
- const [incompleteList] = incompleteAttemptsCallback.mock.calls[0];
882
- expect(Array.isArray(incompleteList)).toBe(true);
883
- expect(incompleteList).toHaveLength(1);
884
- expect(incompleteList[0].uuid).toBe(studentUuid1);
885
- expect(incompleteList[0].fullName).toBe('Alice');
886
- });
887
- });