json-fs-query 0.1.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.
- package/.browserslistrc +1 -0
- package/.nvmrc +1 -0
- package/LICENSE +674 -0
- package/MAIN.md +15 -0
- package/README.md +63 -0
- package/babel.config.js +3 -0
- package/build-tools.config.json +30 -0
- package/dist/data/introspect/create.d.ts +11 -0
- package/dist/data/introspect/create.js +45 -0
- package/dist/data/introspect/create.min.js +1 -0
- package/dist/data/introspect/definitions/field.d.ts +28 -0
- package/dist/data/introspect/definitions/field.js +36 -0
- package/dist/data/introspect/definitions/field.min.js +1 -0
- package/dist/data/introspect/definitions/key.d.ts +26 -0
- package/dist/data/introspect/definitions/key.js +53 -0
- package/dist/data/introspect/definitions/key.min.js +1 -0
- package/dist/data/introspect/definitions/keyReference.d.ts +18 -0
- package/dist/data/introspect/definitions/keyReference.js +20 -0
- package/dist/data/introspect/definitions/keyReference.min.js +1 -0
- package/dist/data/introspect/definitions/record.d.ts +26 -0
- package/dist/data/introspect/definitions/record.js +32 -0
- package/dist/data/introspect/definitions/record.min.js +1 -0
- package/dist/data/introspect/describe.d.ts +2 -0
- package/dist/data/introspect/describe.js +15 -0
- package/dist/data/introspect/describe.min.js +1 -0
- package/dist/data/introspect/drop.d.ts +2 -0
- package/dist/data/introspect/drop.js +37 -0
- package/dist/data/introspect/drop.min.js +1 -0
- package/dist/data/introspect/index.d.ts +8 -0
- package/dist/data/introspect/index.js +30 -0
- package/dist/data/introspect/index.min.js +1 -0
- package/dist/data/introspect/policies/foreignKeyExists.d.ts +9 -0
- package/dist/data/introspect/policies/foreignKeyExists.js +25 -0
- package/dist/data/introspect/policies/foreignKeyExists.min.js +1 -0
- package/dist/data/introspect/policies/keyGenerate.d.ts +2 -0
- package/dist/data/introspect/policies/keyGenerate.js +17 -0
- package/dist/data/introspect/policies/keyGenerate.min.js +1 -0
- package/dist/data/introspect/policies/keyUnique.d.ts +2 -0
- package/dist/data/introspect/policies/keyUnique.js +21 -0
- package/dist/data/introspect/policies/keyUnique.min.js +1 -0
- package/dist/data/introspect/policies/typeCheck.d.ts +12 -0
- package/dist/data/introspect/policies/typeCheck.js +57 -0
- package/dist/data/introspect/policies/typeCheck.min.js +1 -0
- package/dist/data/introspect/policies/typeEnum.d.ts +2 -0
- package/dist/data/introspect/policies/typeEnum.js +14 -0
- package/dist/data/introspect/policies/typeEnum.min.js +1 -0
- package/dist/data/introspect/policies/useDefault.d.ts +2 -0
- package/dist/data/introspect/policies/useDefault.js +13 -0
- package/dist/data/introspect/policies/useDefault.min.js +1 -0
- package/dist/data/queries/deleteEntity.d.ts +2 -0
- package/dist/data/queries/deleteEntity.js +79 -0
- package/dist/data/queries/deleteEntity.min.js +1 -0
- package/dist/data/queries/groupBy.d.ts +2 -0
- package/dist/data/queries/groupBy.js +22 -0
- package/dist/data/queries/groupBy.min.js +1 -0
- package/dist/data/queries/insertEntity.d.ts +2 -0
- package/dist/data/queries/insertEntity.js +115 -0
- package/dist/data/queries/insertEntity.min.js +1 -0
- package/dist/data/queries/joinEntity.d.ts +2 -0
- package/dist/data/queries/joinEntity.js +27 -0
- package/dist/data/queries/joinEntity.min.js +1 -0
- package/dist/data/queries/mergeJoins.d.ts +2 -0
- package/dist/data/queries/mergeJoins.js +19 -0
- package/dist/data/queries/mergeJoins.min.js +1 -0
- package/dist/data/queries/parser.d.ts +2 -0
- package/dist/data/queries/parser.js +30 -0
- package/dist/data/queries/parser.min.js +1 -0
- package/dist/data/queries/query.d.ts +2 -0
- package/dist/data/queries/query.js +18 -0
- package/dist/data/queries/query.min.js +1 -0
- package/dist/data/queries/readEntity.d.ts +2 -0
- package/dist/data/queries/readEntity.js +14 -0
- package/dist/data/queries/readEntity.min.js +1 -0
- package/dist/data/queries/sortBy.d.ts +2 -0
- package/dist/data/queries/sortBy.js +25 -0
- package/dist/data/queries/sortBy.min.js +1 -0
- package/dist/data/queries/updateEntity.d.ts +2 -0
- package/dist/data/queries/updateEntity.js +164 -0
- package/dist/data/queries/updateEntity.min.js +1 -0
- package/dist/data/queries/where.d.ts +7 -0
- package/dist/data/queries/where.js +56 -0
- package/dist/data/queries/where.min.js +1 -0
- package/dist/data/utilities/deregisterRecord.d.ts +8 -0
- package/dist/data/utilities/deregisterRecord.js +34 -0
- package/dist/data/utilities/deregisterRecord.min.js +1 -0
- package/dist/data/utilities/getConditionIndexedList.d.ts +3 -0
- package/dist/data/utilities/getConditionIndexedList.js +47 -0
- package/dist/data/utilities/getConditionIndexedList.min.js +1 -0
- package/dist/data/utilities/getJoinIndexedList.d.ts +3 -0
- package/dist/data/utilities/getJoinIndexedList.js +59 -0
- package/dist/data/utilities/getJoinIndexedList.min.js +1 -0
- package/dist/data/utilities/getMatchingIndexes.d.ts +2 -0
- package/dist/data/utilities/getMatchingIndexes.js +28 -0
- package/dist/data/utilities/getMatchingIndexes.min.js +1 -0
- package/dist/data/utilities/matchIndexedJoins.d.ts +2 -0
- package/dist/data/utilities/matchIndexedJoins.js +61 -0
- package/dist/data/utilities/matchIndexedJoins.min.js +1 -0
- package/dist/data/utilities/mergeJoin.d.ts +2 -0
- package/dist/data/utilities/mergeJoin.js +44 -0
- package/dist/data/utilities/mergeJoin.min.js +1 -0
- package/dist/data/utilities/parsers/findCommand.d.ts +10 -0
- package/dist/data/utilities/parsers/findCommand.js +25 -0
- package/dist/data/utilities/parsers/findCommand.min.js +1 -0
- package/dist/data/utilities/parsers/findConditions.d.ts +15 -0
- package/dist/data/utilities/parsers/findConditions.js +28 -0
- package/dist/data/utilities/parsers/findConditions.min.js +1 -0
- package/dist/data/utilities/parsers/findGroupBy.d.ts +9 -0
- package/dist/data/utilities/parsers/findGroupBy.js +22 -0
- package/dist/data/utilities/parsers/findGroupBy.min.js +1 -0
- package/dist/data/utilities/parsers/findInsertValues.d.ts +13 -0
- package/dist/data/utilities/parsers/findInsertValues.js +34 -0
- package/dist/data/utilities/parsers/findInsertValues.min.js +1 -0
- package/dist/data/utilities/parsers/findJoinClauses.d.ts +13 -0
- package/dist/data/utilities/parsers/findJoinClauses.js +27 -0
- package/dist/data/utilities/parsers/findJoinClauses.min.js +1 -0
- package/dist/data/utilities/parsers/findJoinEntity.d.ts +9 -0
- package/dist/data/utilities/parsers/findJoinEntity.js +22 -0
- package/dist/data/utilities/parsers/findJoinEntity.min.js +1 -0
- package/dist/data/utilities/parsers/findLimit.d.ts +9 -0
- package/dist/data/utilities/parsers/findLimit.js +22 -0
- package/dist/data/utilities/parsers/findLimit.min.js +1 -0
- package/dist/data/utilities/parsers/findMergeJoins.d.ts +12 -0
- package/dist/data/utilities/parsers/findMergeJoins.js +26 -0
- package/dist/data/utilities/parsers/findMergeJoins.min.js +1 -0
- package/dist/data/utilities/parsers/findOffset.d.ts +9 -0
- package/dist/data/utilities/parsers/findOffset.js +22 -0
- package/dist/data/utilities/parsers/findOffset.min.js +1 -0
- package/dist/data/utilities/parsers/findSelects.d.ts +12 -0
- package/dist/data/utilities/parsers/findSelects.js +30 -0
- package/dist/data/utilities/parsers/findSelects.min.js +1 -0
- package/dist/data/utilities/parsers/findSortClauses.d.ts +12 -0
- package/dist/data/utilities/parsers/findSortClauses.js +30 -0
- package/dist/data/utilities/parsers/findSortClauses.min.js +1 -0
- package/dist/data/utilities/parsers/findUpdateValues.d.ts +15 -0
- package/dist/data/utilities/parsers/findUpdateValues.js +34 -0
- package/dist/data/utilities/parsers/findUpdateValues.min.js +1 -0
- package/dist/data/utilities/parsers/isEntityProperty.d.ts +2 -0
- package/dist/data/utilities/parsers/isEntityProperty.js +8 -0
- package/dist/data/utilities/parsers/isEntityProperty.min.js +1 -0
- package/dist/data/utilities/parsers/parseLiteralValue.d.ts +12 -0
- package/dist/data/utilities/parsers/parseLiteralValue.js +34 -0
- package/dist/data/utilities/parsers/parseLiteralValue.min.js +1 -0
- package/dist/data/utilities/parsers/parseQuery.d.ts +33 -0
- package/dist/data/utilities/parsers/parseQuery.js +51 -0
- package/dist/data/utilities/parsers/parseQuery.min.js +1 -0
- package/dist/data/utilities/parsers/removeFromQuery.d.ts +2 -0
- package/dist/data/utilities/parsers/removeFromQuery.js +8 -0
- package/dist/data/utilities/parsers/removeFromQuery.min.js +1 -0
- package/dist/data/utilities/parsers/splitEntityProperty.d.ts +5 -0
- package/dist/data/utilities/parsers/splitEntityProperty.js +23 -0
- package/dist/data/utilities/parsers/splitEntityProperty.min.js +1 -0
- package/dist/data/utilities/parsers/splitQueries.d.ts +2 -0
- package/dist/data/utilities/parsers/splitQueries.js +11 -0
- package/dist/data/utilities/parsers/splitQueries.min.js +1 -0
- package/dist/data/utilities/reconcileJoin.d.ts +2 -0
- package/dist/data/utilities/reconcileJoin.js +26 -0
- package/dist/data/utilities/reconcileJoin.min.js +1 -0
- package/dist/data/utilities/reconcileJoins.d.ts +2 -0
- package/dist/data/utilities/reconcileJoins.js +27 -0
- package/dist/data/utilities/reconcileJoins.min.js +1 -0
- package/dist/data/utilities/registerRecord.d.ts +8 -0
- package/dist/data/utilities/registerRecord.js +67 -0
- package/dist/data/utilities/registerRecord.min.js +1 -0
- package/dist/data/utilities/removeFromIndex.d.ts +14 -0
- package/dist/data/utilities/removeFromIndex.js +46 -0
- package/dist/data/utilities/removeFromIndex.min.js +1 -0
- package/dist/data/utilities/restrictIndexedList.d.ts +2 -0
- package/dist/data/utilities/restrictIndexedList.js +45 -0
- package/dist/data/utilities/restrictIndexedList.min.js +1 -0
- package/dist/data/utilities/retrieveFile.d.ts +2 -0
- package/dist/data/utilities/retrieveFile.js +28 -0
- package/dist/data/utilities/retrieveFile.min.js +1 -0
- package/dist/data/utilities/retrieveIndexes.d.ts +2 -0
- package/dist/data/utilities/retrieveIndexes.js +24 -0
- package/dist/data/utilities/retrieveIndexes.min.js +1 -0
- package/dist/data/utilities/retrieveRecord.d.ts +2 -0
- package/dist/data/utilities/retrieveRecord.js +9 -0
- package/dist/data/utilities/retrieveRecord.min.js +1 -0
- package/dist/data/utilities/retrieveRecords.d.ts +2 -0
- package/dist/data/utilities/retrieveRecords.js +21 -0
- package/dist/data/utilities/retrieveRecords.min.js +1 -0
- package/dist/data/utilities/runQuery.d.ts +2 -0
- package/dist/data/utilities/runQuery.js +72 -0
- package/dist/data/utilities/runQuery.min.js +1 -0
- package/dist/data/utilities/updateIndex.d.ts +17 -0
- package/dist/data/utilities/updateIndex.js +56 -0
- package/dist/data/utilities/updateIndex.min.js +1 -0
- package/dist/data/utilities/useJoinClause.d.ts +6 -0
- package/dist/data/utilities/useJoinClause.js +51 -0
- package/dist/data/utilities/useJoinClause.min.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +35 -0
- package/dist/index.min.js +1 -0
- package/gulpfile.mjs +15 -0
- package/package.json +44 -0
- package/src/data/introspect/create.test.js +67 -0
- package/src/data/introspect/create.ts +29 -0
- package/src/data/introspect/definitions/field.test.js +23 -0
- package/src/data/introspect/definitions/field.ts +48 -0
- package/src/data/introspect/definitions/key.test.js +100 -0
- package/src/data/introspect/definitions/key.ts +64 -0
- package/src/data/introspect/definitions/keyReference.test.js +8 -0
- package/src/data/introspect/definitions/keyReference.ts +23 -0
- package/src/data/introspect/definitions/record.ts +42 -0
- package/src/data/introspect/describe.test.js +36 -0
- package/src/data/introspect/describe.ts +9 -0
- package/src/data/introspect/drop.test.js +77 -0
- package/src/data/introspect/drop.ts +20 -0
- package/src/data/introspect/index.ts +8 -0
- package/src/data/introspect/policies/foreignKeyExists.test.js +44 -0
- package/src/data/introspect/policies/foreignKeyExists.ts +15 -0
- package/src/data/introspect/policies/keyGenerate.ts +11 -0
- package/src/data/introspect/policies/keyUnique.ts +11 -0
- package/src/data/introspect/policies/typeCheck.test.js +84 -0
- package/src/data/introspect/policies/typeCheck.ts +49 -0
- package/src/data/introspect/policies/typeEnum.ts +6 -0
- package/src/data/introspect/policies/useDefault.ts +6 -0
- package/src/data/queries/deleteEntity.test.js +86 -0
- package/src/data/queries/deleteEntity.ts +56 -0
- package/src/data/queries/groupBy.test.js +135 -0
- package/src/data/queries/groupBy.ts +16 -0
- package/src/data/queries/insertEntity.test.js +156 -0
- package/src/data/queries/insertEntity.ts +113 -0
- package/src/data/queries/joinEntity.test.js +109 -0
- package/src/data/queries/joinEntity.ts +20 -0
- package/src/data/queries/mergeJoins.test.js +174 -0
- package/src/data/queries/mergeJoins.ts +13 -0
- package/src/data/queries/parser.test.js +343 -0
- package/src/data/queries/parser.ts +24 -0
- package/src/data/queries/query.test.js +233 -0
- package/src/data/queries/query.ts +12 -0
- package/src/data/queries/readEntity.test.js +85 -0
- package/src/data/queries/readEntity.ts +8 -0
- package/src/data/queries/sortBy.test.js +131 -0
- package/src/data/queries/sortBy.ts +18 -0
- package/src/data/queries/updateEntity.test.js +228 -0
- package/src/data/queries/updateEntity.ts +162 -0
- package/src/data/queries/where.test.js +129 -0
- package/src/data/queries/where.ts +54 -0
- package/src/data/utilities/deregisterRecord.test.js +26 -0
- package/src/data/utilities/deregisterRecord.ts +24 -0
- package/src/data/utilities/getConditionIndexedList.ts +43 -0
- package/src/data/utilities/getJoinIndexedList.ts +53 -0
- package/src/data/utilities/getMatchingIndexes.ts +21 -0
- package/src/data/utilities/matchIndexedJoins.ts +45 -0
- package/src/data/utilities/mergeJoin.test.js +102 -0
- package/src/data/utilities/mergeJoin.ts +28 -0
- package/src/data/utilities/parsers/findCommand.ts +25 -0
- package/src/data/utilities/parsers/findConditions.test.js +38 -0
- package/src/data/utilities/parsers/findConditions.ts +32 -0
- package/src/data/utilities/parsers/findGroupBy.ts +20 -0
- package/src/data/utilities/parsers/findInsertValues.test.js +26 -0
- package/src/data/utilities/parsers/findInsertValues.ts +32 -0
- package/src/data/utilities/parsers/findJoinClauses.ts +29 -0
- package/src/data/utilities/parsers/findJoinEntity.ts +20 -0
- package/src/data/utilities/parsers/findLimit.ts +20 -0
- package/src/data/utilities/parsers/findMergeJoins.ts +27 -0
- package/src/data/utilities/parsers/findOffset.ts +20 -0
- package/src/data/utilities/parsers/findSelects.ts +31 -0
- package/src/data/utilities/parsers/findSortClauses.ts +31 -0
- package/src/data/utilities/parsers/findUpdateValues.test.js +37 -0
- package/src/data/utilities/parsers/findUpdateValues.ts +32 -0
- package/src/data/utilities/parsers/isEntityProperty.ts +1 -0
- package/src/data/utilities/parsers/parseLiteralValue.test.js +41 -0
- package/src/data/utilities/parsers/parseLiteralValue.ts +29 -0
- package/src/data/utilities/parsers/parseQuery.ts +73 -0
- package/src/data/utilities/parsers/removeFromQuery.ts +1 -0
- package/src/data/utilities/parsers/splitEntityProperty.test.js +8 -0
- package/src/data/utilities/parsers/splitEntityProperty.ts +17 -0
- package/src/data/utilities/parsers/splitQueries.test.js +33 -0
- package/src/data/utilities/parsers/splitQueries.ts +4 -0
- package/src/data/utilities/reconcileJoin.test.js +38 -0
- package/src/data/utilities/reconcileJoin.ts +23 -0
- package/src/data/utilities/reconcileJoins.test.js +160 -0
- package/src/data/utilities/reconcileJoins.ts +15 -0
- package/src/data/utilities/registerRecord.test.js +44 -0
- package/src/data/utilities/registerRecord.ts +44 -0
- package/src/data/utilities/removeFromIndex.test.js +44 -0
- package/src/data/utilities/removeFromIndex.ts +34 -0
- package/src/data/utilities/restrictIndexedList.ts +38 -0
- package/src/data/utilities/retrieveFile.test.js +49 -0
- package/src/data/utilities/retrieveFile.ts +19 -0
- package/src/data/utilities/retrieveIndexes.ts +17 -0
- package/src/data/utilities/retrieveRecord.ts +3 -0
- package/src/data/utilities/retrieveRecords.test.js +223 -0
- package/src/data/utilities/retrieveRecords.ts +16 -0
- package/src/data/utilities/runQuery.test.js +88 -0
- package/src/data/utilities/runQuery.ts +75 -0
- package/src/data/utilities/updateIndex.test.js +44 -0
- package/src/data/utilities/updateIndex.ts +45 -0
- package/src/data/utilities/useJoinClause.test.js +94 -0
- package/src/data/utilities/useJoinClause.ts +44 -0
- package/src/global.d.ts +1 -0
- package/src/index.test.js +52 -0
- package/src/index.ts +17 -0
- package/test-data/__RECORDS.json +32 -0
- package/test-data/__indexes/exercise_muscle/fk_exercise_id.json +14 -0
- package/test-data/__indexes/exercise_muscle/fk_muscle_id.json +14 -0
- package/test-data/__indexes/exercise_muscle/pk__id.json +14 -0
- package/test-data/__indexes/exercises/pk__id.json +14 -0
- package/test-data/__indexes/muscles/pk__id.json +58 -0
- package/test-data/__indexes/workouts/fk_exercise_id.json +23 -0
- package/test-data/__indexes/workouts/index_date.json +18 -0
- package/test-data/__indexes/workouts/pk__id.json +26 -0
- package/test-data/copyDatabase.ts +64 -0
- package/test-data/exercise_muscle/bench_press_pectorals.json +6 -0
- package/test-data/exercise_muscle/deadlift_hamstrings.json +6 -0
- package/test-data/exercise_muscle/squat_quadriceps.json +6 -0
- package/test-data/exercise_muscle.json +69 -0
- package/test-data/exercises/bench_press.json +5 -0
- package/test-data/exercises/deadlift.json +5 -0
- package/test-data/exercises/squat.json +5 -0
- package/test-data/exercises.json +34 -0
- package/test-data/muscles/abdominal.json +6 -0
- package/test-data/muscles/biceps.json +6 -0
- package/test-data/muscles/calves.json +6 -0
- package/test-data/muscles/deltoids.json +6 -0
- package/test-data/muscles/erector_spinae.json +6 -0
- package/test-data/muscles/forearms.json +6 -0
- package/test-data/muscles/gluteus_maximus.json +6 -0
- package/test-data/muscles/hamstrings.json +6 -0
- package/test-data/muscles/latissimus_dorsi.json +6 -0
- package/test-data/muscles/obliques.json +6 -0
- package/test-data/muscles/pectorals.json +6 -0
- package/test-data/muscles/quadriceps.json +6 -0
- package/test-data/muscles/trapezius.json +6 -0
- package/test-data/muscles/triceps.json +6 -0
- package/test-data/muscles.json +50 -0
- package/test-data/workouts/2024-06-02-001.json +10 -0
- package/test-data/workouts/2024-06-02-002.json +10 -0
- package/test-data/workouts/2024-06-02-003.json +10 -0
- package/test-data/workouts/2024-06-05-001.json +10 -0
- package/test-data/workouts/2024-06-05-002.json +10 -0
- package/test-data/workouts/2024-06-05-003.json +10 -0
- package/test-data/workouts.json +84 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { create } from './create'
|
|
2
|
+
import { drop } from './drop'
|
|
3
|
+
import { insertEntity } from '../queries/insertEntity'
|
|
4
|
+
import { testHelpers } from 'js-build-tools/testHelpers'
|
|
5
|
+
|
|
6
|
+
const { writeFile, readFile } = require('fs/promises')
|
|
7
|
+
|
|
8
|
+
const databasePath = 'drop-database/'
|
|
9
|
+
const gulpConfig = testHelpers.gulpConfig
|
|
10
|
+
gulpConfig.set('databasePath', databasePath)
|
|
11
|
+
testHelpers.setDefaults(databasePath)
|
|
12
|
+
|
|
13
|
+
beforeEach(testHelpers.beforeEach)
|
|
14
|
+
|
|
15
|
+
afterEach(testHelpers.afterEach)
|
|
16
|
+
|
|
17
|
+
describe('drop', () => {
|
|
18
|
+
test('returns null for the reserved __RECORDS name, without touching anything', async () => {
|
|
19
|
+
expect(await drop('__RECORDS')).toBeNull()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('removes every entry file listed on the record', async () => {
|
|
23
|
+
const recordName = 'foo'
|
|
24
|
+
await create(recordName, [], [])
|
|
25
|
+
// insertEntity is not implemented yet, so simulate a real entry directly: write the data file, and record it
|
|
26
|
+
// on the record's own entries list, exactly as a working insertEntity would.
|
|
27
|
+
const entryFile = 'entry1.json'
|
|
28
|
+
await writeFile(`${databasePath}${recordName}/${entryFile}`, JSON.stringify({ _id: 1 }))
|
|
29
|
+
const recordContent = JSON.parse(await readFile(`${databasePath}${recordName}.json`, 'utf8'))
|
|
30
|
+
recordContent.entries = [entryFile]
|
|
31
|
+
await writeFile(`${databasePath}${recordName}.json`, JSON.stringify(recordContent))
|
|
32
|
+
|
|
33
|
+
expect(testHelpers.fileExists(`${databasePath}${recordName}/${entryFile}`)).toBeTruthy()
|
|
34
|
+
await drop(recordName)
|
|
35
|
+
expect(testHelpers.fileExists(`${databasePath}${recordName}/${entryFile}`)).toBeFalsy()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('deregisters the record from __RECORDS', async () => {
|
|
39
|
+
await create('foo', [], [])
|
|
40
|
+
await create('bar', [], [])
|
|
41
|
+
await drop('foo')
|
|
42
|
+
const registry = JSON.parse(await readFile(`${databasePath}__RECORDS.json`, 'utf8'))
|
|
43
|
+
expect(registry.entries).toEqual(['bar.json'])
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('removes the record\'s own definition file and data directory', async () => {
|
|
47
|
+
await create('foo', [], [])
|
|
48
|
+
expect(testHelpers.fileExists(`${databasePath}foo.json`)).toBeTruthy()
|
|
49
|
+
expect(testHelpers.fileExists(`${databasePath}foo`)).toBeTruthy()
|
|
50
|
+
|
|
51
|
+
await drop('foo')
|
|
52
|
+
|
|
53
|
+
expect(testHelpers.fileExists(`${databasePath}foo.json`)).toBeFalsy()
|
|
54
|
+
expect(testHelpers.fileExists(`${databasePath}foo`)).toBeFalsy()
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('removes the record\'s own index directory', async () => {
|
|
58
|
+
const definition = [
|
|
59
|
+
{ name: '_id', type: 'string', optional: false, autoGenerate: true }
|
|
60
|
+
]
|
|
61
|
+
const keys = [
|
|
62
|
+
{ type: 'primary', fields: ['_id'], lookup: 'pk__id.json' }
|
|
63
|
+
]
|
|
64
|
+
await create('foo', definition, keys)
|
|
65
|
+
await insertEntity('foo', [{}], {})
|
|
66
|
+
expect(testHelpers.fileExists(`${databasePath}__indexes/foo/pk__id.json`)).toBeTruthy()
|
|
67
|
+
|
|
68
|
+
await drop('foo')
|
|
69
|
+
|
|
70
|
+
expect(testHelpers.fileExists(`${databasePath}__indexes/foo`)).toBeFalsy()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('does not throw when the record has no index directory to remove', async () => {
|
|
74
|
+
await create('foo', [], [])
|
|
75
|
+
await expect(drop('foo')).resolves.not.toThrow()
|
|
76
|
+
})
|
|
77
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { retrieveFile } from '../utilities/retrieveFile'
|
|
2
|
+
import { deregisterRecord } from '../utilities/deregisterRecord'
|
|
3
|
+
|
|
4
|
+
const gulpConfig = require('js-build-tools/gulp.config')
|
|
5
|
+
const { rm } = require('fs/promises')
|
|
6
|
+
|
|
7
|
+
export const drop = async (record) => {
|
|
8
|
+
const databasePath = gulpConfig.get('databasePath', 'database/')
|
|
9
|
+
if (record === '__RECORDS') {
|
|
10
|
+
return null
|
|
11
|
+
}
|
|
12
|
+
const recordFile = await retrieveFile(`${record}.json`)
|
|
13
|
+
for (const file of recordFile.entries) {
|
|
14
|
+
await rm(`${databasePath}${recordFile.path}/${file}`)
|
|
15
|
+
}
|
|
16
|
+
await rm(`${databasePath}${recordFile.path}`, { recursive: true, force: true })
|
|
17
|
+
await rm(`${databasePath}__indexes/${recordFile.path}`, { recursive: true, force: true })
|
|
18
|
+
await rm(`${databasePath}${record}.json`)
|
|
19
|
+
await deregisterRecord(record)
|
|
20
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { create } from './create'
|
|
2
|
+
export { drop } from './drop'
|
|
3
|
+
export { describe } from './describe'
|
|
4
|
+
|
|
5
|
+
export { fieldProperties, fieldDefinition, fieldName } from './definitions/field'
|
|
6
|
+
export { keyProperties, keyDefinition, indexType, indexLocation } from './definitions/key'
|
|
7
|
+
export { referenceProperties, reference } from './definitions/keyReference'
|
|
8
|
+
export { recordProperties, recordDefinition, recordPath, entityPath } from './definitions/record'
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { create } from '../create'
|
|
2
|
+
import { insertEntity } from '../../queries/insertEntity'
|
|
3
|
+
import { foreignKeyExists } from './foreignKeyExists'
|
|
4
|
+
import { testHelpers } from 'js-build-tools/testHelpers'
|
|
5
|
+
|
|
6
|
+
const databasePath = 'foreign-key-exists-database/'
|
|
7
|
+
const gulpConfig = testHelpers.gulpConfig
|
|
8
|
+
gulpConfig.set('databasePath', databasePath)
|
|
9
|
+
testHelpers.setDefaults(databasePath)
|
|
10
|
+
|
|
11
|
+
beforeEach(testHelpers.beforeEach)
|
|
12
|
+
|
|
13
|
+
afterEach(testHelpers.afterEach)
|
|
14
|
+
|
|
15
|
+
const definition = [
|
|
16
|
+
{ name: '_id', type: 'string', optional: false, autoGenerate: true },
|
|
17
|
+
{ name: 'name', type: 'string', optional: true, autoGenerate: false }
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
const keys = [
|
|
21
|
+
{ type: 'primary', fields: ['_id'], lookup: 'pk__id.json' }
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
describe('foreignKeyExists', () => {
|
|
25
|
+
test('returns true when a record has the given property set to the given value', async () => {
|
|
26
|
+
await create('exercises', definition, keys)
|
|
27
|
+
const inserted = await insertEntity('exercises', [{ name: 'Bench Press' }], {})
|
|
28
|
+
const exists = await foreignKeyExists('exercises', '_id', inserted.exercises[0]._id)
|
|
29
|
+
expect(exists).toBe(true)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('returns false when no record matches', async () => {
|
|
33
|
+
await create('exercises', definition, keys)
|
|
34
|
+
await insertEntity('exercises', [{ name: 'Bench Press' }], {})
|
|
35
|
+
const exists = await foreignKeyExists('exercises', '_id', 'does-not-exist')
|
|
36
|
+
expect(exists).toBe(false)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('returns false for an entity with no entries', async () => {
|
|
40
|
+
await create('exercises', definition, keys)
|
|
41
|
+
const exists = await foreignKeyExists('exercises', '_id', 'anything')
|
|
42
|
+
expect(exists).toBe(false)
|
|
43
|
+
})
|
|
44
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { retrieveRecords } from '../../utilities/retrieveRecords'
|
|
2
|
+
import { where } from '../../queries/where'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Check whether any record in the referenced entity has the given property set to the given
|
|
6
|
+
* value - the mirror image of keyUnique(), which checks that no record already has a value.
|
|
7
|
+
* @param entity
|
|
8
|
+
* @param property
|
|
9
|
+
* @param value
|
|
10
|
+
*/
|
|
11
|
+
export const foreignKeyExists = async (entity: string, property: string, value: any): Promise<boolean> => {
|
|
12
|
+
const records = await retrieveRecords(entity)
|
|
13
|
+
const found = where(records, { property, comparator: '=', value })
|
|
14
|
+
return found.length > 0
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { keyUnique } from './keyUnique'
|
|
2
|
+
|
|
3
|
+
export const keyGenerate = async (entity, references = []) => {
|
|
4
|
+
let uuid = null
|
|
5
|
+
let isUnique = false
|
|
6
|
+
while (!isUnique) {
|
|
7
|
+
uuid = crypto.randomUUID()
|
|
8
|
+
isUnique = await keyUnique(entity, references, uuid)
|
|
9
|
+
}
|
|
10
|
+
return uuid
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { retrieveRecords } from '../../utilities/retrieveRecords'
|
|
2
|
+
import { where } from '../../queries/where'
|
|
3
|
+
|
|
4
|
+
export const keyUnique = async (entity, references = [], checkKey = null) => {
|
|
5
|
+
if (references.length !== 1) {
|
|
6
|
+
return false
|
|
7
|
+
}
|
|
8
|
+
const records = await retrieveRecords(entity)
|
|
9
|
+
const found = where(records, { property: references[0], comparator: '=', value: checkKey })
|
|
10
|
+
return !found.length
|
|
11
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { typeCheck } from './typeCheck'
|
|
2
|
+
|
|
3
|
+
describe('typeCheck', () => {
|
|
4
|
+
test('passes an undefined value through unchanged, for optional fields', () => {
|
|
5
|
+
expect(typeCheck('string', undefined)).toBeUndefined()
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
test('does not constrain an unrecognized type name', () => {
|
|
9
|
+
expect(typeCheck('whatever', 42)).toBe(42)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
describe('string', () => {
|
|
13
|
+
test('leaves an existing string alone', () => {
|
|
14
|
+
expect(typeCheck('string', 'foo')).toBe('foo')
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('coerces a non-string to a string', () => {
|
|
18
|
+
expect(typeCheck('string', 42)).toBe('42')
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe('int', () => {
|
|
23
|
+
test('leaves an existing integer alone', () => {
|
|
24
|
+
expect(typeCheck('int', 42)).toBe(42)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('coerces a numeric string to a number', () => {
|
|
28
|
+
expect(typeCheck('int', '42')).toBe(42)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('rejects a non-integer number', () => {
|
|
32
|
+
expect(() => typeCheck('int', 4.2)).toThrow('Value "4.2" is not a valid int')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('rejects a value that cannot be coerced to a number', () => {
|
|
36
|
+
expect(() => typeCheck('int', 'not a number')).toThrow('Value "not a number" is not a valid int')
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe('boolean', () => {
|
|
41
|
+
test('leaves an existing boolean alone', () => {
|
|
42
|
+
expect(typeCheck('boolean', false)).toBe(false)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('coerces the strings "true"/"false" to real booleans', () => {
|
|
46
|
+
expect(typeCheck('boolean', 'true')).toBe(true)
|
|
47
|
+
expect(typeCheck('boolean', 'false')).toBe(false)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('rejects anything else', () => {
|
|
51
|
+
expect(() => typeCheck('boolean', 'yes')).toThrow('Value "yes" is not a valid boolean')
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
describe('object', () => {
|
|
56
|
+
test('accepts a plain object', () => {
|
|
57
|
+
const value = { a: 1 }
|
|
58
|
+
expect(typeCheck('object', value)).toBe(value)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('rejects null', () => {
|
|
62
|
+
expect(() => typeCheck('object', null)).toThrow('Value "null" is not a valid object')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('rejects an array', () => {
|
|
66
|
+
expect(() => typeCheck('object', [])).toThrow('is not a valid object')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('rejects a primitive', () => {
|
|
70
|
+
expect(() => typeCheck('object', 'foo')).toThrow('is not a valid object')
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
describe('array', () => {
|
|
75
|
+
test('accepts an array', () => {
|
|
76
|
+
const value = [1, 2, 3]
|
|
77
|
+
expect(typeCheck('array', value)).toBe(value)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('rejects a non-array', () => {
|
|
81
|
+
expect(() => typeCheck('array', { a: 1 })).toThrow('is not a valid array')
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
})
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate (and where sensible, coerce) a value against a field's declared type. Runs only
|
|
3
|
+
* against values being written by insertEntity/updateEntity - it never re-validates data already
|
|
4
|
+
* on disk, so a field whose declared type doesn't match its already-stored values (which can
|
|
5
|
+
* happen, since nothing enforced this before) is left alone until something actually updates it.
|
|
6
|
+
* An unrecognized type name is treated as unconstrained rather than rejected, so a typo in a
|
|
7
|
+
* schema's field type can't silently block every write to that field.
|
|
8
|
+
* @param type
|
|
9
|
+
* @param value
|
|
10
|
+
*/
|
|
11
|
+
export const typeCheck = (type: string = 'string', value: any = undefined): any => {
|
|
12
|
+
if (typeof value === 'undefined') {
|
|
13
|
+
return value
|
|
14
|
+
}
|
|
15
|
+
switch (type) {
|
|
16
|
+
case 'string':
|
|
17
|
+
return typeof value === 'string' ? value : String(value)
|
|
18
|
+
case 'int': {
|
|
19
|
+
const coerced = Number(value)
|
|
20
|
+
if (Number.isNaN(coerced) || !Number.isInteger(coerced)) {
|
|
21
|
+
throw new Error(`Value "${value}" is not a valid int`)
|
|
22
|
+
}
|
|
23
|
+
return coerced
|
|
24
|
+
}
|
|
25
|
+
case 'boolean':
|
|
26
|
+
if (typeof value === 'boolean') {
|
|
27
|
+
return value
|
|
28
|
+
}
|
|
29
|
+
if (value === 'true') {
|
|
30
|
+
return true
|
|
31
|
+
}
|
|
32
|
+
if (value === 'false') {
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`Value "${value}" is not a valid boolean`)
|
|
36
|
+
case 'object':
|
|
37
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
38
|
+
throw new Error(`Value "${value}" is not a valid object`)
|
|
39
|
+
}
|
|
40
|
+
return value
|
|
41
|
+
case 'array':
|
|
42
|
+
if (!Array.isArray(value)) {
|
|
43
|
+
throw new Error(`Value "${value}" is not a valid array`)
|
|
44
|
+
}
|
|
45
|
+
return value
|
|
46
|
+
default:
|
|
47
|
+
return value
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { create } from '../introspect/create'
|
|
2
|
+
import { insertEntity } from './insertEntity'
|
|
3
|
+
import { deleteEntity } from './deleteEntity'
|
|
4
|
+
import { testHelpers } from 'js-build-tools/testHelpers'
|
|
5
|
+
|
|
6
|
+
const { readFile } = require('fs/promises')
|
|
7
|
+
|
|
8
|
+
const databasePath = 'delete-entity-database/'
|
|
9
|
+
const gulpConfig = testHelpers.gulpConfig
|
|
10
|
+
gulpConfig.set('databasePath', databasePath)
|
|
11
|
+
testHelpers.setDefaults(databasePath)
|
|
12
|
+
|
|
13
|
+
beforeEach(testHelpers.beforeEach)
|
|
14
|
+
|
|
15
|
+
afterEach(testHelpers.afterEach)
|
|
16
|
+
|
|
17
|
+
const definition = [
|
|
18
|
+
{ name: '_id', type: 'string', optional: false, autoGenerate: true },
|
|
19
|
+
{ name: 'bar', type: 'string', optional: true, autoGenerate: false }
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
const keys = [
|
|
23
|
+
{ type: 'primary', fields: ['_id'], lookup: 'pk__id.json' }
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
describe('deleteEntity', () => {
|
|
27
|
+
test('does nothing when dataSet has no rows to delete', async () => {
|
|
28
|
+
await create('foo', definition, keys)
|
|
29
|
+
const inserted = await insertEntity('foo', [{ bar: 'keep' }], {})
|
|
30
|
+
const result = await deleteEntity('foo', { foo: [] })
|
|
31
|
+
expect(result).toEqual({ foo: [] })
|
|
32
|
+
const recordFile = JSON.parse(await readFile(`${databasePath}foo.json`, 'utf8'))
|
|
33
|
+
expect(recordFile.entries).toEqual([`${inserted.foo[0]._id}.json`])
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('throws when the record has no single-field primary key', async () => {
|
|
37
|
+
await create('foo', definition, [])
|
|
38
|
+
await expect(deleteEntity('foo', { foo: [{ _id: '1' }] }))
|
|
39
|
+
.rejects.toThrow('Cannot delete from "foo" without a single-field primary key')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('removes the matching entry file, entries[] entry, and index entry', async () => {
|
|
43
|
+
await create('foo', definition, keys)
|
|
44
|
+
const inserted = await insertEntity('foo', [{ bar: 'one' }, { bar: 'two' }], {})
|
|
45
|
+
const [keep, remove] = inserted.foo
|
|
46
|
+
|
|
47
|
+
await deleteEntity('foo', { foo: [remove] })
|
|
48
|
+
|
|
49
|
+
expect(testHelpers.fileExists(`${databasePath}foo/${remove._id}.json`)).toBeFalsy()
|
|
50
|
+
expect(testHelpers.fileExists(`${databasePath}foo/${keep._id}.json`)).toBeTruthy()
|
|
51
|
+
|
|
52
|
+
const recordFile = JSON.parse(await readFile(`${databasePath}foo.json`, 'utf8'))
|
|
53
|
+
expect(recordFile.entries).toEqual([`${keep._id}.json`])
|
|
54
|
+
|
|
55
|
+
const indexFile = JSON.parse(await readFile(`${databasePath}__indexes/foo/pk__id.json`, 'utf8'))
|
|
56
|
+
expect(indexFile).toEqual([{ value: keep._id, record: [`${keep._id}.json`] }])
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('finds the right file to delete even when a filename does not match the primary key', async () => {
|
|
60
|
+
await create('foo', definition, keys)
|
|
61
|
+
const inserted = await insertEntity('foo', [{ bar: 'one' }], {})
|
|
62
|
+
const entryFile = `${inserted.foo[0]._id}.json`
|
|
63
|
+
const { rename } = require('fs/promises')
|
|
64
|
+
await rename(`${databasePath}foo/${entryFile}`, `${databasePath}foo/custom-name.json`)
|
|
65
|
+
const recordFile = JSON.parse(await readFile(`${databasePath}foo.json`, 'utf8'))
|
|
66
|
+
recordFile.entries = ['custom-name.json']
|
|
67
|
+
const { writeFile } = require('fs/promises')
|
|
68
|
+
await writeFile(`${databasePath}foo.json`, JSON.stringify(recordFile))
|
|
69
|
+
|
|
70
|
+
await deleteEntity('foo', { foo: [inserted.foo[0]] })
|
|
71
|
+
|
|
72
|
+
expect(testHelpers.fileExists(`${databasePath}foo/custom-name.json`)).toBeFalsy()
|
|
73
|
+
const finalRecordFile = JSON.parse(await readFile(`${databasePath}foo.json`, 'utf8'))
|
|
74
|
+
expect(finalRecordFile.entries).toEqual([])
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('deletes multiple rows from one call', async () => {
|
|
78
|
+
await create('foo', definition, keys)
|
|
79
|
+
const inserted = await insertEntity('foo', [{ bar: 'one' }, { bar: 'two' }, { bar: 'three' }], {})
|
|
80
|
+
|
|
81
|
+
await deleteEntity('foo', { foo: [inserted.foo[0], inserted.foo[2]] })
|
|
82
|
+
|
|
83
|
+
const recordFile = JSON.parse(await readFile(`${databasePath}foo.json`, 'utf8'))
|
|
84
|
+
expect(recordFile.entries).toEqual([`${inserted.foo[1]._id}.json`])
|
|
85
|
+
})
|
|
86
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { retrieveRecord } from '../utilities/retrieveRecord'
|
|
2
|
+
import { retrieveFile } from '../utilities/retrieveFile'
|
|
3
|
+
import { removeFromIndex } from '../utilities/removeFromIndex'
|
|
4
|
+
import { recordDefinition } from '../introspect/definitions/record'
|
|
5
|
+
|
|
6
|
+
const gulpConfig = require('js-build-tools/gulp.config')
|
|
7
|
+
const { rm, writeFile } = require('fs/promises')
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A record object carries no memory of which entry file it was read from, and files are not
|
|
11
|
+
* guaranteed to be named after any field's value, so the only reliable way to find a record's
|
|
12
|
+
* file is to re-scan every entry and match it by its primary key value.
|
|
13
|
+
* @param entity
|
|
14
|
+
* @param record
|
|
15
|
+
* @param primaryField
|
|
16
|
+
* @param deletedValues
|
|
17
|
+
*/
|
|
18
|
+
const removeMatchingEntries = async (entity: string, record: recordDefinition, primaryField: string, deletedValues: Set<any>): Promise<string[]> => {
|
|
19
|
+
const databasePath = gulpConfig.get('databasePath', 'database/')
|
|
20
|
+
const remainingEntries = []
|
|
21
|
+
for (const fileName of record.entries) {
|
|
22
|
+
const data = await retrieveFile(`${record.path}/${fileName}`)
|
|
23
|
+
if (!deletedValues.has(data[primaryField])) {
|
|
24
|
+
remainingEntries.push(fileName)
|
|
25
|
+
continue
|
|
26
|
+
}
|
|
27
|
+
await rm(`${databasePath}${record.path}/${fileName}`)
|
|
28
|
+
for (const key of record.keys) {
|
|
29
|
+
if (key.fields.length === 1) {
|
|
30
|
+
await removeFromIndex(record.path, key, data[key.fields[0]], fileName)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return remainingEntries
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const deleteEntity = async (entity: string = '', dataSet: Object = {}): Promise<Object> => {
|
|
38
|
+
const databasePath = gulpConfig.get('databasePath', 'database/')
|
|
39
|
+
const record = await retrieveRecord(entity)
|
|
40
|
+
const primaryKey = record.keys.find(key => key.type === 'primary' && key.fields.length === 1)
|
|
41
|
+
if (!primaryKey) {
|
|
42
|
+
throw new Error(`Cannot delete from "${entity}" without a single-field primary key`)
|
|
43
|
+
}
|
|
44
|
+
const primaryField = primaryKey.fields[0]
|
|
45
|
+
|
|
46
|
+
const toDelete = dataSet[entity] || []
|
|
47
|
+
if (!toDelete.length) {
|
|
48
|
+
return dataSet
|
|
49
|
+
}
|
|
50
|
+
const deletedValues = new Set(toDelete.map(row => row[primaryField]))
|
|
51
|
+
|
|
52
|
+
record.entries = await removeMatchingEntries(entity, record, primaryField, deletedValues)
|
|
53
|
+
await writeFile(`${databasePath}${record.path}.json`, JSON.stringify(record, null, 2))
|
|
54
|
+
|
|
55
|
+
return dataSet
|
|
56
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { groupBy } from './groupBy'
|
|
2
|
+
|
|
3
|
+
describe('groupBy', () => {
|
|
4
|
+
test('it groups by the given property', () => {
|
|
5
|
+
const dataSet = [
|
|
6
|
+
{
|
|
7
|
+
_id: 2,
|
|
8
|
+
exercise_id: 2,
|
|
9
|
+
weight_effort: '155lbs',
|
|
10
|
+
reps: 10,
|
|
11
|
+
sets: 3,
|
|
12
|
+
note: '',
|
|
13
|
+
order: 1,
|
|
14
|
+
date: '2024-06-02'
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
_id: 1,
|
|
18
|
+
exercise_id: 1,
|
|
19
|
+
weight_effort: '115lbs',
|
|
20
|
+
reps: 10,
|
|
21
|
+
sets: 3,
|
|
22
|
+
note: '',
|
|
23
|
+
order: 0,
|
|
24
|
+
date: '2024-06-02'
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
_id: 1,
|
|
28
|
+
exercise_id: 1,
|
|
29
|
+
weight_effort: '115lbs',
|
|
30
|
+
reps: 10,
|
|
31
|
+
sets: 3,
|
|
32
|
+
note: '',
|
|
33
|
+
order: 0,
|
|
34
|
+
date: '2024-06-05'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
_id: 3,
|
|
38
|
+
exercise_id: 3,
|
|
39
|
+
weight_effort: '155lbs',
|
|
40
|
+
reps: 10,
|
|
41
|
+
sets: 3,
|
|
42
|
+
note: '',
|
|
43
|
+
order: 2,
|
|
44
|
+
date: '2024-06-02'
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
_id: 2,
|
|
48
|
+
exercise_id: 2,
|
|
49
|
+
weight_effort: '155lbs',
|
|
50
|
+
reps: 10,
|
|
51
|
+
sets: 3,
|
|
52
|
+
note: '',
|
|
53
|
+
order: 1,
|
|
54
|
+
date: '2024-06-05'
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
_id: 3,
|
|
58
|
+
exercise_id: 3,
|
|
59
|
+
weight_effort: '155lbs',
|
|
60
|
+
reps: 10,
|
|
61
|
+
sets: 3,
|
|
62
|
+
note: '',
|
|
63
|
+
order: 2,
|
|
64
|
+
date: '2024-06-05'
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
const result = groupBy(dataSet, 'date')
|
|
68
|
+
expect(result).toEqual({
|
|
69
|
+
'2024-06-02': [
|
|
70
|
+
{
|
|
71
|
+
_id: 2,
|
|
72
|
+
exercise_id: 2,
|
|
73
|
+
weight_effort: '155lbs',
|
|
74
|
+
reps: 10,
|
|
75
|
+
sets: 3,
|
|
76
|
+
note: '',
|
|
77
|
+
order: 1,
|
|
78
|
+
date: '2024-06-02'
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
_id: 1,
|
|
82
|
+
exercise_id: 1,
|
|
83
|
+
weight_effort: '115lbs',
|
|
84
|
+
reps: 10,
|
|
85
|
+
sets: 3,
|
|
86
|
+
note: '',
|
|
87
|
+
order: 0,
|
|
88
|
+
date: '2024-06-02'
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
_id: 3,
|
|
92
|
+
exercise_id: 3,
|
|
93
|
+
weight_effort: '155lbs',
|
|
94
|
+
reps: 10,
|
|
95
|
+
sets: 3,
|
|
96
|
+
note: '',
|
|
97
|
+
order: 2,
|
|
98
|
+
date: '2024-06-02'
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
'2024-06-05': [
|
|
102
|
+
{
|
|
103
|
+
_id: 1,
|
|
104
|
+
exercise_id: 1,
|
|
105
|
+
weight_effort: '115lbs',
|
|
106
|
+
reps: 10,
|
|
107
|
+
sets: 3,
|
|
108
|
+
note: '',
|
|
109
|
+
order: 0,
|
|
110
|
+
date: '2024-06-05'
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
_id: 2,
|
|
114
|
+
exercise_id: 2,
|
|
115
|
+
weight_effort: '155lbs',
|
|
116
|
+
reps: 10,
|
|
117
|
+
sets: 3,
|
|
118
|
+
note: '',
|
|
119
|
+
order: 1,
|
|
120
|
+
date: '2024-06-05'
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
_id: 3,
|
|
124
|
+
exercise_id: 3,
|
|
125
|
+
weight_effort: '155lbs',
|
|
126
|
+
reps: 10,
|
|
127
|
+
sets: 3,
|
|
128
|
+
note: '',
|
|
129
|
+
order: 2,
|
|
130
|
+
date: '2024-06-05'
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const groupBy = (dataSet = [], groupProperty = '') => {
|
|
2
|
+
if (!groupProperty) {
|
|
3
|
+
return dataSet
|
|
4
|
+
}
|
|
5
|
+
return dataSet.reduce(
|
|
6
|
+
(groupedData, data) => {
|
|
7
|
+
const property = data[groupProperty]
|
|
8
|
+
if (!groupedData.hasOwnProperty(property)) {
|
|
9
|
+
groupedData[property] = []
|
|
10
|
+
}
|
|
11
|
+
groupedData[property].push(data)
|
|
12
|
+
return groupedData
|
|
13
|
+
},
|
|
14
|
+
{}
|
|
15
|
+
)
|
|
16
|
+
}
|