diff --git a/database/migrations/000108_data_sources.down.sql b/database/migrations/000108_data_sources.down.sql new file mode 100644 index 0000000000..ef0e2c4e2f --- /dev/null +++ b/database/migrations/000108_data_sources.down.sql @@ -0,0 +1,10 @@ +-- SPDX-FileCopyrightText: Copyright 2024 The Minder Authors +-- SPDX-License-Identifier: Apache-2.0 + +BEGIN; + +DROP TABLE rule_type_data_sources; +DROP TABLE data_sources_functions; +DROP TABLE data_sources; + +COMMIT; diff --git a/database/migrations/000108_data_sources.up.sql b/database/migrations/000108_data_sources.up.sql new file mode 100644 index 0000000000..2bbac07f8c --- /dev/null +++ b/database/migrations/000108_data_sources.up.sql @@ -0,0 +1,60 @@ +-- SPDX-FileCopyrightText: Copyright 2024 The Minder Authors +-- SPDX-License-Identifier: Apache-2.0 + +BEGIN; + +-- This migration adds storage support for data sources. The only +-- constraints we enforce at the database layer are +-- +-- * functions can only reference one data source, and must be deleted +-- if the data source is deleted +-- * data sources are tied to a project, and must be deleted if the +-- project is deleted +-- * rule types can reference one or more data source, and we want to +-- prevent deletion of a data source if there's a rule type +-- referencing it +-- +-- The first two are simple foreign keys, while the third one is +-- enforced by the lack of `ON DELETE ...` clause in the +-- `rule_type_data_sources` table. +-- +-- We also want to prevent the creation of a data source with a given +-- name if another data source with the same name exists in the +-- project hierarchy. I'm not sure how to express this as a database +-- constraint, nor I believe this would be efficient, so we decided to +-- let the application layer enforce that as we do with profiles. + +CREATE TABLE data_sources( + id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY, + name TEXT NOT NULL, + display_name TEXT NOT NULL, + project_id UUID NOT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +); + +CREATE UNIQUE INDEX data_sources_name_lower_idx ON data_sources (project_id, lower(name)); + +CREATE TABLE data_sources_functions( + id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY, + name TEXT NOT NULL, + type TEXT NOT NULL, + data_source_id UUID NOT NULL, + definition JSONB NOT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + FOREIGN KEY (data_source_id) REFERENCES data_sources(id) ON DELETE CASCADE +); + +CREATE UNIQUE INDEX data_sources_functions_name_lower_idx ON data_sources_functions (data_source_id, lower(name)); + +CREATE TABLE rule_type_data_sources( + rule_type_id UUID NOT NULL, + data_sources_id UUID NOT NULL, + FOREIGN KEY (rule_type_id) REFERENCES rule_type(id), + FOREIGN KEY (data_sources_id) REFERENCES data_sources(id), + UNIQUE (rule_type_id, data_sources_id) +); + +COMMIT; diff --git a/internal/db/models.go b/internal/db/models.go index e39a96b748..85de72ccc2 100644 --- a/internal/db/models.go +++ b/internal/db/models.go @@ -495,6 +495,25 @@ type Bundle struct { Name string `json:"name"` } +type DataSource struct { + ID uuid.UUID `json:"id"` + Name string `json:"name"` + DisplayName string `json:"display_name"` + ProjectID uuid.UUID `json:"project_id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type DataSourcesFunction struct { + ID uuid.UUID `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + DataSourceID uuid.UUID `json:"data_source_id"` + Definition json.RawMessage `json:"definition"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + type Entitlement struct { ID uuid.UUID `json:"id"` Feature string `json:"feature"` @@ -736,6 +755,11 @@ type RuleType struct { ShortFailureMessage string `json:"short_failure_message"` } +type RuleTypeDataSource struct { + RuleTypeID uuid.UUID `json:"rule_type_id"` + DataSourcesID uuid.UUID `json:"data_sources_id"` +} + type SessionStore struct { ID int32 `json:"id"` Provider string `json:"provider"`