1
0
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:
Christopher Kolstad 2021-03-26 11:03:30 +01:00 committed by GitHub
parent ad04eeb9b5
commit 205ad921d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 257 additions and 188 deletions

View File

@ -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",

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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;

View 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;

View File

@ -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,

View File

@ -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;

View 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;

View File

@ -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),
),
};
};

View File

@ -22,5 +22,6 @@ module.exports = () => {
_tagTypes.splice(0, _tagTypes.length);
return Promise.resolve();
},
exists: name => Promise.resolve(_tagTypes.some(t => t.name === name)),
};
};