diff --git a/backend/graphql/notification_query/src/mutations/create.rs b/backend/graphql/notification_query/src/mutations/create.rs index 8282562b..2e09b6a7 100644 --- a/backend/graphql/notification_query/src/mutations/create.rs +++ b/backend/graphql/notification_query/src/mutations/create.rs @@ -13,6 +13,7 @@ use super::{map_error, ModifyNotificationQueryResponse}; pub struct CreateNotificationQueryInput { pub id: String, pub name: String, + pub reference_name: String, } pub fn create_notification_query( @@ -41,7 +42,17 @@ pub fn create_notification_query( } impl From for CreateNotificationQuery { - fn from(CreateNotificationQueryInput { id, name }: CreateNotificationQueryInput) -> Self { - CreateNotificationQuery { id, name } + fn from( + CreateNotificationQueryInput { + id, + name, + reference_name, + }: CreateNotificationQueryInput, + ) -> Self { + CreateNotificationQuery { + id, + name, + reference_name, + } } } diff --git a/backend/graphql/notification_query/src/mutations/mod.rs b/backend/graphql/notification_query/src/mutations/mod.rs index 6566b43b..c6f3c942 100644 --- a/backend/graphql/notification_query/src/mutations/mod.rs +++ b/backend/graphql/notification_query/src/mutations/mod.rs @@ -33,6 +33,9 @@ pub fn map_error(error: ModifyNotificationQueryError) -> Result InternalError(s), ModifyNotificationQueryError::InvalidNotificationQueryName => BadUserInput(formatted_error), ModifyNotificationQueryError::BadUserInput(s) => BadUserInput(s), + ModifyNotificationQueryError::ReferenceNameAlreadyExists => { + BadUserInput("Reference name must be unique".to_string()) + } }; Err(graphql_error.extend()) diff --git a/backend/graphql/notification_query/src/mutations/update.rs b/backend/graphql/notification_query/src/mutations/update.rs index ea60410e..cc3dc790 100644 --- a/backend/graphql/notification_query/src/mutations/update.rs +++ b/backend/graphql/notification_query/src/mutations/update.rs @@ -13,6 +13,7 @@ use super::{map_error, ModifyNotificationQueryResponse}; pub struct UpdateNotificationQueryInput { pub id: String, pub name: Option, + pub reference_name: Option, pub description: Option, pub query: Option, pub required_parameters: Option>, @@ -47,6 +48,7 @@ impl From for UpdateNotificationQuery { UpdateNotificationQueryInput { id, name, + reference_name, description, query, required_parameters, @@ -55,6 +57,7 @@ impl From for UpdateNotificationQuery { UpdateNotificationQuery { id, name, + reference_name, description, query, required_parameters, diff --git a/backend/graphql/notification_query/src/types/inputs.rs b/backend/graphql/notification_query/src/types/inputs.rs index 0a3faa6e..4ac69b61 100644 --- a/backend/graphql/notification_query/src/types/inputs.rs +++ b/backend/graphql/notification_query/src/types/inputs.rs @@ -46,6 +46,7 @@ impl From for NotificationQueryFilter { NotificationQueryFilter { id: f.id.map(EqualFilter::from), name: f.name.map(StringFilter::from), + reference_name: None, search: f.search, } } diff --git a/backend/graphql/notification_query/src/types/notification_query.rs b/backend/graphql/notification_query/src/types/notification_query.rs index 57c60ed6..3ddcad24 100644 --- a/backend/graphql/notification_query/src/types/notification_query.rs +++ b/backend/graphql/notification_query/src/types/notification_query.rs @@ -29,6 +29,9 @@ impl NotificationQueryNode { pub async fn name(&self) -> &str { &self.row().name } + pub async fn reference_name(&self) -> &str { + &self.row().reference_name + } pub async fn description(&self) -> &str { &self.row().description } diff --git a/backend/repository/migrations/2023-10-09-042324_add_reference_name/down.sql b/backend/repository/migrations/2023-10-09-042324_add_reference_name/down.sql new file mode 100644 index 00000000..291a97c5 --- /dev/null +++ b/backend/repository/migrations/2023-10-09-042324_add_reference_name/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/backend/repository/migrations/2023-10-09-042324_add_reference_name/up.sql b/backend/repository/migrations/2023-10-09-042324_add_reference_name/up.sql new file mode 100644 index 00000000..4b5afdc5 --- /dev/null +++ b/backend/repository/migrations/2023-10-09-042324_add_reference_name/up.sql @@ -0,0 +1,4 @@ +ALTER TABLE notification_query ADD COLUMN reference_name TEXT NOT NULL default id; +UPDATE notification_query SET reference_name = id; + +CREATE UNIQUE INDEX ui_notification_query_reference_name ON notification_query(reference_name); \ No newline at end of file diff --git a/backend/repository/src/db_diesel/notification_query.rs b/backend/repository/src/db_diesel/notification_query.rs index d767336e..0f15e909 100644 --- a/backend/repository/src/db_diesel/notification_query.rs +++ b/backend/repository/src/db_diesel/notification_query.rs @@ -18,6 +18,7 @@ pub type NotificationQuery = NotificationQueryRow; pub struct NotificationQueryFilter { pub id: Option>, pub name: Option, + pub reference_name: Option, pub search: Option, } @@ -100,17 +101,29 @@ fn create_filtered_query(filter: Option) -> BoxedQuery let mut query = notification_query_dsl::notification_query.into_boxed(); if let Some(f) = filter { - let NotificationQueryFilter { id, name, search } = f; + let NotificationQueryFilter { + id, + name, + reference_name, + search, + } = f; apply_equal_filter!(query, id, notification_query_dsl::id); apply_string_filter!(query, name, notification_query_dsl::name); + apply_string_filter!( + query, + reference_name, + notification_query_dsl::reference_name + ); if let Some(search) = search { let search_term = format!("%{}%", search); query = query.filter( notification_query_dsl::name .like(search_term.clone()) - .or(notification_query_dsl::description.like(search_term)), + .or(notification_query_dsl::description.like(search_term.clone())) + .or(notification_query_dsl::reference_name.like(search_term.clone())) + .or(notification_query_dsl::query.like(search_term.clone())), ); } } @@ -131,6 +144,10 @@ impl NotificationQueryFilter { self.name = Some(filter); self } + pub fn reference_name(mut self, filter: StringFilter) -> Self { + self.reference_name = Some(filter); + self + } pub fn search(mut self, filter: String) -> Self { self.search = Some(filter); diff --git a/backend/repository/src/db_diesel/notification_query_row.rs b/backend/repository/src/db_diesel/notification_query_row.rs index ec83457f..d45367c6 100644 --- a/backend/repository/src/db_diesel/notification_query_row.rs +++ b/backend/repository/src/db_diesel/notification_query_row.rs @@ -9,6 +9,7 @@ table! { notification_query (id) { id -> Text, name -> Text, + reference_name -> Text, description -> Text, query -> Text, required_parameters -> Text, @@ -24,6 +25,7 @@ table! { pub struct NotificationQueryRow { pub id: String, pub name: String, + pub reference_name: String, pub description: String, pub query: String, pub required_parameters: String, diff --git a/backend/service/src/notification_query/create.rs b/backend/service/src/notification_query/create.rs index a8a0f67e..d3e4874f 100644 --- a/backend/service/src/notification_query/create.rs +++ b/backend/service/src/notification_query/create.rs @@ -3,6 +3,7 @@ use super::{ validate::{ check_list_name_doesnt_contain_special_characters, check_list_name_is_appropriate_length, check_notification_query_does_not_exist, check_notification_query_name_is_unique, + check_notification_query_reference_name_is_unique, }, ModifyNotificationQueryError, }; @@ -19,6 +20,7 @@ use repository::{ pub struct CreateNotificationQuery { pub id: String, pub name: String, + pub reference_name: String, } pub fn create_notification_query( @@ -73,15 +75,28 @@ pub fn validate( return Err(ModifyNotificationQueryError::NotificationQueryAlreadyExists); } + if !check_notification_query_reference_name_is_unique( + &new_notification_query.id, + Some(new_notification_query.reference_name.clone()), + connection, + )? { + return Err(ModifyNotificationQueryError::ReferenceNameAlreadyExists); + } + Ok(()) } pub fn generate( - CreateNotificationQuery { id, name }: CreateNotificationQuery, + CreateNotificationQuery { + id, + name, + reference_name, + }: CreateNotificationQuery, ) -> Result { Ok(NotificationQueryRow { id, name: name.trim().to_string(), + reference_name: reference_name.trim().to_string(), description: "".to_string(), query: "".to_string(), required_parameters: "[]".to_string(), diff --git a/backend/service/src/notification_query/mod.rs b/backend/service/src/notification_query/mod.rs index b5a2cfb8..7953d16a 100644 --- a/backend/service/src/notification_query/mod.rs +++ b/backend/service/src/notification_query/mod.rs @@ -69,9 +69,10 @@ pub trait NotificationQueryServiceTrait: Sync + Send { pub struct NotificationQueryService {} impl NotificationQueryServiceTrait for NotificationQueryService {} -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum ModifyNotificationQueryError { NotificationQueryAlreadyExists, + ReferenceNameAlreadyExists, ModifiedRecordNotFound, DatabaseError(RepositoryError), NotificationQueryDoesNotExist, @@ -79,38 +80,6 @@ pub enum ModifyNotificationQueryError { InternalError(String), BadUserInput(String), } - -// PartialEq is only needed for tests -impl PartialEq for ModifyNotificationQueryError { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - ( - ModifyNotificationQueryError::NotificationQueryAlreadyExists, - ModifyNotificationQueryError::NotificationQueryAlreadyExists, - ) => true, - - ( - ModifyNotificationQueryError::ModifiedRecordNotFound, - ModifyNotificationQueryError::ModifiedRecordNotFound, - ) => true, - ( - ModifyNotificationQueryError::DatabaseError(self_err), - ModifyNotificationQueryError::DatabaseError(other_err), - ) => self_err == other_err, - - ( - ModifyNotificationQueryError::NotificationQueryDoesNotExist, - ModifyNotificationQueryError::NotificationQueryDoesNotExist, - ) => true, - ( - ModifyNotificationQueryError::InvalidNotificationQueryName, - ModifyNotificationQueryError::InvalidNotificationQueryName, - ) => true, - _ => false, - } - } -} - impl From for ModifyNotificationQueryError { fn from(err: RepositoryError) -> Self { ModifyNotificationQueryError::DatabaseError(err) diff --git a/backend/service/src/notification_query/tests/query.rs b/backend/service/src/notification_query/tests/query.rs index 09a9fa29..061476ed 100644 --- a/backend/service/src/notification_query/tests/query.rs +++ b/backend/service/src/notification_query/tests/query.rs @@ -113,6 +113,7 @@ mod notification_query_query_test { id: id1.clone(), name: name1.clone(), description: description1.clone(), + reference_name: "reference_name1".to_string(), ..Default::default() }; repo.insert_one(¬ification_query).unwrap(); @@ -124,6 +125,7 @@ mod notification_query_query_test { id: id2.clone(), name: name2.clone(), description: description2.clone(), + reference_name: "reference_name2".to_string(), ..Default::default() }; repo.insert_one(¬ification_query).unwrap(); @@ -135,6 +137,7 @@ mod notification_query_query_test { id: id3.clone(), name: name3.clone(), description: description3.clone(), + reference_name: "reference_name3".to_string(), ..Default::default() }; repo.insert_one(¬ification_query).unwrap(); diff --git a/backend/service/src/notification_query/tests/update.rs b/backend/service/src/notification_query/tests/update.rs index d1584172..d44748e6 100644 --- a/backend/service/src/notification_query/tests/update.rs +++ b/backend/service/src/notification_query/tests/update.rs @@ -35,6 +35,7 @@ mod notification_query_update_tests { let notification_query = NotificationQueryRow { id: id1.clone(), name: name1.clone(), + reference_name: "reference_name1".to_string(), ..Default::default() }; repo.insert_one(¬ification_query).unwrap(); @@ -44,6 +45,7 @@ mod notification_query_update_tests { let notification_query = NotificationQueryRow { id: id2.clone(), name: name2.clone(), + reference_name: "reference_name2".to_string(), ..Default::default() }; repo.insert_one(¬ification_query).unwrap(); @@ -74,6 +76,19 @@ mod notification_query_update_tests { Err(ModifyNotificationQueryError::NotificationQueryAlreadyExists) ); + // Trying to update to a reference_name that already exists should fail (even with added whitespace) + assert_eq!( + service.update_notification_query( + &context, + UpdateNotificationQuery { + id: id1.clone(), + reference_name: Some("reference_name2 ".to_string()), + ..Default::default() + }, + ), + Err(ModifyNotificationQueryError::ReferenceNameAlreadyExists) + ); + // Trying to update to a name with illegal characters should fail assert_eq!( service.update_notification_query( diff --git a/backend/service/src/notification_query/update.rs b/backend/service/src/notification_query/update.rs index d17674b1..ae5b76d3 100644 --- a/backend/service/src/notification_query/update.rs +++ b/backend/service/src/notification_query/update.rs @@ -3,6 +3,7 @@ use super::{ validate::{ check_list_name_doesnt_contain_special_characters, check_list_name_is_appropriate_length, check_notification_query_exists, check_notification_query_name_is_unique, + check_notification_query_reference_name_is_unique, }, ModifyNotificationQueryError, }; @@ -17,6 +18,7 @@ use repository::{ pub struct UpdateNotificationQuery { pub id: String, pub name: Option, + pub reference_name: Option, pub description: Option, pub query: Option, pub required_parameters: Option>, @@ -78,6 +80,14 @@ pub fn validate( return Err(ModifyNotificationQueryError::NotificationQueryAlreadyExists); } + if !check_notification_query_reference_name_is_unique( + &new_notification_query.id, + new_notification_query.reference_name.clone(), + connection, + )? { + return Err(ModifyNotificationQueryError::ReferenceNameAlreadyExists); + } + Ok(notification_query_row) } @@ -85,6 +95,7 @@ pub fn generate( UpdateNotificationQuery { id: _id, //ID is already used for look up so we can assume it's the same name, + reference_name, description, query, required_parameters, @@ -95,6 +106,9 @@ pub fn generate( if let Some(name) = name { new_notification_query_row.name = name.trim().to_string(); } + if let Some(reference_name) = reference_name { + new_notification_query_row.reference_name = reference_name.trim().to_string(); + } if let Some(description) = description { new_notification_query_row.description = description; } diff --git a/backend/service/src/notification_query/validate.rs b/backend/service/src/notification_query/validate.rs index 7479fc37..2bb30017 100644 --- a/backend/service/src/notification_query/validate.rs +++ b/backend/service/src/notification_query/validate.rs @@ -46,6 +46,24 @@ pub fn check_notification_query_name_is_unique( } } +pub fn check_notification_query_reference_name_is_unique( + id: &str, + reference_name: Option, + connection: &StorageConnection, +) -> Result { + let Some(reference_name) = reference_name else { + return Ok(true); + }; + + let notification_queries = NotificationQueryRepository::new(connection).query_by_filter( + NotificationQueryFilter::new() + .reference_name(StringFilter::equal_to(&reference_name.trim().to_string())) + .id(EqualFilter::not_equal_to(id)), + )?; + + Ok(notification_queries.is_empty()) +} + // TODO: Refactor as part of https://github.com/openmsupply/notify/issues/140 pub fn check_list_name_is_appropriate_length(name: &str) -> Result { Ok(name.trim().len() >= 3 && name.len() <= 70) diff --git a/frontend/packages/common/src/intl/locales/en/common.json b/frontend/packages/common/src/intl/locales/en/common.json index 16f74188..43472c44 100644 --- a/frontend/packages/common/src/intl/locales/en/common.json +++ b/frontend/packages/common/src/intl/locales/en/common.json @@ -181,5 +181,6 @@ "warning.caps-lock": "Warning: Caps lock is on", "label.subject-template": "Subject template", "label.body-template": "Body template", - "label.parameters": "Parameters" + "label.parameters": "Parameters", + "label.reference-name": "Reference Name" } \ No newline at end of file diff --git a/frontend/packages/common/src/intl/locales/en/system.json b/frontend/packages/common/src/intl/locales/en/system.json index 6b829aec..461d406c 100644 --- a/frontend/packages/common/src/intl/locales/en/system.json +++ b/frontend/packages/common/src/intl/locales/en/system.json @@ -14,7 +14,9 @@ "error.parsing-notification-config": "Your notification configuration couldn't be loaded. Please contact support.", "error.unknown-error": "Unknown Error", "error.username-invalid-characters": "Username must not contain any special characters or spaces", - "error.username-too-short": "Username must be at least 3 character long", + "error.name-invalid-characters": "Name must not contain any special characters or spaces, and must start with a letter", + "error.username-too-short": "Username must be at least 3 characters long", + "error.name-too-short": "Name must be at least 3 characters long", "filename.users": "Users", "heading.import-tips": "Import Tips", "helper-text.recipient-list-name": "List name must be between 3-75 characters, and only contain letters, numbers, spaces or these special characters: .-_:/+@()", @@ -27,7 +29,7 @@ "label.coldchain-low-temp-alerts": "Send low temperature alerts", "label.coldchain-no-data-alerts": "Send 'no temperature data' alert after", "label.coldchain-reminder-alerts": "Send follow-up reminders until alert is resolved, every", - "label.coldchain-message-alerts-resolved": "Send confirmation message when alert is resolved", + "label.coldchain-message-alerts-resolved": "Send confirmation message when alert is resolved", "label.disable-user": "Disable User", "label.download-example": "Download an example file here", "label.edit-recipient": "Edit Recipient", @@ -83,7 +85,8 @@ "label.telegram-recipient-test-message": "Send Test Message", "helper-text.sql-query-parameters": "Parameters must be provided in json format for now. For example: {\"param1\": \"string\", \"param2\": \"string\"}", "helper-text.recipient-sql-query": "Your sql query must return the following columns: id, name, notification_type, to_address.", - "helper-text.sql-query": "Your sql query can contain parameters, which are created using double curly braces within the query. For example: SELECT * FROM my_table WHERE id = {{param1}} AND name = {{param2}}", + "helper-text.sql-query": "Your sql query can contain parameters, which are created using double curly braces within the query. For example: SELECT * FROM my_table WHERE id = '{{param1}}' AND name = '{{param2}}'", + "helper-text.reference_name": "Reference Name is used to refer to the results of the query in a template. For example: {{reference_name.0.name}} will get the name from the first row of the query results.", "label.test-sql-query": "Test SQL Query", "tooltip.manage-recipient-list": "Manage Recipient List", "message.no-parameters": "No parameters found" diff --git a/frontend/packages/common/src/types/schema.ts b/frontend/packages/common/src/types/schema.ts index e4a41d37..f3734673 100644 --- a/frontend/packages/common/src/types/schema.ts +++ b/frontend/packages/common/src/types/schema.ts @@ -68,6 +68,7 @@ export type CreateNotificationConfigInput = { export type CreateNotificationQueryInput = { id: Scalars['String']['input']; name: Scalars['String']['input']; + referenceName: Scalars['String']['input']; }; export type CreateRecipientInput = { @@ -628,6 +629,7 @@ export type NotificationQueryNode = { id: Scalars['String']['output']; name: Scalars['String']['output']; query: Scalars['String']['output']; + referenceName: Scalars['String']['output']; requiredParameters: Array; }; @@ -842,6 +844,7 @@ export type UpdateNotificationQueryInput = { id: Scalars['String']['input']; name?: InputMaybe; query?: InputMaybe; + referenceName?: InputMaybe; requiredParameters?: InputMaybe>; }; diff --git a/frontend/packages/common/src/utils/validation/index.ts b/frontend/packages/common/src/utils/validation/index.ts index 16dc158a..a4f68ed3 100644 --- a/frontend/packages/common/src/utils/validation/index.ts +++ b/frontend/packages/common/src/utils/validation/index.ts @@ -1 +1,2 @@ export * from './username'; +export * from './variablename'; diff --git a/frontend/packages/common/src/utils/validation/variablename.ts b/frontend/packages/common/src/utils/validation/variablename.ts new file mode 100644 index 00000000..7aa3686c --- /dev/null +++ b/frontend/packages/common/src/utils/validation/variablename.ts @@ -0,0 +1,23 @@ +import { LocaleKey, TypedTFunction } from '@common/intl'; + +const MIN_LENGTH = 3; +const invalidVariableName = new RegExp('[^0-9A-Za-z_]'); + +export const isValidVariableName = (name: string): boolean => { + return ( + (name.length >= MIN_LENGTH && !invalidVariableName.test(name)) ?? false + ); +}; + +export const validateVariableNameHelperText = ( + name: string, + t: TypedTFunction // from useTranslation() +): string | undefined => { + if (name.length < MIN_LENGTH) { + return t('error.name-too-short', { ns: 'system' }); + } + if (invalidVariableName.test(name)) { + return t('error.name-invalid-characters', { ns: 'system' }); + } + return undefined; +}; diff --git a/frontend/packages/system/src/Queries/NotificationQueries/CreateNotificationQueryModal.tsx b/frontend/packages/system/src/Queries/NotificationQueries/CreateNotificationQueryModal.tsx index 2fd20828..0d499847 100644 --- a/frontend/packages/system/src/Queries/NotificationQueries/CreateNotificationQueryModal.tsx +++ b/frontend/packages/system/src/Queries/NotificationQueries/CreateNotificationQueryModal.tsx @@ -1,4 +1,6 @@ import React, { useState } from 'react'; +import Alert from '@mui/material/Alert'; +import AlertTitle from '@mui/material/AlertTitle'; import { useDialog, DialogButton, @@ -10,6 +12,9 @@ import { BasicTextInput, Box, RouteBuilder, + isValidVariableName, + validateVariableNameHelperText, + Tooltip, } from '@notify-frontend/common'; import { useCreateNotificationQuery } from '../api'; import { AppRoute } from 'packages/config/src'; @@ -29,6 +34,9 @@ export const CreateNotificationQueryModal = ({ const { mutateAsync: create, isLoading } = useCreateNotificationQuery(); const [name, setName] = useState(''); + const [referenceName, setReferenceName] = useState(''); + const [referenceNameEdited, setReferenceNameEdited] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); const { Modal } = useDialog({ isOpen, onClose }); @@ -45,12 +53,18 @@ export const CreateNotificationQueryModal = ({ input: { id: id, name: name, + referenceName: referenceName, }, - }).then(() => { - navigate( - RouteBuilder.create(AppRoute.Queries).addPart(id).build() - ); - }); + }).then( + () => { + navigate( + RouteBuilder.create(AppRoute.Queries).addPart(id).build() + ); + }, + e => { + setErrorMessage(e.message); + } + ); }} isLoading={isLoading} startIcon={} @@ -67,10 +81,50 @@ export const CreateNotificationQueryModal = ({ fullWidth value={name} required - onChange={e => setName(e.target.value)} + onChange={e => { + setName(e.target.value); + if (!referenceNameEdited) { + const referenceName = e.target.value + .replace(/[^a-zA-Z0-9]/g, '_') + .toLocaleLowerCase(); + setReferenceName(referenceName); + } + }} label={t('label.name')} InputLabelProps={{ shrink: true }} /> + + + {validateVariableNameHelperText(referenceName, t) ?? + t('helper-text.reference_name')} + + + } + required + onChange={e => { + setReferenceName(e.target.value); + setReferenceNameEdited(true); + }} + label={t('label.reference-name')} + InputLabelProps={{ shrink: true }} + /> + {errorMessage ? ( + { + setErrorMessage(''); + }} + > + {t('error')} + {errorMessage} + + ) : null} ); diff --git a/frontend/packages/system/src/Queries/NotificationQueries/ListView.tsx b/frontend/packages/system/src/Queries/NotificationQueries/ListView.tsx index 79c3cab5..7276139b 100644 --- a/frontend/packages/system/src/Queries/NotificationQueries/ListView.tsx +++ b/frontend/packages/system/src/Queries/NotificationQueries/ListView.tsx @@ -28,6 +28,7 @@ export const ListView = () => { const columns = useColumns( [ { key: 'name', label: 'label.name' }, + { key: 'referenceName', label: 'label.reference-name' }, { key: 'description', label: 'label.description', diff --git a/frontend/packages/system/src/Queries/NotificationQueries/QueryEditor.tsx b/frontend/packages/system/src/Queries/NotificationQueries/QueryEditor.tsx index c2f6c217..0ab7fe65 100644 --- a/frontend/packages/system/src/Queries/NotificationQueries/QueryEditor.tsx +++ b/frontend/packages/system/src/Queries/NotificationQueries/QueryEditor.tsx @@ -11,12 +11,15 @@ import { LoadingButton, SaveIcon, TeraUtils, + Tooltip, Typography, ZapIcon, + isValidVariableName, useDetailPanel, useNotification, useToggle, useTranslation, + validateVariableNameHelperText, } from '@notify-frontend/common'; import { DraftNotificationQuery } from './types'; import { useUpdateNotificationQuery } from '../api'; @@ -28,6 +31,7 @@ const createNotificationQuery = ( ): DraftNotificationQuery => ({ id: FnUtils.generateUUID(), name: '', + referenceName: '', description: '', query: '', requiredParameters: [], @@ -92,8 +96,16 @@ export const QueryEditor = ({ useUpdateNotificationQuery(); const onSave = async (draft: DraftNotificationQuery) => { - const { id, name, description, query, requiredParameters } = draft; - const input = { id, name, description, query, requiredParameters }; + const { id, name, referenceName, description, query, requiredParameters } = + draft; + const input = { + id, + name, + referenceName, + description, + query, + requiredParameters, + }; await update({ input }); editNameToggleOff(); @@ -144,19 +156,39 @@ export const QueryEditor = ({ /> {isEditingName ? ( - onUpdate({ name: e.target.value })} - label={t('label.name')} - InputLabelProps={{ shrink: true }} - /> + <> + onUpdate({ name: e.target.value })} + label={t('label.name')} + InputLabelProps={{ shrink: true }} + /> + + + {validateVariableNameHelperText(draft.referenceName, t) ?? + t('helper-text.reference_name')} + + + } + onChange={e => onUpdate({ referenceName: e.target.value })} + label={t('label.reference-name')} + InputLabelProps={{ shrink: true }} + /> + ) : ( - {draft?.name} + {draft?.name} ({draft?.referenceName}) } diff --git a/frontend/packages/system/src/Queries/NotificationQueries/types.ts b/frontend/packages/system/src/Queries/NotificationQueries/types.ts index e52e8931..467bd24b 100644 --- a/frontend/packages/system/src/Queries/NotificationQueries/types.ts +++ b/frontend/packages/system/src/Queries/NotificationQueries/types.ts @@ -1,6 +1,7 @@ export interface DraftNotificationQuery { id: string; name: string; + referenceName: string; description: string; query: string; requiredParameters: string[]; diff --git a/frontend/packages/system/src/Queries/api/hooks/useDeleteNotificationQuery.ts b/frontend/packages/system/src/Queries/api/hooks/useDeleteNotificationQuery.ts index 675025b0..da4010cc 100644 --- a/frontend/packages/system/src/Queries/api/hooks/useDeleteNotificationQuery.ts +++ b/frontend/packages/system/src/Queries/api/hooks/useDeleteNotificationQuery.ts @@ -9,7 +9,7 @@ export const useDeleteNotificationQuery = () => { const queryClient = useQueryClient(); const result = useMutation(async (id: string) => - sdk.deleteNotificationQuery({ sqlRecipientListId: id }) + sdk.deleteNotificationQuery({ id }) ); return { ...result, diff --git a/frontend/packages/system/src/Queries/api/operations.generated.ts b/frontend/packages/system/src/Queries/api/operations.generated.ts index 2da92f69..21b10f1b 100644 --- a/frontend/packages/system/src/Queries/api/operations.generated.ts +++ b/frontend/packages/system/src/Queries/api/operations.generated.ts @@ -3,7 +3,7 @@ import * as Types from '@notify-frontend/common'; import { GraphQLClient } from 'graphql-request'; import * as Dom from 'graphql-request/dist/types.dom'; import gql from 'graphql-tag'; -export type NotificationQueryRowFragment = { __typename: 'NotificationQueryNode', id: string, name: string, description: string, query: string, requiredParameters: Array }; +export type NotificationQueryRowFragment = { __typename: 'NotificationQueryNode', id: string, name: string, referenceName: string, description: string, query: string, requiredParameters: Array }; export type NotificationQueriesQueryVariables = Types.Exact<{ filter?: Types.InputMaybe; @@ -12,24 +12,24 @@ export type NotificationQueriesQueryVariables = Types.Exact<{ }>; -export type NotificationQueriesQuery = { __typename: 'FullQuery', notificationQueries: { __typename: 'NotificationQueryConnector', totalCount: number, nodes: Array<{ __typename: 'NotificationQueryNode', id: string, name: string, description: string, query: string, requiredParameters: Array }> } }; +export type NotificationQueriesQuery = { __typename: 'FullQuery', notificationQueries: { __typename: 'NotificationQueryConnector', totalCount: number, nodes: Array<{ __typename: 'NotificationQueryNode', id: string, name: string, referenceName: string, description: string, query: string, requiredParameters: Array }> } }; export type CreateNotificationQueryMutationVariables = Types.Exact<{ input: Types.CreateNotificationQueryInput; }>; -export type CreateNotificationQueryMutation = { __typename: 'FullMutation', createNotificationQuery: { __typename: 'NotificationQueryNode', id: string, name: string, description: string, query: string, requiredParameters: Array } }; +export type CreateNotificationQueryMutation = { __typename: 'FullMutation', createNotificationQuery: { __typename: 'NotificationQueryNode', id: string, name: string, referenceName: string, description: string, query: string, requiredParameters: Array } }; export type UpdateNotificationQueryMutationVariables = Types.Exact<{ input: Types.UpdateNotificationQueryInput; }>; -export type UpdateNotificationQueryMutation = { __typename: 'FullMutation', updateNotificationQuery: { __typename: 'NotificationQueryNode', id: string, name: string, description: string, query: string, requiredParameters: Array } }; +export type UpdateNotificationQueryMutation = { __typename: 'FullMutation', updateNotificationQuery: { __typename: 'NotificationQueryNode', id: string, name: string, referenceName: string, description: string, query: string, requiredParameters: Array } }; export type DeleteNotificationQueryMutationVariables = Types.Exact<{ - sqlRecipientListId: Types.Scalars['String']['input']; + id: Types.Scalars['String']['input']; }>; @@ -47,6 +47,7 @@ export const NotificationQueryRowFragmentDoc = gql` fragment NotificationQueryRow on NotificationQueryNode { id name + referenceName description query requiredParameters @@ -83,8 +84,8 @@ export const UpdateNotificationQueryDocument = gql` } ${NotificationQueryRowFragmentDoc}`; export const DeleteNotificationQueryDocument = gql` - mutation deleteNotificationQuery($sqlRecipientListId: String!) { - deleteNotificationQuery(sqlRecipientListId: $sqlRecipientListId) { + mutation deleteNotificationQuery($id: String!) { + deleteNotificationQuery(id: $id) { ... on DeleteResponse { id } diff --git a/frontend/packages/system/src/Queries/api/operations.graphql b/frontend/packages/system/src/Queries/api/operations.graphql index 9c35afed..37a89683 100644 --- a/frontend/packages/system/src/Queries/api/operations.graphql +++ b/frontend/packages/system/src/Queries/api/operations.graphql @@ -1,6 +1,7 @@ fragment NotificationQueryRow on NotificationQueryNode { id name + referenceName description query requiredParameters @@ -37,8 +38,8 @@ mutation updateNotificationQuery($input: UpdateNotificationQueryInput!) { } } -mutation deleteNotificationQuery($sqlRecipientListId: String!) { - deleteNotificationQuery(sqlRecipientListId: $sqlRecipientListId) { +mutation deleteNotificationQuery($id: String!) { + deleteNotificationQuery(id: $id) { ... on DeleteResponse { id }