@drax/settings-back 0.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/controller/SettingController.js +89 -0
- package/dist/factory/SettingServiceFactory.js +24 -0
- package/dist/index.js +8 -0
- package/dist/interfaces/ISettingRepository.js +1 -0
- package/dist/model/SettingsModel.js +28 -0
- package/dist/permissions/SettingPermissions.js +8 -0
- package/dist/repository/mongo/SettingMongoRepository.js +13 -0
- package/dist/repository/sqlite/SettingSqliteRepository.js +113 -0
- package/dist/routes/SettingRoutes.js +10 -0
- package/dist/schemas/SettingSchema.js +7 -0
- package/dist/services/SettingService.js +95 -0
- package/package.json +66 -0
- package/src/controller/SettingController.ts +92 -0
- package/src/factory/SettingServiceFactory.ts +32 -0
- package/src/index.ts +24 -0
- package/src/interfaces/ISettingRepository.ts +6 -0
- package/src/model/SettingsModel.ts +46 -0
- package/src/permissions/SettingPermissions.ts +9 -0
- package/src/repository/mongo/SettingMongoRepository.ts +23 -0
- package/src/repository/sqlite/SettingSqliteRepository.ts +176 -0
- package/src/routes/SettingRoutes.ts +17 -0
- package/src/schemas/SettingSchema.ts +11 -0
- package/src/services/SettingService.ts +111 -0
- package/test/repository/mongo/MongoInMemory.ts +43 -0
- package/test/repository/mongo/SettingMongoRepository.test.ts +180 -0
- package/test/repository/sqlite/SettingSqliteRepository.test.ts +175 -0
- package/test/services/SettingService.test.ts +319 -0
- package/test.db +0 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/types/controller/SettingController.d.ts +15 -0
- package/types/controller/SettingController.d.ts.map +1 -0
- package/types/factory/SettingServiceFactory.d.ts +4 -0
- package/types/factory/SettingServiceFactory.d.ts.map +1 -0
- package/types/index.d.ts +11 -0
- package/types/index.d.ts.map +1 -0
- package/types/interfaces/ISettingRepository.d.ts +6 -0
- package/types/interfaces/ISettingRepository.d.ts.map +1 -0
- package/types/model/SettingsModel.d.ts +19 -0
- package/types/model/SettingsModel.d.ts.map +1 -0
- package/types/permissions/SettingPermissions.d.ts +8 -0
- package/types/permissions/SettingPermissions.d.ts.map +1 -0
- package/types/repository/mongo/SettingMongoRepository.d.ts +9 -0
- package/types/repository/mongo/SettingMongoRepository.d.ts.map +1 -0
- package/types/repository/sqlite/SettingSqliteRepository.d.ts +22 -0
- package/types/repository/sqlite/SettingSqliteRepository.d.ts.map +1 -0
- package/types/routes/SettingRoutes.d.ts +4 -0
- package/types/routes/SettingRoutes.d.ts.map +1 -0
- package/types/schemas/SettingSchema.d.ts +10 -0
- package/types/schemas/SettingSchema.d.ts.map +1 -0
- package/types/services/SettingService.d.ts +23 -0
- package/types/services/SettingService.d.ts.map +1 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {mongoose} from '@drax/common-back';
|
|
2
|
+
import uniqueValidator from 'mongoose-unique-validator';
|
|
3
|
+
import {ISetting} from "@drax/settings-share";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const SettingSchema = new mongoose.Schema<ISetting>({
|
|
7
|
+
|
|
8
|
+
key: {type: String, required: true, unique: true},
|
|
9
|
+
value: {type: mongoose.Schema.Types.Mixed, required: false, unique: false},
|
|
10
|
+
//valueList: [{type: String, required: false, unique: false}],
|
|
11
|
+
label: {type: String, required: false},
|
|
12
|
+
group: {type: String, required: true},
|
|
13
|
+
type: {type: String, default: "string", enum: ['string','longString','number','enum','boolean', 'password', 'stringList','numberList', 'enumList', 'ref', 'secret'], required: false, unique: false},
|
|
14
|
+
options: [{type: String}],
|
|
15
|
+
regex: {type: String},
|
|
16
|
+
entity: {type: String, required: false},
|
|
17
|
+
entityValue: {type: String, required: false},
|
|
18
|
+
entityText: {type: String, required: false, unique: false},
|
|
19
|
+
prefix: {type: String, required: false},
|
|
20
|
+
suffix: {type: String, required: false},
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
SettingSchema.virtual("id").get(function () {
|
|
24
|
+
return this._id.toString();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
SettingSchema.set('toJSON', {getters: true, virtuals: true});
|
|
29
|
+
|
|
30
|
+
SettingSchema.set('toObject', {getters: true, virtuals: true});
|
|
31
|
+
|
|
32
|
+
SettingSchema.plugin(uniqueValidator, {message: 'validation.unique'});
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
const MODEL_NAME = 'Setting';
|
|
37
|
+
const COLLECTION_NAME = 'settings';
|
|
38
|
+
|
|
39
|
+
const SettingModel = mongoose.model<ISetting>(MODEL_NAME, SettingSchema,COLLECTION_NAME);
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
SettingSchema,
|
|
43
|
+
SettingModel
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default SettingModel
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {AbstractMongoRepository} from "@drax/crud-back";
|
|
2
|
+
import type {ISettingRepository} from "../../interfaces/ISettingRepository";
|
|
3
|
+
import {SettingModel} from "../../model/SettingsModel.js";
|
|
4
|
+
import {ISetting, ISettingBase} from "@drax/settings-share";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SettingMongoRepository extends AbstractMongoRepository<ISetting,ISettingBase,ISettingBase> implements ISettingRepository {
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
//@ts-ignore
|
|
13
|
+
this._model = SettingModel;
|
|
14
|
+
|
|
15
|
+
this._searchFields = ['_id', 'key'];
|
|
16
|
+
this._populateFields = [];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default SettingMongoRepository;
|
|
23
|
+
export {SettingMongoRepository};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import {ISettingRepository} from '../../interfaces/ISettingRepository'
|
|
2
|
+
import {UUID} from "crypto";
|
|
3
|
+
import sqlite from "better-sqlite3";
|
|
4
|
+
import {randomUUID} from "node:crypto";
|
|
5
|
+
import {IDraxPaginateResult, IDraxPaginateOptions} from "@drax/crud-share";
|
|
6
|
+
import {ISetting, ISettingBase} from "@drax/settings-share";
|
|
7
|
+
import {
|
|
8
|
+
SqliteErrorToValidationError,
|
|
9
|
+
SqliteTableBuilder,
|
|
10
|
+
SqliteTableField,
|
|
11
|
+
SqlQueryFilter,
|
|
12
|
+
SqlSort
|
|
13
|
+
} from "@drax/common-back";
|
|
14
|
+
|
|
15
|
+
const tableFields: SqliteTableField[] = [
|
|
16
|
+
{name: "key", type: "TEXT", unique: true, primary: false},
|
|
17
|
+
{name: "value", type: "TEXT", unique: false, primary: false},
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SettingSqliteRepository implements ISettingRepository{
|
|
22
|
+
|
|
23
|
+
protected db: any;
|
|
24
|
+
protected dataBaseFile: string;
|
|
25
|
+
protected _searchFields: string[] = []
|
|
26
|
+
|
|
27
|
+
constructor(dataBaseFile:string, verbose:boolean = false) {
|
|
28
|
+
this.dataBaseFile = dataBaseFile;
|
|
29
|
+
this._searchFields = ['_id', 'key'];
|
|
30
|
+
this.db = new sqlite(dataBaseFile, {verbose: verbose ? console.log : null});
|
|
31
|
+
this.table()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
table() {
|
|
35
|
+
const builder = new SqliteTableBuilder(
|
|
36
|
+
this.dataBaseFile,
|
|
37
|
+
'settings',
|
|
38
|
+
tableFields,
|
|
39
|
+
false);
|
|
40
|
+
builder.build('id')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async create(data: ISettingBase): Promise<ISetting> {
|
|
46
|
+
try{
|
|
47
|
+
|
|
48
|
+
if(!data.id){
|
|
49
|
+
data.id = randomUUID()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// data.createdAt = (new Date().toISOString())
|
|
53
|
+
|
|
54
|
+
const fields = Object.keys(data)
|
|
55
|
+
.map(field => `${field}`)
|
|
56
|
+
.join(', ');
|
|
57
|
+
|
|
58
|
+
const values = Object.keys(data)
|
|
59
|
+
.map(field => `@${field}`)
|
|
60
|
+
.join(', ');
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
const stmt = this.db.prepare(`INSERT INTO settings (${fields}) VALUES (${values})`);
|
|
64
|
+
stmt.run(data)
|
|
65
|
+
return this.findById(data.id as UUID)
|
|
66
|
+
}catch (e){
|
|
67
|
+
console.log(e)
|
|
68
|
+
throw SqliteErrorToValidationError(e, data)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async update(id: string, data: ISettingBase): Promise<ISetting> {
|
|
75
|
+
try{
|
|
76
|
+
// data.updatedAt = (new Date().toISOString())
|
|
77
|
+
|
|
78
|
+
const setClauses = Object.keys(data)
|
|
79
|
+
.map(field => `${field} = @${field}`)
|
|
80
|
+
.join(', ');
|
|
81
|
+
|
|
82
|
+
data.id = id
|
|
83
|
+
|
|
84
|
+
const stmt = this.db.prepare( `UPDATE settings SET ${setClauses} WHERE id = @id `);
|
|
85
|
+
|
|
86
|
+
stmt.run(data);
|
|
87
|
+
|
|
88
|
+
return this.findById(id)
|
|
89
|
+
}catch (e){
|
|
90
|
+
console.log(e)
|
|
91
|
+
throw SqliteErrorToValidationError(e, data)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async paginate({
|
|
97
|
+
page= 1,
|
|
98
|
+
limit= 5,
|
|
99
|
+
orderBy= '',
|
|
100
|
+
order= false,
|
|
101
|
+
search= '',
|
|
102
|
+
filters= []} : IDraxPaginateOptions): Promise<IDraxPaginateResult<ISetting>>{
|
|
103
|
+
const offset = page > 1 ? (page - 1) * limit : 0
|
|
104
|
+
|
|
105
|
+
let where=""
|
|
106
|
+
if (search) {
|
|
107
|
+
where = ` WHERE name LIKE '%${search}%'`
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
where = SqlQueryFilter.applyFilters(where, filters)
|
|
111
|
+
const sort = SqlSort.applySort(orderBy, order)
|
|
112
|
+
|
|
113
|
+
const rCount = this.db.prepare('SELECT COUNT(*) as count FROM settings'+where).get();
|
|
114
|
+
where += sort
|
|
115
|
+
const settings = this.db.prepare('SELECT * FROM settings ' + where + ' LIMIT ? OFFSET ?').all([limit, offset]);
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
page: page,
|
|
120
|
+
limit: limit,
|
|
121
|
+
total: rCount.count,
|
|
122
|
+
items: settings
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async delete(id: string): Promise<boolean> {
|
|
127
|
+
const stmt = this.db.prepare('DELETE FROM settings WHERE id = ?');
|
|
128
|
+
stmt.run(id);
|
|
129
|
+
return true
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async deleteAll(): Promise<boolean> {
|
|
133
|
+
const stmt = this.db.prepare('DELETE FROM settings');
|
|
134
|
+
stmt.run();
|
|
135
|
+
return true
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async findById(id: string): Promise<ISetting | null>{
|
|
139
|
+
const setting = this.db.prepare('SELECT * FROM settings WHERE id = ?').get(id);
|
|
140
|
+
if(setting){
|
|
141
|
+
return setting
|
|
142
|
+
}
|
|
143
|
+
return undefined
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async findByKey(key: string): Promise<ISetting | null>{
|
|
147
|
+
const setting = this.db.prepare('SELECT * FROM settings WHERE key = ?').get(key);
|
|
148
|
+
if(setting){
|
|
149
|
+
return setting
|
|
150
|
+
}
|
|
151
|
+
return undefined
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async fetchAll(): Promise<ISetting[]>{
|
|
155
|
+
const settings = this.db.prepare('SELECT * FROM settings').all();
|
|
156
|
+
return settings
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
async search(value: any, limit: number = 1000): Promise<any[]>{
|
|
161
|
+
let where=""
|
|
162
|
+
if (value && this._searchFields.length > 0) {
|
|
163
|
+
where = ` WHERE ${this._searchFields.map(field => `${field} LIKE '%${value}%'`).join(" OR ")}`
|
|
164
|
+
}
|
|
165
|
+
const items = this.db.prepare(`SELECT * FROM settings ${where}`).all();
|
|
166
|
+
return items
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export default SettingSqliteRepository
|
|
176
|
+
export {SettingSqliteRepository}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import SettingController from "../controller/SettingController.js";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async function SettingsRoutes(fastify, options) {
|
|
5
|
+
const controller: SettingController = new SettingController()
|
|
6
|
+
|
|
7
|
+
fastify.get('/api/settings', (req,rep) => controller.fetchAll(req,rep))
|
|
8
|
+
|
|
9
|
+
fastify.get('/api/settings/grouped', (req,rep) => controller.fetchGrouped(req,rep))
|
|
10
|
+
|
|
11
|
+
fastify.get('/api/settings/:key', (req,rep) => controller.findByKey(req,rep))
|
|
12
|
+
|
|
13
|
+
fastify.patch('/api/settings/:id', (req,rep) => controller.updateValue(req,rep))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default SettingsRoutes
|
|
17
|
+
export {SettingsRoutes}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import {ISettingRepository} from "../interfaces/ISettingRepository";
|
|
2
|
+
import {settingSchema} from "../schemas/SettingSchema.js";
|
|
3
|
+
import {ISetting, ISettingBase} from "@drax/settings-share";
|
|
4
|
+
import {AbstractService} from "@drax/crud-back";
|
|
5
|
+
import {DraxCache, ZodErrorToValidationError} from "@drax/common-back";
|
|
6
|
+
import {ZodError} from "zod";
|
|
7
|
+
|
|
8
|
+
class SettingService extends AbstractService<ISetting, ISettingBase, ISettingBase> {
|
|
9
|
+
|
|
10
|
+
protected _repository: ISettingRepository
|
|
11
|
+
protected _cache: DraxCache<ISetting> = new DraxCache()
|
|
12
|
+
|
|
13
|
+
constructor(repostitory: ISettingRepository) {
|
|
14
|
+
super(repostitory, settingSchema)
|
|
15
|
+
this._repository = repostitory
|
|
16
|
+
console.log("SettingService constructor")
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async cache(key: string): Promise<ISetting> {
|
|
21
|
+
return this._cache.getOrLoad(key,
|
|
22
|
+
async () => {
|
|
23
|
+
return await this.findByKey(key)
|
|
24
|
+
},
|
|
25
|
+
20000)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async cacheValue(key: string): Promise<any> {
|
|
29
|
+
let setting = await this.cache(key)
|
|
30
|
+
return setting?.value
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async findByKey(key: string): Promise<ISetting | undefined> {
|
|
34
|
+
return this._repository.findOneBy("key", key)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async fetchAll(): Promise<ISetting[]> {
|
|
38
|
+
return await this._repository.fetchAll()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async fetchGrouped(): Promise<{ [key: string]: ISetting[] }> {
|
|
42
|
+
const settings = await this._repository.fetchAll()
|
|
43
|
+
return settings.reduce((acc, setting) => {
|
|
44
|
+
if (!acc[setting.group]) {
|
|
45
|
+
acc[setting.group] = [];
|
|
46
|
+
}
|
|
47
|
+
acc[setting.group].push(setting);
|
|
48
|
+
return acc;
|
|
49
|
+
}, {} as { [key: string]: ISetting[] });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async updateValue(id: string, value: string): Promise<ISetting | undefined> {
|
|
53
|
+
return this._repository.updatePartial(id, {value})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async create(data: ISettingBase): Promise<ISetting> {
|
|
57
|
+
try {
|
|
58
|
+
await settingSchema.parseAsync(data)
|
|
59
|
+
if (Array.isArray(data.value)) {
|
|
60
|
+
data.value = JSON.stringify(data.value)
|
|
61
|
+
}
|
|
62
|
+
const setting = await this._repository.create(data)
|
|
63
|
+
return setting
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.error("Error on create setting", e)
|
|
66
|
+
if (e instanceof ZodError) {
|
|
67
|
+
throw ZodErrorToValidationError(e, data)
|
|
68
|
+
}
|
|
69
|
+
throw e
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async update(id: string, data: ISettingBase): Promise<ISetting> {
|
|
74
|
+
try {
|
|
75
|
+
await settingSchema.parseAsync(data)
|
|
76
|
+
//Borramos el valor para evitar el conflicto de actualización
|
|
77
|
+
delete data.value
|
|
78
|
+
const setting = await this._repository.update(id, data)
|
|
79
|
+
return setting
|
|
80
|
+
} catch (e) {
|
|
81
|
+
console.error("Error on update setting", e)
|
|
82
|
+
if (e instanceof ZodError) {
|
|
83
|
+
throw ZodErrorToValidationError(e, data)
|
|
84
|
+
}
|
|
85
|
+
throw e
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async createOrUpdate(data: ISettingBase): Promise<ISetting> {
|
|
90
|
+
try {
|
|
91
|
+
await settingSchema.parseAsync(data)
|
|
92
|
+
const setting = await this._repository.findOneBy("key", data.key)
|
|
93
|
+
if (setting) {
|
|
94
|
+
delete data.value
|
|
95
|
+
return await this._repository.updatePartial(setting.id, data)
|
|
96
|
+
} else {
|
|
97
|
+
return await this._repository.create(data)
|
|
98
|
+
}
|
|
99
|
+
} catch (e) {
|
|
100
|
+
console.error("Error on create or update setting", e)
|
|
101
|
+
if (e instanceof ZodError) {
|
|
102
|
+
throw ZodErrorToValidationError(e, data)
|
|
103
|
+
}
|
|
104
|
+
throw e
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export default SettingService
|
|
111
|
+
export {SettingService}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {mongoose} from '@drax/common-back';
|
|
2
|
+
import { MongoMemoryServer } from 'mongodb-memory-server';
|
|
3
|
+
|
|
4
|
+
let mongoServer
|
|
5
|
+
|
|
6
|
+
class MongoInMemory{
|
|
7
|
+
|
|
8
|
+
static async connect(){
|
|
9
|
+
mongoServer = await MongoMemoryServer.create();
|
|
10
|
+
if(mongoServer.state == "new"){
|
|
11
|
+
await mongoServer.start()
|
|
12
|
+
}
|
|
13
|
+
if(!mongoose.connection.readyState){
|
|
14
|
+
await mongoose.connect(mongoServer.getUri(), { dbName: "verifyMASTER" });
|
|
15
|
+
}
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
static get mongooseStatus(){
|
|
19
|
+
return mongoose.connection.readyState
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static get serverStatus(){
|
|
23
|
+
return mongoServer.state
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static get status(){
|
|
27
|
+
return mongoose.connection.readyState
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static async disconnect(){
|
|
31
|
+
await mongoose.disconnect();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static async DropAndClose(){
|
|
35
|
+
if (mongoServer) {
|
|
36
|
+
await mongoose.connection.dropDatabase();
|
|
37
|
+
await mongoose.connection.close();
|
|
38
|
+
await mongoServer.stop();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default MongoInMemory
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import {describe, beforeAll, it, vi, expect} from "vitest"
|
|
2
|
+
import {SettingMongoRepository} from "../../../src/repository/mongo/SettingMongoRepository.js";
|
|
3
|
+
import {ISetting, ISettingBase} from "@drax/settings-share";
|
|
4
|
+
import {AbstractMongoRepository} from "@drax/crud-back";
|
|
5
|
+
import MongoInMemory from "./MongoInMemory.js";
|
|
6
|
+
|
|
7
|
+
describe('SettingMongoRepository', () => {
|
|
8
|
+
|
|
9
|
+
beforeAll(async () => {
|
|
10
|
+
await MongoInMemory.connect();
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
it('should initialize _searchFields with the correct default values', () => {
|
|
15
|
+
// Arrange & Act
|
|
16
|
+
const repository = new SettingMongoRepository();
|
|
17
|
+
|
|
18
|
+
// Assert
|
|
19
|
+
expect(repository._searchFields).toEqual(['_id', 'key']);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should verify that SettingMongoRepository is an instance of AbstractMongoRepository', () => {
|
|
23
|
+
// Arrange
|
|
24
|
+
const repository = new SettingMongoRepository();
|
|
25
|
+
|
|
26
|
+
// Assert
|
|
27
|
+
expect(repository).toBeInstanceOf(AbstractMongoRepository);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
it('should correctly handle adding a new setting to the repository', async () => {
|
|
33
|
+
// Arrange
|
|
34
|
+
const repository = new SettingMongoRepository();
|
|
35
|
+
const newSetting: ISettingBase = {
|
|
36
|
+
key: 'newKey',
|
|
37
|
+
value: 'newValue',
|
|
38
|
+
label: 'New Key Label',
|
|
39
|
+
group: 'Default Group',
|
|
40
|
+
type: 'string',
|
|
41
|
+
options: []
|
|
42
|
+
};
|
|
43
|
+
const expectedSetting = {id: '2', ...newSetting};
|
|
44
|
+
|
|
45
|
+
// Mock the create method of the repository
|
|
46
|
+
vi.spyOn(repository, 'create').mockResolvedValue(expectedSetting);
|
|
47
|
+
|
|
48
|
+
// Act
|
|
49
|
+
const createdSetting = await repository.create(newSetting);
|
|
50
|
+
|
|
51
|
+
// Assert
|
|
52
|
+
expect(createdSetting).toEqual(expectedSetting);
|
|
53
|
+
expect(repository.create).toHaveBeenCalledWith(newSetting);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should correctly handle updating an existing setting in the repository', async () => {
|
|
57
|
+
// Arrange
|
|
58
|
+
const repository = new SettingMongoRepository();
|
|
59
|
+
const existingSettingId = '1';
|
|
60
|
+
const updatedData: ISettingBase = {
|
|
61
|
+
key: 'existingKey',
|
|
62
|
+
value: 'updatedValue',
|
|
63
|
+
label: 'Updated Key Label',
|
|
64
|
+
group: 'Default Group',
|
|
65
|
+
type: 'string',
|
|
66
|
+
options: []
|
|
67
|
+
};
|
|
68
|
+
const expectedUpdatedSetting: ISetting = {
|
|
69
|
+
id: existingSettingId,
|
|
70
|
+
...updatedData
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Mock the update method of the repository
|
|
74
|
+
vi.spyOn(repository, 'update').mockResolvedValue(expectedUpdatedSetting);
|
|
75
|
+
|
|
76
|
+
// Act
|
|
77
|
+
const updatedSetting = await repository.update(existingSettingId, updatedData);
|
|
78
|
+
|
|
79
|
+
// Assert
|
|
80
|
+
expect(updatedSetting).toEqual(expectedUpdatedSetting);
|
|
81
|
+
expect(repository.update).toHaveBeenCalledWith(existingSettingId, updatedData);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
it('should correctly handle searching for a setting by _id', async () => {
|
|
86
|
+
// Arrange
|
|
87
|
+
const repository = new SettingMongoRepository();
|
|
88
|
+
const settingId = '1';
|
|
89
|
+
const expectedSetting: ISetting = {
|
|
90
|
+
id: settingId,
|
|
91
|
+
key: 'existingKey',
|
|
92
|
+
value: 'existingValue',
|
|
93
|
+
label: 'Existing Key Label',
|
|
94
|
+
group: 'Default Group',
|
|
95
|
+
type: 'string',
|
|
96
|
+
options: []
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Mock the findById method of the repository
|
|
100
|
+
vi.spyOn(repository, 'findById').mockResolvedValue(expectedSetting);
|
|
101
|
+
|
|
102
|
+
// Act
|
|
103
|
+
const foundSetting = await repository.findById(settingId);
|
|
104
|
+
|
|
105
|
+
// Assert
|
|
106
|
+
expect(foundSetting).toEqual(expectedSetting);
|
|
107
|
+
expect(repository.findById).toHaveBeenCalledWith(settingId);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
it('should correctly handle searching for a setting by key', async () => {
|
|
112
|
+
// Arrange
|
|
113
|
+
const repository = new SettingMongoRepository();
|
|
114
|
+
const searchKey = 'sampleKey';
|
|
115
|
+
const expectedSettings: ISetting[] = [{
|
|
116
|
+
id: '1',
|
|
117
|
+
key: 'sampleKey',
|
|
118
|
+
value: 'sampleValue',
|
|
119
|
+
label: 'Sample Setting Label',
|
|
120
|
+
group: 'Sample Group',
|
|
121
|
+
type: 'string',
|
|
122
|
+
options: []
|
|
123
|
+
}];
|
|
124
|
+
|
|
125
|
+
// Mock the search method of the repository
|
|
126
|
+
vi.spyOn(repository, 'search').mockResolvedValue(expectedSettings);
|
|
127
|
+
|
|
128
|
+
// Act
|
|
129
|
+
const foundSettings = await repository.search(searchKey);
|
|
130
|
+
|
|
131
|
+
// Assert
|
|
132
|
+
expect(foundSettings).toEqual(expectedSettings);
|
|
133
|
+
expect(repository.search).toHaveBeenCalledWith(searchKey);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
it('should correctly handle deleting a setting from the repository', async () => {
|
|
139
|
+
// Arrange
|
|
140
|
+
const repository = new SettingMongoRepository();
|
|
141
|
+
const settingId = '1';
|
|
142
|
+
const expectedDeletionResult = true;
|
|
143
|
+
|
|
144
|
+
// Mock the delete method of the repository
|
|
145
|
+
vi.spyOn(repository, 'delete').mockResolvedValue(expectedDeletionResult);
|
|
146
|
+
|
|
147
|
+
// Act
|
|
148
|
+
const deletionResult = await repository.delete(settingId);
|
|
149
|
+
|
|
150
|
+
// Assert
|
|
151
|
+
expect(deletionResult).toBe(expectedDeletionResult);
|
|
152
|
+
expect(repository.delete).toHaveBeenCalledWith(settingId);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
it('should correctly handle retrieving all settings from the repository', async () => {
|
|
157
|
+
// Arrange
|
|
158
|
+
const repository = new SettingMongoRepository();
|
|
159
|
+
const expectedSettings: ISetting[] = [{
|
|
160
|
+
id: '1',
|
|
161
|
+
key: 'existingKey',
|
|
162
|
+
value: 'existingValue',
|
|
163
|
+
label: 'Existing Key Label',
|
|
164
|
+
group: 'Default Group',
|
|
165
|
+
type: 'string',
|
|
166
|
+
options: []
|
|
167
|
+
}];
|
|
168
|
+
|
|
169
|
+
// Mock the fetchAll method of the repository
|
|
170
|
+
vi.spyOn(repository, 'fetchAll').mockResolvedValue(expectedSettings);
|
|
171
|
+
|
|
172
|
+
// Act
|
|
173
|
+
const allSettings = await repository.fetchAll();
|
|
174
|
+
|
|
175
|
+
// Assert
|
|
176
|
+
expect(allSettings).toEqual(expectedSettings);
|
|
177
|
+
expect(repository.fetchAll).toHaveBeenCalled();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
})
|