Skip to content
This repository was archived by the owner on Dec 21, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
66b1391
feat(cache-improve): improved caching
dmytro-kulchytskyi Oct 27, 2021
33344cd
feat(cache-improve): improved caching
dmytro-kulchytskyi Oct 27, 2021
833e357
feat(cache-improve): improved caching
dmytro-kulchytskyi Oct 29, 2021
da43fec
feat(cache-improve): improved caching
dmytro-kulchytskyi Oct 29, 2021
d2b6c02
feat(cache-improve): improved caching
dmytro-kulchytskyi Oct 29, 2021
39ba638
Merge branch 'master' into feature/WALL-2186
dmytro-kulchytskyi Oct 29, 2021
92a70e6
feat(cache-improve): improved caching
dmytro-kulchytskyi Nov 3, 2021
d3f96f6
Merge branch 'master' into feature/WALL-2186
dmytro-kulchytskyi Jan 28, 2022
d1bca17
2.0.0-beta.0
dmytro-kulchytskyi Feb 2, 2022
3499356
feat(cache-improve): improved caching
dmytro-kulchytskyi Feb 2, 2022
bdf34a3
2.0.0-beta.1
dmytro-kulchytskyi Feb 2, 2022
2f7d4ac
feat(cache-improve): improved caching
dmytro-kulchytskyi Feb 2, 2022
3077884
2.0.0-beta.2
dmytro-kulchytskyi Feb 2, 2022
e5a55e1
feat(cache-improve): improved caching
dmytro-kulchytskyi Feb 2, 2022
e1394f7
2.0.0-beta.3
dmytro-kulchytskyi Feb 2, 2022
809f2bd
feat(cache-improve): improved caching
dmytro-kulchytskyi Feb 7, 2022
2e8ba98
3.1.0-beta.4
dmytro-kulchytskyi Feb 7, 2022
ad0f432
2.0.0-beta.4
dmytro-kulchytskyi Feb 7, 2022
0a26132
feat(cache-improve): improved caching
dmytro-kulchytskyi Feb 7, 2022
74df124
2.0.0-beta.5
dmytro-kulchytskyi Feb 7, 2022
d8e784e
feat(cache-improve): improved caching
dmytro-kulchytskyi Feb 9, 2022
b501fb2
2.0.0-beta.6
dmytro-kulchytskyi Feb 9, 2022
fcf07a9
feat(cache-improve): improved caching
dmytro-kulchytskyi Feb 9, 2022
47476af
2.0.0-beta.7
dmytro-kulchytskyi Feb 9, 2022
54359cd
feat(cache-improve): improved caching
dmytro-kulchytskyi Feb 9, 2022
89ad097
2.0.0-beta.8
dmytro-kulchytskyi Feb 9, 2022
23b5865
feat(cache-improve): improved caching
dmytro-kulchytskyi Feb 9, 2022
90ed4e3
2.0.0-beta.9
dmytro-kulchytskyi Feb 9, 2022
8d4061a
Merge branch 'master' into feature/WALL-2186
dmytro-kulchytskyi Mar 25, 2022
de7b3d3
2.0.0-beta.10
dmytro-kulchytskyi Mar 25, 2022
1bd5f0c
Merge branch 'master' into feature/WALL-2186
dmytro-kulchytskyi Mar 31, 2022
00ee623
2.0.0-beta.11
dmytro-kulchytskyi Mar 31, 2022
6a84f31
Merge branch 'master' into feature/WALL-2186
dmytro-kulchytskyi Mar 31, 2022
aab0f96
2.0.0-beta.12
dmytro-kulchytskyi Mar 31, 2022
ba777c3
2.0.0-beta.13
dmytro-kulchytskyi Mar 31, 2022
6754f7d
Merge branch 'master' into feature/WALL-2186
dmytro-kulchytskyi Mar 31, 2022
133eec4
feat(error): errors consts
dmytro-kulchytskyi Apr 4, 2022
3ec884d
2.0.0-beta.14
dmytro-kulchytskyi Apr 4, 2022
486b21a
fix(destroy): fixed delete func
dmytro-kulchytskyi May 17, 2022
dc67c4d
2.0.0-beta.15
dmytro-kulchytskyi May 17, 2022
6687cdb
fix(destroy): fixed delete func
dmytro-kulchytskyi May 17, 2022
b8abc34
2.0.0-beta.16
dmytro-kulchytskyi May 17, 2022
937776c
Merge branch 'master' into feature/WALL-2186
dmytro-kulchytskyi Jun 7, 2022
e2003c9
2.0.0-beta.17
dmytro-kulchytskyi Jun 7, 2022
891631c
Merge branch 'master' into feature/WALL-2186
dmytro-kulchytskyi Jul 5, 2022
ddfa03c
2.0.0-beta.18
dmytro-kulchytskyi Jul 5, 2022
0870591
feat(upd-caching): fixed tests
dmytro-kulchytskyi Jul 5, 2022
489a5f2
2.0.0-beta.19
dmytro-kulchytskyi Jul 5, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@charge-tech/chargejs",
"version": "0.1.0",
"version": "2.0.0-beta.19",
"description": "The Charge Core JS implementation.",
"main": "src/index.js",
"engines": {
Expand Down Expand Up @@ -55,11 +55,13 @@
"http-status": "^1.5.0",
"https": "^1.0.0",
"lodash": "^4.17.20",
"paginate-info": "^1.0.4",
"node-device-detector": "^1.3.11",
"structure": "^2.0.1",
"winston": "^3.2.1"
},
"peerDependencies": {
"awilix": "^5.0.1",
"awilix-express": "^5.0.0"
},
"directories": {
Expand Down
3 changes: 2 additions & 1 deletion src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ export const ERROR_TYPES = {
CACHE_DISABLED: 'CacheDisabledError',
INVALID_FILTER: 'InvalidFilterError',
INVALID_FILTER_VALUE: 'InvalidFilterValueError',
INVALID_FILTER_TYPE: 'InvalidFIlterTypeError',
INVALID_FILTER_TYPE: 'InvalidFilterTypeError',
EMPTY_UPDATE_FIELDS: 'EmptyUpdateFields',
INVALID_PAGINATE_PARAMS: 'InvalidPaginateParamsError'
}

Expand Down
10 changes: 10 additions & 0 deletions src/errors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ export const VALIDATION_ERROR = {
code: ERROR_TYPES.VALIDATION_ERROR
}

export const CONFLICT_ERROR = {
message: 'Conflict error',
code: ERROR_TYPES.CONFLICT
}

export const INVALID_ENV = {
message: `Invalid environment. Allowed values ${ALLOWED_ENVIRONMENTS}`,
code: CODES.INVALID_ENV
Expand Down Expand Up @@ -48,6 +53,11 @@ export const INVALID_FILTER_TYPE = {
code: ERROR_TYPES.INVALID_FILTER_TYPE
}

export const EMPTY_UPDATE_FIELDS = {
message: 'No fileds to update',
code: ERROR_TYPES.EMPTY_UPDATE_FIELDS
}

export const INVALID_CURRENT_PAGE = {
message: 'currentPage parameter should be a valid number',
code: ERROR_TYPES.INVALID_PAGINATE_PARAMS
Expand Down
173 changes: 136 additions & 37 deletions src/infra/BaseRepository.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
SEQUELIZE_NOT_FOUNT_ERROR,
SEQUELIZE_VALIDATION_ERROR,
INVALID_PAGE_SIZE,
INVALID_CURRENT_PAGE
INVALID_CURRENT_PAGE,
EMPTY_UPDATE_FIELDS
} from '../errors'

/**
Expand All @@ -32,45 +33,48 @@ class BaseRepository {
redisRepository,
calculateLimitAndOffset,
paginate,
config: { logmsg },
config,
standardError
}) {
this.transactionProvider = transactionProvider
this.redisRepository = redisRepository
this.calculateLimitAndOffset = calculateLimitAndOffset
this.paginate = paginate
this.logmsg = logmsg
this.config = config
this.logmsg = config.logmsg
this.standardError = standardError
}

/**
* Provides configuration for BaseRepository.
*
* @param {Object} input The input object as injected in src/container.js
* @param {string} input.modelName The model name
* @param {Object} input.model The db model
* @param {Object} input.mapper The entity mapper
* @param {string[]} [input.patchAllowedFields] The patch allowed fields array
* @param {Object[]} [input.include] Included related entities array
* @param {boolean} [input.cacheDisabled=false] Disable cahing
*/
init ({
modelName,
model,
mapper,
patchAllowedFields = undefined,
include = undefined,
cacheDisabled = false
}) {
this.modelName = modelName
this.modelName = this._getModelName(model)
this.model = model
this.mapper = mapper
this.patchAllowedFields = patchAllowedFields
this.include = include
this.cacheDisabled = cacheDisabled

const indexes = !cacheDisabled ? model._indexes : []
this.redisRepository.init(modelName, indexes, include)
this.redisRepository.init({
modelName: this.modelName,
indexes: this._getIndexes(model),
include: this._normalizeInclude(include),
references: this._getReferences(model)
})
}

/**
Expand Down Expand Up @@ -117,25 +121,64 @@ class BaseRepository {
}
}

/**
* Сount entities by filter
*
* @param {Object} where filter
* @param {Object} [options] additional sequelize options
* @returns {Promise<Number>} number of entities
*/
count (where, options) {
return this.model.count({
...options,
where,
include: this.include,
transaction: this._getTransaction()
})
}

/**
* Returns latest entry
*
* @param {Object} [where] filter
* @param {Object} [options] additional sequelize options
* @param {String} [options.orderBy] order field
* @returns {Promise<Object>} entity
* @throws {module:interface.standardError}
*/
async findLatest (where = {}, { rejectOnEmpty = true, ...opts } = {}) {
const [result] = await this.findAll(where, {
limit: 1,
...opts
})
if (result) {
return result
}
if (rejectOnEmpty) {
throw this._getNotFoundError()
}
}

/**
* Finds entities by filter
*
* @param {Object} where filter
* @param {Object} [options] additional sequelize options
* @param {String} [options.orderBy] order field
* @returns {Promise<Object[]>} entities
* @throws {module:interface.standardError}
*/
async findAll (where, options = {}) {
async findAll (where, { orderBy = 'createdAt', ...opts } = {}) {
this._validateFilter(where)

const results = await this.model.findAll({
order: [['createdAt', 'DESC']],
...options,
const rows = await this.model.findAll({
order: [[orderBy, 'DESC']],
...opts,
where,
include: this.include,
transaction: this._getTransaction()
})
return results.map((entry) => entry.toJSON())
return rows.map(row => this.mapper.toEntity(row.toJSON()))
}

/**
Expand All @@ -150,18 +193,19 @@ class BaseRepository {
* @param {number} currentPage current page number
* @param {number} pageSize page size
* @param {Object} [options] additional sequelize options
* @param {String} [options.orderBy] order field
* @returns {Promise<{ data: Object[], meta: Meta>} entities
* @throws {module:interface.standardError}
*/
async findAndCountAll (where, currentPage, pageSize, options = {}) {
async findAndCountAll (where, currentPage, pageSize, { orderBy = 'createdAt', ...opts } = {}) {
this._validateFilter(where)
this._validatePaginateParams(currentPage, pageSize)

const { limit, offset } = this.calculateLimitAndOffset(currentPage, pageSize)

const { rows, count } = await this.model.findAndCountAll({
order: [['createdAt', 'DESC']],
...options,
order: [[orderBy, 'DESC']],
...opts,
limit,
offset,
distinct: true,
Expand All @@ -170,7 +214,10 @@ class BaseRepository {
transaction: this._getTransaction()
})
const meta = this.paginate(currentPage, count, rows, pageSize)
return { data: rows.map((row) => row.toJSON()), meta }
return {
data: rows.map(row => this.mapper.toEntity(row.toJSON())),
meta
}
}

/**
Expand All @@ -185,36 +232,47 @@ class BaseRepository {
if (!valid) {
throw this._getValidationError(errors)
}
const dbEntry = await this.model.create(this.mapper.toDatabase(entity), {
transaction: this._getTransaction()
})
return this.mapper.toEntity(dbEntry.toJSON())
const dbEntry = await this.model.create(
this.mapper.toDatabase(entity),
{ transaction: this._getTransaction() }
)
const json = dbEntry.toJSON()
await this.redisRepository.clearReferenced(json)
return this.mapper.toEntity(json)
}

/**
* Updates entity by uuid
*
* @param {string} id entity uuid
* @param {Object} updateFields the fields to be updated
* @param {boolean} [filterFields=true] should filter updateFields
* @returns {Promise<module:domain.BaseDomain>} updated domain entity
* @throws {module:interface.standardError}
*/
async patchById (id, updateFields) {
return this.patch({ id }, updateFields)
async patchById (id, updateFields, filterFields = true) {
return this.patch({ id }, updateFields, filterFields)
}

/**
* Updates entity by filter
*
* @param {Object} where filter
* @param {Object} updateFields the fields to be updated
* @param {boolean} [filterFields=true] should filter updateFields
* @returns {Promise<module:domain.BaseDomain>} updated domain entity
* @throws {module:interface.standardError}
*/
async patch (where, updateFields) {
async patch (where, updateFields, filterFields = true) {
this._validateFilter(where)

const filteredFields = this._filterPatchFields(updateFields)
const filteredFields = filterFields ? this._filterPatchFields(updateFields) : updateFields
if (!Object.keys(filteredFields).length) {
throw this._getValidationError([{
message: EMPTY_UPDATE_FIELDS.message,
path: 'updateFields'
}])
}
try {
const filter = await this._getPatchFilter(where)
const [, result] = await this.model.update(filteredFields, {
Expand All @@ -227,7 +285,7 @@ class BaseRepository {
throw this._getNotFoundError()
}
const json = result.toJSON()
await this._clearCache(json.id)
await this._clearCache(json)
return this.mapper.toEntity(json)
} catch (error) {
switch (error.name) {
Expand Down Expand Up @@ -263,16 +321,20 @@ class BaseRepository {
this._validateFilter(where)

try {
const filter = await this._getPatchFilter(where)
const [, result] = await this.model.destroy({
where: filter,
const result = await this.model.findOne({
where,
distinct: true,
transaction: this._getTransaction()
})
if (!result) {
throw this._getNotFoundError()
}
await this.model.destroy({
where: { id: result.id },
transaction: this._getTransaction()
})
const json = result.toJSON()
await this._clearCache(json.id)
await this._clearCache(json)
return this.mapper.toEntity(json)
} catch (error) {
switch (error.name) {
Expand All @@ -287,6 +349,47 @@ class BaseRepository {
}

// Private
_getModelName (model) {
const modelName = model.options.name.singular
return modelName.charAt(0).toLowerCase() + modelName.slice(1)
}

_getReferences (model) {
const models = Object.values(model.sequelize.models)
return Object.values(model.rawAttributes)
.filter(({ references }) => references && references.key === 'id')
.map(attr => {
const { model } = attr.references
const m = models.find(m => m.tableName === model)
return {
modelName: this._getModelName(m),
fieldName: attr.fieldName
}
})
}

_normalizeInclude (include) {
if (!include) {
return []
}
return include.map(({ model, as, include }) => ({
modelName: this._getModelName(model),
as,
include: include && this._normalizeInclude(include)
}))
}

_getIndexes (model) {
const models = Object.values(model.sequelize.models)
const entries = models.map(model => {
const modelName = this._getModelName(model)
const indexes = (model.options.indexes || [])
.filter(index => index.unique)
.map(index => index.fields)
return [modelName, indexes]
})
return Object.fromEntries(entries)
}

async _dbFindOne (where) {
const result = await this.model.findOne({
Expand All @@ -300,11 +403,8 @@ class BaseRepository {
}
}

async _clearCache (id) {
if (!this.cacheDisabled) {
await this.redisRepository.delete(id)
}
await this.redisRepository.clearRelated(id)
_clearCache (entity) {
return this.redisRepository.clear(entity, this.cacheDisabled)
}

async _getPatchFilter (where) {
Expand Down Expand Up @@ -342,7 +442,6 @@ class BaseRepository {
error.type = INVALID_CURRENT_PAGE.code
throw error
}

if (Number.isNaN(Number(pageSize))) {
// Error for developers only, can only occur if the pageSize is invalid
const error = new Error(INVALID_PAGE_SIZE.message)
Expand Down Expand Up @@ -398,7 +497,7 @@ class BaseRepository {
}

_getCapitalizedModelName () {
return this.modelName.charAt(0).toUpperCase() + this.modelName.slice(1)
return this.model.options.name.singular
}

_notFoundErrorMessage () {
Expand Down
Loading