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

#2149-work-package-template-endpoint #2361

Merged
merged 25 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3a2ca8c
added method to create wpt, haven't manually tested yet
lphan48 Apr 22, 2024
9198fee
#2149 change return logic
bderbs30 Apr 26, 2024
5ac38d2
Merge branch 'develop' into #2149-work-package-endpoint
bderbs30 Apr 26, 2024
1c6cfed
#2149 remove already present types
bderbs30 Apr 26, 2024
db08ccc
#2149 create prisma call data change
bderbs30 Apr 27, 2024
6b084a0
#2149 Cleanup Code
Peyton-McKee May 9, 2024
866d565
#2149 Prettier
Peyton-McKee May 9, 2024
939aa57
Merge branch 'develop' into #2149-work-package-endpoint
bderbs30 May 10, 2024
b7eb72a
#2149 delete blockbyinfo schema model change createwptemplate
bderbs30 May 10, 2024
fb74e20
#2149 edit wptemplate service
bderbs30 May 11, 2024
dcda535
Merge branch 'develop' into #2149-work-package-endpoint
bderbs30 May 14, 2024
e2c17ee
#2149 fixes to transformer and service for wptemplate
bderbs30 May 14, 2024
5043dfe
#2149 lint
bderbs30 May 14, 2024
136c3a5
#2149 prettier
bderbs30 May 14, 2024
5d84219
#2149 routes
bderbs30 May 17, 2024
38512e5
#2149 seed data
bderbs30 May 17, 2024
6498f47
Merge branch 'feature/multitenancy' into #2149-work-package-endpoint
Peyton-McKee May 18, 2024
21325a1
Merge branch 'feature/multitenancy' into #2149-work-package-endpoint
Peyton-McKee May 18, 2024
48987be
#2149 merge conflicts
Peyton-McKee May 18, 2024
2a5248a
#2149 connecting blocked by
bderbs30 May 18, 2024
3778b5d
#2149 updated func doc
bderbs30 May 18, 2024
b0769d5
#2149 descriptionbullets refactor
bderbs30 May 18, 2024
71c5eb2
#2149 templateId instead of templatename for error
bderbs30 May 18, 2024
d327ce8
#2149 env getorgid
bderbs30 May 19, 2024
b0b56b5
#2149 org id and dev mode
bderbs30 May 20, 2024
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
31 changes: 31 additions & 0 deletions src/backend/src/controllers/work-packages.controllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,37 @@ export default class WorkPackagesController {
}
}

// Create a work package template with the given details
static async createWorkPackageTemplate(req: Request, res: Response, next: NextFunction) {
try {
const { templateName, templateNotes, workPackageName, duration, expectedActivities, deliverables, blockedBy } =
req.body;

let { stage } = req.body;
if (stage === 'NONE') {
stage = null;
}

const user = await getCurrentUser(res);

const workPackageTemplate: WorkPackageTemplate = await WorkPackagesService.createWorkPackageTemplate(
user,
templateName,
templateNotes,
workPackageName,
stage,
duration,
expectedActivities,
deliverables,
blockedBy
);

res.status(200).json(workPackageTemplate);
} catch (error: unknown) {
next(error);
}
}

// Edit a work package to the given specifications
static async editWorkPackage(req: Request, res: Response, next: NextFunction) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ CREATE TABLE "Work_Package_Template" (
"workPackageName" TEXT,
"stage" "Work_Package_Stage",
"duration" INTEGER,
"dateCreated" TIMESTAMP(3) NOT NULL,
"dateCreated" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"userCreatedId" INTEGER NOT NULL,
"dateDeleted" TIMESTAMP(3),
"userDeletedId" INTEGER,
Expand Down
2 changes: 1 addition & 1 deletion src/backend/src/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,7 @@ model Work_Package_Template {
blockedBy Work_Package_Template[] @relation("blocking")
blocking Work_Package_Template[] @relation("blocking")
descriptionBullets Description_Bullet[]
dateCreated DateTime
dateCreated DateTime @default(now())
userCreated User @relation(fields: [userCreatedId], references: [userId], name: "workPackageTemplateCreator")
userCreatedId Int
dateDeleted DateTime?
Expand Down
37 changes: 37 additions & 0 deletions src/backend/src/prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import DesignReviewsService from '../services/design-reviews.services';
import BillOfMaterialsService from '../services/boms.services';
import UsersService from '../services/users.services';
import { transformDate } from '../utils/datetime.utils';
import WorkPackagesService from '../services/work-packages.services';

const prisma = new PrismaClient();

Expand Down Expand Up @@ -1924,6 +1925,42 @@ const performSeed: () => Promise<void> = async () => {
links: []
}
);

const workPackageTemplate1 = await WorkPackagesService.createWorkPackageTemplate(
batman,
'Batmobile Config 1',
'This is the first Batmobile configuration',
null,
null,
5,
[],
[],
organizationId
);

const schematicWpTemplate = await WorkPackagesService.createWorkPackageTemplate(
batman,
'Schematic',
'This is the schematic template',
null,
WorkPackageStage.Design,
4,
[],
[],
organizationId
);

const layoutWpTemplate = await WorkPackagesService.createWorkPackageTemplate(
batman,
'Layout ',
'This is the Layout template',
null,
WorkPackageStage.Design,
4,
[],
[schematicWpTemplate.workPackageTemplateId],
organizationId
);
};

performSeed()
Expand Down
18 changes: 18 additions & 0 deletions src/backend/src/routes/work-package-templates.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,22 @@ workPackageTemplatesRouter.post(
WorkPackagesController.editWorkPackageTemplate
);

