@openstax/ts-utils 1.33.1 → 1.34.0

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 (504) hide show
  1. package/.cfnlintrc +2 -0
  2. package/.github/CODEOWNERS +1 -0
  3. package/.github/workflows/ci.yml +36 -0
  4. package/.github/workflows/lint.yml +55 -0
  5. package/.nvmrc +1 -0
  6. package/.syncignore +4 -0
  7. package/.syncpackrc +18 -0
  8. package/CONTRIBUTING.md +96 -0
  9. package/LICENSE +661 -0
  10. package/Procfile +1 -0
  11. package/README.md +62 -90
  12. package/app.json +23 -0
  13. package/cspell.json +32 -0
  14. package/deploy/constants.env +21 -0
  15. package/deploy/deploy.bash +157 -0
  16. package/deploy/deployment-alt-region.cfn.yml +70 -0
  17. package/deploy/deployment.cfn.yml +650 -0
  18. package/deploy/destroy-deployment.bash +23 -0
  19. package/deploy/shared.cfn.yml +94 -0
  20. package/docs/lambda-build.md +35 -0
  21. package/package.json +12 -228
  22. package/packages/frontend/README.md +46 -0
  23. package/packages/frontend/package.json +101 -0
  24. package/packages/frontend/public/favicon.ico +0 -0
  25. package/packages/frontend/public/index.html +107 -0
  26. package/packages/frontend/public/maintenance.html +59 -0
  27. package/packages/frontend/public/manifest.json +15 -0
  28. package/packages/frontend/public/robots.txt +3 -0
  29. package/packages/frontend/script/make-certificate.bash +49 -0
  30. package/packages/frontend/script/server/cli.js +11 -0
  31. package/packages/frontend/script/server/index.js +47 -0
  32. package/packages/frontend/script/start.bash +22 -0
  33. package/packages/frontend/script/trust-localhost.bash +7 -0
  34. package/packages/frontend/src/auth/authProvider.ts +10 -0
  35. package/packages/frontend/src/auth/useAuth.ts +33 -0
  36. package/packages/frontend/src/components/Pagination.tsx +26 -0
  37. package/packages/frontend/src/configProvider/index.ts +53 -0
  38. package/packages/frontend/src/configProvider/use.ts +41 -0
  39. package/packages/frontend/src/core/context/services.spec.tsx +39 -0
  40. package/packages/frontend/src/core/context/services.tsx +16 -0
  41. package/packages/frontend/src/core/index.spec.ts +7 -0
  42. package/packages/frontend/src/core/index.ts +20 -0
  43. package/packages/frontend/src/core/services.tsx +14 -0
  44. package/packages/frontend/src/core/types.ts +3 -0
  45. package/packages/frontend/src/example/api.ts +28 -0
  46. package/packages/frontend/src/example/components/Layout.tsx +23 -0
  47. package/packages/frontend/src/example/screens/Home.spec.tsx +68 -0
  48. package/packages/frontend/src/example/screens/Home.tsx +78 -0
  49. package/packages/frontend/src/example/screens/ThingList.spec.tsx +60 -0
  50. package/packages/frontend/src/example/screens/ThingList.tsx +75 -0
  51. package/packages/frontend/src/example/screens/ThingView.spec.tsx +71 -0
  52. package/packages/frontend/src/example/screens/ThingView.tsx +47 -0
  53. package/packages/frontend/src/example/screens/index.ts +9 -0
  54. package/packages/frontend/src/index.css +159 -0
  55. package/packages/frontend/src/index.tsx +67 -0
  56. package/packages/frontend/src/react-app-env.d.ts +1 -0
  57. package/packages/frontend/src/routing/components/RouteLink.spec.tsx +55 -0
  58. package/packages/frontend/src/routing/components/RouteLink.tsx +35 -0
  59. package/packages/frontend/src/routing/middleware.ts +6 -0
  60. package/packages/frontend/src/routing/useQuery.ts +14 -0
  61. package/packages/frontend/src/setupProxy.js +19 -0
  62. package/packages/frontend/src/setupTests.ts +9 -0
  63. package/packages/frontend/src/tests/testServices.tsx +23 -0
  64. package/packages/frontend/tsconfig.json +27 -0
  65. package/packages/lambda/.eslintrc.js +64 -0
  66. package/packages/lambda/jest-global-setup.js +3 -0
  67. package/packages/lambda/jest-setup-after-env.js +1 -0
  68. package/packages/lambda/jest.config.js +31 -0
  69. package/packages/lambda/jest.resolver.js +17 -0
  70. package/packages/lambda/package.json +68 -0
  71. package/packages/lambda/script/build.bash +19 -0
  72. package/packages/lambda/script/bundle-functions.bash +10 -0
  73. package/packages/lambda/script/lambdaLocalProxy.js +16 -0
  74. package/packages/lambda/script/lambdaLocalProxy.spec.ts +147 -0
  75. package/packages/lambda/script/utils/getRouteData.ts +7 -0
  76. package/packages/lambda/script/utils/routeDataLoader.js +8 -0
  77. package/packages/lambda/script/utils/routeDataLoader.spec.ts +8 -0
  78. package/packages/lambda/src/functions/serviceApi/core/index.ts +7 -0
  79. package/packages/lambda/src/functions/serviceApi/core/request.spec.ts +38 -0
  80. package/packages/lambda/src/functions/serviceApi/core/request.ts +42 -0
  81. package/packages/lambda/src/functions/serviceApi/core/routes.spec.ts +7 -0
  82. package/packages/lambda/src/functions/serviceApi/core/routes.ts +10 -0
  83. package/packages/lambda/src/functions/serviceApi/core/services.ts +9 -0
  84. package/packages/lambda/src/functions/serviceApi/core/types.ts +13 -0
  85. package/packages/lambda/src/functions/serviceApi/entry/lambda/https-xray.ts +4 -0
  86. package/packages/lambda/src/functions/serviceApi/entry/lambda/index.spec.ts +48 -0
  87. package/packages/lambda/src/functions/serviceApi/entry/lambda/index.ts +58 -0
  88. package/packages/lambda/src/functions/serviceApi/entry/lambda/services.ts +36 -0
  89. package/packages/lambda/src/functions/serviceApi/entry/local.ts +71 -0
  90. package/packages/lambda/src/functions/serviceApi/versions/v0/example/documentSearchMiddleware.spec.ts +16 -0
  91. package/packages/lambda/src/functions/serviceApi/versions/v0/example/documentSearchMiddleware.ts +41 -0
  92. package/packages/lambda/src/functions/serviceApi/versions/v0/example/documentStoreMiddleware.spec.ts +78 -0
  93. package/packages/lambda/src/functions/serviceApi/versions/v0/example/documentStoreMiddleware.ts +70 -0
  94. package/packages/lambda/src/functions/serviceApi/versions/v0/example/routes.spec.ts +306 -0
  95. package/packages/lambda/src/functions/serviceApi/versions/v0/example/routes.ts +176 -0
  96. package/packages/lambda/src/functions/serviceApi/versions/v0/index.spec.ts +263 -0
  97. package/packages/lambda/src/functions/serviceApi/versions/v0/index.ts +134 -0
  98. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/authMiddleware.spec.ts +23 -0
  99. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/authMiddleware.ts +32 -0
  100. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/configMiddleware.spec.ts +10 -0
  101. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/configMiddleware.ts +7 -0
  102. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/frontendFileServerMiddleware.spec.ts +13 -0
  103. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/frontendFileServerMiddleware.ts +23 -0
  104. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/paginationMiddleware.spec.ts +9 -0
  105. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/paginationMiddleware.ts +9 -0
  106. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/searchMiddleware.spec.ts +12 -0
  107. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/searchMiddleware.ts +21 -0
  108. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/userRoleValidatorMiddleware.spec.ts +21 -0
  109. package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/userRoleValidatorMiddleware.ts +18 -0
  110. package/packages/lambda/tsconfig.json +30 -0
  111. package/packages/lambda/webpack.config.js +97 -0
  112. package/packages/utils/.eslintrc.js +64 -0
  113. package/packages/utils/README.md +118 -0
  114. package/packages/utils/jest-global-setup.js +3 -0
  115. package/packages/utils/jest.config.js +25 -0
  116. package/packages/utils/jest.resolver.js +17 -0
  117. package/packages/utils/package.json +238 -0
  118. package/packages/utils/src/assertions/index.spec.ts +126 -0
  119. package/{dist/esm/assertions/index.js → packages/utils/src/assertions/index.ts} +64 -49
  120. package/packages/utils/src/aws/ssmService.ts +7 -0
  121. package/packages/utils/src/config/awsParameterConfig.ts +24 -0
  122. package/packages/utils/src/config/envConfig.ts +58 -0
  123. package/packages/utils/src/config/index.spec.ts +165 -0
  124. package/{dist/esm/config/index.d.ts → packages/utils/src/config/index.ts} +29 -13
  125. package/packages/utils/src/config/lambdaParameterConfig.ts +49 -0
  126. package/{dist/esm/config/replaceConfig.js → packages/utils/src/config/replaceConfig.ts} +16 -6
  127. package/packages/utils/src/config/resolveConfigValue.ts +10 -0
  128. package/packages/utils/src/errors/index.spec.ts +35 -0
  129. package/{dist/esm/errors/index.js → packages/utils/src/errors/index.ts} +57 -41
  130. package/packages/utils/src/fetch/fetchStatusRetry.spec.ts +197 -0
  131. package/packages/utils/src/fetch/fetchStatusRetry.ts +33 -0
  132. package/packages/utils/src/fetch/index.spec.ts +34 -0
  133. package/packages/utils/src/fetch/index.ts +87 -0
  134. package/packages/utils/src/guards/index.spec.ts +58 -0
  135. package/{dist/esm/guards/index.d.ts → packages/utils/src/guards/index.ts} +10 -7
  136. package/packages/utils/src/index.spec.ts +471 -0
  137. package/packages/utils/src/middleware/apiErrorHandler.spec.ts +65 -0
  138. package/packages/utils/src/middleware/apiErrorHandler.ts +67 -0
  139. package/packages/utils/src/middleware/apiSlowResponseMiddleware.spec.ts +184 -0
  140. package/packages/utils/src/middleware/apiSlowResponseMiddleware.ts +71 -0
  141. package/packages/utils/src/middleware/index.spec.ts +99 -0
  142. package/{dist/cjs/middleware/index.d.ts → packages/utils/src/middleware/index.ts} +53 -5
  143. package/packages/utils/src/middleware/lambdaCorsResponseMiddleware.spec.ts +103 -0
  144. package/packages/utils/src/middleware/lambdaCorsResponseMiddleware.ts +52 -0
  145. package/packages/utils/src/middleware/throwNotFoundMiddleware.spec.ts +20 -0
  146. package/packages/utils/src/middleware/throwNotFoundMiddleware.ts +11 -0
  147. package/packages/utils/src/misc/hashValue.ts +18 -0
  148. package/packages/utils/src/misc/helpers.ts +259 -0
  149. package/packages/utils/src/misc/merge.ts +48 -0
  150. package/{dist/esm/misc/partitionSequence.js → packages/utils/src/misc/partitionSequence.ts} +23 -15
  151. package/packages/utils/src/pagination/index.spec.ts +150 -0
  152. package/packages/utils/src/pagination/index.ts +117 -0
  153. package/{dist/esm/routing/helpers.js → packages/utils/src/routing/helpers.ts} +42 -30
  154. package/packages/utils/src/routing/index.spec.ts +553 -0
  155. package/packages/utils/src/routing/index.ts +424 -0
  156. package/packages/utils/src/routing/validators/zod.spec.ts +16 -0
  157. package/packages/utils/src/routing/validators/zod.ts +14 -0
  158. package/packages/utils/src/services/accountsGateway/README.md +3 -0
  159. package/packages/utils/src/services/accountsGateway/index.spec.ts +518 -0
  160. package/packages/utils/src/services/accountsGateway/index.ts +251 -0
  161. package/packages/utils/src/services/apiGateway/README.md +93 -0
  162. package/packages/utils/src/services/apiGateway/index.spec.ts +254 -0
  163. package/packages/utils/src/services/apiGateway/index.ts +189 -0
  164. package/packages/utils/src/services/authProvider/README.md +21 -0
  165. package/packages/utils/src/services/authProvider/browser.spec.ts +391 -0
  166. package/packages/utils/src/services/authProvider/browser.ts +209 -0
  167. package/packages/utils/src/services/authProvider/decryption.spec.ts +337 -0
  168. package/packages/utils/src/services/authProvider/decryption.ts +98 -0
  169. package/packages/utils/src/services/authProvider/index.ts +93 -0
  170. package/packages/utils/src/services/authProvider/stub.spec.ts +29 -0
  171. package/packages/utils/src/services/authProvider/subrequest.spec.ts +105 -0
  172. package/packages/utils/src/services/authProvider/subrequest.ts +68 -0
  173. package/packages/utils/src/services/authProvider/utils/decryptAndVerify.spec.ts +128 -0
  174. package/packages/utils/src/services/authProvider/utils/decryptAndVerify.ts +106 -0
  175. package/packages/utils/src/services/authProvider/utils/embeddedAuthProvider.spec.ts +26 -0
  176. package/packages/utils/src/services/authProvider/utils/embeddedAuthProvider.ts +57 -0
  177. package/packages/utils/src/services/authProvider/utils/userRoleValidator.spec.ts +135 -0
  178. package/packages/utils/src/services/authProvider/utils/userRoleValidator.ts +49 -0
  179. package/packages/utils/src/services/authProvider/utils/userSubrequest.spec.ts +26 -0
  180. package/packages/utils/src/services/authProvider/utils/userSubrequest.ts +10 -0
  181. package/packages/utils/src/services/documentStore/dynamoEncoding.ts +57 -0
  182. package/packages/utils/src/services/documentStore/fileSystemAssert.spec.ts +43 -0
  183. package/packages/utils/src/services/documentStore/fileSystemAssert.ts +10 -0
  184. package/{dist/cjs/services/documentStore/index.d.ts → packages/utils/src/services/documentStore/index.ts} +8 -8
  185. package/packages/utils/src/services/documentStore/unversioned/README.md +13 -0
  186. package/packages/utils/src/services/documentStore/unversioned/dynamodb.spec.ts +859 -0
  187. package/packages/utils/src/services/documentStore/unversioned/dynamodb.ts +243 -0
  188. package/packages/utils/src/services/documentStore/unversioned/file-system.spec.ts +629 -0
  189. package/packages/utils/src/services/documentStore/unversioned/file-system.ts +194 -0
  190. package/{dist/cjs/services/documentStore/unversioned/index.d.ts → packages/utils/src/services/documentStore/unversioned/index.ts} +2 -0
  191. package/packages/utils/src/services/documentStore/versioned/README.md +13 -0
  192. package/packages/utils/src/services/documentStore/versioned/dynamodb.spec.ts +376 -0
  193. package/packages/utils/src/services/documentStore/versioned/dynamodb.ts +167 -0
  194. package/packages/utils/src/services/documentStore/versioned/file-system.spec.ts +262 -0
  195. package/packages/utils/src/services/documentStore/versioned/file-system.ts +90 -0
  196. package/packages/utils/src/services/documentStore/versioned/index.ts +25 -0
  197. package/packages/utils/src/services/exercisesGateway/README.md +5 -0
  198. package/packages/utils/src/services/exercisesGateway/index.spec.ts +326 -0
  199. package/packages/utils/src/services/exercisesGateway/index.ts +163 -0
  200. package/packages/utils/src/services/fileServer/index.spec.ts +88 -0
  201. package/packages/utils/src/services/fileServer/index.ts +43 -0
  202. package/packages/utils/src/services/fileServer/localFileServer.spec.ts +182 -0
  203. package/packages/utils/src/services/fileServer/localFileServer.ts +159 -0
  204. package/packages/utils/src/services/fileServer/s3FileServer.spec.ts +266 -0
  205. package/packages/utils/src/services/fileServer/s3FileServer.ts +155 -0
  206. package/packages/utils/src/services/launchParams/index.spec.ts +366 -0
  207. package/packages/utils/src/services/launchParams/signer.ts +73 -0
  208. package/packages/utils/src/services/launchParams/verifier.ts +120 -0
  209. package/packages/utils/src/services/logger/console.spec.ts +29 -0
  210. package/{dist/esm/services/logger/console.js → packages/utils/src/services/logger/console.ts} +5 -2
  211. package/packages/utils/src/services/logger/index.spec.ts +65 -0
  212. package/{dist/esm/services/logger/index.d.ts → packages/utils/src/services/logger/index.ts} +23 -9
  213. package/packages/utils/src/services/lrsGateway/README.md +5 -0
  214. package/packages/utils/src/services/lrsGateway/addStatementDefaultFields.ts +22 -0
  215. package/packages/utils/src/services/lrsGateway/attempt-utils.spec.ts +847 -0
  216. package/packages/utils/src/services/lrsGateway/attempt-utils.ts +358 -0
  217. package/packages/utils/src/services/lrsGateway/file-system.spec.ts +363 -0
  218. package/packages/utils/src/services/lrsGateway/file-system.ts +165 -0
  219. package/packages/utils/src/services/lrsGateway/index.spec.ts +194 -0
  220. package/packages/utils/src/services/lrsGateway/index.ts +257 -0
  221. package/packages/utils/src/services/lrsGateway/xapiUtils.spec.ts +887 -0
  222. package/packages/utils/src/services/lrsGateway/xapiUtils.ts +262 -0
  223. package/packages/utils/src/services/postgresConnection/index.spec.ts +170 -0
  224. package/packages/utils/src/services/postgresConnection/index.ts +84 -0
  225. package/packages/utils/src/services/searchProvider/README.md +3 -0
  226. package/packages/utils/src/services/searchProvider/index.ts +59 -0
  227. package/packages/utils/src/services/searchProvider/memorySearchTheBadWay.spec.ts +526 -0
  228. package/packages/utils/src/services/searchProvider/memorySearchTheBadWay.ts +223 -0
  229. package/packages/utils/src/services/searchProvider/openSearch.spec.ts +926 -0
  230. package/packages/utils/src/services/searchProvider/openSearch.ts +195 -0
  231. package/{dist/esm/types.d.ts → packages/utils/src/types.ts} +34 -6
  232. package/packages/utils/tsconfig.json +31 -0
  233. package/packages/utils/tsconfig.without-specs.cjs.json +7 -0
  234. package/packages/utils/tsconfig.without-specs.esm.json +7 -0
  235. package/packages/utils/tsconfig.without-specs.json +6 -0
  236. package/scripts/build.bash +24 -0
  237. package/scripts/ci.bash +10 -0
  238. package/scripts/start.bash +29 -0
  239. package/dist/cjs/assertions/index.d.ts +0 -89
  240. package/dist/cjs/assertions/index.js +0 -157
  241. package/dist/cjs/aws/ssmService.d.ts +0 -5
  242. package/dist/cjs/aws/ssmService.js +0 -9
  243. package/dist/cjs/config/awsParameterConfig.d.ts +0 -10
  244. package/dist/cjs/config/awsParameterConfig.js +0 -26
  245. package/dist/cjs/config/envConfig.d.ts +0 -24
  246. package/dist/cjs/config/envConfig.js +0 -57
  247. package/dist/cjs/config/index.d.ts +0 -48
  248. package/dist/cjs/config/index.js +0 -35
  249. package/dist/cjs/config/lambdaParameterConfig.d.ts +0 -12
  250. package/dist/cjs/config/lambdaParameterConfig.js +0 -45
  251. package/dist/cjs/config/replaceConfig.d.ts +0 -14
  252. package/dist/cjs/config/replaceConfig.js +0 -22
  253. package/dist/cjs/config/resolveConfigValue.d.ts +0 -5
  254. package/dist/cjs/config/resolveConfigValue.js +0 -12
  255. package/dist/cjs/errors/index.d.ts +0 -88
  256. package/dist/cjs/errors/index.js +0 -123
  257. package/dist/cjs/fetch/fetchStatusRetry.d.ts +0 -8
  258. package/dist/cjs/fetch/fetchStatusRetry.js +0 -27
  259. package/dist/cjs/fetch/index.d.ts +0 -64
  260. package/dist/cjs/fetch/index.js +0 -55
  261. package/dist/cjs/guards/index.d.ts +0 -38
  262. package/dist/cjs/guards/index.js +0 -44
  263. package/dist/cjs/index.js +0 -20
  264. package/dist/cjs/middleware/apiErrorHandler.d.ts +0 -24
  265. package/dist/cjs/middleware/apiErrorHandler.js +0 -42
  266. package/dist/cjs/middleware/apiSlowResponseMiddleware.d.ts +0 -23
  267. package/dist/cjs/middleware/apiSlowResponseMiddleware.js +0 -54
  268. package/dist/cjs/middleware/index.js +0 -48
  269. package/dist/cjs/middleware/lambdaCorsResponseMiddleware.d.ts +0 -20
  270. package/dist/cjs/middleware/lambdaCorsResponseMiddleware.js +0 -44
  271. package/dist/cjs/middleware/throwNotFoundMiddleware.d.ts +0 -4
  272. package/dist/cjs/middleware/throwNotFoundMiddleware.js +0 -14
  273. package/dist/cjs/misc/hashValue.d.ts +0 -10
  274. package/dist/cjs/misc/hashValue.js +0 -17
  275. package/dist/cjs/misc/helpers.d.ts +0 -124
  276. package/dist/cjs/misc/helpers.js +0 -214
  277. package/dist/cjs/misc/merge.d.ts +0 -21
  278. package/dist/cjs/misc/merge.js +0 -45
  279. package/dist/cjs/misc/partitionSequence.d.ts +0 -35
  280. package/dist/cjs/misc/partitionSequence.js +0 -55
  281. package/dist/cjs/pagination/index.d.ts +0 -91
  282. package/dist/cjs/pagination/index.js +0 -83
  283. package/dist/cjs/routing/helpers.d.ts +0 -57
  284. package/dist/cjs/routing/helpers.js +0 -90
  285. package/dist/cjs/routing/index.d.ts +0 -290
  286. package/dist/cjs/routing/index.js +0 -295
  287. package/dist/cjs/routing/validators/zod.d.ts +0 -4
  288. package/dist/cjs/routing/validators/zod.js +0 -14
  289. package/dist/cjs/services/accountsGateway/index.d.ts +0 -92
  290. package/dist/cjs/services/accountsGateway/index.js +0 -138
  291. package/dist/cjs/services/apiGateway/index.d.ts +0 -68
  292. package/dist/cjs/services/apiGateway/index.js +0 -118
  293. package/dist/cjs/services/authProvider/browser.d.ts +0 -40
  294. package/dist/cjs/services/authProvider/browser.js +0 -155
  295. package/dist/cjs/services/authProvider/decryption.d.ts +0 -19
  296. package/dist/cjs/services/authProvider/decryption.js +0 -73
  297. package/dist/cjs/services/authProvider/index.d.ts +0 -63
  298. package/dist/cjs/services/authProvider/index.js +0 -34
  299. package/dist/cjs/services/authProvider/subrequest.d.ts +0 -13
  300. package/dist/cjs/services/authProvider/subrequest.js +0 -49
  301. package/dist/cjs/services/authProvider/utils/decryptAndVerify.d.ts +0 -28
  302. package/dist/cjs/services/authProvider/utils/decryptAndVerify.js +0 -91
  303. package/dist/cjs/services/authProvider/utils/embeddedAuthProvider.d.ts +0 -26
  304. package/dist/cjs/services/authProvider/utils/embeddedAuthProvider.js +0 -47
  305. package/dist/cjs/services/authProvider/utils/userRoleValidator.d.ts +0 -13
  306. package/dist/cjs/services/authProvider/utils/userRoleValidator.js +0 -37
  307. package/dist/cjs/services/authProvider/utils/userSubrequest.d.ts +0 -3
  308. package/dist/cjs/services/authProvider/utils/userSubrequest.js +0 -13
  309. package/dist/cjs/services/documentStore/dynamoEncoding.d.ts +0 -10
  310. package/dist/cjs/services/documentStore/dynamoEncoding.js +0 -52
  311. package/dist/cjs/services/documentStore/fileSystemAssert.d.ts +0 -1
  312. package/dist/cjs/services/documentStore/fileSystemAssert.js +0 -14
  313. package/dist/cjs/services/documentStore/index.js +0 -2
  314. package/dist/cjs/services/documentStore/unversioned/dynamodb.d.ts +0 -31
  315. package/dist/cjs/services/documentStore/unversioned/dynamodb.js +0 -233
  316. package/dist/cjs/services/documentStore/unversioned/file-system.d.ts +0 -32
  317. package/dist/cjs/services/documentStore/unversioned/file-system.js +0 -214
  318. package/dist/cjs/services/documentStore/unversioned/index.js +0 -2
  319. package/dist/cjs/services/documentStore/versioned/dynamodb.d.ts +0 -25
  320. package/dist/cjs/services/documentStore/versioned/dynamodb.js +0 -143
  321. package/dist/cjs/services/documentStore/versioned/file-system.d.ts +0 -25
  322. package/dist/cjs/services/documentStore/versioned/file-system.js +0 -73
  323. package/dist/cjs/services/documentStore/versioned/index.d.ts +0 -17
  324. package/dist/cjs/services/documentStore/versioned/index.js +0 -2
  325. package/dist/cjs/services/exercisesGateway/index.d.ts +0 -67
  326. package/dist/cjs/services/exercisesGateway/index.js +0 -107
  327. package/dist/cjs/services/fileServer/index.d.ts +0 -30
  328. package/dist/cjs/services/fileServer/index.js +0 -19
  329. package/dist/cjs/services/fileServer/localFileServer.d.ts +0 -13
  330. package/dist/cjs/services/fileServer/localFileServer.js +0 -132
  331. package/dist/cjs/services/fileServer/s3FileServer.d.ts +0 -14
  332. package/dist/cjs/services/fileServer/s3FileServer.js +0 -132
  333. package/dist/cjs/services/launchParams/index.js +0 -7
  334. package/dist/cjs/services/launchParams/signer.d.ts +0 -23
  335. package/dist/cjs/services/launchParams/signer.js +0 -58
  336. package/dist/cjs/services/launchParams/verifier.d.ts +0 -21
  337. package/dist/cjs/services/launchParams/verifier.js +0 -129
  338. package/dist/cjs/services/logger/console.d.ts +0 -4
  339. package/dist/cjs/services/logger/console.js +0 -12
  340. package/dist/cjs/services/logger/index.d.ts +0 -39
  341. package/dist/cjs/services/logger/index.js +0 -31
  342. package/dist/cjs/services/lrsGateway/addStatementDefaultFields.d.ts +0 -5
  343. package/dist/cjs/services/lrsGateway/addStatementDefaultFields.js +0 -21
  344. package/dist/cjs/services/lrsGateway/attempt-utils.d.ts +0 -70
  345. package/dist/cjs/services/lrsGateway/attempt-utils.js +0 -258
  346. package/dist/cjs/services/lrsGateway/file-system.d.ts +0 -15
  347. package/dist/cjs/services/lrsGateway/file-system.js +0 -150
  348. package/dist/cjs/services/lrsGateway/index.d.ts +0 -122
  349. package/dist/cjs/services/lrsGateway/index.js +0 -148
  350. package/dist/cjs/services/lrsGateway/xapiUtils.d.ts +0 -68
  351. package/dist/cjs/services/lrsGateway/xapiUtils.js +0 -109
  352. package/dist/cjs/services/postgresConnection/index.d.ts +0 -28
  353. package/dist/cjs/services/postgresConnection/index.js +0 -65
  354. package/dist/cjs/services/searchProvider/index.d.ts +0 -67
  355. package/dist/cjs/services/searchProvider/index.js +0 -2
  356. package/dist/cjs/services/searchProvider/memorySearchTheBadWay.d.ts +0 -20
  357. package/dist/cjs/services/searchProvider/memorySearchTheBadWay.js +0 -191
  358. package/dist/cjs/services/searchProvider/openSearch.d.ts +0 -28
  359. package/dist/cjs/services/searchProvider/openSearch.js +0 -154
  360. package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +0 -1
  361. package/dist/cjs/types.d.ts +0 -31
  362. package/dist/cjs/types.js +0 -2
  363. package/dist/esm/assertions/index.d.ts +0 -89
  364. package/dist/esm/aws/ssmService.d.ts +0 -5
  365. package/dist/esm/aws/ssmService.js +0 -6
  366. package/dist/esm/config/awsParameterConfig.d.ts +0 -10
  367. package/dist/esm/config/awsParameterConfig.js +0 -22
  368. package/dist/esm/config/envConfig.d.ts +0 -24
  369. package/dist/esm/config/envConfig.js +0 -53
  370. package/dist/esm/config/index.js +0 -17
  371. package/dist/esm/config/lambdaParameterConfig.d.ts +0 -12
  372. package/dist/esm/config/lambdaParameterConfig.js +0 -38
  373. package/dist/esm/config/replaceConfig.d.ts +0 -14
  374. package/dist/esm/config/resolveConfigValue.d.ts +0 -5
  375. package/dist/esm/config/resolveConfigValue.js +0 -8
  376. package/dist/esm/errors/index.d.ts +0 -88
  377. package/dist/esm/fetch/fetchStatusRetry.d.ts +0 -8
  378. package/dist/esm/fetch/fetchStatusRetry.js +0 -23
  379. package/dist/esm/fetch/index.d.ts +0 -64
  380. package/dist/esm/fetch/index.js +0 -46
  381. package/dist/esm/guards/index.js +0 -36
  382. package/dist/esm/index.d.ts +0 -4
  383. package/dist/esm/index.js +0 -4
  384. package/dist/esm/middleware/apiErrorHandler.d.ts +0 -24
  385. package/dist/esm/middleware/apiErrorHandler.js +0 -38
  386. package/dist/esm/middleware/apiSlowResponseMiddleware.d.ts +0 -23
  387. package/dist/esm/middleware/apiSlowResponseMiddleware.js +0 -50
  388. package/dist/esm/middleware/index.d.ts +0 -47
  389. package/dist/esm/middleware/index.js +0 -44
  390. package/dist/esm/middleware/lambdaCorsResponseMiddleware.d.ts +0 -20
  391. package/dist/esm/middleware/lambdaCorsResponseMiddleware.js +0 -40
  392. package/dist/esm/middleware/throwNotFoundMiddleware.d.ts +0 -4
  393. package/dist/esm/middleware/throwNotFoundMiddleware.js +0 -10
  394. package/dist/esm/misc/hashValue.d.ts +0 -10
  395. package/dist/esm/misc/hashValue.js +0 -13
  396. package/dist/esm/misc/helpers.d.ts +0 -124
  397. package/dist/esm/misc/helpers.js +0 -199
  398. package/dist/esm/misc/merge.d.ts +0 -21
  399. package/dist/esm/misc/merge.js +0 -40
  400. package/dist/esm/misc/partitionSequence.d.ts +0 -35
  401. package/dist/esm/pagination/index.d.ts +0 -91
  402. package/dist/esm/pagination/index.js +0 -77
  403. package/dist/esm/routing/helpers.d.ts +0 -57
  404. package/dist/esm/routing/index.d.ts +0 -290
  405. package/dist/esm/routing/index.js +0 -246
  406. package/dist/esm/routing/validators/zod.d.ts +0 -4
  407. package/dist/esm/routing/validators/zod.js +0 -10
  408. package/dist/esm/services/accountsGateway/index.d.ts +0 -92
  409. package/dist/esm/services/accountsGateway/index.js +0 -131
  410. package/dist/esm/services/apiGateway/index.d.ts +0 -68
  411. package/dist/esm/services/apiGateway/index.js +0 -77
  412. package/dist/esm/services/authProvider/browser.d.ts +0 -40
  413. package/dist/esm/services/authProvider/browser.js +0 -151
  414. package/dist/esm/services/authProvider/decryption.d.ts +0 -19
  415. package/dist/esm/services/authProvider/decryption.js +0 -69
  416. package/dist/esm/services/authProvider/index.d.ts +0 -63
  417. package/dist/esm/services/authProvider/index.js +0 -26
  418. package/dist/esm/services/authProvider/subrequest.d.ts +0 -13
  419. package/dist/esm/services/authProvider/subrequest.js +0 -45
  420. package/dist/esm/services/authProvider/utils/decryptAndVerify.d.ts +0 -28
  421. package/dist/esm/services/authProvider/utils/decryptAndVerify.js +0 -85
  422. package/dist/esm/services/authProvider/utils/embeddedAuthProvider.d.ts +0 -26
  423. package/dist/esm/services/authProvider/utils/embeddedAuthProvider.js +0 -40
  424. package/dist/esm/services/authProvider/utils/userRoleValidator.d.ts +0 -13
  425. package/dist/esm/services/authProvider/utils/userRoleValidator.js +0 -33
  426. package/dist/esm/services/authProvider/utils/userSubrequest.d.ts +0 -3
  427. package/dist/esm/services/authProvider/utils/userSubrequest.js +0 -6
  428. package/dist/esm/services/documentStore/dynamoEncoding.d.ts +0 -10
  429. package/dist/esm/services/documentStore/dynamoEncoding.js +0 -45
  430. package/dist/esm/services/documentStore/fileSystemAssert.d.ts +0 -1
  431. package/dist/esm/services/documentStore/fileSystemAssert.js +0 -10
  432. package/dist/esm/services/documentStore/index.d.ts +0 -14
  433. package/dist/esm/services/documentStore/index.js +0 -1
  434. package/dist/esm/services/documentStore/unversioned/dynamodb.d.ts +0 -31
  435. package/dist/esm/services/documentStore/unversioned/dynamodb.js +0 -226
  436. package/dist/esm/services/documentStore/unversioned/file-system.d.ts +0 -32
  437. package/dist/esm/services/documentStore/unversioned/file-system.js +0 -174
  438. package/dist/esm/services/documentStore/unversioned/index.d.ts +0 -2
  439. package/dist/esm/services/documentStore/unversioned/index.js +0 -1
  440. package/dist/esm/services/documentStore/versioned/dynamodb.d.ts +0 -25
  441. package/dist/esm/services/documentStore/versioned/dynamodb.js +0 -139
  442. package/dist/esm/services/documentStore/versioned/file-system.d.ts +0 -25
  443. package/dist/esm/services/documentStore/versioned/file-system.js +0 -69
  444. package/dist/esm/services/documentStore/versioned/index.d.ts +0 -17
  445. package/dist/esm/services/documentStore/versioned/index.js +0 -1
  446. package/dist/esm/services/exercisesGateway/index.d.ts +0 -67
  447. package/dist/esm/services/exercisesGateway/index.js +0 -70
  448. package/dist/esm/services/fileServer/index.d.ts +0 -30
  449. package/dist/esm/services/fileServer/index.js +0 -13
  450. package/dist/esm/services/fileServer/localFileServer.d.ts +0 -13
  451. package/dist/esm/services/fileServer/localFileServer.js +0 -125
  452. package/dist/esm/services/fileServer/s3FileServer.d.ts +0 -14
  453. package/dist/esm/services/fileServer/s3FileServer.js +0 -125
  454. package/dist/esm/services/launchParams/index.d.ts +0 -2
  455. package/dist/esm/services/launchParams/index.js +0 -2
  456. package/dist/esm/services/launchParams/signer.d.ts +0 -23
  457. package/dist/esm/services/launchParams/signer.js +0 -51
  458. package/dist/esm/services/launchParams/verifier.d.ts +0 -21
  459. package/dist/esm/services/launchParams/verifier.js +0 -92
  460. package/dist/esm/services/logger/console.d.ts +0 -4
  461. package/dist/esm/services/logger/index.js +0 -27
  462. package/dist/esm/services/lrsGateway/addStatementDefaultFields.d.ts +0 -5
  463. package/dist/esm/services/lrsGateway/addStatementDefaultFields.js +0 -14
  464. package/dist/esm/services/lrsGateway/attempt-utils.d.ts +0 -70
  465. package/dist/esm/services/lrsGateway/attempt-utils.js +0 -236
  466. package/dist/esm/services/lrsGateway/file-system.d.ts +0 -15
  467. package/dist/esm/services/lrsGateway/file-system.js +0 -110
  468. package/dist/esm/services/lrsGateway/index.d.ts +0 -122
  469. package/dist/esm/services/lrsGateway/index.js +0 -111
  470. package/dist/esm/services/lrsGateway/xapiUtils.d.ts +0 -68
  471. package/dist/esm/services/lrsGateway/xapiUtils.js +0 -99
  472. package/dist/esm/services/postgresConnection/index.d.ts +0 -28
  473. package/dist/esm/services/postgresConnection/index.js +0 -58
  474. package/dist/esm/services/searchProvider/index.d.ts +0 -67
  475. package/dist/esm/services/searchProvider/index.js +0 -1
  476. package/dist/esm/services/searchProvider/memorySearchTheBadWay.d.ts +0 -20
  477. package/dist/esm/services/searchProvider/memorySearchTheBadWay.js +0 -187
  478. package/dist/esm/services/searchProvider/openSearch.d.ts +0 -28
  479. package/dist/esm/services/searchProvider/openSearch.js +0 -150
  480. package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +0 -1
  481. package/dist/esm/types.js +0 -1
  482. package/script/bin/.init-params-script.bash.swp +0 -0
  483. /package/{script → packages/utils/script}/bin/copy-from-template.bash +0 -0
  484. /package/{script → packages/utils/script}/bin/delete-stack.bash +0 -0
  485. /package/{script → packages/utils/script}/bin/deploy.bash +0 -0
  486. /package/{script → packages/utils/script}/bin/destroy-deployment.bash +0 -0
  487. /package/{script → packages/utils/script}/bin/empty-bucket.bash +0 -0
  488. /package/{script → packages/utils/script}/bin/get-arg.bash +0 -0
  489. /package/{script → packages/utils/script}/bin/get-deployed-environments.bash +0 -0
  490. /package/{script → packages/utils/script}/bin/get-env-param.bash +0 -0
  491. /package/{script → packages/utils/script}/bin/get-kwarg.bash +0 -0
  492. /package/{script → packages/utils/script}/bin/get-stack-param.bash +0 -0
  493. /package/{script → packages/utils/script}/bin/has-flag.bash +0 -0
  494. /package/{script → packages/utils/script}/bin/init-constants-script.bash +0 -0
  495. /package/{script → packages/utils/script}/bin/init-params-script.bash +0 -0
  496. /package/{script → packages/utils/script}/bin/stack-exists.bash +0 -0
  497. /package/{script → packages/utils/script}/bin/update-utils.bash +0 -0
  498. /package/{script → packages/utils/script}/bin/upload-pager-duty-endpoints.bash +0 -0
  499. /package/{script → packages/utils/script}/bin/upload-params.bash +0 -0
  500. /package/{script → packages/utils/script}/bin/which.bash +0 -0
  501. /package/{script → packages/utils/script}/bin-entry.bash +0 -0
  502. /package/{script → packages/utils/script}/build.bash +0 -0
  503. /package/{dist/cjs/index.d.ts → packages/utils/src/index.ts} +0 -0
  504. /package/{dist/cjs/services/launchParams/index.d.ts → packages/utils/src/services/launchParams/index.ts} +0 -0
