Skip to content
Commits on Source (6)
# [1.36.0](https://gitlab.coko.foundation/ketida/server/compare/v1.35.0...v1.36.0) (2024-07-24)
### Bug Fixes
* **controllers:** pass 4th parameter as null when adding component to newly created book ([fde8da1](https://gitlab.coko.foundation/ketida/server/commit/fde8da138a96fb90ca841a865eeb8df0b6f9b416))
### Features
* comments for book components ([399d717](https://gitlab.coko.foundation/ketida/server/commit/399d717c93f699e911191fb8449d81a84a96fa85))
# [1.35.0](https://gitlab.coko.foundation/ketida/server/compare/v1.34.6...v1.35.0) (2024-07-22)
......
type Comment {
id: ID!
created: Date
updated: Date
bookId: ID!
chapterId: ID!
content: String
}
input CommentInput {
bookId: ID!
chapterId: ID!
content: String
}
extend type Query {
getChapterComments(bookId: ID!, chapterId: ID!): Comment
}
extend type Mutation {
addComments(commentData: CommentInput!): Comment!
}
const {
getComments,
addComments,
} = require('../../../controllers/comments.controller')
const getCommentsResolver = (_, { bookId, chapterId }) => {
return getComments(bookId, chapterId)
}
const addCommentsResolver = (_, { commentData }) => {
const { bookId, chapterId, content } = commentData
return addComments(bookId, chapterId, content) // ctx.user
}
module.exports = {
Query: {
getChapterComments: getCommentsResolver,
},
Mutation: {
addComments: addCommentsResolver,
},
}
const fs = require('fs')
const path = require('path')
const resolvers = require('./comments.resolvers')
module.exports = {
resolvers,
typeDefs: fs.readFileSync(path.join(__dirname, 'comments.graphql'), 'utf-8'),
}
......@@ -15,6 +15,7 @@ const exportProfile = require('./exportProfile')
const openAi = require('./openAi')
const invitations = require('./invitations')
const document = require('./document')
const comments = require('./comments')
module.exports = {
typeDefs: [
......@@ -34,6 +35,7 @@ module.exports = {
openAi.typeDefs,
invitations.typeDefs,
document.typeDefs,
comments.typeDefs,
].join(' '),
resolvers: merge(
{},
......@@ -53,5 +55,6 @@ module.exports = {
openAi.resolvers,
invitations.resolvers,
document.resolvers,
comments.resolvers,
),
}
......@@ -15,6 +15,7 @@ module.exports = [
'./models/bookComponentState',
'./models/bookComponentTranslation',
'./models/bookSettings',
'./models/bookComments',
'./models/invitations',
'./models/bookTranslation',
'./models/customTag',
......
......@@ -789,6 +789,7 @@ const permissions = {
openAi: isAuthenticatedRule,
ragSearch: isAuthenticatedRule,
getDocuments: isAuthenticatedRule,
getChapterComments: isAuthenticatedRule,
currentUser: isAuthenticatedRule,
getApplicationParameters: allow,
getBook: getBookRule,
......@@ -804,6 +805,7 @@ const permissions = {
},
Mutation: {
'*': deny,
addComment: addTeamMembersRule,
addTeamMembers: addTeamMembersRule,
createBook: createBookRule,
createExportProfile: createExportProfileRule,
......
......@@ -356,7 +356,7 @@ const createBook = async (data = {}) => {
div => div.label === 'Body',
)
await addBookComponent(bodyDivision.id, bookId, 'chapter', {
await addBookComponent(bodyDivision.id, bookId, 'chapter', null, {
trx: tr,
})
}
......
const { useTransaction, logger } = require('@coko/server')
const { Comments } = require('../models').models
const BASE_MESSAGE = '[COMMENTS CONTROLLER]'
const getComments = async (bookId, componentId) => {
const CONTROLLER_MESSAGE = `${BASE_MESSAGE} getComments:`
logger.info(
`${CONTROLLER_MESSAGE} Getting comments for book ${bookId}, bookComponent ${componentId}`,
)
try {
const comments = await Comments.findOne({
bookId,
componentId,
})
return comments ? { ...comments, chapterId: comments.componentId } : null
} catch (error) {
logger.error(`${CONTROLLER_MESSAGE} getComments: ${error.message}`)
throw new Error(error)
}
}
const addComments = async (bookId, componentId, content, options = {}) => {
const CONTROLLER_MESSAGE = `${BASE_MESSAGE} addComment:`
logger.info(
`${CONTROLLER_MESSAGE} adding comment for book ${bookId}, bookComponent ${componentId}`,
)
const { trx } = options
return useTransaction(
async tr => {
const comments = await Comments.findOne({
bookId,
componentId,
})
if (comments) {
return Comments.patchAndFetchById(comments.id, {
content,
})
}
return Comments.insert(
{
bookId,
componentId,
content,
},
{ trx: tr },
)
},
{ trx, passedTrxOnly: true },
)
}
module.exports = {
getComments,
addComments,
}
const { Model } = require('objection')
const { BaseModel } = require('@coko/server')
const { id, string } = require('../helpers').schema
class Comments extends BaseModel {
constructor(properties) {
super(properties)
this.type = 'book_comment'
}
static get tableName() {
return 'book_comments'
}
static get relationMappings() {
/* eslint-disable global-require */
const Book = require('../book/book.model')
const BookComponent = require('../bookComponent/bookComponent.model')
/* eslint-enable global-require */
return {
book: {
relation: Model.BelongsToOneRelation,
modelClass: Book,
join: {
from: 'Comments.bookId',
to: 'Book.id',
},
},
chapter: {
relation: Model.BelongsToOneRelation,
modelClass: BookComponent,
join: {
from: 'Comments.componentId',
to: 'BookComponent.id',
},
},
}
}
static get schema() {
return {
type: 'object',
required: ['bookId', 'componentId'],
properties: {
id,
created: { type: 'string', format: 'date-time' },
updated: { type: 'string', format: 'date-time' },
bookId: id,
componentId: id,
content: string,
},
}
}
// $parseJson(json, opt) {
// const data = super.$parseJson(json, opt)
// // console.log(data)
// // // transform stringified wax content to json before storing in the db
// // if (data.content && typeof data.content === 'string') {
// // data.content = JSON.parse(data.content)
// // }
// console.log('data after')
// console.log(data)
// return data
// }
}
module.exports = Comments
const model = require('./bookComments.model')
module.exports = {
model,
modelName: 'Comments',
}
const { logger } = require('@coko/server')
exports.up = knex => {
try {
return knex.schema.createTable('book_comments', table => {
table.uuid('id').primary()
table.text('type').notNullable()
table
.timestamp('created', { useTz: true })
.notNullable()
.defaultTo(knex.fn.now())
table
.timestamp('updated', { useTz: true })
.notNullable()
.defaultTo(knex.fn.now())
table.text('content').nullable()
table.uuid('bookId').notNullable()
table.uuid('componentId').notNullable()
})
} catch (e) {
logger.error(e)
throw new Error(`Migration: Comments: initial migration failed`)
}
}
exports.down = knex => {
return knex.schema.dropTable('book_comments')
}
......@@ -21,6 +21,7 @@ const { models } = require('./dataloader')
const invitations = require('./invitations')
const embeddings = require('./embeddings')
const document = require('./document')
const bookComments = require('./bookComments')
const loader = models.reduce((r, c) => Object.assign(r, c), {})
......@@ -48,6 +49,7 @@ module.exports = {
invitations,
document,
embeddings,
bookComments,
models: {
ApplicationParameter: applicationParameter.model,
Book: book.model,
......@@ -72,5 +74,6 @@ module.exports = {
Invitations: invitations.model,
Document: document.model,
Embedding: embeddings.model,
Comments: bookComments.model,
},
}
{
"name": "server",
"version": "1.35.0",
"version": "1.36.0",
"private": true,
"description": "Ketida's Platform common server",
"repository": {
......
const { migrate } = require('@coko/server')
migrate()
......@@ -7,7 +7,7 @@ sh scripts/wait-for-it http://epubchecker:3001/healthcheck -t 60
sh scripts/wait-for-it http://pagedjs:3003/healthcheck -t 60
sh scripts/wait-for-it http://xsweet:3004/healthcheck -t 60
node_modules/.bin/coko-server migrate
node scripts/runners/migrations.js
node scripts/runners/createGlobalTeams.js
node scripts/runners/createAdmin.js
node scripts/runners/createApplicationParameters.js
......