mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-07 18:27:44 +02:00
Support for rating system (#10124)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
"rigPackageName": "@hcengineering/platform-rig"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
|
||||
roots: ["./src"],
|
||||
coverageReporters: ["text-summary", "html"]
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "@hcengineering/server-rating",
|
||||
"version": "0.7.0",
|
||||
"main": "lib/index.js",
|
||||
"svelte": "src/index.ts",
|
||||
"types": "types/index.d.ts",
|
||||
"files": [
|
||||
"lib/**/*",
|
||||
"types/**/*",
|
||||
"tsconfig.json"
|
||||
],
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"build:watch": "compile",
|
||||
"format": "format src",
|
||||
"test": "jest --passWithNoTests --silent",
|
||||
"_phase:build": "compile transpile src",
|
||||
"_phase:test": "jest --passWithNoTests --silent",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hcengineering/platform-rig": "^0.7.19",
|
||||
"@types/node": "^22.15.29",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-n": "^15.4.0",
|
||||
"eslint": "^8.54.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5.9.3",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"@types/jest": "^29.5.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/core": "^0.7.10",
|
||||
"@hcengineering/platform": "^0.7.5",
|
||||
"@hcengineering/server-core": "^0.7.8",
|
||||
"@hcengineering/rating": "^0.7.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
//
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import core, {
|
||||
TxProcessor,
|
||||
type Doc,
|
||||
type MeasureContext,
|
||||
type Tx,
|
||||
type TxCreateDoc,
|
||||
type TxCUD,
|
||||
type TxRemoveDoc,
|
||||
type TxUpdateDoc
|
||||
} from '@hcengineering/core'
|
||||
import type { Plugin } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import rating, { ReactionKind, type DocReaction } from '@hcengineering/rating'
|
||||
import {
|
||||
BaseMiddleware,
|
||||
type Middleware,
|
||||
type PipelineContext,
|
||||
type TxMiddlewareResult
|
||||
} from '@hcengineering/server-core'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const serverRatingId = 'server-rating' as Plugin
|
||||
|
||||
export class RatingMiddleware extends BaseMiddleware {
|
||||
static async create (ctx: MeasureContext, context: PipelineContext, next?: Middleware): Promise<Middleware> {
|
||||
return new RatingMiddleware(context, next)
|
||||
}
|
||||
|
||||
async tx (ctx: MeasureContext, tx: Tx[]): Promise<TxMiddlewareResult> {
|
||||
for (const t of tx) {
|
||||
if (TxProcessor.isExtendsCUD(t._class)) {
|
||||
const cud = t as TxCUD<Doc>
|
||||
|
||||
if (cud.objectClass === rating.class.DocRating || cud.objectClass === rating.class.PersonRating) {
|
||||
throw new Error('Direct modifications of ratings are not allowed.')
|
||||
}
|
||||
const c = rating.class.DocReaction
|
||||
if (cud.objectClass === c) {
|
||||
// Star/Like,Rate values could only be used one per user.
|
||||
switch (cud._class) {
|
||||
case core.class.TxCreateDoc:
|
||||
// Check for duplicate of like, rate value, Emojii
|
||||
await this.validateNewReaction(ctx, cud as TxCreateDoc<DocReaction>)
|
||||
break
|
||||
case core.class.TxUpdateDoc:
|
||||
// Disallow update for Emojii, Star, Like, Usefull
|
||||
// Allow only for RateValue
|
||||
await this.validateReactionUpdate(ctx, cud as TxUpdateDoc<DocReaction>)
|
||||
break
|
||||
case core.class.TxRemoveDoc:
|
||||
await this.validateReactionRemove(ctx, cud as TxRemoveDoc<DocReaction>)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return await this.provideTx(ctx, tx)
|
||||
}
|
||||
|
||||
private async validateNewReaction (ctx: MeasureContext, create: TxCreateDoc<DocReaction>): Promise<void> {
|
||||
if (create.attributes.reactionType !== ReactionKind.Star) {
|
||||
// Should allow only one reaction per document per user for Emoji, Like, Usefull
|
||||
const current = await this.provideFindAll(ctx, rating.class.DocReaction, {
|
||||
attachedTo: create.attachedTo,
|
||||
attachedToClass: create.objectClass
|
||||
})
|
||||
if (
|
||||
current.some(
|
||||
(it) =>
|
||||
it.reactionType === create.attributes.reactionType &&
|
||||
(it.value === create.attributes.value ||
|
||||
(create.attributes.reactionType === ReactionKind.Emoji && it.emoji === create.attributes.emoji))
|
||||
)
|
||||
) {
|
||||
throw new Error('Duplicate emoji reaction is not allowed.')
|
||||
}
|
||||
} else {
|
||||
// For stars, we probable need to merge them into one value.
|
||||
}
|
||||
}
|
||||
|
||||
private async validateReactionUpdate (ctx: MeasureContext, upd: TxUpdateDoc<DocReaction>): Promise<void> {
|
||||
if (upd.operations.reactionType !== undefined) {
|
||||
throw new Error('Modifications of reaction type are not allowed.')
|
||||
}
|
||||
// Find current reaction tried to be modified
|
||||
const current = (
|
||||
await this.provideFindAll(ctx, rating.class.DocReaction, {
|
||||
_id: upd.objectId,
|
||||
_class: upd.objectClass
|
||||
})
|
||||
).shift()
|
||||
if (current === undefined) {
|
||||
throw new Error('Reaction not found.')
|
||||
}
|
||||
if (upd.operations.value === undefined || upd.operations.value < 0 || upd.operations.value > 10) {
|
||||
throw new Error('Reaction value modification is required.')
|
||||
}
|
||||
}
|
||||
|
||||
private async validateReactionRemove (ctx: MeasureContext, upd: TxRemoveDoc<DocReaction>): Promise<void> {
|
||||
// Find current reaction tried to be modified
|
||||
// const current = (
|
||||
// await this.provideFindAll(ctx, rating.class.DocReaction, {
|
||||
// _id: upd.objectId,
|
||||
// _class: upd.objectClass
|
||||
// })
|
||||
// ).shift()
|
||||
// if (current === undefined) {
|
||||
// No error, already removed
|
||||
// }
|
||||
// if (current.reactionType === ReactionKind.Star) {
|
||||
// throw new Error('Star reactions could not be removed.')
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export default plugin(serverRatingId, {})
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "lib", "dist", "types", "bundle"]
|
||||
}
|
||||
Reference in New Issue
Block a user