mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-17 01:17:29 +02:00
Feat/tag type colors backend (#9565)
Adds backend color support for tag types
This commit is contained in:
parent
0542fef5d8
commit
a65c8baf56
@ -4,6 +4,7 @@ export interface ITagType {
|
||||
name: string;
|
||||
description?: string;
|
||||
icon?: string | null;
|
||||
color?: string | null;
|
||||
}
|
||||
|
||||
export interface ITagTypeStore extends Store<ITagType, string> {
|
||||
|
@ -6,13 +6,14 @@ import NotFoundError from '../../error/notfound-error';
|
||||
import type { ITagType, ITagTypeStore } from './tag-type-store-type';
|
||||
import type { Db } from '../../db/db';
|
||||
|
||||
const COLUMNS = ['name', 'description', 'icon'];
|
||||
const COLUMNS = ['name', 'description', 'icon', 'color'];
|
||||
const TABLE = 'tag_types';
|
||||
|
||||
interface ITagTypeTable {
|
||||
name: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export default class TagTypeStore implements ITagTypeStore {
|
||||
@ -96,9 +97,16 @@ export default class TagTypeStore implements ITagTypeStore {
|
||||
return [];
|
||||
}
|
||||
|
||||
async updateTagType({ name, description, icon }: ITagType): Promise<void> {
|
||||
async updateTagType({
|
||||
name,
|
||||
description,
|
||||
icon,
|
||||
color,
|
||||
}: ITagType): Promise<void> {
|
||||
const stopTimer = this.timer('updateTagType');
|
||||
await this.db(TABLE).where({ name }).update({ description, icon });
|
||||
await this.db(TABLE)
|
||||
.where({ name })
|
||||
.update({ description, icon, color });
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
@ -109,6 +117,7 @@ export default class TagTypeStore implements ITagTypeStore {
|
||||
name: row.name,
|
||||
description: row.description,
|
||||
icon: row.icon,
|
||||
color: row.color,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -212,11 +212,19 @@ class TagTypeController extends Controller {
|
||||
req: IAuthRequest<{ name: string }, unknown, UpdateTagTypeSchema>,
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
const { description, icon } = req.body;
|
||||
const { description, icon, color } = req.body;
|
||||
const { name } = req.params;
|
||||
|
||||
await this.tagTypeService.transactional((service) =>
|
||||
service.updateTagType({ name, description, icon }, req.audit),
|
||||
service.updateTagType(
|
||||
{
|
||||
name,
|
||||
description,
|
||||
icon,
|
||||
color: color as string | null | undefined,
|
||||
},
|
||||
req.audit,
|
||||
),
|
||||
);
|
||||
res.status(200).end();
|
||||
}
|
||||
|
@ -79,6 +79,25 @@ test('Can create a new tag type', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Can create a new tag type with color', async () => {
|
||||
await app.request
|
||||
.post('/api/admin/tag-types')
|
||||
.send({
|
||||
name: 'colored-tag',
|
||||
description: 'A tag type with a color',
|
||||
icon: 'icon',
|
||||
color: '#FF5733',
|
||||
})
|
||||
.expect(201);
|
||||
return app.request
|
||||
.get('/api/admin/tag-types/colored-tag')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.tagType.color).toBe('#FF5733');
|
||||
});
|
||||
});
|
||||
|
||||
test('Invalid tag types gets rejected', async () => {
|
||||
await app.request
|
||||
.post('/api/admin/tag-types')
|
||||
@ -96,6 +115,20 @@ test('Invalid tag types gets rejected', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Tag type with invalid color format gets rejected', async () => {
|
||||
const res = await app.request
|
||||
.post('/api/admin/tag-types')
|
||||
.send({
|
||||
name: 'invalid-color-tag',
|
||||
description: 'A tag with invalid color',
|
||||
color: 'not-a-color',
|
||||
})
|
||||
.set('Content-Type', 'application/json')
|
||||
.expect(400);
|
||||
|
||||
expect(res.body.details[0].message).toMatch(/color/);
|
||||
});
|
||||
|
||||
test('Can update a tag types description and icon', async () => {
|
||||
await app.request.get('/api/admin/tag-types/simple').expect(200);
|
||||
await app.request
|
||||
@ -113,6 +146,32 @@ test('Can update a tag types description and icon', async () => {
|
||||
expect(res.body.tagType.icon).toBe('$');
|
||||
});
|
||||
});
|
||||
|
||||
test('Can update a tag type color', async () => {
|
||||
await app.request
|
||||
.post('/api/admin/tag-types')
|
||||
.send({
|
||||
name: 'color-update-tag',
|
||||
description: 'A tag type to test color updates',
|
||||
color: '#FFFFFF',
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
await app.request
|
||||
.put('/api/admin/tag-types/color-update-tag')
|
||||
.send({
|
||||
color: '#00FF00',
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const res = await app.request
|
||||
.get('/api/admin/tag-types/color-update-tag')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
|
||||
expect(res.body.tagType.color).toBe('#00FF00');
|
||||
});
|
||||
|
||||
test('Numbers are coerced to strings for icons and descriptions', async () => {
|
||||
await app.request.get('/api/admin/tag-types/simple').expect(200);
|
||||
await app.request
|
||||
@ -139,6 +198,34 @@ test('Validation of tag-types returns 200 for valid tag-types', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Validation of tag-types with valid color is successful', async () => {
|
||||
const res = await app.request
|
||||
.post('/api/admin/tag-types/validate')
|
||||
.send({
|
||||
name: 'color-validation',
|
||||
description: 'A tag type with a valid color',
|
||||
color: '#123ABC',
|
||||
})
|
||||
.set('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(res.body.valid).toBe(true);
|
||||
});
|
||||
|
||||
test('Validation of tag-types with invalid color format is unsuccessful', async () => {
|
||||
const res = await app.request
|
||||
.post('/api/admin/tag-types/validate')
|
||||
.send({
|
||||
name: 'invalid-color-validation',
|
||||
description: 'A tag type with an invalid color',
|
||||
color: 'not-a-color',
|
||||
})
|
||||
.set('Content-Type', 'application/json')
|
||||
.expect(400);
|
||||
|
||||
expect(res.body.details[0].message).toMatch(/color/);
|
||||
});
|
||||
|
||||
test('Validation of tag types allows numbers for description and icons because of coercion', async () => {
|
||||
await app.request
|
||||
.post('/api/admin/tag-types/validate')
|
||||
@ -216,3 +303,19 @@ test('Only required argument should be name', async () => {
|
||||
expect(res.body.name).toBe(name);
|
||||
});
|
||||
});
|
||||
|
||||
test('Creating a tag type with null color is allowed', async () => {
|
||||
const name = 'null-color-tag';
|
||||
const res = await app.request
|
||||
.post('/api/admin/tag-types')
|
||||
.send({
|
||||
name,
|
||||
description: 'A tag with null color',
|
||||
color: null,
|
||||
})
|
||||
.set('Content-Type', 'application/json')
|
||||
.expect(201);
|
||||
|
||||
expect(res.body.name).toBe(name);
|
||||
expect(res.body.color).toBe(null);
|
||||
});
|
||||
|
@ -23,6 +23,13 @@ export const tagTypeSchema = {
|
||||
description: 'The icon of the tag type.',
|
||||
example: 'not-really-used',
|
||||
},
|
||||
color: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
description: 'The hexadecimal color code for the tag type.',
|
||||
example: '#FFFFFF',
|
||||
pattern: '^#[0-9A-Fa-f]{6}$',
|
||||
},
|
||||
},
|
||||
components: {},
|
||||
} as const;
|
||||
|
@ -9,11 +9,13 @@ test('tagTypesSchema', () => {
|
||||
name: 'simple',
|
||||
description: 'Used to simplify filtering of features',
|
||||
icon: '#',
|
||||
color: '#FF0000',
|
||||
},
|
||||
{
|
||||
name: 'hashtag',
|
||||
description: '',
|
||||
icon: null,
|
||||
color: null,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -15,6 +15,12 @@ export const updateTagTypeSchema = {
|
||||
description: 'The icon of the tag type.',
|
||||
example: 'not-really-used',
|
||||
},
|
||||
color: {
|
||||
type: 'string',
|
||||
description: 'The hexadecimal color code for the tag type.',
|
||||
example: '#FFFFFF',
|
||||
pattern: '^#[0-9A-Fa-f]{6}$',
|
||||
},
|
||||
},
|
||||
components: {},
|
||||
} as const;
|
||||
|
@ -6,6 +6,10 @@ export const tagTypeSchema = Joi.object()
|
||||
name: customJoi.isUrlFriendly().min(2).max(50).required(),
|
||||
description: Joi.string().allow(''),
|
||||
icon: Joi.string().allow(null).allow(''),
|
||||
color: Joi.string()
|
||||
.pattern(/^#[0-9A-Fa-f]{6}$/)
|
||||
.allow(null)
|
||||
.allow(''),
|
||||
})
|
||||
.options({
|
||||
allowUnknown: false,
|
||||
|
Loading…
Reference in New Issue
Block a user