From 346ac32948177883f56ececdc6b78c71239b706f Mon Sep 17 00:00:00 2001 From: Rich Bayliss Date: Tue, 13 Oct 2020 15:59:24 +0100 Subject: [PATCH] model: Add UUID to Application model Change-type: minor Signed-off-by: Rich Bayliss --- src/balena.sbvr | 5 +++- src/features/applications/hooks/defaults.ts | 25 +++++++++++++++++++ src/migrations/00033-add-application-uuid.sql | 11 ++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src/migrations/00033-add-application-uuid.sql diff --git a/src/balena.sbvr b/src/balena.sbvr index a3000b17e..0d812805e 100644 --- a/src/balena.sbvr +++ b/src/balena.sbvr @@ -439,7 +439,10 @@ Fact type: application has application type Necessity: each application has exactly one application type. Fact type: application is host Fact type: application is archived - +Fact type: application has uuid + Necessity: each application has exactly one uuid. + Necessity: each uuid is of exactly one application. + Necessity: each application has a uuid that has a Length (Type) that is equal to 32. -- service instance diff --git a/src/features/applications/hooks/defaults.ts b/src/features/applications/hooks/defaults.ts index 0feb933c8..755329075 100644 --- a/src/features/applications/hooks/defaults.ts +++ b/src/features/applications/hooks/defaults.ts @@ -1,6 +1,22 @@ import { hooks, errors } from '@balena/pinejs'; +import * as uuid from 'uuid'; import { DefaultApplicationType } from '../../application-types/application-types'; +// reconstitute the value into a properly formatted UUID... +const toUuid = (strippedUuid: string): string => { + if (strippedUuid.length !== 32) { + return ''; + } + + const parts: string[] = []; + parts.push(strippedUuid.substr(0, 8)); + parts.push(strippedUuid.substr(8, 4)); + parts.push(strippedUuid.substr(12, 4)); + parts.push(strippedUuid.substr(16, 4)); + parts.push(strippedUuid.substr(20, 12)); + return parts.join('-'); +}; + hooks.addPureHook('POST', 'resin', 'application', { POSTPARSE: async (args) => { const { request } = args; @@ -14,6 +30,15 @@ hooks.addPureHook('POST', 'resin', 'application', { ); } + request.values.uuid = request.values.uuid ?? uuid.v4().replace(/-/g, ''); + + const appUuid = toUuid(request.values.uuid); + if (!uuid.validate(appUuid) || uuid.version(appUuid) !== 4) { + throw new errors.BadRequestError( + 'Application UUID must be a 32 character long lower case UUID version 4.', + ); + } + request.values.should_track_latest_release = true; request.values.slug ??= appName.toLowerCase(); }, diff --git a/src/migrations/00033-add-application-uuid.sql b/src/migrations/00033-add-application-uuid.sql new file mode 100644 index 000000000..4a8cac6d4 --- /dev/null +++ b/src/migrations/00033-add-application-uuid.sql @@ -0,0 +1,11 @@ +-- load the UUID functions... +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +-- migrate to add a new UUID value to the application model... +ALTER TABLE "application" +ADD COLUMN IF NOT EXISTS "uuid" TEXT NOT NULL DEFAULT REPLACE(CAST(UUID_GENERATE_V4() AS TEXT), '-', ''), +ADD CONSTRAINT "application_uuid_key" UNIQUE ("uuid"); + +-- remove the default +ALTER TABLE "application" +ALTER COLUMN "uuid" DROP DEFAULT;