Skip to content
Snippets Groups Projects
Commit 697038f4 authored by Alexandros Georgantas's avatar Alexandros Georgantas Committed by Yannis Barlas
Browse files

feat(*): add activity log service

parent 434bec9e
No related branches found
No related tags found
1 merge request!55feat(*): audit log service added
......@@ -9,4 +9,5 @@ module.exports = [
'./src/models/__tests__/helpers/fake',
'./src/models/serviceCredential',
'./src/services/chatGPT',
'./src/models/activityLog',
]
......@@ -3,4 +3,5 @@ module.exports = [
'./src/models/teamMember',
'./src/models/user',
'./src/models/identity',
'./src/models/activityLog',
]
......@@ -27,6 +27,8 @@ const {
const WaxToDocxConverter = require('./services/docx/docx.service')
const activityLog = require('./services/activityLog')
// Do not expose connectToFileStorage
const fileStorage = {
healthCheck,
......@@ -62,7 +64,7 @@ module.exports = {
deleteFiles,
// serviceHandshake,
sendEmail,
activityLog,
BaseModel,
File,
logger,
......
const BaseModel = require('../base.model')
const {
id,
stringNotEmpty,
stringNullable,
object,
objectNullable,
} = require('../_helpers/types')
const affectedObject = {
type: 'object',
additionalProperties: false,
required: ['id', 'objectType'],
properties: {
id,
objectType: stringNotEmpty,
},
}
const affectedObjects = {
type: 'array',
default: [],
additionalProperties: false,
items: affectedObject,
}
class ActivityLog extends BaseModel {
constructor(properties) {
super(properties)
this.type = 'activityLog'
}
static get tableName() {
return 'activityLogs'
}
static get schema() {
return {
type: 'object',
required: ['actorId', 'actionType'],
properties: {
actorId: id,
actionType: stringNotEmpty,
message: stringNullable,
valueBefore: objectNullable,
valueAfter: objectNullable,
affectedObjects,
additionalData: object,
},
}
}
static get relationMappings() {
/* eslint-disable-next-line global-require */
const User = require('../user/user.model')
return {
user: {
relation: BaseModel.BelongsToOneRelation,
modelClass: User,
join: {
from: 'activityLogs.actorId',
to: 'users.id',
},
},
}
}
}
module.exports = ActivityLog
module.exports = {
labels: {
ACTIVITY_LOG_SERVICE: '[ACTIVITY_LOG_SERVICE] -',
},
actionTypes: {
CREATE: 'CREATE',
UPDATE: 'UPDATE',
PATCH: 'PATCH',
DELETE: 'DELETE',
},
}
const model = require('./activityLog.model')
module.exports = {
model,
modelName: 'ActivityLog',
}
const logger = require('@pubsweet/logger')
exports.up = knex => {
try {
return knex.schema.createTable('activity_logs', table => {
table.uuid('id').primary()
table
.timestamp('created', { useTz: true })
.notNullable()
.defaultTo(knex.fn.now())
table.timestamp('updated', { useTz: true })
table.uuid('actor_id').references('users.id').notNullable()
table.text('action_type').notNullable()
table.jsonb('value_before')
table.jsonb('value_after')
table.jsonb('affected_objects').defaultTo([])
table.text('message')
table.jsonb('additional_data')
table.text('type').notNullable()
})
} catch (e) {
logger.error('Acitivity log: Initial: Migration failed!')
throw new Error(e)
}
}
exports.down = knex => knex.schema.dropTable('activity_logs')
......@@ -10,6 +10,7 @@ const User = require('./user/user.model')
const Identity = require('./identity/identity.model')
const File = require('./file/file.model')
const ActivityLog = require('./activityLog/activityLog.model')
const useTransaction = require('./useTransaction')
const ServiceCredential = require('./serviceCredential/serviceCredential.model')
......@@ -27,6 +28,7 @@ module.exports = {
Identity,
File,
ActivityLog,
useTransaction,
ServiceCredential,
......
const activityLog = require('../activityLog')
const Fake = require('../../models/__tests__/helpers/fake/fake.model')
const { createUser } = require('../../models/__tests__/helpers/users')
const clearDb = require('../../models/__tests__/_clearDb')
const ActivityLog = require('../../models/activityLog/activityLog.model')
const { actionTypes } = require('../../models/activityLog/constants')
describe('Activity Log Service', () => {
beforeEach(() => clearDb())
afterAll(() => {
const knex = Fake.knex()
knex.destroy()
})
it('creates a log entry', async () => {
const actor = await createUser()
const dummyUser = await createUser()
const log = await activityLog({
actorId: actor.id,
actionType: actionTypes.CREATE,
message: 'create a new user',
valueAfter: dummyUser,
affectedObjects: [{ id: dummyUser.id, objectType: 'user' }],
})
const { result: activities } = await ActivityLog.find({})
expect(log).toBeDefined()
expect(activities).toHaveLength(1)
})
})
const logger = require('@pubsweet/logger')
const useTransaction = require('../models/useTransaction')
const ActivityLog = require('../models/activityLog/activityLog.model')
const {
labels: { ACTIVITY_LOG_SERVICE },
} = require('../models/activityLog/constants')
const activityLog = async (data, options = {}) => {
try {
const { trx } = options
return useTransaction(
async tr => {
return ActivityLog.insert(data, { trx: tr })
},
{
trx,
passedTrxOnly: true,
},
)
} catch (e) {
logger.error(`${ACTIVITY_LOG_SERVICE} activityLog: ${e.message}`)
throw new Error(e)
}
}
module.exports = activityLog
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment