Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1294 categories for organisations #1315

Merged
merged 9 commits into from
Oct 24, 2024
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
-- SPDX-FileCopyrightText: 2023 - 2024 Felix Mühlbauer (GFZ) <[email protected]>
-- SPDX-FileCopyrightText: 2023 - 2024 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
-- SPDX-FileCopyrightText: 2024 Christian Meeßen (GFZ) <[email protected]>
-- SPDX-FileCopyrightText: 2024 Dusan Mijatovic (Netherlands eScience Center)
--
-- SPDX-License-Identifier: Apache-2.0

Expand Down Expand Up @@ -88,29 +89,24 @@ CREATE TABLE category (
id UUID PRIMARY KEY,
parent UUID REFERENCES category DEFAULT NULL,
community UUID REFERENCES community(id) DEFAULT NULL,
organisation UUID REFERENCES organisation(id) DEFAULT NULL,
allow_software BOOLEAN NOT NULL DEFAULT FALSE,
allow_projects BOOLEAN NOT NULL DEFAULT FALSE,
short_name VARCHAR(100) NOT NULL,
name VARCHAR(250) NOT NULL,
properties JSONB NOT NULL DEFAULT '{}'::jsonb,
provenance_iri VARCHAR(250) DEFAULT NULL, -- e.g. https://www.w3.org/TR/skos-reference/#mapping

CONSTRAINT unique_short_name UNIQUE NULLS NOT DISTINCT (parent, short_name, community),
CONSTRAINT unique_name UNIQUE NULLS NOT DISTINCT (parent, name, community),
CONSTRAINT only_one_entity CHECK (community IS NULL OR organisation IS NULL),
CONSTRAINT unique_short_name UNIQUE NULLS NOT DISTINCT (parent, short_name, community, organisation),
CONSTRAINT unique_name UNIQUE NULLS NOT DISTINCT (parent, name, community, organisation),
CONSTRAINT invalid_value_for_properties CHECK (properties - '{icon, is_highlight, description, subtitle, tree_level_labels}'::text[] = '{}'::jsonb),
CONSTRAINT highlight_must_be_top_level_category CHECK (NOT ((properties->>'is_highlight')::boolean AND parent IS NOT NULL))
);

CREATE INDEX category_parent_idx ON category(parent);
CREATE INDEX category_community_idx ON category(community);


CREATE TABLE category_for_software (
software_id UUID REFERENCES software (id),
category_id UUID REFERENCES category (id),
PRIMARY KEY (software_id, category_id)
);

CREATE INDEX category_for_software_category_id_idx ON category_for_software(category_id);

CREATE INDEX category_organisation_idx ON category(organisation);

-- sanitize categories

Expand Down Expand Up @@ -224,6 +220,19 @@ $$
$$;


-- TABLE FOR software categories
-- includes organisation, community and general categories
-- Note! to filter specific categories of an community or organisation use join with community table

CREATE TABLE category_for_software (
software_id UUID REFERENCES software (id),
category_id UUID REFERENCES category (id),
PRIMARY KEY (software_id, category_id)
);

CREATE INDEX category_for_software_category_id_idx ON category_for_software(category_id);

-- RPC for software page to show all software categories
CREATE FUNCTION category_paths_by_software_expanded(software_id UUID)
RETURNS JSON
LANGUAGE SQL STABLE AS
Expand All @@ -238,3 +247,45 @@ $$
ELSE '[]'::json
END AS result
$$;


-- TABLE FOR project categories
-- currently used only for organisation categories
CREATE TABLE category_for_project (
project_id UUID REFERENCES project (id),
category_id UUID REFERENCES category (id),
PRIMARY KEY (project_id, category_id)
);

CREATE INDEX category_for_project_category_id_idx ON category_for_project(category_id);

-- RPC for project page to show all project categories
CREATE FUNCTION category_paths_by_project_expanded(project_id UUID)
RETURNS JSON
LANGUAGE SQL STABLE AS
$$
WITH
cat_ids AS
(SELECT
category_id
FROM
category_for_project
WHERE
category_for_project.project_id = category_paths_by_project_expanded.project_id
),
paths AS
(
SELECT
category_path_expanded(category_id) AS path
FROM cat_ids
)
SELECT
CASE
WHEN EXISTS(
SELECT 1 FROM cat_ids
) THEN (
SELECT json_agg(path) FROM paths
)
ELSE '[]'::json
END AS result
$$;
39 changes: 37 additions & 2 deletions database/109-category-functions.sql
Original file line number Diff line number Diff line change
@@ -1,13 +1,48 @@
-- SPDX-FileCopyrightText: 2024 Dusan Mijatovic (Netherlands eScience Center)
-- SPDX-FileCopyrightText: 2024 Ewan Cahen (Netherlands eScience Center) <[email protected]>
-- SPDX-FileCopyrightText: 2024 Netherlands eScience Center
--
-- SPDX-License-Identifier: Apache-2.0

