Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2766 - setOrganizationFeaturedProjects Endpoint #2837

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/backend/src/controllers/organizations.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,15 @@ export default class OrganizationsController {
next(error);
}
}

static async setOrganizationFeaturedProjects(req: Request, res: Response, next: NextFunction) {
try {
const { projectIds } = req.body;
const featuredProjects = await OrganizationsService.setFeaturedProjects(projectIds, req.organization, req.currentUser);

res.status(200).json(featuredProjects);
} catch (error: unknown) {
next(error);
}
}
}
10 changes: 9 additions & 1 deletion src/backend/src/routes/organizations.routes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import express from 'express';
import { linkValidators, validateInputs } from '../utils/validation.utils';
import { linkValidators, nonEmptyString, validateInputs } from '../utils/validation.utils';
import OrganizationsController from '../controllers/organizations.controller';
import multer, { memoryStorage } from 'multer';
import { body } from 'express-validator';

const organizationRouter = express.Router();
const upload = multer({ limits: { fileSize: 30000000 }, storage: memoryStorage() });
Expand All @@ -18,4 +19,11 @@ organizationRouter.post(
);

organizationRouter.get('/images', OrganizationsController.getOrganizationImages);
organizationRouter.post(
'/featured-projects/set',
body('projectIds').isArray(),
nonEmptyString(body('projectIds.*')),
validateInputs,
OrganizationsController.setOrganizationFeaturedProjects
);
export default organizationRouter;
21 changes: 21 additions & 0 deletions src/backend/src/services/organizations.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { createUsefulLinks } from '../utils/organizations.utils';
import { linkTransformer } from '../transformers/links.transformer';
import { getLinkQueryArgs } from '../prisma-query-args/links.query-args';
import { uploadFile } from '../utils/google-integration.utils';
import { getProjects } from '../utils/projects.utils';

export default class OrganizationsService {
/**
Expand Down Expand Up @@ -131,4 +132,24 @@ export default class OrganizationsService {
exploreAsGuestImage: organization.exploreAsGuestImageId
};
}

static async setFeaturedProjects(projectIds: string[], organization: Organization, submitter: User) {
if (!(await userHasPermission(submitter.userId, organization.organizationId, isAdmin)))
throw new AccessDeniedAdminOnlyException('update featured projects');

//throws if all projects are not found
const featuredProjects = await getProjects(projectIds, organization.organizationId);

const updatedOrg = await prisma.organization.update({
where: { organizationId: organization.organizationId },
data: {
featuredProjects: {
set: featuredProjects.map((project) => ({ projectId: project.projectId }))
}
},
include: { featuredProjects: true }
});

return updatedOrg;
}
}
37 changes: 35 additions & 2 deletions src/backend/src/utils/projects.utils.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { WBS_Element_Status } from '@prisma/client';
import { Project, WBS_Element_Status } from '@prisma/client';
import prisma from '../prisma/prisma';
import { DescriptionBulletPreview, LinkCreateArgs, WbsElementStatus } from 'shared';
import { DeletedException, NotFoundException } from './errors.utils';
import { DeletedException, HttpException, NotFoundException } from './errors.utils';
import { ChangeCreateArgs, createChange, createListChanges, getDescriptionBulletChanges } from './changes.utils';
import { DescriptionBulletDestination, addRawDescriptionBullets, editDescriptionBullets } from './description-bullets.utils';
import { linkToChangeListValue, updateLinks } from './links.utils';
import { getLinkQueryArgs } from '../prisma-query-args/links.query-args';
import { getDescriptionBulletQueryArgs } from '../prisma-query-args/description-bullets.query-args';
import { getProjectQueryArgs } from '../prisma-query-args/projects.query-args';

export type ProjectWithId = {
projectId: String;
};

/**
* calculate the project's status based on its workpacakges' status
* @param proj a given project to be calculated on its status
Expand Down Expand Up @@ -213,3 +217,32 @@ export const checkMaterialInputs = async (
if (!unit) throw new NotFoundException('Unit', unitName);
}
};

/**
* Produce a array of primsa formated projectIDs, given the array of Project
* @param projectIds the projectIds to get as users
* @returns projectIds in prisma format
*/
export const getProjects = async (projectIds: string[], organizationId: string) => {
const projects = await prisma.project.findMany({
where: { projectId: { in: projectIds } },
...getProjectQueryArgs(organizationId)
});

validateFoundProjects(projects, projectIds);

return projects;
};

/**
* Validates that the projects found in the database match the given projectIds
* @param projects the projects found in the database
* @param projectIds the requested projectIds to retrieve
*/
const validateFoundProjects = (projects: Project[], projectIds: string[]) => {
if (projects.length !== projectIds.length) {
const primsaProjectIds = projects.map((project) => project.projectId);
const missingProjectIds = projectIds.filter((id) => !primsaProjectIds.includes(id));
throw new HttpException(404, `Projects(s) with the following ids not found: ${missingProjectIds.join(', ')}`);
}
};
Loading