Skip to content

Commit

Permalink
Support nested objects and arrays in the model definition (#113)
Browse files Browse the repository at this point in the history
* feat: support nested objects and arrays in model definition

* chore(defineRelationalProperties): use custom "definePropertyAtPath" function

* feat: store primary keys in the "PrimaryKey" class

* feat: store relations in the "Relation" class

* fix: support querying through nested relations

* test(update): add nested property update test

* fix: support sorting through nested properties

Co-authored-by: Artem Zakharchenko <[email protected]>
  • Loading branch information
gidesan and kettanaito authored Oct 11, 2021
1 parent 3016d6d commit f5478f9
Show file tree
Hide file tree
Showing 43 changed files with 2,167 additions and 1,397 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@
"typescript": "4.3.5"
},
"dependencies": {
"@types/lodash.get": "^4.4.6",
"@types/lodash": "^4.14.172",
"@types/md5": "^2.3.0",
"@types/pluralize": "^0.0.29",
"@types/uuid": "^8.3.0",
"date-fns": "^2.21.1",
"graphql": "^15.5.0",
"lodash.get": "^4.4.2",
"lodash": "^4.17.21",
"md5": "^2.3.0",
"pluralize": "^8.0.0",
"strict-event-emitter": "^0.2.0",
Expand Down
8 changes: 3 additions & 5 deletions src/db/drop.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { factory } from '../factory'
import { FactoryAPI } from '../glossary'

export function drop(db: ReturnType<typeof factory>): void {
export function drop(db: FactoryAPI<any>): void {
Object.values(db).forEach((model) => {
model.deleteMany({
where: {},
})
model.deleteMany({ where: {} })
})
}
60 changes: 33 additions & 27 deletions src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export function factory<Dictionary extends ModelDictionary>(

function createModelApi<
Dictionary extends ModelDictionary,
ModelName extends string
ModelName extends string,
>(
dictionary: Dictionary,
modelName: ModelName,
Expand Down Expand Up @@ -80,14 +80,14 @@ function createModelApi<
] as string

invariant(
!entityId,
entityId,
`Failed to create a "${modelName}" entity: expected the primary key "${primaryKey}" to have a value, but got: ${entityId}`,
new OperationError(OperationErrorType.MissingPrimaryKey),
)

// Prevent creation of multiple entities with the same primary key value.
invariant(
db.has(modelName, entityId),
!db.has(modelName, entityId),
`Failed to create a "${modelName}" entity: an entity with the same primary key "${entityId}" ("${
entity[InternalEntityProperty.primaryKey]
}") already exists.`,
Expand All @@ -109,39 +109,45 @@ function createModelApi<
const results = executeQuery(modelName, primaryKey, query, db)
const firstResult = first(results)

invariant(
query.strict && !firstResult,
`Failed to execute "findFirst" on the "${modelName}" model: no entity found matching the query "${JSON.stringify(
query.where,
)}".`,
new OperationError(OperationErrorType.EntityNotFound),
)
if (query.strict) {
invariant(
firstResult,
`Failed to execute "findFirst" on the "${modelName}" model: no entity found matching the query "${JSON.stringify(
query.where,
)}".`,
new OperationError(OperationErrorType.EntityNotFound),
)
}

return firstResult ? removeInternalProperties(firstResult) : null
},
findMany(query) {
const results = executeQuery(modelName, primaryKey, query, db)

invariant(
query.strict && results.length === 0,
`Failed to execute "findMany" on the "${modelName}" model: no entities found matching the query "${JSON.stringify(
query.where,
)}".`,
new OperationError(OperationErrorType.EntityNotFound),
)
if (query.strict) {
invariant(
results.length > 0,
`Failed to execute "findMany" on the "${modelName}" model: no entities found matching the query "${JSON.stringify(
query.where,
)}".`,
new OperationError(OperationErrorType.EntityNotFound),
)
}

return results.map(removeInternalProperties)
return results.map((record) => removeInternalProperties(record))
},
getAll() {
return db.listEntities(modelName).map(removeInternalProperties)
return db
.listEntities(modelName)
.map((entity) => removeInternalProperties(entity))
},
update({ strict, ...query }) {
const results = executeQuery(modelName, primaryKey, query, db)
const prevRecord = first(results)

if (!prevRecord) {
invariant(
strict,
!strict,
`Failed to execute "update" on the "${modelName}" model: no entity found matching the query "${JSON.stringify(
query.where,
)}".`,
Expand All @@ -158,7 +164,7 @@ function createModelApi<
prevRecord[prevRecord[InternalEntityProperty.primaryKey]]
) {
invariant(
db.has(
!db.has(
modelName,
nextRecord[prevRecord[InternalEntityProperty.primaryKey]],
),
Expand All @@ -179,7 +185,7 @@ function createModelApi<

if (records.length === 0) {
invariant(
strict,
!strict,
`Failed to execute "updateMany" on the "${modelName}" model: no entities found matching the query "${JSON.stringify(
query.where,
)}".`,
Expand All @@ -197,7 +203,7 @@ function createModelApi<
prevRecord[prevRecord[InternalEntityProperty.primaryKey]]
) {
invariant(
db.has(
!db.has(
modelName,
nextRecord[prevRecord[InternalEntityProperty.primaryKey]],
),
Expand All @@ -212,15 +218,15 @@ function createModelApi<
updatedRecords.push(nextRecord)
})

return updatedRecords.map(removeInternalProperties)
return updatedRecords.map((record) => removeInternalProperties(record))
},
delete({ strict, ...query }) {
const results = executeQuery(modelName, primaryKey, query, db)
const record = first(results)

if (!record) {
invariant(
strict,
!strict,
`Failed to execute "delete" on the "${modelName}" model: no entity found matching the query "${JSON.stringify(
query.where,
)}".`,
Expand All @@ -241,7 +247,7 @@ function createModelApi<

if (records.length === 0) {
invariant(
strict,
!strict,
`Failed to execute "deleteMany" on the "${modelName}" model: no entities found matching the query "${JSON.stringify(
query.where,
)}".`,
Expand All @@ -258,7 +264,7 @@ function createModelApi<
)
})

return records.map(removeInternalProperties)
return records.map((record) => removeInternalProperties(record))
},
toHandlers(type: 'rest' | 'graphql', baseUrl: string): any {
if (type === 'graphql') {
Expand Down
Loading

0 comments on commit f5478f9

Please sign in to comment.