diff --git a/.gitignore b/.gitignore index b91ee9308..8b958631a 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,6 @@ htmlcov .cache .npm-debug.log Dockerfile-e -build/ .vscode *.iml .DS_Store diff --git a/static/js/services/build/build.service.impl.spec.ts b/static/js/services/build/build.service.impl.spec.ts new file mode 100644 index 000000000..a2e748206 --- /dev/null +++ b/static/js/services/build/build.service.impl.spec.ts @@ -0,0 +1,78 @@ +import { BuildServiceImpl } from './build.service.impl'; + + +describe("BuildServiceImpl", () => { + var buildServiceImpl: BuildServiceImpl; + var build: {phase: string}; + + beforeEach(() => { + buildServiceImpl = new BuildServiceImpl(); + build = {phase: ""}; + }); + + describe("isActive", () => { + var phases: string[]; + + beforeEach(() => { + phases = ['complete', 'error', 'expired', 'cancelled']; + }); + + it("returns false if given build's phase matches an inactive phase", () => { + phases.forEach((phase: string) => { + build.phase = phase; + + expect(buildServiceImpl.isActive(build)).toBe(false); + }); + }); + + it("returns true if given build's phase does not match inactive phases", () => { + build.phase = 'initializing'; + + expect(buildServiceImpl.isActive(build)).toBe(true); + }); + }); + + describe("getBuildMessage", () => { + var buildMessages: {phase?: string, message: string}[]; + + beforeEach(() => { + buildMessages = [ + {message: ""}, + {phase: null, message: ""}, + {phase: 'cannot_load', message: 'Cannot load build status'}, + {phase: 'starting', message: 'Starting Dockerfile build'}, + {phase: 'initializing', message: 'Starting Dockerfile build'}, + {phase: 'waiting', message: 'Waiting for available build worker'}, + {phase: 'unpacking', message: 'Unpacking build package'}, + {phase: 'pulling', message: 'Pulling base image'}, + {phase: 'building', message: 'Building image from Dockerfile'}, + {phase: 'checking-cache', message: 'Looking up cached images'}, + {phase: 'priming-cache', message: 'Priming cache for build'}, + {phase: 'build-scheduled', message: 'Preparing build node'}, + {phase: 'pushing', message: 'Pushing image built from Dockerfile'}, + {phase: 'complete', message: 'Dockerfile build completed and pushed'}, + {phase: 'error', message: 'Dockerfile build failed'}, + {phase: 'expired', message: 'Build did not complete after 3 attempts. Re-submit this build to try again.'}, + {phase: 'internalerror', message: 'An internal system error occurred while building; the build will be retried in the next few minutes.'}, + {phase: 'cancelled', message: 'This build was previously cancelled.'}, + ]; + }); + + it("returns the correct message for the given phase", () => { + buildMessages.forEach((buildMessage) => { + expect(buildServiceImpl.getBuildMessage(buildMessage.phase)).toEqual(buildMessage.message, buildMessage); + }); + }); + + it("throws an error if given phase is not supported", () => { + var phase: string = "not-a-phase"; + + try { + buildServiceImpl.getBuildMessage(phase); + fail("Should throw error"); + } catch (error) { + expect(error.message).toEqual(`Invalid build phase: ${phase.toString()}`); + } + }); + }); +}); \ No newline at end of file diff --git a/static/js/services/build/build.service.impl.ts b/static/js/services/build/build.service.impl.ts new file mode 100644 index 000000000..ace6ef7fd --- /dev/null +++ b/static/js/services/build/build.service.impl.ts @@ -0,0 +1,94 @@ +import { BuildService } from './build.service'; +import { Injectable } from 'ng-metadata/core'; + + +@Injectable(BuildService.name) +export class BuildServiceImpl implements BuildService { + + private inactivePhases: string[] = ['complete', 'error', 'expired', 'cancelled']; + + public isActive(build: {phase: string}): boolean { + return this.inactivePhases.indexOf(build.phase) == -1; + } + + public getBuildMessage(phase: string): string { + var message: string; + switch (phase) { + case null: + case undefined: + message = ''; + break; + + case 'cannot_load': + message = 'Cannot load build status'; + break; + + case 'starting': + case 'initializing': + message = 'Starting Dockerfile build'; + break; + + case 'waiting': + message = 'Waiting for available build worker'; + break; + + case 'unpacking': + message = 'Unpacking build package'; + break; + + case 'pulling': + message = 'Pulling base image'; + break; + + case 'building': + message = 'Building image from Dockerfile'; + break; + + case 'checking-cache': + message = 'Looking up cached images'; + break; + + case 'priming-cache': + message = 'Priming cache for build'; + break; + + case 'build-scheduled': + message = 'Preparing build node'; + break; + + case 'pushing': + message = 'Pushing image built from Dockerfile'; + break; + + case 'complete': + message = 'Dockerfile build completed and pushed'; + break; + + case 'error': + message = 'Dockerfile build failed'; + break; + + case 'expired': + message = 'Build did not complete after 3 attempts. Re-submit this build to try again.'; + break; + + case 'internalerror': + message = 'An internal system error occurred while building; ' + + 'the build will be retried in the next few minutes.'; + break; + + case 'cancelled': + message = 'This build was previously cancelled.'; + break; + + case 'incomplete': + message = 'This build was not completed.'; + break; + + default: + throw new Error(`Invalid build phase: ${phase.toString()}`); + } + + return message; + } +} diff --git a/static/js/services/build/build.service.ts b/static/js/services/build/build.service.ts new file mode 100644 index 000000000..8cdabbfae --- /dev/null +++ b/static/js/services/build/build.service.ts @@ -0,0 +1,19 @@ +/** + * Service which provides helper methods for reasoning about builds. + */ +export abstract class BuildService { + + /** + * Determine if the given build is active. + * @param build The build object. + * @return isActive If the given build is active. + */ + public abstract isActive(build: {phase: string}): boolean; + + /** + * Generate a message based on a given phase. + * @param phase The phase type. + * @return buildMessage The message associated with the given phase. + */ + public abstract getBuildMessage(phase: string): string; +}