json-fs-query 0.1.0 → 1.0.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/README.md +41 -6
- package/browser/jsonFsQuery.js +28271 -0
- package/browser/jsonFsQuery.min.js +1 -0
- package/dist/data/introspect/create.js +6 -4
- package/dist/data/introspect/create.min.js +1 -1
- package/dist/data/introspect/drop.js +2 -2
- package/dist/data/introspect/drop.min.js +1 -1
- package/dist/data/introspect/policies/hasDependents.d.ts +13 -0
- package/dist/data/introspect/policies/hasDependents.js +52 -0
- package/dist/data/introspect/policies/hasDependents.min.js +1 -0
- package/dist/data/introspect/policies/keyGenerate.js +1 -1
- package/dist/data/introspect/policies/keyGenerate.min.js +1 -1
- package/dist/data/introspect/policies/keyUnique.d.ts +9 -1
- package/dist/data/introspect/policies/keyUnique.js +19 -9
- package/dist/data/introspect/policies/keyUnique.min.js +1 -1
- package/dist/data/queries/deleteEntity.js +36 -15
- package/dist/data/queries/deleteEntity.min.js +1 -1
- package/dist/data/queries/groupBy.js +22 -22
- package/dist/data/queries/groupBy.min.js +1 -1
- package/dist/data/queries/insertEntity.js +14 -12
- package/dist/data/queries/insertEntity.min.js +1 -1
- package/dist/data/queries/readEntity.js +1 -1
- package/dist/data/queries/readEntity.min.js +1 -1
- package/dist/data/queries/updateEntity.js +68 -42
- package/dist/data/queries/updateEntity.min.js +1 -1
- package/dist/data/queries/where.js +1 -1
- package/dist/data/queries/where.min.js +1 -1
- package/dist/data/utilities/computeKeyValue.d.ts +13 -0
- package/dist/data/utilities/computeKeyValue.js +26 -0
- package/dist/data/utilities/computeKeyValue.min.js +1 -0
- package/dist/data/utilities/config.d.ts +28 -0
- package/dist/data/utilities/config.js +45 -0
- package/dist/data/utilities/config.min.js +1 -0
- package/dist/data/utilities/deregisterRecord.js +2 -2
- package/dist/data/utilities/deregisterRecord.min.js +1 -1
- package/dist/data/utilities/getConditionIndexedList.js +1 -1
- package/dist/data/utilities/getConditionIndexedList.min.js +1 -1
- package/dist/data/utilities/getJoinIndexedList.js +2 -2
- package/dist/data/utilities/getJoinIndexedList.min.js +1 -1
- package/dist/data/utilities/getMatchingIndexes.js +2 -2
- package/dist/data/utilities/getMatchingIndexes.min.js +1 -1
- package/dist/data/utilities/matchIndexedJoins.js +2 -2
- package/dist/data/utilities/matchIndexedJoins.min.js +1 -1
- package/dist/data/utilities/reconcileJoin.js +26 -26
- package/dist/data/utilities/reconcileJoin.min.js +1 -1
- package/dist/data/utilities/reconcileJoins.js +26 -27
- package/dist/data/utilities/reconcileJoins.min.js +1 -1
- package/dist/data/utilities/registerRecord.js +2 -2
- package/dist/data/utilities/registerRecord.min.js +1 -1
- package/dist/data/utilities/removeFromIndex.js +2 -2
- package/dist/data/utilities/removeFromIndex.min.js +1 -1
- package/dist/data/utilities/restrictIndexedList.js +45 -45
- package/dist/data/utilities/restrictIndexedList.min.js +1 -1
- package/dist/data/utilities/retrieveFile.js +3 -3
- package/dist/data/utilities/retrieveFile.min.js +1 -1
- package/dist/data/utilities/retrieveIndexes.js +1 -1
- package/dist/data/utilities/retrieveIndexes.min.js +1 -1
- package/dist/data/utilities/updateIndex.js +2 -2
- package/dist/data/utilities/updateIndex.min.js +1 -1
- package/dist/data/utilities/useJoinClause.js +4 -4
- package/dist/data/utilities/useJoinClause.min.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7 -0
- package/dist/index.min.js +1 -1
- package/package.json +11 -4
- package/.browserslistrc +0 -1
- package/.nvmrc +0 -1
- package/MAIN.md +0 -15
- package/babel.config.js +0 -3
- package/build-tools.config.json +0 -30
- package/gulpfile.mjs +0 -15
- package/src/data/introspect/create.test.js +0 -67
- package/src/data/introspect/create.ts +0 -29
- package/src/data/introspect/definitions/field.test.js +0 -23
- package/src/data/introspect/definitions/field.ts +0 -48
- package/src/data/introspect/definitions/key.test.js +0 -100
- package/src/data/introspect/definitions/key.ts +0 -64
- package/src/data/introspect/definitions/keyReference.test.js +0 -8
- package/src/data/introspect/definitions/keyReference.ts +0 -23
- package/src/data/introspect/definitions/record.ts +0 -42
- package/src/data/introspect/describe.test.js +0 -36
- package/src/data/introspect/describe.ts +0 -9
- package/src/data/introspect/drop.test.js +0 -77
- package/src/data/introspect/drop.ts +0 -20
- package/src/data/introspect/index.ts +0 -8
- package/src/data/introspect/policies/foreignKeyExists.test.js +0 -44
- package/src/data/introspect/policies/foreignKeyExists.ts +0 -15
- package/src/data/introspect/policies/keyGenerate.ts +0 -11
- package/src/data/introspect/policies/keyUnique.ts +0 -11
- package/src/data/introspect/policies/typeCheck.test.js +0 -84
- package/src/data/introspect/policies/typeCheck.ts +0 -49
- package/src/data/introspect/policies/typeEnum.ts +0 -6
- package/src/data/introspect/policies/useDefault.ts +0 -6
- package/src/data/queries/deleteEntity.test.js +0 -86
- package/src/data/queries/deleteEntity.ts +0 -56
- package/src/data/queries/groupBy.test.js +0 -135
- package/src/data/queries/groupBy.ts +0 -16
- package/src/data/queries/insertEntity.test.js +0 -156
- package/src/data/queries/insertEntity.ts +0 -113
- package/src/data/queries/joinEntity.test.js +0 -109
- package/src/data/queries/joinEntity.ts +0 -20
- package/src/data/queries/mergeJoins.test.js +0 -174
- package/src/data/queries/mergeJoins.ts +0 -13
- package/src/data/queries/parser.test.js +0 -343
- package/src/data/queries/parser.ts +0 -24
- package/src/data/queries/query.test.js +0 -233
- package/src/data/queries/query.ts +0 -12
- package/src/data/queries/readEntity.test.js +0 -85
- package/src/data/queries/readEntity.ts +0 -8
- package/src/data/queries/sortBy.test.js +0 -131
- package/src/data/queries/sortBy.ts +0 -18
- package/src/data/queries/updateEntity.test.js +0 -228
- package/src/data/queries/updateEntity.ts +0 -162
- package/src/data/queries/where.test.js +0 -129
- package/src/data/queries/where.ts +0 -54
- package/src/data/utilities/deregisterRecord.test.js +0 -26
- package/src/data/utilities/deregisterRecord.ts +0 -24
- package/src/data/utilities/getConditionIndexedList.ts +0 -43
- package/src/data/utilities/getJoinIndexedList.ts +0 -53
- package/src/data/utilities/getMatchingIndexes.ts +0 -21
- package/src/data/utilities/matchIndexedJoins.ts +0 -45
- package/src/data/utilities/mergeJoin.test.js +0 -102
- package/src/data/utilities/mergeJoin.ts +0 -28
- package/src/data/utilities/parsers/findCommand.ts +0 -25
- package/src/data/utilities/parsers/findConditions.test.js +0 -38
- package/src/data/utilities/parsers/findConditions.ts +0 -32
- package/src/data/utilities/parsers/findGroupBy.ts +0 -20
- package/src/data/utilities/parsers/findInsertValues.test.js +0 -26
- package/src/data/utilities/parsers/findInsertValues.ts +0 -32
- package/src/data/utilities/parsers/findJoinClauses.ts +0 -29
- package/src/data/utilities/parsers/findJoinEntity.ts +0 -20
- package/src/data/utilities/parsers/findLimit.ts +0 -20
- package/src/data/utilities/parsers/findMergeJoins.ts +0 -27
- package/src/data/utilities/parsers/findOffset.ts +0 -20
- package/src/data/utilities/parsers/findSelects.ts +0 -31
- package/src/data/utilities/parsers/findSortClauses.ts +0 -31
- package/src/data/utilities/parsers/findUpdateValues.test.js +0 -37
- package/src/data/utilities/parsers/findUpdateValues.ts +0 -32
- package/src/data/utilities/parsers/isEntityProperty.ts +0 -1
- package/src/data/utilities/parsers/parseLiteralValue.test.js +0 -41
- package/src/data/utilities/parsers/parseLiteralValue.ts +0 -29
- package/src/data/utilities/parsers/parseQuery.ts +0 -73
- package/src/data/utilities/parsers/removeFromQuery.ts +0 -1
- package/src/data/utilities/parsers/splitEntityProperty.test.js +0 -8
- package/src/data/utilities/parsers/splitEntityProperty.ts +0 -17
- package/src/data/utilities/parsers/splitQueries.test.js +0 -33
- package/src/data/utilities/parsers/splitQueries.ts +0 -4
- package/src/data/utilities/reconcileJoin.test.js +0 -38
- package/src/data/utilities/reconcileJoin.ts +0 -23
- package/src/data/utilities/reconcileJoins.test.js +0 -160
- package/src/data/utilities/reconcileJoins.ts +0 -15
- package/src/data/utilities/registerRecord.test.js +0 -44
- package/src/data/utilities/registerRecord.ts +0 -44
- package/src/data/utilities/removeFromIndex.test.js +0 -44
- package/src/data/utilities/removeFromIndex.ts +0 -34
- package/src/data/utilities/restrictIndexedList.ts +0 -38
- package/src/data/utilities/retrieveFile.test.js +0 -49
- package/src/data/utilities/retrieveFile.ts +0 -19
- package/src/data/utilities/retrieveIndexes.ts +0 -17
- package/src/data/utilities/retrieveRecord.ts +0 -3
- package/src/data/utilities/retrieveRecords.test.js +0 -223
- package/src/data/utilities/retrieveRecords.ts +0 -16
- package/src/data/utilities/runQuery.test.js +0 -88
- package/src/data/utilities/runQuery.ts +0 -75
- package/src/data/utilities/updateIndex.test.js +0 -44
- package/src/data/utilities/updateIndex.ts +0 -45
- package/src/data/utilities/useJoinClause.test.js +0 -94
- package/src/data/utilities/useJoinClause.ts +0 -44
- package/src/global.d.ts +0 -1
- package/src/index.test.js +0 -52
- package/src/index.ts +0 -17
- package/test-data/__RECORDS.json +0 -32
- package/test-data/__indexes/exercise_muscle/fk_exercise_id.json +0 -14
- package/test-data/__indexes/exercise_muscle/fk_muscle_id.json +0 -14
- package/test-data/__indexes/exercise_muscle/pk__id.json +0 -14
- package/test-data/__indexes/exercises/pk__id.json +0 -14
- package/test-data/__indexes/muscles/pk__id.json +0 -58
- package/test-data/__indexes/workouts/fk_exercise_id.json +0 -23
- package/test-data/__indexes/workouts/index_date.json +0 -18
- package/test-data/__indexes/workouts/pk__id.json +0 -26
- package/test-data/copyDatabase.ts +0 -64
- package/test-data/exercise_muscle/bench_press_pectorals.json +0 -6
- package/test-data/exercise_muscle/deadlift_hamstrings.json +0 -6
- package/test-data/exercise_muscle/squat_quadriceps.json +0 -6
- package/test-data/exercise_muscle.json +0 -69
- package/test-data/exercises/bench_press.json +0 -5
- package/test-data/exercises/deadlift.json +0 -5
- package/test-data/exercises/squat.json +0 -5
- package/test-data/exercises.json +0 -34
- package/test-data/muscles/abdominal.json +0 -6
- package/test-data/muscles/biceps.json +0 -6
- package/test-data/muscles/calves.json +0 -6
- package/test-data/muscles/deltoids.json +0 -6
- package/test-data/muscles/erector_spinae.json +0 -6
- package/test-data/muscles/forearms.json +0 -6
- package/test-data/muscles/gluteus_maximus.json +0 -6
- package/test-data/muscles/hamstrings.json +0 -6
- package/test-data/muscles/latissimus_dorsi.json +0 -6
- package/test-data/muscles/obliques.json +0 -6
- package/test-data/muscles/pectorals.json +0 -6
- package/test-data/muscles/quadriceps.json +0 -6
- package/test-data/muscles/trapezius.json +0 -6
- package/test-data/muscles/triceps.json +0 -6
- package/test-data/muscles.json +0 -50
- package/test-data/workouts/2024-06-02-001.json +0 -10
- package/test-data/workouts/2024-06-02-002.json +0 -10
- package/test-data/workouts/2024-06-02-003.json +0 -10
- package/test-data/workouts/2024-06-05-001.json +0 -10
- package/test-data/workouts/2024-06-05-002.json +0 -10
- package/test-data/workouts/2024-06-05-003.json +0 -10
- package/test-data/workouts.json +0 -84
- package/tsconfig.json +0 -15
|
@@ -1,86 +0,0 @@
|
|
|
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
|
-
})
|
|
@@ -1,56 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
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
|
-
})
|
|
@@ -1,16 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import { create } from '../introspect/create'
|
|
2
|
-
import { insertEntity } from './insertEntity'
|
|
3
|
-
import { testHelpers } from 'js-build-tools/testHelpers'
|
|
4
|
-
|
|
5
|
-
const { readFile } = require('fs/promises')
|
|
6
|
-
|
|
7
|
-
const databasePath = 'insert-entity-database/'
|
|
8
|
-
const gulpConfig = testHelpers.gulpConfig
|
|
9
|
-
gulpConfig.set('databasePath', databasePath)
|
|
10
|
-
testHelpers.setDefaults(databasePath)
|
|
11
|
-
|
|
12
|
-
beforeEach(testHelpers.beforeEach)
|
|
13
|
-
|
|
14
|
-
afterEach(testHelpers.afterEach)
|
|
15
|
-
|
|
16
|
-
const definition = [
|
|
17
|
-
{ name: '_id', type: 'string', optional: false, autoGenerate: true },
|
|
18
|
-
{ name: 'bar', type: 'string', optional: true, autoGenerate: false }
|
|
19
|
-
]
|
|
20
|
-
|
|
21
|
-
const keys = [
|
|
22
|
-
{ type: 'primary', fields: ['_id'], lookup: 'pk__id.json' }
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
describe('insertEntity', () => {
|
|
26
|
-
test('inserts a new entity, generating its primary key', async () => {
|
|
27
|
-
await create('foo', definition, keys)
|
|
28
|
-
const dataSet = await insertEntity('foo', [{ bar: 'baz' }], {})
|
|
29
|
-
|
|
30
|
-
expect(dataSet.foo).toHaveLength(1)
|
|
31
|
-
const inserted = dataSet.foo[0]
|
|
32
|
-
expect(typeof inserted._id).toBe('string')
|
|
33
|
-
expect(inserted.bar).toBe('baz')
|
|
34
|
-
|
|
35
|
-
expect(testHelpers.fileExists(`${databasePath}foo/${inserted._id}.json`)).toBeTruthy()
|
|
36
|
-
const entryFile = JSON.parse(await readFile(`${databasePath}foo/${inserted._id}.json`, 'utf8'))
|
|
37
|
-
expect(entryFile).toEqual(inserted)
|
|
38
|
-
|
|
39
|
-
const recordFile = JSON.parse(await readFile(`${databasePath}foo.json`, 'utf8'))
|
|
40
|
-
expect(recordFile.entries).toEqual([`${inserted._id}.json`])
|
|
41
|
-
|
|
42
|
-
const indexFile = JSON.parse(await readFile(`${databasePath}__indexes/foo/pk__id.json`, 'utf8'))
|
|
43
|
-
expect(indexFile).toEqual([{ value: inserted._id, record: [`${inserted._id}.json`] }])
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
test('inserts multiple entities from one call', async () => {
|
|
47
|
-
await create('foo', definition, keys)
|
|
48
|
-
const dataSet = await insertEntity('foo', [{ bar: 'one' }, { bar: 'two' }], {})
|
|
49
|
-
|
|
50
|
-
expect(dataSet.foo).toHaveLength(2)
|
|
51
|
-
const recordFile = JSON.parse(await readFile(`${databasePath}foo.json`, 'utf8'))
|
|
52
|
-
expect(recordFile.entries).toHaveLength(2)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
test('appends onto an existing dataSet entry instead of replacing it', async () => {
|
|
56
|
-
await create('foo', definition, keys)
|
|
57
|
-
const dataSet = await insertEntity('foo', [{ bar: 'one' }], { foo: [{ bar: 'existing' }] })
|
|
58
|
-
expect(dataSet.foo).toHaveLength(2)
|
|
59
|
-
expect(dataSet.foo[0]).toEqual({ bar: 'existing' })
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
test('applies a field default when no value is given', async () => {
|
|
63
|
-
const definitionWithDefault = [
|
|
64
|
-
{ name: '_id', type: 'string', optional: false, autoGenerate: true },
|
|
65
|
-
{ name: 'status', type: 'string', optional: false, useDefault: true, defaultValue: 'pending' }
|
|
66
|
-
]
|
|
67
|
-
await create('foo', definitionWithDefault, keys)
|
|
68
|
-
const dataSet = await insertEntity('foo', [{}], {})
|
|
69
|
-
expect(dataSet.foo[0].status).toBe('pending')
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
test('throws when a required field has no value, default, or autoGenerate', async () => {
|
|
73
|
-
const definitionRequiringBar = [
|
|
74
|
-
{ name: '_id', type: 'string', optional: false, autoGenerate: true },
|
|
75
|
-
{ name: 'bar', type: 'string', optional: false, autoGenerate: false }
|
|
76
|
-
]
|
|
77
|
-
await create('foo', definitionRequiringBar, keys)
|
|
78
|
-
await expect(insertEntity('foo', [{}], {})).rejects.toThrow('Missing required field "bar" for foo')
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
test('throws on a duplicate primary key value instead of writing the file', async () => {
|
|
82
|
-
const definitionExplicitId = [
|
|
83
|
-
{ name: '_id', type: 'string', optional: false, autoGenerate: false },
|
|
84
|
-
{ name: 'bar', type: 'string', optional: true }
|
|
85
|
-
]
|
|
86
|
-
await create('foo', definitionExplicitId, keys)
|
|
87
|
-
await insertEntity('foo', [{ _id: 'dupe', bar: 'first' }], {})
|
|
88
|
-
await expect(insertEntity('foo', [{ _id: 'dupe', bar: 'second' }], {}))
|
|
89
|
-
.rejects.toThrow('Duplicate value for primary key "_id" on foo')
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
test('coerces a value to match its field\'s declared type', async () => {
|
|
93
|
-
const definitionWithInt = [
|
|
94
|
-
{ name: '_id', type: 'string', optional: false, autoGenerate: true },
|
|
95
|
-
{ name: 'reps', type: 'int', optional: false, autoGenerate: false }
|
|
96
|
-
]
|
|
97
|
-
await create('foo', definitionWithInt, keys)
|
|
98
|
-
const dataSet = await insertEntity('foo', [{ reps: '10' }], {})
|
|
99
|
-
expect(dataSet.foo[0].reps).toBe(10)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
test('rejects a value that does not match its field\'s declared type', async () => {
|
|
103
|
-
const definitionWithInt = [
|
|
104
|
-
{ name: '_id', type: 'string', optional: false, autoGenerate: true },
|
|
105
|
-
{ name: 'reps', type: 'int', optional: false, autoGenerate: false }
|
|
106
|
-
]
|
|
107
|
-
await create('foo', definitionWithInt, keys)
|
|
108
|
-
await expect(insertEntity('foo', [{ reps: 'ten' }], {}))
|
|
109
|
-
.rejects.toThrow('Value "ten" is not a valid int')
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
describe('foreign keys', () => {
|
|
113
|
-
const exerciseDefinition = [
|
|
114
|
-
{ name: '_id', type: 'string', optional: false, autoGenerate: true },
|
|
115
|
-
{ name: 'name', type: 'string', optional: true, autoGenerate: false }
|
|
116
|
-
]
|
|
117
|
-
const exerciseKeys = [
|
|
118
|
-
{ type: 'primary', fields: ['_id'], lookup: 'pk__id.json' }
|
|
119
|
-
]
|
|
120
|
-
const workoutDefinition = [
|
|
121
|
-
{ name: '_id', type: 'string', optional: false, autoGenerate: true },
|
|
122
|
-
{ name: 'exercise_id', type: 'string', optional: true, autoGenerate: false }
|
|
123
|
-
]
|
|
124
|
-
const workoutKeys = [
|
|
125
|
-
{ type: 'primary', fields: ['_id'], lookup: 'pk__id.json' },
|
|
126
|
-
{
|
|
127
|
-
type: 'foreign',
|
|
128
|
-
fields: ['exercise_id'],
|
|
129
|
-
lookup: 'fk_exercise_id.json',
|
|
130
|
-
references: [{ fields: ['exercises._id'], lookup: 'exercises.json' }]
|
|
131
|
-
}
|
|
132
|
-
]
|
|
133
|
-
|
|
134
|
-
test('accepts a foreign key value that exists in the referenced record', async () => {
|
|
135
|
-
await create('exercises', exerciseDefinition, exerciseKeys)
|
|
136
|
-
const exercise = await insertEntity('exercises', [{ name: 'Bench Press' }], {})
|
|
137
|
-
await create('workouts', workoutDefinition, workoutKeys)
|
|
138
|
-
const result = await insertEntity('workouts', [{ exercise_id: exercise.exercises[0]._id }], {})
|
|
139
|
-
expect(result.workouts[0].exercise_id).toBe(exercise.exercises[0]._id)
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
test('rejects a foreign key value that does not exist in the referenced record', async () => {
|
|
143
|
-
await create('exercises', exerciseDefinition, exerciseKeys)
|
|
144
|
-
await create('workouts', workoutDefinition, workoutKeys)
|
|
145
|
-
await expect(insertEntity('workouts', [{ exercise_id: 'does-not-exist' }], {}))
|
|
146
|
-
.rejects.toThrow('Foreign key "exercise_id" on workouts references a nonexistent exercises._id = does-not-exist')
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
test('skips the check when an optional foreign key field is omitted', async () => {
|
|
150
|
-
await create('exercises', exerciseDefinition, exerciseKeys)
|
|
151
|
-
await create('workouts', workoutDefinition, workoutKeys)
|
|
152
|
-
const result = await insertEntity('workouts', [{}], {})
|
|
153
|
-
expect(result.workouts[0].exercise_id).toBeUndefined()
|
|
154
|
-
})
|
|
155
|
-
})
|
|
156
|
-
})
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { retrieveRecord } from '../utilities/retrieveRecord'
|
|
2
|
-
import { updateIndex } from '../utilities/updateIndex'
|
|
3
|
-
import { keyGenerate } from '../introspect/policies/keyGenerate'
|
|
4
|
-
import { keyUnique } from '../introspect/policies/keyUnique'
|
|
5
|
-
import { useDefault } from '../introspect/policies/useDefault'
|
|
6
|
-
import { typeCheck } from '../introspect/policies/typeCheck'
|
|
7
|
-
import { foreignKeyExists } from '../introspect/policies/foreignKeyExists'
|
|
8
|
-
import { splitEntityProperty } from '../utilities/parsers/splitEntityProperty'
|
|
9
|
-
import { recordDefinition } from '../introspect/definitions/record'
|
|
10
|
-
|
|
11
|
-
const gulpConfig = require('js-build-tools/gulp.config')
|
|
12
|
-
const { writeFile } = require('fs/promises')
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Build one entity's stored values from the raw input values, generating any autoGenerate
|
|
16
|
-
* fields and applying field defaults, then throw if a required field still has no value.
|
|
17
|
-
* @param entity
|
|
18
|
-
* @param record
|
|
19
|
-
* @param rowValues
|
|
20
|
-
*/
|
|
21
|
-
const buildEntityValues = async (entity: string, record: recordDefinition, rowValues: Object = {}): Promise<Object> => {
|
|
22
|
-
const entityValues = {}
|
|
23
|
-
for (const field of record.definition) {
|
|
24
|
-
let value = rowValues[field.name]
|
|
25
|
-
if (field.autoGenerate) {
|
|
26
|
-
value = await keyGenerate(entity, [field.name])
|
|
27
|
-
} else if (field.hasOwnProperty('default')) {
|
|
28
|
-
value = useDefault(field['default'], value)
|
|
29
|
-
}
|
|
30
|
-
if (typeof value === 'undefined') {
|
|
31
|
-
if (!field.optional) {
|
|
32
|
-
throw new Error(`Missing required field "${field.name}" for ${entity}`)
|
|
33
|
-
}
|
|
34
|
-
continue
|
|
35
|
-
}
|
|
36
|
-
entityValues[field.name] = typeCheck(field.type, value)
|
|
37
|
-
}
|
|
38
|
-
return entityValues
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Reject the insert if it collides with an existing primary or unique single-field key value.
|
|
43
|
-
* @param entity
|
|
44
|
-
* @param record
|
|
45
|
-
* @param entityValues
|
|
46
|
-
*/
|
|
47
|
-
const assertUnique = async (entity: string, record: recordDefinition, entityValues: Object): Promise<void> => {
|
|
48
|
-
for (const key of record.keys) {
|
|
49
|
-
if ((key.type !== 'primary' && key.type !== 'unique') || key.fields.length !== 1) {
|
|
50
|
-
continue
|
|
51
|
-
}
|
|
52
|
-
const isUnique = await keyUnique(entity, key.fields, entityValues[key.fields[0]])
|
|
53
|
-
if (!isUnique) {
|
|
54
|
-
throw new Error(`Duplicate value for ${key.type} key "${key.fields[0]}" on ${entity}`)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Reject the insert if a single-field foreign key's value doesn't exist in the record it
|
|
61
|
-
* references.
|
|
62
|
-
* @param entity
|
|
63
|
-
* @param record
|
|
64
|
-
* @param entityValues
|
|
65
|
-
*/
|
|
66
|
-
const assertForeignKeys = async (entity: string, record: recordDefinition, entityValues: Object): Promise<void> => {
|
|
67
|
-
for (const key of record.keys) {
|
|
68
|
-
if (key.type !== 'foreign' || key.fields.length !== 1 || !key.references || key.references.length !== 1) {
|
|
69
|
-
continue
|
|
70
|
-
}
|
|
71
|
-
const field = key.fields[0]
|
|
72
|
-
if (!entityValues.hasOwnProperty(field)) {
|
|
73
|
-
continue
|
|
74
|
-
}
|
|
75
|
-
const { entity: refEntity, property: refProperty } = splitEntityProperty(key.references[0].fields[0])
|
|
76
|
-
const exists = await foreignKeyExists(refEntity, refProperty, entityValues[field])
|
|
77
|
-
if (!exists) {
|
|
78
|
-
throw new Error(`Foreign key "${field}" on ${entity} references a nonexistent ${refEntity}.${refProperty} = ${entityValues[field]}`)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export const insertEntity = async (entity: string = '', values: Object[] = [], dataSet: Object = {}): Promise<Object> => {
|
|
84
|
-
const databasePath = gulpConfig.get('databasePath', 'database/')
|
|
85
|
-
const record = await retrieveRecord(entity)
|
|
86
|
-
const primaryKey = record.keys.find(key => key.type === 'primary' && key.fields.length === 1)
|
|
87
|
-
|
|
88
|
-
if (!dataSet.hasOwnProperty(entity)) {
|
|
89
|
-
dataSet[entity] = []
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
for (const rowValues of values) {
|
|
93
|
-
const entityValues = await buildEntityValues(entity, record, rowValues)
|
|
94
|
-
await assertUnique(entity, record, entityValues)
|
|
95
|
-
await assertForeignKeys(entity, record, entityValues)
|
|
96
|
-
|
|
97
|
-
const fileName = `${primaryKey ? entityValues[primaryKey.fields[0]] : crypto.randomUUID()}.json`
|
|
98
|
-
await writeFile(`${databasePath}${record.path}/${fileName}`, JSON.stringify(entityValues, null, 2))
|
|
99
|
-
|
|
100
|
-
record.entries.push(fileName)
|
|
101
|
-
await writeFile(`${databasePath}${record.path}.json`, JSON.stringify(record, null, 2))
|
|
102
|
-
|
|
103
|
-
for (const key of record.keys) {
|
|
104
|
-
if (key.fields.length === 1) {
|
|
105
|
-
await updateIndex(record.path, key, entityValues[key.fields[0]], fileName)
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
dataSet[entity].push(entityValues)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return dataSet
|
|
113
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { joinEntity } from './joinEntity'
|
|
2
|
-
import { testHelpers } from 'js-build-tools/testHelpers'
|
|
3
|
-
import { copyDatabase } from '../../../test-data/copyDatabase'
|
|
4
|
-
|
|
5
|
-
const databasePath = 'join-entity-database/'
|
|
6
|
-
const gulpConfig = testHelpers.gulpConfig
|
|
7
|
-
gulpConfig.set('databasePath', databasePath)
|
|
8
|
-
testHelpers.setDefaults(databasePath)
|
|
9
|
-
|
|
10
|
-
beforeEach(
|
|
11
|
-
() => testHelpers.beforeEach()
|
|
12
|
-
.then(() => copyDatabase(databasePath))
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
afterEach(testHelpers.afterEach)
|
|
16
|
-
|
|
17
|
-
describe('joinEntity', () => {
|
|
18
|
-
test('it will find the joins', async () => {
|
|
19
|
-
const joinClauses = [
|
|
20
|
-
{
|
|
21
|
-
and: undefined,
|
|
22
|
-
propertyA: 'workouts.exercise_id',
|
|
23
|
-
comparator: '=',
|
|
24
|
-
propertyB: 'exercises._id'
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
and: 'and',
|
|
28
|
-
propertyA: 'exercises._id',
|
|
29
|
-
comparator: '=',
|
|
30
|
-
propertyB: 'exercise_muscle.exercise_id'
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
and: 'and',
|
|
34
|
-
propertyA: 'exercise_muscle.muscle_id',
|
|
35
|
-
comparator: '=',
|
|
36
|
-
propertyB: 'muscles._id'
|
|
37
|
-
}
|
|
38
|
-
]
|
|
39
|
-
const result = await joinEntity('workouts', 'muscles', {}, joinClauses)
|
|
40
|
-
expect(result['workouts']).toEqual([
|
|
41
|
-
{
|
|
42
|
-
_id: 1,
|
|
43
|
-
exercise_id: 1,
|
|
44
|
-
weight_effort: '115lbs',
|
|
45
|
-
reps: 10,
|
|
46
|
-
sets: 3,
|
|
47
|
-
note: '',
|
|
48
|
-
order: 0,
|
|
49
|
-
date: '2024-06-02',
|
|
50
|
-
muscles: [{ _id: 11, name: 'Pectorals', alias: 'chest', image: '' }]
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
_id: 2,
|
|
54
|
-
exercise_id: 2,
|
|
55
|
-
weight_effort: '155lbs',
|
|
56
|
-
reps: 10,
|
|
57
|
-
sets: 3,
|
|
58
|
-
note: '',
|
|
59
|
-
order: 1,
|
|
60
|
-
date: '2024-06-02',
|
|
61
|
-
muscles: [{ _id: 8, name: 'Hamstrings', alias: 'abs', image: '' }]
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
_id: 3,
|
|
65
|
-
exercise_id: 3,
|
|
66
|
-
weight_effort: '155lbs',
|
|
67
|
-
reps: 10,
|
|
68
|
-
sets: 3,
|
|
69
|
-
note: '',
|
|
70
|
-
order: 2,
|
|
71
|
-
date: '2024-06-02',
|
|
72
|
-
muscles: [{ _id: 12, name: 'Quadriceps', alias: 'quads', image: '' }]
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
_id: 4,
|
|
76
|
-
exercise_id: 1,
|
|
77
|
-
weight_effort: '120lbs',
|
|
78
|
-
reps: 10,
|
|
79
|
-
sets: 3,
|
|
80
|
-
note: '',
|
|
81
|
-
order: 0,
|
|
82
|
-
date: '2024-06-05',
|
|
83
|
-
muscles: [{ _id: 11, name: 'Pectorals', alias: 'chest', image: '' }]
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
_id: 5,
|
|
87
|
-
exercise_id: 2,
|
|
88
|
-
weight_effort: '160lbs',
|
|
89
|
-
reps: 10,
|
|
90
|
-
sets: 3,
|
|
91
|
-
note: '',
|
|
92
|
-
order: 1,
|
|
93
|
-
date: '2024-06-05',
|
|
94
|
-
muscles: [{ _id: 8, name: 'Hamstrings', alias: 'abs', image: '' }]
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
_id: 6,
|
|
98
|
-
exercise_id: 3,
|
|
99
|
-
weight_effort: '160lbs',
|
|
100
|
-
reps: 10,
|
|
101
|
-
sets: 3,
|
|
102
|
-
note: '',
|
|
103
|
-
order: 2,
|
|
104
|
-
date: '2024-06-05',
|
|
105
|
-
muscles: [{ _id: 12, name: 'Quadriceps', alias: 'quads', image: '' }]
|
|
106
|
-
}
|
|
107
|
-
])
|
|
108
|
-
})
|
|
109
|
-
})
|