mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +01:00
feat: add project owners to personal dashboard project payload (#8248)
This PR adds project owner information to the personal dashboard's project payload. To do so, it uses the existing project owners read model. I've had to make a few changes to the project owners read model to accomodate this: - make the input type to `addOwners` more lenient. We only need the project ids, so we can make that the only required property - fall back to using email as the name if the user has no name or username (such as if you sign up with the demo auth)
This commit is contained in:
parent
e680921517
commit
44bf6615a3
@ -110,6 +110,11 @@ test('should return personal dashboard with membered projects', async () => {
|
||||
type: 'root',
|
||||
},
|
||||
],
|
||||
owners: [
|
||||
{
|
||||
ownerType: 'system',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: projectA.name,
|
||||
@ -121,6 +126,15 @@ test('should return personal dashboard with membered projects', async () => {
|
||||
type: 'project',
|
||||
},
|
||||
],
|
||||
owners: [
|
||||
{
|
||||
email: 'user1@test.com',
|
||||
imageUrl:
|
||||
'https://gravatar.com/avatar/a8cc79d8407a64b0d8982df34e3525afd298a479fe68f300651380730dbf23e9?s=42&d=retro&r=g',
|
||||
name: 'user1@test.com',
|
||||
ownerType: 'user',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: projectC.name,
|
||||
@ -132,6 +146,15 @@ test('should return personal dashboard with membered projects', async () => {
|
||||
type: 'project',
|
||||
},
|
||||
],
|
||||
owners: [
|
||||
{
|
||||
email: 'user2@test.com',
|
||||
imageUrl:
|
||||
'https://gravatar.com/avatar/706150f3ef810ea66acb30c6d55f1a7e545338747072609e47df71c7c7ccc6a4?s=42&d=retro&r=g',
|
||||
name: 'user2@test.com',
|
||||
ownerType: 'user',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -181,6 +204,11 @@ test('should return projects where users are part of a group', async () => {
|
||||
type: 'root',
|
||||
},
|
||||
],
|
||||
owners: [
|
||||
{
|
||||
ownerType: 'system',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: projectA.name,
|
||||
@ -197,6 +225,19 @@ test('should return projects where users are part of a group', async () => {
|
||||
type: 'project',
|
||||
},
|
||||
],
|
||||
owners: [
|
||||
{
|
||||
email: 'user1@test.com',
|
||||
imageUrl:
|
||||
'https://gravatar.com/avatar/a8cc79d8407a64b0d8982df34e3525afd298a479fe68f300651380730dbf23e9?s=42&d=retro&r=g',
|
||||
name: 'user1@test.com',
|
||||
ownerType: 'user',
|
||||
},
|
||||
{
|
||||
name: 'groupA',
|
||||
ownerType: 'group',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
@ -1,3 +1,5 @@
|
||||
import type { ProjectOwners } from '../project/project-owners-read-model.type';
|
||||
|
||||
export type PersonalFeature = { name: string; type: string; project: string };
|
||||
export type PersonalProject = {
|
||||
name: string;
|
||||
@ -8,6 +10,9 @@ export type PersonalProject = {
|
||||
type: 'custom' | 'project' | 'root' | 'custom-root';
|
||||
}[];
|
||||
};
|
||||
export type PersonalProjectWithOwners = PersonalProject & {
|
||||
owners: ProjectOwners;
|
||||
};
|
||||
|
||||
export interface IPersonalDashboardReadModel {
|
||||
getPersonalFeatures(userId: number): Promise<PersonalFeature[]>;
|
||||
|
@ -1,21 +1,36 @@
|
||||
import type { IProjectOwnersReadModel } from '../project/project-owners-read-model.type';
|
||||
import type {
|
||||
IPersonalDashboardReadModel,
|
||||
PersonalFeature,
|
||||
PersonalProject,
|
||||
PersonalProjectWithOwners,
|
||||
} from './personal-dashboard-read-model-type';
|
||||
|
||||
export class PersonalDashboardService {
|
||||
private personalDashboardReadModel: IPersonalDashboardReadModel;
|
||||
|
||||
constructor(personalDashboardReadModel: IPersonalDashboardReadModel) {
|
||||
private projectOwnersReadModel: IProjectOwnersReadModel;
|
||||
|
||||
constructor(
|
||||
personalDashboardReadModel: IPersonalDashboardReadModel,
|
||||
projectOwnersReadModel: IProjectOwnersReadModel,
|
||||
) {
|
||||
this.personalDashboardReadModel = personalDashboardReadModel;
|
||||
this.projectOwnersReadModel = projectOwnersReadModel;
|
||||
}
|
||||
|
||||
getPersonalFeatures(userId: number): Promise<PersonalFeature[]> {
|
||||
return this.personalDashboardReadModel.getPersonalFeatures(userId);
|
||||
}
|
||||
|
||||
getPersonalProjects(userId: number): Promise<PersonalProject[]> {
|
||||
return this.personalDashboardReadModel.getPersonalProjects(userId);
|
||||
async getPersonalProjects(
|
||||
userId: number,
|
||||
): Promise<PersonalProjectWithOwners[]> {
|
||||
const projects =
|
||||
await this.personalDashboardReadModel.getPersonalProjects(userId);
|
||||
|
||||
const withOwners =
|
||||
await this.projectOwnersReadModel.addOwners(projects);
|
||||
|
||||
return withOwners;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
import type {
|
||||
IProjectOwnersReadModel,
|
||||
IProjectForUiWithOwners,
|
||||
WithProjectOwners,
|
||||
} from './project-owners-read-model.type';
|
||||
import type { ProjectForUi } from './project-read-model-type';
|
||||
|
||||
export class FakeProjectOwnersReadModel implements IProjectOwnersReadModel {
|
||||
async addOwners(
|
||||
projects: ProjectForUi[],
|
||||
): Promise<IProjectForUiWithOwners[]> {
|
||||
async addOwners<T extends { id: string }>(
|
||||
projects: T[],
|
||||
): Promise<WithProjectOwners<T>> {
|
||||
return projects.map((project) => ({
|
||||
...project,
|
||||
owners: [{ ownerType: 'system' }],
|
||||
|
@ -4,11 +4,10 @@ import { anonymise, generateImageUrl } from '../../util';
|
||||
import type {
|
||||
GroupProjectOwner,
|
||||
IProjectOwnersReadModel,
|
||||
IProjectForUiWithOwners,
|
||||
ProjectOwnersDictionary,
|
||||
UserProjectOwner,
|
||||
WithProjectOwners,
|
||||
} from './project-owners-read-model.type';
|
||||
import type { ProjectForUi } from './project-read-model-type';
|
||||
|
||||
const T = {
|
||||
ROLE_USER: 'role_user',
|
||||
@ -24,10 +23,10 @@ export class ProjectOwnersReadModel implements IProjectOwnersReadModel {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
static addOwnerData(
|
||||
projects: ProjectForUi[],
|
||||
static addOwnerData<T extends { id: string }>(
|
||||
projects: T[],
|
||||
owners: ProjectOwnersDictionary,
|
||||
): IProjectForUiWithOwners[] {
|
||||
): WithProjectOwners<T> {
|
||||
return projects.map((project) => ({
|
||||
...project,
|
||||
owners: owners[project.id] || [{ ownerType: 'system' }],
|
||||
@ -64,7 +63,10 @@ export class ProjectOwnersReadModel implements IProjectOwnersReadModel {
|
||||
|
||||
const data: UserProjectOwner = {
|
||||
ownerType: 'user',
|
||||
name: user?.name || user?.username,
|
||||
name:
|
||||
user?.name ||
|
||||
user?.username ||
|
||||
processSensitiveData(user?.email),
|
||||
email: processSensitiveData(user?.email),
|
||||
imageUrl: generateImageUrl(user),
|
||||
};
|
||||
@ -138,10 +140,10 @@ export class ProjectOwnersReadModel implements IProjectOwnersReadModel {
|
||||
return dict;
|
||||
}
|
||||
|
||||
async addOwners(
|
||||
projects: ProjectForUi[],
|
||||
async addOwners<T extends { id: string }>(
|
||||
projects: T[],
|
||||
anonymizeProjectOwners: boolean = false,
|
||||
): Promise<IProjectForUiWithOwners[]> {
|
||||
): Promise<WithProjectOwners<T>> {
|
||||
const owners = await this.getAllProjectOwners(anonymizeProjectOwners);
|
||||
|
||||
return ProjectOwnersReadModel.addOwnerData(projects, owners);
|
||||
|
@ -11,7 +11,7 @@ export type GroupProjectOwner = {
|
||||
ownerType: 'group';
|
||||
name: string;
|
||||
};
|
||||
type ProjectOwners =
|
||||
export type ProjectOwners =
|
||||
| [SystemOwner]
|
||||
| Array<UserProjectOwner | GroupProjectOwner>;
|
||||
|
||||
@ -21,9 +21,13 @@ export type IProjectForUiWithOwners = ProjectForUi & {
|
||||
owners: ProjectOwners;
|
||||
};
|
||||
|
||||
export type WithProjectOwners<T extends { id: string }> = (T & {
|
||||
owners: ProjectOwners;
|
||||
})[];
|
||||
|
||||
export interface IProjectOwnersReadModel {
|
||||
addOwners(
|
||||
projects: ProjectForUi[],
|
||||
addOwners<T extends { id: string }>(
|
||||
projects: T[],
|
||||
anonymizeProjectOwners?: boolean,
|
||||
): Promise<IProjectForUiWithOwners[]>;
|
||||
): Promise<WithProjectOwners<T>>;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { FromSchema } from 'json-schema-to-ts';
|
||||
import { projectSchema } from './project-schema';
|
||||
|
||||
export const personalDashboardSchema = {
|
||||
$id: '#/components/schemas/personalDashboardSchema',
|
||||
@ -24,6 +25,7 @@ export const personalDashboardSchema = {
|
||||
example: 'My Project',
|
||||
description: 'The name of the project',
|
||||
},
|
||||
owners: projectSchema.properties.owners,
|
||||
roles: {
|
||||
type: 'array',
|
||||
description:
|
||||
|
@ -149,6 +149,8 @@ import { OnboardingService } from '../features/onboarding/onboarding-service';
|
||||
import { PersonalDashboardService } from '../features/personal-dashboard/personal-dashboard-service';
|
||||
import { PersonalDashboardReadModel } from '../features/personal-dashboard/personal-dashboard-read-model';
|
||||
import { FakePersonalDashboardReadModel } from '../features/personal-dashboard/fake-personal-dashboard-read-model';
|
||||
import { ProjectOwnersReadModel } from '../features/project/project-owners-read-model';
|
||||
import { FakeProjectOwnersReadModel } from '../features/project/fake-project-owners-read-model';
|
||||
|
||||
export const createServices = (
|
||||
stores: IUnleashStores,
|
||||
@ -408,6 +410,8 @@ export const createServices = (
|
||||
db
|
||||
? new PersonalDashboardReadModel(db)
|
||||
: new FakePersonalDashboardReadModel(),
|
||||
|
||||
db ? new ProjectOwnersReadModel(db) : new FakeProjectOwnersReadModel(),
|
||||
);
|
||||
|
||||
return {
|
||||
|
Loading…
Reference in New Issue
Block a user