-- DELETE organisation categories for specific community
CREATE FUNCTION delete_community_categories_from_software(software_id UUID, community_id UUID)
RETURNS VOID
LANGUAGE sql AS
$$
DELETE FROM category_for_software
USING category
WHERE category_for_software.category_id = category.id AND category_for_software.software_id = software_id AND category.community = community_id;
USING
category
WHERE
category_for_software.category_id = category.id AND
category_for_software.software_id = delete_community_categories_from_software.software_id AND
category.community = delete_community_categories_from_software.community_id;
$$;


-- DELETE organisation categories for specific software
CREATE FUNCTION delete_organisation_categories_from_software(software_id UUID, organisation_id UUID)
RETURNS VOID
LANGUAGE sql AS
$$
DELETE FROM category_for_software
USING
category
WHERE
category_for_software.category_id = category.id AND
category_for_software.software_id = delete_organisation_categories_from_software.software_id AND
category.organisation = delete_organisation_categories_from_software.organisation_id;
$$;

-- DELETE organisation categories for specific project
CREATE FUNCTION delete_organisation_categories_from_project(project_id UUID, organisation_id UUID)
RETURNS VOID
LANGUAGE sql AS
$$
DELETE FROM category_for_project
USING
category
WHERE
category_for_project.category_id = category.id AND
category_for_project.project_id = delete_organisation_categories_from_project.project_id AND
category.organisation = delete_organisation_categories_from_project.organisation_id;
$$;
70 changes: 66 additions & 4 deletions frontend/auth/permissions/isMaintainerOfOrganisation.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// SPDX-FileCopyrightText: 2022 - 2023 Dusan Mijatovic (dv4all)
// SPDX-FileCopyrightText: 2022 - 2023 dv4all
// SPDX-FileCopyrightText: 2023 Dusan Mijatovic (Netherlands eScience Center)
// SPDX-FileCopyrightText: 2023 Netherlands eScience Center
// SPDX-FileCopyrightText: 2023 - 2024 Dusan Mijatovic (Netherlands eScience Center)
// SPDX-FileCopyrightText: 2023 - 2024 Netherlands eScience Center
//
// SPDX-License-Identifier: Apache-2.0

import {createJsonHeaders, getBaseUrl} from '../../utils/fetchHelpers'
import logger from '../../utils/logger'
import {OrganisationsOfProject} from '~/types/Project'
import {EditOrganisation, OrganisationsForSoftware} from '~/types/Organisation'
import {createJsonHeaders, getBaseUrl} from '~/utils/fetchHelpers'
import logger from '~/utils/logger'
import {RsdRole} from '../index'

type IsOrganisationMaintainerProps = {
Expand Down Expand Up @@ -92,5 +94,65 @@ export async function getMaintainerOrganisations({token}:
}
}

type CanEditOrganisationsProps={
account?: string
token?: string
organisations: OrganisationsOfProject[]| OrganisationsForSoftware[]
}

export async function canEditOrganisations({organisations,account,token}:CanEditOrganisationsProps){
try{
// collect isMaintainerRequests
const promises:Promise<boolean>[] = []
// prepare organisation list
const orgList = organisations.map((item, pos) => {
// save isMaintainer request
promises.push(isMaintainerOfOrganisation({
organisation: item.id,
account,
token
}))
// extract only needed props
const org: EditOrganisation = {
...item,
// additional props for edit type
position: pos + 1,
logo_b64: null,
logo_mime_type: null,
source: 'RSD' as 'RSD',
status: item.status,
// false by default
canEdit: false
}
return org
})
// run all isMaintainer requests in parallel
const isMaintainer = await Promise.all(promises)
const canEditOrganisations = orgList.map((item, pos) => {
// update canEdit based on isMaintainer requests
if (isMaintainer[pos]) item.canEdit = isMaintainer[pos]
return item
})
return canEditOrganisations
}catch(e:any){
logger(`canEditOrganisations: ${e.message}`, 'error')
// on error all items set to false
return organisations.map((item, pos) => {
// extract only needed props
const org: EditOrganisation = {
...item,
// additional props for edit type
position: pos + 1,
logo_b64: null,
logo_mime_type: null,
source: 'RSD' as 'RSD',
status: item.status,
// false by default
canEdit: false
}
return org
})
}
}

export default isMaintainerOfOrganisation
1 change: 1 addition & 0 deletions frontend/components/admin/categories/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default function AdminCategories() {
<CategoryEditTree
roots={roots}
community={null}
organisation={null}
onMutation={onMutation}
/>
}
Expand Down
Loading