workPackageTemplatesRouter.post(
'/create',
nonEmptyString(body('templateName')),
nonEmptyString(body('templateNotes')),
nonEmptyString(body('workPackageName').optional()),
isWorkPackageStageOrNone(body('stage').optional()),
intMinZero(body('duration').optional()),
body('expectedActivities').isArray(),
nonEmptyString(body('expectedActivities.*')),
body('deliverables').isArray(),
nonEmptyString(body('deliverables.*')),
body('blockedBy').isArray(),
nonEmptyString(body('blockedByInfo.*.name')),
isWorkPackageStageOrNone(body('blockedByInfo.*.stage').optional()),
validateInputs,
WorkPackagesController.createWorkPackageTemplate
);

export default workPackageTemplatesRouter;
1 change: 1 addition & 0 deletions src/backend/src/routes/work-packages.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ workPackagesRouter.post(
validateInputs,
WorkPackagesController.createWorkPackage
);

workPackagesRouter.post(
'/edit',
intMinZero(body('workPackageId')),
Expand Down
82 changes: 79 additions & 3 deletions src/backend/src/services/work-packages.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ import {
descriptionBulletToChangeListValue,
descriptionBulletsToChangeListValues,
editDescriptionBullets,
markDescriptionBulletsAsDeleted
markDescriptionBulletsAsDeleted,
validateDescriptionBullets
} from '../utils/description-bullets.utils';
import { getBlockingWorkPackages, validateBlockedByTemplates, validateBlockedBys } from '../utils/work-packages.utils';
import { getBlockingWorkPackages, validateBlockedBys, validateBlockedByTemplates } from '../utils/work-packages.utils';
import { workPackageTemplateTransformer } from '../transformers/work-package-template.transformer';
import { getWorkPackageTemplateQueryArgs } from '../prisma-query-args/work-package-template.query-args';
import { getDescriptionBulletQueryArgs } from '../prisma-query-args/description-bullets.query-args';
Expand Down Expand Up @@ -605,6 +606,81 @@ export default class WorkPackagesService {
return workPackageTemplates.map(workPackageTemplateTransformer);
}

/**
* Creates a Work_Package_Template in the database
*
* @param user the user creating the work package template
* @param templateName the template name
* @param templateNotes the template notes
* @param workPackageName the name of the work packge
* @param stage the stage
* @param duration the duration of the work package template in weeks
* @param expectedActivities the expected activities descriptions for this WPT
* @param deliverables the expected deliverables descriptions for this WPT
* @param blockedBy the WBS elements that need to be completed before this WPT
* @param organizationId the id of the organization that the user is currently in
* @returns the WBS number of the successfully created work package template
* @throws if the work package template could not be created
*/
static async createWorkPackageTemplate(
user: User,
templateName: string,
templateNotes: string,
workPackageName: string | null,
stage: WorkPackageStage | null,
duration: number,
descriptionBullets: DescriptionBulletPreview[],
blockedByIds: string[],
organizationId: string
): Promise<WorkPackageTemplate> {
if (!(await userHasPermission(user.userId, organizationId, isAdmin)))
throw new AccessDeniedAdminOnlyException('create work package templates');

// get the corresponding IDs of all work package templates in BlockedBy,
// and throw an errror if the template doesn't exist
await Promise.all(
blockedByIds.map(async (workPackageTemplateId) => {
const template = await prisma.work_Package_Template.findFirst({
where: { workPackageTemplateId }
});

if (!template) {
throw new NotFoundException('Work Package Template', templateName);
}
return template.workPackageTemplateId;
})
);

await validateDescriptionBullets(descriptionBullets, organizationId);

// add to the db
const created = await prisma.work_Package_Template.create({
data: {
templateName,
templateNotes,
workPackageName,
stage,
duration,
userCreatedId: user.userId,
organizationId,
blockedBy: {
connect: blockedByIds.map((blockedById) => ({ workPackageTemplateId: blockedById }))
}
},

...getWorkPackageTemplateQueryArgs(organizationId)
});

await addRawDescriptionBullets(
descriptionBullets,
DescriptionBulletDestination.TEMPLATE,
created.workPackageTemplateId,
created.organizationId
);

return workPackageTemplateTransformer(created);
}

/**
* Edits a work package template given the specified parameters
* @param submitter user who is submitting the edit
Expand All @@ -613,7 +689,7 @@ export default class WorkPackagesService {
* @param templateNotes notes about the work package template
* @param duration duration value on the template
* @param stage stage value on the template
* @param blockedByInfo array of templates blocking this
* @param blockedByIds array of templates blocking this
* @param expectedActivities array of expected activity values on the template
* @param deliverables array of deliverable values on the template
* @param workPackageName name value on the template
Expand Down
3 changes: 3 additions & 0 deletions src/frontend/src/utils/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ const workPackageTemplates = () => `${API_URL}/templates`;
const workPackageTemplatesById = (workPackageTemplateId: string) => `${workPackageTemplates()}/${workPackageTemplateId}`;
const workPackageTemplatesEdit = (workPackageTemplateId: string) =>
`${workPackageTemplatesById(workPackageTemplateId)}/edit`;
const workPackageTemplatesCreate = () => `${workPackageTemplates()}/create`;

/**************** Other Endpoints ****************/
const version = () => `https://api.github.com/repos/Northeastern-Electric-Racing/FinishLine/releases/latest`;
Expand Down Expand Up @@ -281,7 +282,9 @@ export const apiUrls = {
designReviewDelete,

workPackageTemplates,
workPackageTemplatesById,
workPackageTemplatesEdit,
workPackageTemplatesCreate,

version
};
Loading