import { OnCallProviderData, OnCallProviderResult, OnCallScheduleInfo, OnCallUser, } from '@atlassian/forge-graphql-types'; import { isValidURL } from '../helpers'; class OnCallProviderError extends Error {} export enum OnCallProviderValidationErrors { SCHEDULE_INFO_NOT_PROVIDED = "Can't get service", INVALID_SCHEDULE_LINK = 'ScheduleLink is not in URL format', SCHEDULE_LEVEL_NOT_PROVIDED = 'No levels in scheduleInfo', INVALID_SCHEDULE_LEVEL_PRIORITY = 'Invalid priority for level in scheduleInfo', SCHEDULE_TIME_RANGES_OVERLAP = 'Schedule time ranges overlap', INVALID_DATE = 'Invalid date', INVALID_SCHEDULE_TIME_RANGE = 'Invalid schedule time range', } /* Builder class for constructing a response object for compass:onCallProvider Forge modules. */ export class OnCallProviderResponse { result: OnCallProviderData = {}; error: string; public build(): OnCallProviderResult { this.validate(); return { success: this.error === undefined, result: this.result, error: this.error, }; } private validate(): void { if (this.result.onCallScheduleInfo) { this.validateOnCallScheduleInfo(); } } public addScheduleInfo( scheduleInfo: OnCallScheduleInfo | null, ): OnCallProviderResponse { if (scheduleInfo === null) { this.error = OnCallProviderValidationErrors.SCHEDULE_INFO_NOT_PROVIDED; return this; } this.result.onCallScheduleInfo = scheduleInfo; return this; } private validateOnCallScheduleInfo(): void { try { const isValidScheduleLink = isValidURL( this.result.onCallScheduleInfo.scheduleLink, ); if (!isValidScheduleLink) { throw new OnCallProviderError( OnCallProviderValidationErrors.INVALID_SCHEDULE_LINK, ); } if (this.result.onCallScheduleInfo.levels.length <= 0) { throw new OnCallProviderError( OnCallProviderValidationErrors.SCHEDULE_LEVEL_NOT_PROVIDED, ); } this.result.onCallScheduleInfo.levels.forEach((level) => { if (level.priority <= 0) { throw new OnCallProviderError( OnCallProviderValidationErrors.INVALID_SCHEDULE_LEVEL_PRIORITY, ); } if (!level.currentOnCall) { return; } if (level.currentOnCall.timeRange === null) { return; } const currentOnCallRange = this.getValidTimeRangesForOnCall( level.currentOnCall, ); if (level.nextOnCall) { const nextOnCallRange = this.getValidTimeRangesForOnCall( level.nextOnCall, ); const isNextOnCallStartsAfterCurrent = nextOnCallRange.scheduleStartTimeDate >= currentOnCallRange.scheduleEndTimeDate; if (!isNextOnCallStartsAfterCurrent) { throw new OnCallProviderError( OnCallProviderValidationErrors.SCHEDULE_TIME_RANGES_OVERLAP, ); } } }); } catch (error) { this.error = error.message; delete this.result.onCallScheduleInfo; } } // eslint-disable-next-line class-methods-use-this private getValidTimeRangesForOnCall(onCallUser: OnCallUser): { scheduleStartTimeDate: Date; scheduleEndTimeDate: Date; } { const { scheduleStartTime, scheduleEndTime } = onCallUser.timeRange; const scheduleStartTimeDate = new Date(scheduleStartTime); const scheduleEndTimeDate = new Date(scheduleEndTime); const isValidStartTimeDate = new Date(scheduleStartTimeDate).getDate(); const isValidEndTimeDate = new Date(scheduleEndTimeDate).getDate(); if (!isValidStartTimeDate || !isValidEndTimeDate) { throw new OnCallProviderError( OnCallProviderValidationErrors.INVALID_DATE, ); } if (scheduleEndTimeDate < scheduleStartTimeDate) { throw new OnCallProviderError( OnCallProviderValidationErrors.INVALID_SCHEDULE_TIME_RANGE, ); } return { scheduleStartTimeDate, scheduleEndTimeDate }; } }