diff --git a/src/aws/auth.controller.ts b/src/aws/auth.controller.ts index a168349..55fd852 100644 --- a/src/aws/auth.controller.ts +++ b/src/aws/auth.controller.ts @@ -4,19 +4,20 @@ import { Post, Headers, Body, + Query, UseGuards, } from "@nestjs/common"; import { AuthService } from "./auth.service"; -import { WriteEntryToTable, UserTimesheets } from "../dynamodb"; +import { getTimesheetsForUsersInGivenTimeFrame, doUUIDSExistInCompanies, GetCompaniesForUser, areUUIDsValid } from "../dynamodb"; import TokenClient from './cognito/cognito.keyparser' import { TimeSheetSchema } from 'src/db/schemas/Timesheet'; import * as frontendTimesheetSchemas from 'src/db/schemas/Timesheet' +import { Roles } from "src/utils/decorators/roles.decorators"; // idk if this is correct import { RolesGuard } from 'src/utils/guards/roles.guard'; import { UploadTimesheet } from 'src/db/timesheets/UploadTimesheet'; import { TimesheetUpdateRequest } from 'src/db/schemas/UpdateTimesheet'; import { Formatter } from 'src/db/timesheets/Formatter'; - @Controller("auth") @UseGuards(RolesGuard) export class AuthController { @@ -51,4 +52,64 @@ export class AuthController { } return []; } + + @Get("getTimesheet") + //@Roles('breaktime-management-role') + //@Roles("breaktime-admin", "breaktime-supervisor") + public async get_timesheets( + @Headers() headers: any, + @Query("userIds") userIDs?: string[] + ): Promise { + // if supervisors dont have access to a uuid throw an error + // if supervisor or admin request non existent uuid throw an error + + // if supervisor ensure all uuids are in company + // if admin just make sure theyre all valid + + // if associate only return their timesheet + + const userID = await TokenClient.grabUserID(headers); + let areAllUUIDsValid; + + if (!userIDs) { + if (1 == 1) { // associate + userIDs = [userID] + } else if (2 === 2) { // supervisor + userIDs = [] // get all users in their companies + } else if (3 === 3) { // admin + userIDs = [] // get all users they own + } else { + // no role no access + // throw error + } + } + + if (1 === 1) { // associate + areAllUUIDsValid = (userIDs.length === 1 && userID === userIDs[0]) + + } else if (2 === 2) { // supervisor + const supervisorCompanies = (await GetCompaniesForUser(userID)).SupervisorCompanyIDs + areAllUUIDsValid = await doUUIDSExistInCompanies(userIDs, supervisorCompanies) + + } else if (3 === 3) { //admin + areAllUUIDsValid = await areUUIDsValid(userIDs); + + } else { + // no role no access + // throw error + } + + if (areAllUUIDsValid) { + return await getTimesheetsForUsersInGivenTimeFrame(userIDs); + } else{ + // throw error + } + + //await getTimesheetsForUsersInGivenTimeFrame(['77566d69-3b61-452a-afe8-73dcda96f876']); + } } + +export type uuidToTimesheetMapping = { + uuid: string, + timesheet: TimeSheetSchema +}; \ No newline at end of file diff --git a/src/dynamodb.ts b/src/dynamodb.ts index 6a2954b..4175acd 100644 --- a/src/dynamodb.ts +++ b/src/dynamodb.ts @@ -7,7 +7,9 @@ import { } from "@aws-sdk/client-dynamodb"; import { unmarshall, marshall } from "@aws-sdk/util-dynamodb"; import * as dotenv from "dotenv"; +import moment = require("moment"); +import { timesheetToUpload } from "./utils"; import {TimeSheetSchema} from './db/schemas/Timesheet' import { CompanySchema, UserCompaniesSchema } from './db/schemas/CompanyUsers'; @@ -160,3 +162,125 @@ export async function WriteEntryToTable(table:TimeSheetSchema): Promise }); return true; } +// EndDate should be start date plus one week +export async function getTimesheetsForUsersInGivenTimeFrame(uuids: string[], StartDate:number = moment().startOf('week').subtract(1, 'week').unix() , EndDate:number = moment().endOf('week').unix()): Promise { + + if (StartDate > EndDate) { + throw new Error("Invalid EndDate") + } + + let result = [] + + for (let uuid of uuids) { + const command = new QueryCommand({ + TableName: "BreaktimeTimesheets", + KeyConditionExpression: "UserID = :s", + ExpressionAttributeValues: { + ":s": { S: `${uuid}` }, + }, + ExpressionAttributeNames: { + "#S": "Status" + }, + ProjectionExpression: "UserID, TimesheetID, CompanyID, ScheduleData, StartDate, #S, WeekNotes" + }); + + // get the items from DynamoDB with our query + const dynamoRawResult = await client.send(command); + + if (dynamoRawResult == null || dynamoRawResult.Items == null) { + throw new Error("Invalid response from DynamoDB, got undefined/null"); + } + + // Convert Dynamo items to JS objects + const unmarshalledItems = dynamoRawResult.Items.map((i) => unmarshall(i)); + + // Parse the items into our expected Company schema. + const timesheetData = unmarshalledItems.map((i) => TimeSheetSchema.parse(i)); + + const uuidSet = new Set(uuids) + + + // TODO: have to check here the timesheets for all weeks exist then and create empty if not + let modifiedTimesheetData = timesheetData.filter((sheet) => {return uuidSet.has(sheet.UserID) && sheet.StartDate >= StartDate && sheet.StartDate < EndDate}) + + let existingWeeks = new Set() + + for (const sheet of modifiedTimesheetData) { + // maybe move to utils and can in theory have locale based issues so configure moment project wide + const beginningOfWeekDate = moment.unix(sheet.StartDate).set('second', 0).set('minute', 0).set('hour', 0).set('millisecond', 0).startOf('week').unix() + existingWeeks.add(beginningOfWeekDate) // make it sunday 00:00:00 + } + + console.log(existingWeeks, StartDate) + for (const m = moment.unix(StartDate); m.isBefore(moment.unix(EndDate)); m.add(1, 'week')) { + if (!(m.unix() in existingWeeks)) { + const newSheet = timesheetToUpload(uuid, "Company 55"); // TODO: should loop through companies user was active in and do it like that + WriteEntryToTable(newSheet); + // add to modifiedTimesheetData + modifiedTimesheetData.push(newSheet); + } + + } + + // go through modified timesheets + // make a set of what weeks it has + // go through start to end and check that it has all weeks + + // modifiedTimesheetData not sorted by date but can be sorted + + const uuidToTimesheet = {"uuid": uuid, timesheet: modifiedTimesheetData} + + result.push(uuidToTimesheet); + }; + + return result +} + +export async function doUUIDSExistInCompanies(uuids: string[], companies: string[]): Promise { + + const dynamoKeys = companies.map((company) => {return {CompanyID: { S: company}}}) + + const command = new BatchGetItemCommand({ + RequestItems: { + BreaktimeCompanyToUsers : { + Keys : dynamoKeys, + ProjectionExpression : "AssociateIDs" + } + } + }); + + const dynamoRawResult = await client.send(command); + + if (dynamoRawResult == null || dynamoRawResult.Responses == null) { + throw new Error("Invalid response from DynamoDB, got undefined/null"); + } + + const results = dynamoRawResult.Responses.BreaktimeCompanyToUsers.map((i) => unmarshall(i))[0].AssociateIDs; + + // so check results are same as uuids + return results.sort().toString() === uuids.sort().toString(); +} + +export async function areUUIDsValid(uuids: string[]): Promise { + + const dynamoKeys = uuids.map((uuid) => {return {UserID: { S: uuid}}}) + + const command = new BatchGetItemCommand({ + RequestItems: { + BreaktimeUserToCompanies : { + Keys : dynamoKeys, + ProjectionExpression : "UserID" + } + } + }); + + const dynamoRawResult = await client.send(command); + + if (dynamoRawResult == null || dynamoRawResult.Responses == null) { + throw new Error("Invalid response from DynamoDB, got undefined/null"); + } + + const results = dynamoRawResult.Responses.BreaktimeUserToCompanies; + + return results.length === uuids.length; +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..e99c0df --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,21 @@ +import { TimeSheetSchema, TimesheetStatus } from "../src/db/schemas/Timesheet" +const moment = require('moment-timezone'); + +export const timesheetToUpload = (UUID: string, CompanyID: string) => +{ + return TimeSheetSchema.parse({ + TimesheetID: Math.round(Math.random() * 1000000000), + UserID: UUID, + StartDate: moment().startOf('week').day(0).unix(), + Status: TimesheetStatus.parse({ + HoursSubmitted: undefined, + HoursReviewed: undefined, + ScheduleSubmitted: undefined, + Finalized: undefined + }), + CompanyID: CompanyID, + HoursData: [], + ScheduleData: [], + WeekNotes: [] + }) +} \ No newline at end of file