Skip to content

Commit

Permalink
refactor: move rsd remote name functionality to admin section instead…
Browse files Browse the repository at this point in the history
… using env variable
  • Loading branch information
dmijatovic committed Jan 17, 2025
1 parent 7b75b24 commit 19c8829
Show file tree
Hide file tree
Showing 17 changed files with 863 additions and 86 deletions.
5 changes: 0 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@
# automatically name the containers.
COMPOSE_PROJECT_NAME="rsd"

# RSD REMOTE NAME
# identify this instance as remote by this name
# it is used as source label in RPC aggregated_software_overview
RSD_REMOTE_NAME=Local RSD

# ---- PUBLIC ENV VARIABLES -------------

# postgresql
Expand Down
28 changes: 13 additions & 15 deletions database/025-rsd-info.sql
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
-- SPDX-FileCopyrightText: 2024 Dusan Mijatovic (Netherlands eScience Center)
-- SPDX-FileCopyrightText: 2024 Netherlands eScience Center
-- SPDX-FileCopyrightText: 2024 - 2025 Dusan Mijatovic (Netherlands eScience Center)
-- SPDX-FileCopyrightText: 2024 - 2025 Netherlands eScience Center
--
-- SPDX-License-Identifier: Apache-2.0

-- RSD info table
-- used to obtain RSD name to use for remotes
-- it should provide basic info about rsd instance (eg. endpoints)
-- it should provide basic info about rsd instance
-- manually insert remote_name property
CREATE TABLE rsd_info (
key VARCHAR(100) PRIMARY KEY,
value VARCHAR(250),
value VARCHAR(250) NOT NULL,
public BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
Expand Down Expand Up @@ -37,22 +39,18 @@ $$;
CREATE TRIGGER sanitise_update_rsd_info BEFORE UPDATE ON
rsd_info FOR EACH ROW EXECUTE PROCEDURE sanitise_update_rsd_info();

-- Insert remote_name key extracted from env variable RSD_REMOTE_NAME, default value is 'Local RSD'
-- AND basic endpoints info
INSERT INTO rsd_info VALUES
('remote_name', COALESCE(current_setting('rsd.remote_name',true),'Local RSD')),
('postgrest_api','/api/v1'),
('images_api','/images'),
('swagger','/swagger'),
('codemeta','/metadata/codemeta')
;
-- EXAMPLE OF PUBLIC PROPERTIES TO INSERT IN THE rsd_info table
-- REMOTE NAME IS used to identify your instance to other RSD instances
INSERT INTO rsd_info VALUES ('remote_name','Not defined',TRUE);

-- RLS
-- rsd info table
ALTER TABLE rsd_info ENABLE ROW LEVEL SECURITY;
-- anyone can read (SELECT)

-- anyone can read (SELECT) public keys
CREATE POLICY anyone_can_read ON rsd_info FOR SELECT TO rsd_web_anon, rsd_user
USING (TRUE);
USING (public = TRUE);

-- rsd_admin has all rights
CREATE POLICY admin_all_rights ON rsd_info TO rsd_admin
USING (TRUE)
Expand Down
5 changes: 1 addition & 4 deletions deployment/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# SPDX-FileCopyrightText: 2022 Dusan Mijatovic (dv4all)
# SPDX-FileCopyrightText: 2022 Helmholtz Centre for Environmental Research (UFZ)
# SPDX-FileCopyrightText: 2022 dv4all
# SPDX-FileCopyrightText: 2023 - 2024 Dusan Mijatovic (Netherlands eScience Center)
# SPDX-FileCopyrightText: 2023 - 2025 Dusan Mijatovic (Netherlands eScience Center)
#
# SPDX-License-Identifier: Apache-2.0

Expand All @@ -24,9 +24,6 @@ services:
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_AUTHENTICATOR_PASSWORD
- RSD_REMOTE_NAME
# insert remote_name into postgres settings, this value is used in initial SQL scripts
command: postgres -D /var/lib/postgresql/data -c 'rsd.remote_name=${RSD_REMOTE_NAME}'
volumes:
# persist data in named docker volume
# to remove use: docker compose down --volumes
Expand Down
5 changes: 1 addition & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# SPDX-FileCopyrightText: 2022 - 2024 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
# SPDX-FileCopyrightText: 2022 Helmholtz Centre for Environmental Research (UFZ)
# SPDX-FileCopyrightText: 2022 Matthias Rüster (GFZ) <[email protected]>
# SPDX-FileCopyrightText: 2023 - 2024 Dusan Mijatovic (Netherlands eScience Center)
# SPDX-FileCopyrightText: 2023 - 2025 Dusan Mijatovic (Netherlands eScience Center)
# SPDX-FileCopyrightText: 2023 Dusan Mijatovic (dv4all) (dv4all)
#
# SPDX-License-Identifier: Apache-2.0
Expand All @@ -26,9 +26,6 @@ services:
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_AUTHENTICATOR_PASSWORD
- RSD_REMOTE_NAME
# insert remote_name into postgres settings, this value is used in initial SQL scripts
command: postgres -D /var/lib/postgresql/data -c 'rsd.remote_name=${RSD_REMOTE_NAME}'
volumes:
# persist data in named docker volume
# to remove use: docker compose down --volumes
Expand Down
11 changes: 9 additions & 2 deletions frontend/components/admin/AdminNav.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: 2023 - 2024 Dusan Mijatovic (Netherlands eScience Center)
// SPDX-FileCopyrightText: 2023 - 2024 Netherlands eScience Center
// SPDX-FileCopyrightText: 2023 - 2025 Dusan Mijatovic (Netherlands eScience Center)
// SPDX-FileCopyrightText: 2023 - 2025 Netherlands eScience Center
// SPDX-FileCopyrightText: 2023 Christian Meeßen (GFZ) <[email protected]>
// SPDX-FileCopyrightText: 2023 Dusan Mijatovic (dv4all)
// SPDX-FileCopyrightText: 2023 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences
Expand Down Expand Up @@ -31,6 +31,7 @@ import CategoryIcon from '@mui/icons-material/Category'
import TerminalIcon from '@mui/icons-material/Terminal'
import ListAltIcon from '@mui/icons-material/ListAlt'
import HubIcon from '@mui/icons-material/Hub'
import CookieIcon from '@mui/icons-material/Cookie'

import {editMenuItemButtonSx} from '~/config/menuItems'

Expand Down Expand Up @@ -107,6 +108,12 @@ export const adminPages = {
icon: <ReceiptLongIcon />,
path: '/admin/mentions',
},
rsd_info:{
title: 'Rsd info',
subtitle: '',
icon: <CookieIcon />,
path: '/admin/rsd-info',
},
remote_rsd: {
title: 'Remotes',
subtitle: '',
Expand Down
4 changes: 3 additions & 1 deletion frontend/components/admin/pages/add/addConfig.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// SPDX-FileCopyrightText: 2022 - 2023 Dusan Mijatovic (dv4all)
// SPDX-FileCopyrightText: 2022 - 2023 dv4all
// SPDX-FileCopyrightText: 2025 Dusan Mijatovic (Netherlands eScience Center)
// SPDX-FileCopyrightText: 2025 Netherlands eScience Center
//
// SPDX-License-Identifier: Apache-2.0

Expand All @@ -26,7 +28,7 @@ export const addConfig = {
maxLength: {value: 200, message: 'Maximum length is 200'},
pattern: {
value: /^[a-z0-9]+(-[a-z0-9]+)*$/,
message: 'Restricted input violiation. Use letters, numbers and dashes "-" only between other input.'
message: 'Restricted input violation. Use letters, numbers and dashes "-" only between other input.'
}
}
}
Expand Down
167 changes: 167 additions & 0 deletions frontend/components/admin/rsd-info/AddRsdInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// SPDX-FileCopyrightText: 2025 Dusan Mijatovic (Netherlands eScience Center)
// SPDX-FileCopyrightText: 2025 Netherlands eScience Center
//
// SPDX-License-Identifier: Apache-2.0

import {useState} from 'react'
import Button from '@mui/material/Button'
import AddIcon from '@mui/icons-material/Add'
import useMediaQuery from '@mui/material/useMediaQuery'
import Dialog from '@mui/material/Dialog'
import DialogTitle from '@mui/material/DialogTitle'
import DialogContent from '@mui/material/DialogContent'
import DialogActions from '@mui/material/DialogActions'

import {useForm} from 'react-hook-form'

import TextFieldWithCounter from '~/components/form/TextFieldWithCounter'
import ControlledSwitch from '~/components/form/ControlledSwitch'
import SubmitButtonWithListener from '~/components/form/SubmitButtonWithListener'
import {RsdInfo} from './apiRsdInfo'
import {rsdInfoForm} from './config'

type RsdInfoModalProps=Readonly<{
onCancel: () => void,
onSubmit: (item: RsdInfo) => void
}>

const formId='add-remote-rsd-form'

function RsdInfoModal({onCancel,onSubmit}:RsdInfoModalProps){
const smallScreen = useMediaQuery('(max-width:600px)')
const {formState:{errors,isValid,isDirty}, control, watch, register, handleSubmit} = useForm<RsdInfo>({
mode: 'onChange',

})

// watch for data change in the form
const [key,value] = watch(['key','value'])

function handleCancel(e:any,reason: 'backdropClick' | 'escapeKeyDown') {
// close only on escape, not if user clicks outside of the modal
if (reason==='escapeKeyDown') onCancel()
}

return (
<Dialog
// use fullScreen modal for small screens (< 600px)
fullScreen={smallScreen}
open={true}
onClose={handleCancel}
>
<DialogTitle sx={{
fontSize: '1.5rem',
borderBottom: '1px solid',
borderColor: 'divider',
color: 'primary.main',
fontWeight: 500
}}>
{rsdInfoForm.modalTitle}
</DialogTitle>
<form
id={formId}
onSubmit={handleSubmit(onSubmit)}
className="w-full">

<DialogContent sx={{
width: ['100%', '37rem'],
padding: '2rem 1.5rem 2.5rem'
}}>
<TextFieldWithCounter
options={{
autofocus:true,
error: errors.key?.message !== undefined,
label: rsdInfoForm.key.label,
helperTextMessage: errors?.key?.message ?? rsdInfoForm.key.help,
helperTextCnt: `${key?.length || 0}/${rsdInfoForm.key.validation.maxLength.value}`,
variant:'outlined',
// endAdornment: validating ? <CircularProgress /> : undefined
}}
register={register('key', {
...rsdInfoForm.key.validation
})}
/>

<div className="py-4" />

<TextFieldWithCounter
options={{
error: errors.value?.message !== undefined,
label: rsdInfoForm.value.label,
helperTextMessage: errors?.value?.message ?? rsdInfoForm.value.help,
helperTextCnt: `${value?.length || 0}/${rsdInfoForm.value.validation.maxLength.value}`,
variant:'outlined',
// endAdornment: validating ? <CircularProgress /> : undefined
}}
register={register('value', {
...rsdInfoForm.value.validation
})}
/>

<div className="grid grid-cols-2 gap-20 items-start pt-8">
<ControlledSwitch
label="Public"
name="public"
control={control}
defaultValue={true}
/>
</div>

</DialogContent>
<DialogActions sx={{
padding: '1rem 1.5rem',
borderTop: '1px solid',
borderColor: 'divider'
}}>
<Button
onClick={onCancel}
color="secondary"
sx={{marginRight:'2rem'}}
>
Cancel
</Button>
<SubmitButtonWithListener
formId={formId}
disabled={isSubmitDisabled()}
/>
</DialogActions>
</form>
</Dialog>
)

function isSubmitDisabled(){
if (isValid===false) return true
// we need additional check on errors object
// due to custom validation of domain
if (Object.keys(errors).length > 0) return true
if (isDirty===false) return true
return false
}
}


export default function AddRsdInfo({onAdd}:Readonly<{onAdd:(data:RsdInfo)=>void}>) {
const [modal,setModal] = useState(false)

return (
<>
<Button
variant='contained'
startIcon={<AddIcon/> }
onClick={()=>setModal(true)}
>
Add
</Button>
{
modal ? <RsdInfoModal
onCancel={()=>setModal(false)}
onSubmit={(data)=>{
setModal(false)
onAdd(data)
}}
/>
: null
}
</>
)
}
Loading

0 comments on commit 19c8829

Please sign in to comment.