Skip to content

Commit

Permalink
config: add Service.RequiredLabels (#3531)
Browse files Browse the repository at this point in the history
* add config entry and start updating form

* handle req. labels in edit

* don't break editing flows

* validate labels on config update

* show label value errors

* update label

---------

Co-authored-by: Nathaniel Cook <[email protected]>
  • Loading branch information
mastercactapus and Forfold authored Jan 15, 2024
1 parent 0161051 commit 5984cef
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 10 deletions.
11 changes: 11 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ type Config struct {
DisableCalendarSubscriptions bool `public:"true" info:"If set, disables all active calendar subscriptions as well as the ability to create new calendar subscriptions."`
}

Services struct {
RequiredLabels []string `public:"true" info:"List of label names to require new services to define."`
}

Maintenance struct {
AlertCleanupDays int `public:"true" info:"Closed alerts will be deleted after this many days (0 means disable cleanup)."`
AlertAutoCloseDays int `public:"true" info:"Unacknowledged alerts will automatically be closed after this many days of inactivity. (0 means disable auto-close)."`
Expand Down Expand Up @@ -447,6 +451,12 @@ func (cfg Config) Validate() error {
}
return validate.OAuthScope(fname, val, "openid")
}
validateLabels := func(fname string, vals []string) (err error) {
for i, v := range vals {
err = validate.Many(err, validate.LabelKey(fmt.Sprintf("%s[%d]", fname, i), v))
}
return err
}

err = validate.Many(
err,
Expand All @@ -456,6 +466,7 @@ func (cfg Config) Validate() error {
validateKey("Slack.ClientSecret", cfg.Slack.ClientSecret),
validateKey("Twilio.AccountSID", cfg.Twilio.AccountSID),
validateKey("Twilio.AuthToken", cfg.Twilio.AuthToken),
validateLabels("Services.RequiredLabels", cfg.Services.RequiredLabels),
validateKey("Twilio.AlternateAuthToken", cfg.Twilio.AlternateAuthToken),
validate.ASCII("Twilio.VoiceName", cfg.Twilio.VoiceName, 0, 50),
validate.ASCII("Twilio.VoiceLanguage", cfg.Twilio.VoiceLanguage, 0, 10),
Expand Down
4 changes: 4 additions & 0 deletions graphql2/mapconfig.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion web/src/app/services/ServiceCreateDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import { fieldErrors, nonFieldErrors } from '../util/errutil'
import FormDialog from '../dialogs/FormDialog'
import ServiceForm, { Value } from './ServiceForm'
import { Redirect } from 'wouter'
import { Label } from '../../schema'

interface InputVar {
name: string
description: string
escalationPolicyID?: string
favorite: boolean
labels: Label[]
newEscalationPolicy?: {
name: string
description: string
Expand All @@ -30,14 +32,15 @@ const createMutation = gql`
`

function inputVars(
{ name, description, escalationPolicyID }: Value,
{ name, description, escalationPolicyID, labels }: Value,
attempt = 0,
): InputVar {
const vars: InputVar = {
name,
description,
escalationPolicyID,
favorite: true,
labels,
}
if (!vars.escalationPolicyID) {
vars.newEscalationPolicy = {
Expand Down Expand Up @@ -68,6 +71,7 @@ export default function ServiceCreateDialog(props: {
name: '',
description: '',
escalationPolicyID: '',
labels: [],
})

const [createKeyStatus, commit] = useMutation(createMutation)
Expand Down
46 changes: 39 additions & 7 deletions web/src/app/services/ServiceEditDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { fieldErrors, nonFieldErrors } from '../util/errutil'
import FormDialog from '../dialogs/FormDialog'
import ServiceForm from './ServiceForm'
import Spinner from '../loading/components/Spinner'
import { Label } from '../../schema'

interface Value {
name: string
description: string
escalationPolicyID?: string
labels: Label[]
}

const query = gql`
Expand All @@ -19,6 +21,10 @@ const query = gql`
id
name
description
labels {
key
value
}
ep: escalationPolicy {
id
name
Expand All @@ -31,6 +37,11 @@ const mutation = gql`
updateService(input: $input)
}
`
const setLabel = gql`
mutation setLabel($input: SetLabelInput!) {
setLabel(input: $input)
}
`

export default function ServiceEditDialog(props: {
serviceID: string
Expand All @@ -43,6 +54,7 @@ export default function ServiceEditDialog(props: {
})

const [saveStatus, save] = useMutation(mutation)
const [saveLabelStatus, saveLabel] = useMutation(setLabel)

if (dataFetching && !data) {
return <Spinner />
Expand All @@ -52,33 +64,53 @@ export default function ServiceEditDialog(props: {
name: data?.service?.name,
description: data?.service?.description,
escalationPolicyID: data?.service?.ep?.id,
labels: data?.service?.labels || [],
}

const fieldErrs = fieldErrors(saveStatus.error)
const fieldErrs = fieldErrors(saveStatus.error).concat(
fieldErrors(saveLabelStatus.error),
)

return (
<FormDialog
title='Edit Service'
loading={saveStatus.fetching || (!data && dataFetching)}
errors={nonFieldErrors(saveStatus.error).concat(
nonFieldErrors(dataError),
nonFieldErrors(saveLabelStatus.error),
)}
onClose={props.onClose}
onSubmit={() => {
save(
onSubmit={async () => {
const saveRes = await save(
{
input: {
...value,
id: props.serviceID,
name: value?.name || '',
description: value?.description || '',
escalationPolicyID: value?.escalationPolicyID || '',
},
},
{
additionalTypenames: ['Service'],
},
).then((res) => {
)
if (saveRes.error) return

for (const label of value?.labels || []) {
const res = await saveLabel({
input: {
target: {
type: 'service',
id: props.serviceID,
},
key: label.key,
value: label.value,
},
})
if (res.error) return
props.onClose()
})
}

props.onClose()
}}
form={
<ServiceForm
Expand Down
64 changes: 62 additions & 2 deletions web/src/app/services/ServiceForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ import TextField from '@mui/material/TextField'
import { EscalationPolicySelect } from '../selection/EscalationPolicySelect'
import { FormContainer, FormField } from '../forms'
import { FieldError } from '../util/errutil'
import { useConfigValue } from '../util/RequireConfig'
import { Label } from '../../schema'
import { InputAdornment } from '@mui/material'

const MaxDetailsLength = 6 * 1024 // 6KiB

export interface Value {
name: string
description: string
escalationPolicyID?: string
labels: Label[]
}

interface ServiceFormProps {
Expand All @@ -26,9 +30,27 @@ interface ServiceFormProps {
}

export default function ServiceForm(props: ServiceFormProps): JSX.Element {
const { epRequired, ...containerProps } = props
const { epRequired, errors, ...containerProps } = props

const formErrs = errors.map((e) => {
if (e.field !== 'value') {
// label value
return e
}
return {
...e,
field: 'labels',
}
})

const [reqLabels] = useConfigValue('Services.RequiredLabels') as [string[]]

return (
<FormContainer {...containerProps} optionalLabels={epRequired}>
<FormContainer
{...containerProps}
errors={formErrs}
optionalLabels={epRequired}
>
<Grid container spacing={2}>
<Grid item xs={12}>
<FormField
Expand Down Expand Up @@ -61,6 +83,44 @@ export default function ServiceForm(props: ServiceFormProps): JSX.Element {
component={EscalationPolicySelect}
/>
</Grid>
{reqLabels &&
reqLabels.map((labelName: string, idx: number) => (
<Grid item xs={12} key={labelName}>
<FormField
fullWidth
name={labelName}
required={!epRequired} // optional when editing
component={TextField}
fieldName='labels'
label={
reqLabels.length === 1
? 'Service Label'
: reqLabels.length > 1 && idx === 0
? 'Service Labels'
: ''
}
InputProps={{
startAdornment: (
<InputAdornment position='start'>
{labelName}:
</InputAdornment>
),
}}
mapOnChangeValue={(newVal: string, value: Value) => {
return [
...value.labels.filter((l) => l.key !== labelName),
{
key: labelName,
value: newVal,
},
]
}}
mapValue={(labels: Label[]) =>
labels.find((l) => l.key === labelName)?.value || ''
}
/>
</Grid>
))}
</Grid>
</FormContainer>
)
Expand Down
1 change: 1 addition & 0 deletions web/src/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1397,6 +1397,7 @@ type ConfigID =
| 'General.DisableSMSLinks'
| 'General.DisableLabelCreation'
| 'General.DisableCalendarSubscriptions'
| 'Services.RequiredLabels'
| 'Maintenance.AlertCleanupDays'
| 'Maintenance.AlertAutoCloseDays'
| 'Maintenance.APIKeyExpireDays'
Expand Down

0 comments on commit 5984cef

Please sign in to comment.