mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-23 00:22:19 +01:00
Tags and tag types to ts (#772)
* chore: Converted tag-store and tag-type-store to ts * chore: Converted tag service and tag type service to ts
This commit is contained in:
parent
ad04eeb9b5
commit
205ad921d4
@ -125,8 +125,8 @@
|
||||
"passport-google-auth": "^1.0.2",
|
||||
"prettier": "^1.19.1",
|
||||
"proxyquire": "^2.1.3",
|
||||
"source-map-support": "^0.5.19",
|
||||
"sinon": "^9.2.4",
|
||||
"source-map-support": "^0.5.19",
|
||||
"superagent": "^6.1.0",
|
||||
"supertest": "^5.0.0",
|
||||
"ts-node": "^9.1.1",
|
||||
|
@ -1,13 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
const metricsHelper = require('../metrics-helper');
|
||||
const { DB_TIME } = require('../events');
|
||||
const NotFoundError = require('../error/notfound-error');
|
||||
import { Knex } from 'knex';
|
||||
import { EventEmitter } from 'events';
|
||||
import { DB_TIME } from '../events';
|
||||
import metricsHelper from '../metrics-helper';
|
||||
import { LogProvider, Logger } from '../logger';
|
||||
import NotFoundError from '../error/notfound-error';
|
||||
|
||||
const COLUMNS = ['type', 'value'];
|
||||
const TABLE = 'tags';
|
||||
class TagStore {
|
||||
constructor(db, eventBus, getLogger) {
|
||||
|
||||
interface ITagTable {
|
||||
type: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface ITag {
|
||||
type: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export default class TagStore {
|
||||
private db: Knex;
|
||||
|
||||
private logger: Logger;
|
||||
|
||||
private readonly timer: Function;
|
||||
|
||||
constructor(db: Knex, eventBus: EventEmitter, getLogger: LogProvider) {
|
||||
this.db = db;
|
||||
this.logger = getLogger('tag-store.js');
|
||||
this.timer = action =>
|
||||
@ -17,7 +37,7 @@ class TagStore {
|
||||
});
|
||||
}
|
||||
|
||||
async getTagsByType(type) {
|
||||
async getTagsByType(type: string): Promise<ITag[]> {
|
||||
const stopTimer = this.timer('getTagByType');
|
||||
const rows = await this.db
|
||||
.select(COLUMNS)
|
||||
@ -27,14 +47,14 @@ class TagStore {
|
||||
return rows.map(this.rowToTag);
|
||||
}
|
||||
|
||||
async getAll() {
|
||||
async getAll(): Promise<ITag[]> {
|
||||
const stopTimer = this.timer('getAll');
|
||||
const rows = await this.db.select(COLUMNS).from(TABLE);
|
||||
stopTimer();
|
||||
return rows.map(this.rowToTag);
|
||||
}
|
||||
|
||||
async getTag(type, value) {
|
||||
async getTag(type: string, value: string): Promise<ITag> {
|
||||
const stopTimer = this.timer('getTag');
|
||||
const tag = await this.db
|
||||
.first(COLUMNS)
|
||||
@ -49,13 +69,24 @@ class TagStore {
|
||||
return tag;
|
||||
}
|
||||
|
||||
async createTag(tag) {
|
||||
async exists(tag: ITag): Promise<boolean> {
|
||||
const stopTimer = this.timer('exists');
|
||||
const result = await this.db.raw(
|
||||
`SELECT EXISTS (SELECT 1 FROM ${TABLE} WHERE type = ? AND value = ?) AS present`,
|
||||
[tag.type, tag.value],
|
||||
);
|
||||
const { present } = result.rows[0];
|
||||
stopTimer();
|
||||
return present;
|
||||
}
|
||||
|
||||
async createTag(tag: ITag): Promise<void> {
|
||||
const stopTimer = this.timer('createTag');
|
||||
await this.db(TABLE).insert(tag);
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
async deleteTag(tag) {
|
||||
async deleteTag(tag: ITag): Promise<void> {
|
||||
const stopTimer = this.timer('deleteTag');
|
||||
await this.db(TABLE)
|
||||
.where(tag)
|
||||
@ -63,13 +94,13 @@ class TagStore {
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
async dropTags() {
|
||||
async dropTags(): Promise<void> {
|
||||
const stopTimer = this.timer('dropTags');
|
||||
await this.db(TABLE).del();
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
async bulkImport(tags) {
|
||||
async bulkImport(tags: ITag[]): Promise<ITag[]> {
|
||||
return this.db(TABLE)
|
||||
.insert(tags)
|
||||
.returning(COLUMNS)
|
||||
@ -77,7 +108,7 @@ class TagStore {
|
||||
.ignore();
|
||||
}
|
||||
|
||||
rowToTag(row) {
|
||||
rowToTag(row: ITagTable): ITag {
|
||||
return {
|
||||
type: row.type,
|
||||
value: row.value,
|
@ -1,14 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
const metricsHelper = require('../metrics-helper');
|
||||
const { DB_TIME } = require('../events');
|
||||
const NotFoundError = require('../error/notfound-error');
|
||||
import { Knex } from 'knex';
|
||||
import { EventEmitter } from 'events';
|
||||
import { LogProvider, Logger } from '../logger';
|
||||
import { DB_TIME } from '../events';
|
||||
import metricsHelper from '../metrics-helper';
|
||||
import NotFoundError from '../error/notfound-error';
|
||||
|
||||
const COLUMNS = ['name', 'description', 'icon'];
|
||||
const TABLE = 'tag_types';
|
||||
|
||||
class TagTypeStore {
|
||||
constructor(db, eventBus, getLogger) {
|
||||
interface ITagTypeTable {
|
||||
name: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface ITagType {
|
||||
name: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export default class TagTypeStore {
|
||||
private db: Knex;
|
||||
|
||||
private logger: Logger;
|
||||
|
||||
private readonly timer: Function;
|
||||
|
||||
constructor(db: Knex, eventBus: EventEmitter, getLogger: LogProvider) {
|
||||
this.db = db;
|
||||
this.logger = getLogger('tag-type-store.js');
|
||||
this.timer = action =>
|
||||
@ -18,14 +37,14 @@ class TagTypeStore {
|
||||
});
|
||||
}
|
||||
|
||||
async getAll() {
|
||||
async getAll(): Promise<ITagType[]> {
|
||||
const stopTimer = this.timer('getTagTypes');
|
||||
const rows = await this.db.select(COLUMNS).from(TABLE);
|
||||
stopTimer();
|
||||
return rows.map(this.rowToTagType);
|
||||
}
|
||||
|
||||
async getTagType(name) {
|
||||
async getTagType(name: string): Promise<ITagType> {
|
||||
const stopTimer = this.timer('getTagTypeByName');
|
||||
return this.db
|
||||
.first(COLUMNS)
|
||||
@ -41,23 +60,24 @@ class TagTypeStore {
|
||||
});
|
||||
}
|
||||
|
||||
async exists(name) {
|
||||
async exists(name: string): Promise<Boolean> {
|
||||
const stopTimer = this.timer('exists');
|
||||
const row = await this.db
|
||||
.first(COLUMNS)
|
||||
.from(TABLE)
|
||||
.where({ name });
|
||||
const result = await this.db.raw(
|
||||
`SELECT EXISTS (SELECT 1 FROM ${TABLE} WHERE name = ?) AS present`,
|
||||
[name],
|
||||
);
|
||||
const { present } = result.rows[0];
|
||||
stopTimer();
|
||||
return row;
|
||||
return present;
|
||||
}
|
||||
|
||||
async createTagType(newTagType) {
|
||||
async createTagType(newTagType: ITagType): Promise<void> {
|
||||
const stopTimer = this.timer('createTagType');
|
||||
await this.db(TABLE).insert(newTagType);
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
async deleteTagType(name) {
|
||||
async deleteTagType(name: string): Promise<void> {
|
||||
const stopTimer = this.timer('deleteTagType');
|
||||
await this.db(TABLE)
|
||||
.where({ name })
|
||||
@ -65,13 +85,13 @@ class TagTypeStore {
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
async dropTagTypes() {
|
||||
async dropTagTypes(): Promise<void> {
|
||||
const stopTimer = this.timer('dropTagTypes');
|
||||
await this.db(TABLE).del();
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
async bulkImport(tagTypes) {
|
||||
async bulkImport(tagTypes: ITagType[]): Promise<ITagType[]> {
|
||||
const rows = await this.db(TABLE)
|
||||
.insert(tagTypes)
|
||||
.returning(COLUMNS)
|
||||
@ -83,7 +103,7 @@ class TagTypeStore {
|
||||
return [];
|
||||
}
|
||||
|
||||
async updateTagType({ name, description, icon }) {
|
||||
async updateTagType({ name, description, icon }: ITagType): Promise<void> {
|
||||
const stopTimer = this.timer('updateTagType');
|
||||
await this.db(TABLE)
|
||||
.where({ name })
|
||||
@ -91,7 +111,7 @@ class TagTypeStore {
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
rowToTagType(row) {
|
||||
rowToTagType(row: ITagTypeTable): ITagType {
|
||||
return {
|
||||
name: row.name,
|
||||
description: row.description,
|
@ -1,13 +1,10 @@
|
||||
'use strict';
|
||||
import Joi from 'joi';
|
||||
|
||||
const joi = require('joi');
|
||||
const { customJoi } = require('../routes/admin-api/util');
|
||||
import { customJoi } from '../routes/admin-api/util';
|
||||
|
||||
const tagSchema = joi
|
||||
.object()
|
||||
export const tagSchema = Joi.object()
|
||||
.keys({
|
||||
value: joi
|
||||
.string()
|
||||
value: Joi.string()
|
||||
.min(2)
|
||||
.max(50),
|
||||
type: customJoi
|
@ -1,62 +0,0 @@
|
||||
const { tagSchema } = require('./tag-schema');
|
||||
const NotFoundError = require('../error/notfound-error');
|
||||
const NameExistsError = require('../error/name-exists-error');
|
||||
const { TAG_CREATED, TAG_DELETED } = require('../event-type');
|
||||
|
||||
class TagService {
|
||||
constructor({ tagStore, eventStore }, { getLogger }) {
|
||||
this.tagStore = tagStore;
|
||||
this.eventStore = eventStore;
|
||||
this.logger = getLogger('services/tag-service.js');
|
||||
}
|
||||
|
||||
async getTags() {
|
||||
return this.tagStore.getAll();
|
||||
}
|
||||
|
||||
async getTagsByType(type) {
|
||||
return this.tagStore.getTagsByType(type);
|
||||
}
|
||||
|
||||
async getTag({ type, value }) {
|
||||
return this.tagStore.getTag(type, value);
|
||||
}
|
||||
|
||||
async validateUnique(tag) {
|
||||
try {
|
||||
await this.tagStore.getTag(tag.type, tag.value);
|
||||
} catch (err) {
|
||||
if (err instanceof NotFoundError) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new NameExistsError(`A tag of ${tag} already exists`);
|
||||
}
|
||||
|
||||
async validate(tag) {
|
||||
const data = await tagSchema.validateAsync(tag);
|
||||
await this.validateUnique(tag);
|
||||
return data;
|
||||
}
|
||||
|
||||
async createTag(tag, userName) {
|
||||
const data = await this.validate(tag);
|
||||
await this.tagStore.createTag(data);
|
||||
await this.eventStore.store({
|
||||
type: TAG_CREATED,
|
||||
createdBy: userName,
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
async deleteTag(tag, userName) {
|
||||
await this.tagStore.deleteTag(tag);
|
||||
await this.eventStore.store({
|
||||
type: TAG_DELETED,
|
||||
createdBy: userName,
|
||||
data: tag,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TagService;
|
66
src/lib/services/tag-service.ts
Normal file
66
src/lib/services/tag-service.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { tagSchema } from './tag-schema';
|
||||
import TagStore, { ITag } from '../db/tag-store';
|
||||
import EventStore from '../db/event-store';
|
||||
import NameExistsError from '../error/name-exists-error';
|
||||
import { TAG_CREATED, TAG_DELETED } from '../event-type';
|
||||
import { Logger } from '../logger';
|
||||
|
||||
export default class TagService {
|
||||
private tagStore: TagStore;
|
||||
|
||||
private eventStore: EventStore;
|
||||
|
||||
private logger: Logger;
|
||||
|
||||
constructor({ tagStore, eventStore }, { getLogger }) {
|
||||
this.tagStore = tagStore;
|
||||
this.eventStore = eventStore;
|
||||
this.logger = getLogger('services/tag-service.js');
|
||||
}
|
||||
|
||||
async getTags(): Promise<ITag[]> {
|
||||
return this.tagStore.getAll();
|
||||
}
|
||||
|
||||
async getTagsByType(type): Promise<ITag[]> {
|
||||
return this.tagStore.getTagsByType(type);
|
||||
}
|
||||
|
||||
async getTag({ type, value }: ITag): Promise<ITag> {
|
||||
return this.tagStore.getTag(type, value);
|
||||
}
|
||||
|
||||
async validateUnique(tag: ITag): Promise<void> {
|
||||
const exists = await this.tagStore.exists(tag);
|
||||
if (exists) {
|
||||
throw new NameExistsError(`A tag of ${tag} already exists`);
|
||||
}
|
||||
}
|
||||
|
||||
async validate(tag): Promise<ITag> {
|
||||
const data = (await tagSchema.validateAsync(tag)) as ITag;
|
||||
await this.validateUnique(tag);
|
||||
return data;
|
||||
}
|
||||
|
||||
async createTag(tag: ITag, userName: string): Promise<void> {
|
||||
const data = await this.validate(tag);
|
||||
await this.tagStore.createTag(data);
|
||||
await this.eventStore.store({
|
||||
type: TAG_CREATED,
|
||||
createdBy: userName,
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
async deleteTag(tag: ITag, userName: string): Promise<void> {
|
||||
await this.tagStore.deleteTag(tag);
|
||||
await this.eventStore.store({
|
||||
type: TAG_DELETED,
|
||||
createdBy: userName,
|
||||
data: tag,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TagService;
|
@ -1,18 +1,15 @@
|
||||
'use strict';
|
||||
import Joi from 'joi';
|
||||
import { customJoi } from '../routes/admin-api/util';
|
||||
|
||||
const joi = require('joi');
|
||||
const { customJoi } = require('../routes/admin-api/util');
|
||||
|
||||
const tagTypeSchema = joi
|
||||
.object()
|
||||
export const tagTypeSchema = Joi.object()
|
||||
.keys({
|
||||
name: customJoi
|
||||
.isUrlFriendly()
|
||||
.min(2)
|
||||
.max(50)
|
||||
.required(),
|
||||
description: joi.string().allow(''),
|
||||
icon: joi.string().allow(''),
|
||||
description: Joi.string().allow(''),
|
||||
icon: Joi.string().allow(''),
|
||||
})
|
||||
.options({
|
||||
allowUnknown: false,
|
@ -1,76 +0,0 @@
|
||||
const NameExistsError = require('../error/name-exists-error');
|
||||
const NotFoundError = require('../error/notfound-error');
|
||||
const { tagTypeSchema } = require('./tag-type-schema');
|
||||
const {
|
||||
TAG_TYPE_CREATED,
|
||||
TAG_TYPE_DELETED,
|
||||
TAG_TYPE_UPDATED,
|
||||
} = require('../event-type');
|
||||
|
||||
class TagTypeService {
|
||||
constructor({ tagTypeStore, eventStore }, { getLogger }) {
|
||||
this.tagTypeStore = tagTypeStore;
|
||||
this.eventStore = eventStore;
|
||||
this.logger = getLogger('services/tag-type-service.js');
|
||||
}
|
||||
|
||||
async getAll() {
|
||||
return this.tagTypeStore.getAll();
|
||||
}
|
||||
|
||||
async getTagType(name) {
|
||||
return this.tagTypeStore.getTagType(name);
|
||||
}
|
||||
|
||||
async createTagType(newTagType, userName) {
|
||||
const data = await tagTypeSchema.validateAsync(newTagType);
|
||||
await this.validateUnique(newTagType);
|
||||
await this.tagTypeStore.createTagType(data);
|
||||
await this.eventStore.store({
|
||||
type: TAG_TYPE_CREATED,
|
||||
createdBy: userName || 'unleash-system',
|
||||
data,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
async validateUnique({ name }) {
|
||||
try {
|
||||
await this.tagTypeStore.getTagType(name);
|
||||
} catch (err) {
|
||||
if (err instanceof NotFoundError) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new NameExistsError(
|
||||
`There already exists a tag-type with the name ${name}`,
|
||||
);
|
||||
}
|
||||
|
||||
async validate(tagType) {
|
||||
await tagTypeSchema.validateAsync(tagType);
|
||||
await this.validateUnique(tagType);
|
||||
}
|
||||
|
||||
async deleteTagType(name, userName) {
|
||||
await this.tagTypeStore.deleteTagType(name);
|
||||
await this.eventStore.store({
|
||||
type: TAG_TYPE_DELETED,
|
||||
createdBy: userName || 'unleash-system',
|
||||
data: { name },
|
||||
});
|
||||
}
|
||||
|
||||
async updateTagType(updatedTagType, userName) {
|
||||
const data = await tagTypeSchema.validateAsync(updatedTagType);
|
||||
await this.tagTypeStore.updateTagType(data);
|
||||
await this.eventStore.store({
|
||||
type: TAG_TYPE_UPDATED,
|
||||
createdBy: userName || 'unleash-system',
|
||||
data,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TagTypeService;
|
91
src/lib/services/tag-type-service.ts
Normal file
91
src/lib/services/tag-type-service.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import NameExistsError from '../error/name-exists-error';
|
||||
|
||||
import { tagTypeSchema } from './tag-type-schema';
|
||||
|
||||
import {
|
||||
TAG_TYPE_CREATED,
|
||||
TAG_TYPE_DELETED,
|
||||
TAG_TYPE_UPDATED,
|
||||
} from '../event-type';
|
||||
import EventStore from '../db/event-store';
|
||||
import { Logger } from '../logger';
|
||||
import TagTypeStore, { ITagType } from '../db/tag-type-store';
|
||||
|
||||
export default class TagTypeService {
|
||||
private tagTypeStore: TagTypeStore;
|
||||
|
||||
private eventStore: EventStore;
|
||||
|
||||
private logger: Logger;
|
||||
|
||||
constructor({ tagTypeStore, eventStore }, { getLogger }) {
|
||||
this.tagTypeStore = tagTypeStore;
|
||||
this.eventStore = eventStore;
|
||||
this.logger = getLogger('services/tag-type-service.js');
|
||||
}
|
||||
|
||||
async getAll(): Promise<ITagType[]> {
|
||||
return this.tagTypeStore.getAll();
|
||||
}
|
||||
|
||||
async getTagType(name: string): Promise<ITagType> {
|
||||
return this.tagTypeStore.getTagType(name);
|
||||
}
|
||||
|
||||
async createTagType(
|
||||
newTagType: ITagType,
|
||||
userName: string,
|
||||
): Promise<ITagType> {
|
||||
const data = (await tagTypeSchema.validateAsync(
|
||||
newTagType,
|
||||
)) as ITagType;
|
||||
await this.validateUnique(data);
|
||||
await this.tagTypeStore.createTagType(data);
|
||||
await this.eventStore.store({
|
||||
type: TAG_TYPE_CREATED,
|
||||
createdBy: userName || 'unleash-system',
|
||||
data,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
async validateUnique({ name }: Partial<ITagType>): Promise<boolean> {
|
||||
const exists = await this.tagTypeStore.exists(name);
|
||||
if (exists) {
|
||||
throw new NameExistsError(
|
||||
`There already exists a tag-type with the name ${name}`,
|
||||
);
|
||||
}
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
async validate(tagType: ITagType): Promise<void> {
|
||||
await tagTypeSchema.validateAsync(tagType);
|
||||
await this.validateUnique(tagType);
|
||||
}
|
||||
|
||||
async deleteTagType(name: string, userName: string): Promise<void> {
|
||||
await this.tagTypeStore.deleteTagType(name);
|
||||
await this.eventStore.store({
|
||||
type: TAG_TYPE_DELETED,
|
||||
createdBy: userName || 'unleash-system',
|
||||
data: { name },
|
||||
});
|
||||
}
|
||||
|
||||
async updateTagType(
|
||||
updatedTagType: ITagType,
|
||||
userName: string,
|
||||
): Promise<ITagType> {
|
||||
const data = await tagTypeSchema.validateAsync(updatedTagType);
|
||||
await this.tagTypeStore.updateTagType(data);
|
||||
await this.eventStore.store({
|
||||
type: TAG_TYPE_UPDATED,
|
||||
createdBy: userName || 'unleash-system',
|
||||
data,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TagTypeService;
|
4
src/test/fixtures/fake-tag-store.js
vendored
4
src/test/fixtures/fake-tag-store.js
vendored
@ -42,5 +42,9 @@ module.exports = (databaseIsUp = true) => {
|
||||
_tags.splice(0, _tags.length);
|
||||
return Promise.resolve();
|
||||
},
|
||||
exists: tag =>
|
||||
Promise.resolve(
|
||||
_tags.some(t => t.type === tag.type && t.value === tag.value),
|
||||
),
|
||||
};
|
||||
};
|
||||
|
1
src/test/fixtures/fake-tag-type-store.js
vendored
1
src/test/fixtures/fake-tag-type-store.js
vendored
@ -22,5 +22,6 @@ module.exports = () => {
|
||||
_tagTypes.splice(0, _tagTypes.length);
|
||||
return Promise.resolve();
|
||||
},
|
||||
exists: name => Promise.resolve(_tagTypes.some(t => t.name === name)),
|
||||
};
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user