@@ -0,0 +1,167 @@
1
+ import { AttributeValue, DynamoDB, PutItemCommand, QueryCommand, ScanCommand } from '@aws-sdk/client-dynamodb';
2
+ import { Config } from '..';
3
+ import { once } from '../../..';
4
+ import { ConfigProviderForConfig, resolveConfigValue } from '../../../config';
5
+ import { ifDefined } from '../../../guards';
6
+ import { decodeDynamoDocument, encodeDynamoAttribute, encodeDynamoDocument } from '../dynamoEncoding';
7
+ import { VersionedDocumentAuthor, VersionedTDocument } from '.';
8
+
9
+ interface Initializer<C> {
10
+ configSpace?: C;
11
+ dynamoClient?: DynamoDB;
12
+ }
13
+
14
+ // i'm not really excited about getAuthor being required, but ts is getting confused about the type when unspecified
15
+ export const dynamoVersionedDocumentStore = <C extends string = 'dynamodb'>(initializer?: Initializer<C>) => {
16
+ const init = ifDefined(initializer, {} as NonNullable<typeof initializer>);
17
+ const dynamodb = once(() => init.dynamoClient ?? new DynamoDB({apiVersion: '2012-08-10'}));
18
+
19
+ return <T extends VersionedTDocument<T>>() => (configProvider: {[_key in C]: ConfigProviderForConfig<Config>}) => <K extends keyof T, A extends undefined | ((...a: any[]) => Promise<VersionedDocumentAuthor>)>(_: {}, hashKey: K, options?: {afterWrite?: (item: T) => void | Promise<void>; getAuthor?: A}) => {
20
+ const tableName = once(() => resolveConfigValue(configProvider[ifDefined(init.configSpace, 'dynamodb' as C)].tableName));
21
+
22
+ return {
23
+ loadAllDocumentsTheBadWay: async() => {
24
+ const loadAllResults = async(ExclusiveStartKey?: {[key: string]: AttributeValue}): Promise<T[]> => {
25
+ const cmd = new ScanCommand({TableName: await tableName(), ExclusiveStartKey});
26
+ const result = await dynamodb().send(cmd);
27
+ const resultItems = result.Items?.map(decodeDynamoDocument) as T[] || [];
28
+
29
+ if (result.LastEvaluatedKey) {
30
+ return [...resultItems, ...await loadAllResults(result.LastEvaluatedKey)];
31
+ }
32
+
33
+ return resultItems;
34
+ };
35
+
36
+ const allResults = await loadAllResults().then(results => results.reduce(
37
+ (result, document) => {
38
+ const current = result.get(document[hashKey]);
39
+ if (!current || current.timestamp < document.timestamp) {
40
+ return result.set(document[hashKey], document);
41
+ }
42
+ return result;
43
+ }
44
+ , new Map<T[K], T>()));
45
+
46
+ return Array.from(allResults.values());
47
+ },
48
+ getVersions: async(id: T[K], startVersion?: number) => {
49
+ const cmd = new QueryCommand({
50
+ TableName: await tableName(),
51
+ KeyConditionExpression: '#hk = :hkv',
52
+ ExpressionAttributeValues: {
53
+ ':hkv': encodeDynamoAttribute(id)
54
+ },
55
+ ExpressionAttributeNames: {
56
+ '#hk': hashKey.toString()
57
+ },
58
+ ...(startVersion
59
+ ? { ExclusiveStartKey: {
60
+ [hashKey]: encodeDynamoAttribute(id),
61
+ timestamp: {N: startVersion.toString()}
62
+ }}
63
+ : {}
64
+ ),
65
+ ScanIndexForward: false,
66
+ });
67
+
68
+ return dynamodb().send(cmd).then(result => {
69
+ const items = result.Items?.map(decodeDynamoDocument) as T[] | undefined;
70
+
71
+ if (!items || items.length === 0) {
72
+ return undefined;
73
+ }
74
+
75
+ return {
76
+ items,
77
+ nextPageToken: result.LastEvaluatedKey ? decodeDynamoDocument(result.LastEvaluatedKey).timestamp as number : undefined
78
+ };
79
+ });
80
+ },
81
+ getItem: async(id: T[K], timestamp?: number) => {
82
+ let keyConditionExpression = '#hk = :hkv';
83
+ const expressionAttributeNames: { [key: string]: string } = {
84
+ '#hk': hashKey.toString()
85
+ };
86
+ const expressionAttributeValues: { [key: string]: AttributeValue } = {
87
+ ':hkv': encodeDynamoAttribute(id)
88
+ };
89
+
90
+ if (timestamp) {
91
+ keyConditionExpression += ' and #ts = :tsv';
92
+ expressionAttributeNames['#ts'] = 'timestamp';
93
+ expressionAttributeValues[':tsv'] = encodeDynamoAttribute(timestamp);
94
+ }
95
+
96
+ const cmd = new QueryCommand({
97
+ TableName: await tableName(),
98
+ KeyConditionExpression: keyConditionExpression,
99
+ ExpressionAttributeNames: expressionAttributeNames,
100
+ ExpressionAttributeValues: expressionAttributeValues,
101
+ ScanIndexForward: false,
102
+ Limit: 1
103
+ });
104
+
105
+ return dynamodb().send(cmd).then(result => {
106
+ return result.Items?.map(decodeDynamoDocument)[0] as T | undefined;
107
+ });
108
+ },
109
+ /* prepares a new version of a document with the given data, then allows some additional
110
+ * changes to be input to a `save` function that actually saves it. useful for additional
111
+ * changes based on the new document version or author. the document version and author
112
+ * cannot be modified */
113
+ prepareItem: async(
114
+ item: Omit<T, 'timestamp' | 'author'>,
115
+ ...authorArgs: A extends Function ? Parameters<A> : [VersionedDocumentAuthor]
116
+ ) => {
117
+ // this getAuthor type is terrible
118
+ const author = options?.getAuthor ? await options.getAuthor(...authorArgs) : authorArgs[0];
119
+ const timestamp = new Date().getTime();
120
+
121
+ return {
122
+ document: {...item, timestamp, author} as T,
123
+ save: async (changes?: Partial<Omit<T, 'timestamp' | 'author'>>) => {
124
+ const document = {
125
+ ...item,
126
+ ...changes,
127
+ timestamp,
128
+ author
129
+ } as T;
130
+
131
+ const cmd = new PutItemCommand({
132
+ TableName: await tableName(),
133
+ Item: encodeDynamoDocument(document),
134
+ });
135
+
136
+ const updatedDoc = await dynamodb().send(cmd)
137
+ .then(() => document);
138
+ await options?.afterWrite?.(updatedDoc);
139
+ return updatedDoc;
140
+ }
141
+ };
142
+ },
143
+ /* saves a new version of a document with the given data */
144
+ putItem: async(
145
+ item: Omit<T, 'timestamp' | 'author'>,
146
+ ...authorArgs: A extends Function ? Parameters<A> : [VersionedDocumentAuthor]
147
+ ) => {
148
+ // this getAuthor type is terrible
149
+ const author = options?.getAuthor ? await options.getAuthor(...authorArgs) : authorArgs[0];
150
+ const document = {
151
+ ...item,
152
+ timestamp: new Date().getTime(),
153
+ author
154
+ } as T;
155
+ const cmd = new PutItemCommand({
156
+ TableName: await tableName(),
157
+ Item: encodeDynamoDocument(document),
158
+ });
159
+
160
+ const updatedDoc = await dynamodb().send(cmd)
161
+ .then(() => document);
162
+ await options?.afterWrite?.(updatedDoc);
163
+ return updatedDoc;
164
+ },
165
+ };
166
+ };
167
+ };
@@ -0,0 +1,262 @@
1
+ import { TextEncoder } from 'util';
2
+ import { hashValue } from '../../..';
3
+ import { fileSystemVersionedDocumentStore } from './file-system';
4
+ import { VersionedDocumentRequiredFields } from '.';
5
+
6
+ type TTestDocument = {
7
+ coolId: string;
8
+ name: string;
9
+ } & VersionedDocumentRequiredFields;
10
+
11
+ describe('fileSystemVersionedDocumentStore', () => {
12
+ const config = {fileSystem: {tableName: 'db'}};
13
+ const middleware = {};
14
+ let readFileSpy: jest.SpyInstance;
15
+ let writeFileSpy: jest.SpyInstance;
16
+ let mkdirSpy: jest.SpyInstance;
17
+ let readdirSpy: jest.SpyInstance;
18
+ let files: string[] = [];
19
+ let data: string | undefined;
20
+ let error: Error | null = null;
21
+ let fs: Pick<typeof import('fs'), 'mkdir' | 'readdir' | 'readFile' | 'writeFile'>;
22
+
23
+ let consoleError: jest.SpyInstance;
24
+
25
+ beforeEach(() => {
26
+ error = null;
27
+ files = [];
28
+ data = undefined;
29
+
30
+ consoleError = jest.spyOn(console, 'error').mockImplementation(() => null);
31
+
32
+ mkdirSpy = jest.fn()
33
+ .mockImplementation((_path, _options, cb) => cb(null));
34
+
35
+ readdirSpy = jest.fn()
36
+ .mockImplementation((_file, cb) => cb(error, files));
37
+
38
+ readFileSpy = jest.fn()
39
+ .mockImplementation((_file, cb) => cb(error, Buffer.from(new TextEncoder().encode(data).buffer)));
40
+ writeFileSpy = jest.fn()
41
+ .mockImplementation((_file, _data, cb) => cb(error));
42
+
43
+ fs = {mkdir: mkdirSpy, readdir: readdirSpy, readFile: readFileSpy, writeFile: writeFileSpy} as any;
44
+ });
45
+
46
+ afterEach(() => {
47
+ jest.clearAllMocks();
48
+ consoleError.mockRestore();
49
+ });
50
+
51
+ describe('loadAllDocumentsTheBadWay', () => {
52
+ it('returns all records', async() => {
53
+ files = [`file/path/to/db/${hashValue('my-document-id')}.json`];
54
+ const testDoc = {coolId: 'my-document-id', timestamp: 123, name: 'my-doc'};
55
+ data = JSON.stringify({id: 'my-document-id', items: [testDoc]});
56
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
57
+
58
+ expect(await store.loadAllDocumentsTheBadWay()).toEqual([testDoc]);
59
+ });
60
+
61
+ it('works with null data', async() => {
62
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
63
+ expect(await store.loadAllDocumentsTheBadWay()).toEqual([]);
64
+ });
65
+ });
66
+
67
+ describe('getItem', () => {
68
+ it('allows loading records from file', async() => {
69
+ const testDoc = {coolId: 'my-document-id', timestamp: 123, name: 'my-doc'};
70
+ data = JSON.stringify({id: 'my-document-id', items: [testDoc]});
71
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
72
+
73
+ expect(await store.getItem('my-document-id')).toEqual(testDoc);
74
+ });
75
+
76
+ it('gets most recent version', async() => {
77
+ const testDoc1 = {coolId: 'my-document-id', timestamp: 123, name: 'my-doc'};
78
+ const testDoc2 = {coolId: 'my-document-id', timestamp: 234, name: 'my-doc'};
79
+ data = JSON.stringify({id: 'my-document-id', items: [testDoc1, testDoc2]});
80
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
81
+
82
+ expect(await store.getItem('my-document-id')).toEqual(testDoc2);
83
+ });
84
+
85
+ it('can get specific versions', async() => {
86
+ const testDoc1 = {coolId: 'my-document-id', timestamp: 123, name: 'my-doc'};
87
+ const testDoc2 = {coolId: 'my-document-id', timestamp: 234, name: 'my-doc'};
88
+ data = JSON.stringify({id: 'my-document-id', items: [testDoc1, testDoc2]});
89
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
90
+ expect(await store.getItem('my-document-id', 123)).toEqual(testDoc1);
91
+ expect(await store.getItem('my-document-id', 234)).toEqual(testDoc2);
92
+ });
93
+
94
+ it('returns undefined for non-existent ids', async() => {
95
+ readFileSpy.mockImplementation((_file, cb) => cb({code: 'ENOENT'}, null));
96
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
97
+ expect(await store.getItem('my-document-id', 345)).toBeUndefined();
98
+ expect(await store.getItem('my-document-id')).toBeUndefined();
99
+ });
100
+
101
+ it('returns undefined for non-existent versions', async() => {
102
+ const testDoc1 = {coolId: 'my-document-id', timestamp: 123, name: 'my-doc'};
103
+ const testDoc2 = {coolId: 'my-document-id', timestamp: 234, name: 'my-doc'};
104
+ data = JSON.stringify({id: 'my-document-id', items: [testDoc1, testDoc2]});
105
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
106
+ expect(await store.getItem('my-document-id', 345)).toBeUndefined();
107
+ });
108
+ });
109
+
110
+ describe('putItem', () => {
111
+ beforeAll(() => {
112
+ jest.useFakeTimers();
113
+ jest.setSystemTime(new Date(2020, 6, 28));
114
+ });
115
+
116
+ afterAll(() => {
117
+ jest.useRealTimers();
118
+ });
119
+
120
+ it('saves document', async() => {
121
+ readFileSpy.mockImplementationOnce((_file, cb) => cb({code: 'ENOENT'}, null));
122
+
123
+ const testDoc = {coolId: 'my-document-id', name: 'my-doc'};
124
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
125
+ await store.putItem(testDoc, {type: 'system', reason: 'testing'});
126
+ const testData = JSON.stringify({id: 'my-document-id', items: [{...testDoc, timestamp: 1595894400000, author: {type: 'system', reason: 'testing'}}]}, null, 2);
127
+ expect(writeFileSpy).toHaveBeenCalledWith(`file/path/to/db/${hashValue('my-document-id')}.json`, testData, expect.anything());
128
+ });
129
+
130
+ it('saves new version of document', async() => {
131
+ const testDoc = {coolId: 'my-document-id', name: 'my-doc'};
132
+ data = JSON.stringify({id: 'my-document-id', items: [{...testDoc, timestamp: 123, author: {type: 'system', reason: 'testing'}}]});
133
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
134
+ await store.putItem(testDoc, {type: 'system', reason: 'testing'});
135
+ const testData = JSON.stringify({id: 'my-document-id', items: [
136
+ {...testDoc, timestamp: 123, author: {type: 'system', reason: 'testing'}},
137
+ {...testDoc, timestamp: 1595894400000, author: {type: 'system', reason: 'testing'}},
138
+ ]}, null, 2);
139
+ expect(writeFileSpy).toHaveBeenCalledWith(`file/path/to/db/${hashValue('my-document-id')}.json`, testData, expect.anything());
140
+ });
141
+
142
+ it('returns new document', async() => {
143
+ readFileSpy.mockImplementationOnce((_file, cb) => cb({code: 'ENOENT'}, null));
144
+ const testDoc = {coolId: 'my-document-id', name: 'my-doc'};
145
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
146
+ const result = await store.putItem(testDoc, {type: 'system', reason: 'testing'});
147
+ expect(result).toEqual({...testDoc, timestamp: expect.any(Number), author: {type: 'system', reason: 'testing'}});
148
+ });
149
+
150
+ it('adds timestamp to document', async() => {
151
+ readFileSpy.mockImplementationOnce((_file, cb) => cb({code: 'ENOENT'}, null));
152
+ const testDoc = {coolId: 'my-document-id', name: 'my-doc'};
153
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
154
+ const result = await store.putItem(testDoc, {type: 'system', reason: 'testing'});
155
+ expect(result).toEqual({...testDoc, timestamp: 1595894400000, author: {type: 'system', reason: 'testing'}});
156
+ });
157
+
158
+ it('uses author provider', async() => {
159
+ readFileSpy.mockImplementationOnce((_file, cb) => cb({code: 'ENOENT'}, null));
160
+ const testDoc = {coolId: 'my-document-id', name: 'my-doc'};
161
+ const getAuthor = (name: string) => Promise.resolve({type: 'user' as const, name, uuid: 'a uuid'});
162
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', {getAuthor});
163
+ const result = await store.putItem(testDoc, 'a name');
164
+ expect(result).toEqual({...testDoc, timestamp: expect.any(Number), author: {type: 'user', name: 'a name', uuid: 'a uuid'}});
165
+ });
166
+ });
167
+
168
+ describe('prepareItem', () => {
169
+ beforeAll(() => {
170
+ jest.useFakeTimers();
171
+ jest.setSystemTime(new Date(2020, 6, 28));
172
+ });
173
+
174
+ afterAll(() => {
175
+ jest.useRealTimers();
176
+ });
177
+
178
+ it('saves document', async() => {
179
+ readFileSpy.mockImplementationOnce((_file, cb) => cb({code: 'ENOENT'}, null));
180
+ const testDoc = {coolId: 'my-document-id', name: 'my-doc'};
181
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', {skipAssert: true});
182
+ const {save} = await store.prepareItem(testDoc, {type: 'system', reason: 'testing'});
183
+ await save();
184
+ const testData = JSON.stringify({id: 'my-document-id', items: [
185
+ {...testDoc, timestamp: 1595894400000, author: {type: 'system', reason: 'testing'}},
186
+ ]}, null, 2);
187
+ expect(writeFileSpy).toHaveBeenCalledWith(`file/path/to/db/${hashValue('my-document-id')}.json`, testData, expect.anything());
188
+ });
189
+
190
+ it('saves version', async() => {
191
+ const testDoc = {coolId: 'my-document-id', name: 'my-doc'};
192
+ data = JSON.stringify({id: 'my-document-id', items: [{...testDoc, timestamp: 123, author: {type: 'system', reason: 'testing'}}]});
193
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', {skipAssert: false});
194
+ const {save} = await store.prepareItem(testDoc, {type: 'system', reason: 'testing'});
195
+ await save();
196
+ const testData = JSON.stringify({id: 'my-document-id', items: [
197
+ {...testDoc, timestamp: 123, author: {type: 'system', reason: 'testing'}},
198
+ {...testDoc, timestamp: 1595894400000, author: {type: 'system', reason: 'testing'}},
199
+ ]}, null, 2);
200
+ expect(writeFileSpy).toHaveBeenCalledWith(`file/path/to/db/${hashValue('my-document-id')}.json`, testData, expect.anything());
201
+ });
202
+
203
+ it('returns new document', async() => {
204
+ const testDoc = {coolId: 'my-document-id', name: 'my-doc'};
205
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
206
+ const {document} = await store.prepareItem(testDoc, {type: 'system', reason: 'testing'});
207
+ expect(document).toEqual({...testDoc, timestamp: expect.any(Number), author: {type: 'system', reason: 'testing'}});
208
+ });
209
+
210
+ it('adds timestamp to document', async() => {
211
+ const testDoc = {coolId: 'my-document-id', name: 'my-doc'};
212
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
213
+ const {document} = await store.prepareItem(testDoc, {type: 'system', reason: 'testing'});
214
+ expect(document).toEqual({...testDoc, timestamp: 1595894400000, author: {type: 'system', reason: 'testing'}});
215
+ });
216
+
217
+ it('uses author provider', async() => {
218
+ const testDoc = {coolId: 'my-document-id', name: 'my-doc'};
219
+ const getAuthor = (name: string) => Promise.resolve({type: 'user' as const, name, uuid: 'a uuid'});
220
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', {getAuthor});
221
+ const {document} = await store.prepareItem(testDoc, 'a name');
222
+ expect(document).toEqual({...testDoc, timestamp: expect.any(Number), author: {type: 'user', name: 'a name', uuid: 'a uuid'}});
223
+ });
224
+ });
225
+
226
+ describe('getVersions', () => {
227
+ it('gets versions for document', async() => {
228
+ const testDoc1 = {coolId: 'my-document-id', timestamp: 123, name: 'my-doc'};
229
+ const testDoc2 = {coolId: 'my-document-id', timestamp: 234, name: 'my-doc'};
230
+ data = JSON.stringify({id: 'my-document-id', items: [ testDoc1, testDoc2 ]}, null, 2);
231
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
232
+ expect((await store.getVersions('my-document-id'))?.items).toEqual([testDoc2, testDoc1]);
233
+ });
234
+
235
+ it('gets next page token', async() => {
236
+ const testDoc1 = {coolId: 'my-document-id', timestamp: 1, name: 'my-doc'};
237
+ const testDoc2 = {coolId: 'my-document-id', timestamp: 2, name: 'my-doc'};
238
+ const testDoc3 = {coolId: 'my-document-id', timestamp: 3, name: 'my-doc'};
239
+ const testDoc4 = {coolId: 'my-document-id', timestamp: 4, name: 'my-doc'};
240
+ const testDoc5 = {coolId: 'my-document-id', timestamp: 5, name: 'my-doc'};
241
+ const testDoc6 = {coolId: 'my-document-id', timestamp: 6, name: 'my-doc'};
242
+ data = JSON.stringify({id: 'my-document-id', items: [ testDoc1, testDoc2, testDoc3, testDoc4, testDoc5, testDoc6 ]}, null, 2);
243
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
244
+ expect((await store.getVersions('my-document-id'))?.nextPageToken).toBe(2);
245
+ });
246
+
247
+ it('gets undefined when data loaded with error', async() => {
248
+ readFileSpy.mockImplementationOnce((_file, cb) => cb({code: 'ENOENT'}, null));
249
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
250
+ expect((await store.getVersions('my-document-id'))?.items).toBeUndefined();
251
+ });
252
+
253
+ it('gets version with indicated start point', async() => {
254
+ const testDoc1 = {coolId: 'my-document-id', timestamp: 123, name: 'my-doc'};
255
+ const testDoc2 = {coolId: 'my-document-id', timestamp: 234, name: 'my-doc'};
256
+ const testDoc3 = {coolId: 'my-document-id', timestamp: 345, name: 'my-doc'};
257
+ data = JSON.stringify({id: 'my-document-id', items: [ testDoc1, testDoc2, testDoc3 ]}, null, 2);
258
+ const store = fileSystemVersionedDocumentStore({fs, dataDir: 'file/path/to'})<TTestDocument>()(config)(middleware, 'coolId', undefined);
259
+ expect((await store.getVersions('my-document-id', 345))?.items).toEqual([testDoc2, testDoc1]);
260
+ });
261
+ });
262
+ });
@@ -0,0 +1,90 @@
1
+ import { Config } from '..';
2
+ import { ConfigProviderForConfig } from '../../../config';
3
+ import { assertNoUndefined } from '../fileSystemAssert';
4
+ import { fileSystemUnversionedDocumentStore } from '../unversioned/file-system';
5
+ import { VersionedDocumentAuthor, VersionedTDocument } from '.';
6
+
7
+ const PAGE_LIMIT=5;
8
+
9
+ interface Initializer<C> {
10
+ dataDir: string;
11
+ fs?: Pick<typeof import('fs'), 'mkdir' | 'readdir' | 'readFile' | 'writeFile'>;
12
+ configSpace?: C;
13
+ }
14
+
15
+ type FileFormat<T extends VersionedTDocument<T>, K extends keyof T> = {id: T[K]; items: T[]};
16
+
17
+ export const fileSystemVersionedDocumentStore = <C extends string = 'fileSystem'>(initializer: Initializer<C>) => <T extends VersionedTDocument<T>>() => (configProvider: {[_key in C]: ConfigProviderForConfig<Config>}) => <K extends keyof T, A extends undefined | ((...a: any[]) => Promise<VersionedDocumentAuthor>)>(_: {}, hashKey: K, options?: {getAuthor?: A; skipAssert?: boolean}) => {
18
+
19
+ const skipAssert = options?.skipAssert ?? false;
20
+
21
+ const unversionedDocuments = fileSystemUnversionedDocumentStore(initializer)<FileFormat<T, K>>()(configProvider)({}, 'id', {skipAssert});
22
+
23
+ return {
24
+ loadAllDocumentsTheBadWay: () => {
25
+ return unversionedDocuments.loadAllDocumentsTheBadWay().then(documents => documents.map(document => {
26
+ return document.items[document.items.length - 1];
27
+ }));
28
+ },
29
+ getVersions: async(id: T[K], startVersion?: number) => {
30
+ if (!skipAssert) assertNoUndefined(id, ['id']);
31
+ const item = await unversionedDocuments.getItem(id);
32
+ const versions = item?.items.reverse();
33
+
34
+ if (!versions) {
35
+ return undefined;
36
+ }
37
+
38
+ const startIndex = startVersion ? versions.findIndex(version => version.timestamp === startVersion) + 1 : 0;
39
+ const items = versions.slice(startIndex, startIndex + PAGE_LIMIT);
40
+ const hasMore = (startIndex + 5) < versions.length;
41
+
42
+ return {
43
+ items,
44
+ nextPageToken: hasMore ? items[items.length - 1].timestamp : undefined
45
+ };
46
+ },
47
+ getItem: async(id: T[K], timestamp?: number) => {
48
+ if (!skipAssert) assertNoUndefined(id, ['id']);
49
+ const item = await unversionedDocuments.getItem(id);
50
+ if (timestamp) { return item?.items.find(version => version.timestamp === timestamp); }
51
+ return item ? item.items[item.items.length - 1] : undefined;
52
+ },
53
+ prepareItem: async(
54
+ item: Omit<T, 'timestamp' | 'author'>,
55
+ ...authorArgs: A extends Function ? Parameters<A> : [VersionedDocumentAuthor]
56
+ ) => {
57
+ // this getAuthor type is terrible
58
+ const author = options?.getAuthor ? await options.getAuthor(...authorArgs) : authorArgs[0];
59
+ const timestamp = new Date().getTime();
60
+
61
+ return {
62
+ document: {...item, timestamp, author} as T,
63
+ save: async (changes?: Partial<Omit<T, 'timestamp' | 'author'>>) => {
64
+ const document = {...item, ...changes, timestamp, author} as T;
65
+ if (!skipAssert) assertNoUndefined(document);
66
+ const container: FileFormat<T, K> = await unversionedDocuments.getItem(document[hashKey]) ?? {id: document[hashKey], items: []};
67
+ const updated = {...container, items: [...container.items, document]};
68
+ await unversionedDocuments.putItem(updated);
69
+
70
+ return document;
71
+ }
72
+ };
73
+ },
74
+ putItem: async(
75
+ item: Omit<T, 'timestamp' | 'author'>,
76
+ ...authorArgs: A extends Function ? Parameters<A> : [VersionedDocumentAuthor]
77
+ ) => {
78
+ const author = options?.getAuthor ? await options.getAuthor(...authorArgs) : authorArgs[0];
79
+ const document = {...item, timestamp: new Date().getTime(), author} as T;
80
+ if (!skipAssert) assertNoUndefined(document);
81
+ const container: FileFormat<T, K> = await unversionedDocuments.getItem(document[hashKey]) ?? {id: document[hashKey], items: []};
82
+
83
+ const updated = {...container, items: [...container.items, document]};
84
+
85
+ await unversionedDocuments.putItem(updated);
86
+
87
+ return document;
88
+ },
89
+ };
90
+ };
@@ -0,0 +1,25 @@
1
+ /* istanbul ignore file */
2
+ import { TDocument } from '..';
3
+ import { dynamoVersionedDocumentStore } from './dynamodb';
4
+
5
+ export type VersionedDocumentAuthor = {
6
+ type: 'user';
7
+ uuid: string;
8
+ name: string;
9
+ reason?: string;
10
+ } | {
11
+ type: 'system';
12
+ reason: string;
13
+ };
14
+
15
+ export type VersionedDocumentRequiredFields = {
16
+ timestamp: number;
17
+ author: VersionedDocumentAuthor;
18
+ };
19
+
20
+ // it would be easier if the data store document could be like `{doc: T, change: {timestamp, ...author}}`
21
+ // and have the gateway abstract the nesting for get/put and expose it only when getting version history
22
+ // but dynamo doesn't allow setting indexes on nested values so the id/timestamp have to be at the top level
23
+ export type VersionedTDocument<T> = TDocument<T> & VersionedDocumentRequiredFields;
24
+
25
+ export type VersionedDocumentStoreCreator = typeof dynamoVersionedDocumentStore;
@@ -0,0 +1,5 @@
1
+ # exercisesGateway
2
+
3
+ Typing/formatting for interacting with OpenStax Exercises
4
+
5
+ Query format (for and/or): 'tag:"andKey1:andValue1" tag:"orKey1:orValue1,orKey2:orValue2"'