1
0
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:
Thomas Heartman 2024-09-25 13:32:33 +02:00 committed by GitHub
parent e680921517
commit 44bf6615a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 94 additions and 22 deletions

View File

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

View File

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

View File

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

View File

@ -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' }],

View File

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

View File

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

View File

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

View File

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