Skip to content

Commit

Permalink
Merge branch 'master' into dest-normalize-schema-fields
Browse files Browse the repository at this point in the history
  • Loading branch information
mastercactapus committed Feb 23, 2024
2 parents 45c653b + 4d43e92 commit 0459d65
Show file tree
Hide file tree
Showing 19 changed files with 1,258 additions and 384 deletions.
5 changes: 5 additions & 0 deletions graphql2/graphqlapp/contactmethod.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ func (m *Mutation) CreateUserContactMethod(ctx context.Context, input graphql2.C
cfg := config.FromContext(ctx)

if input.Dest != nil {
err := validate.IDName("input.name", input.Name)
if err != nil {
addInputError(ctx, err)
return nil, nil
}
if ok, err := (*App)(m).ValidateDestination(ctx, "input.dest", input.Dest); !ok {
return nil, err
}
Expand Down
18 changes: 18 additions & 0 deletions graphql2/graphqlapp/destinationvalidation.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,24 @@ func addDestFieldError(ctx context.Context, parentField, fieldID string, err err
return nil
}

func addInputError(ctx context.Context, err error) {
field := err.(validation.FieldError).Field()

p := graphql.GetPath(ctx)
parentParts := strings.Split(field, ".")
for _, part := range parentParts {
p = append(p, ast.PathName(part))
}

graphql.AddError(ctx, &gqlerror.Error{
Message: errReason(err),
Path: p,
Extensions: map[string]interface{}{
"code": graphql2.ErrorCodeInvalidInputValue,
},
})
}

// ValidateDestination will validate a destination input.
//
// In the future this will be a call to the plugin system.
Expand Down
22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,17 @@
"@mui/system": "5.15.6",
"@mui/x-data-grid": "6.19.3",
"@playwright/test": "1.41.2",
"@storybook/addon-essentials": "7.6.13",
"@storybook/addon-interactions": "7.6.13",
"@storybook/addon-links": "7.6.13",
"@storybook/addons": "7.6.13",
"@storybook/blocks": "7.6.13",
"@storybook/preview-api": "7.6.13",
"@storybook/react": "7.6.13",
"@storybook/react-vite": "7.6.13",
"@storybook/test": "7.6.13",
"@storybook/addon-essentials": "7.6.17",
"@storybook/addon-interactions": "7.6.17",
"@storybook/addon-links": "7.6.17",
"@storybook/addons": "7.6.17",
"@storybook/blocks": "7.6.17",
"@storybook/preview-api": "7.6.17",
"@storybook/react": "7.6.17",
"@storybook/react-vite": "7.6.17",
"@storybook/test": "7.6.17",
"@storybook/test-runner": "0.16.0",
"@storybook/types": "7.6.13",
"@storybook/types": "7.6.17",
"@types/chance": "1.1.4",
"@types/diff": "5.0.8",
"@types/glob": "8.1.0",
Expand Down Expand Up @@ -133,7 +133,7 @@
"remark-breaks": "4.0.0",
"remark-gfm": "4.0.0",
"semver": "7.6.0",
"storybook": "7.6.13",
"storybook": "7.6.17",
"storybook-addon-mock": "4.3.0",
"stylelint": "16.2.1",
"stylelint-config-standard": "34.0.0",
Expand Down
20 changes: 20 additions & 0 deletions test/integration/lib/integration-key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Page, expect } from '@playwright/test'

export async function createIntegrationKey(
page: Page,
intKey: number,
isMobile: boolean,
): Promise<void> {
await page.getByRole('link', { name: 'Integration Keys' }).click()

if (isMobile) {
await page.getByRole('button', { name: 'Create Integration Key' }).click()
} else {
await page.getByTestId('create-key').click()
}
await page.getByLabel('Name').fill(intKey)
await page.getByRole('button', { name: 'Submit' }).click()

await expect(page.getByText(intKey)).toBeVisible()
await expect(page.getByText('Generic Webhook URL')).toBeVisible()
}
25 changes: 25 additions & 0 deletions test/integration/lib/label.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Page, expect } from '@playwright/test'

export async function createLabel(
page: Page,
key: string,
value: number,
isMobile: boolean,
): Promise<void> {
// Create a label for the service
await page.getByRole('link', { name: 'Labels' }).click()

if (isMobile) {
await page.getByRole('button', { name: 'Add' }).click()
} else {
await page.getByTestId('create-label').click()
}

await page.getByLabel('Key', { exact: true }).fill(key)
await page.getByText('Create "' + key + '"').click()
await page.getByLabel('Value', { exact: true }).fill(value)
await page.click('[role=dialog] button[type=submit]')

await expect(page.getByText(key)).toBeVisible()
await expect(page.getByText(value)).toBeVisible()
}
138 changes: 104 additions & 34 deletions test/integration/service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import { test, expect } from '@playwright/test'
import { userSessionFile } from './lib'
import Chance from 'chance'
import { createService } from './lib/service'
import { createLabel } from './lib/label'
import { createIntegrationKey } from './lib/integration-key'
const c = new Chance()

const description = c.sentence()
let name = 'pw-service ' + c.name()

test.describe.configure({ mode: 'parallel' })
test.use({ storageState: userSessionFile })

// test create, verify, and delete of an EMAIL contact method
test('Service', async ({ page, isMobile }) => {
let name = 'pw-service ' + c.name()
const description = c.sentence()

test('Service Information', async ({ page }) => {
await createService(page, name, description)

// We should be on the details page, so let's try editing it after validating the data on the page.
Expand All @@ -22,20 +23,26 @@ test('Service', async ({ page, isMobile }) => {
// and the breadcrumb link
await expect(page.getByRole('link', { name, exact: true })).toBeVisible()

// We should also find the description on the page
await expect(page.getByText(description)).toBeVisible()

// Lastly ensure there is a link to a policy named "<name> Policy"
await expect(page.getByRole('link', { name: name + ' Policy' })).toBeVisible()
})

test('Service Editing', async ({ page }) => {
name = 'pw-service ' + c.name()
await createService(page, name, description)

// Now let's edit the service name
await page.getByRole('button', { name: 'Edit' }).click()

name = 'pw-service ' + c.name()
await page.fill('input[name=name]', name)
await page.click('[role=dialog] button[type=submit]')

await expect(page.getByRole('heading', { name, level: 1 })).toBeVisible()
})

test('Heartbeat Monitors', async ({ page, isMobile }) => {
name = 'pw-service ' + c.name()
await createService(page, name, description)

// Navigate to the heartbeat monitors
await page.getByRole('link', { name: 'Heartbeat Monitors' }).click()
Expand Down Expand Up @@ -107,6 +114,11 @@ test('Service', async ({ page, isMobile }) => {
} else {
await page.getByRole('link', { name, exact: true }).click()
}
})

test('Alerts', async ({ page, isMobile }) => {
name = 'pw-service ' + c.name()
await createService(page, name, description)

// Go to the alerts page
await page
Expand Down Expand Up @@ -158,6 +170,11 @@ test('Service', async ({ page, isMobile }) => {
} else {
await page.getByRole('link', { name, exact: true }).click()
}
})

test('Metric', async ({ page, isMobile }) => {
name = 'pw-service ' + c.name()
await createService(page, name, description)

// Navigate to the metrics
await page.getByRole('link', { name: 'Metrics' }).click()
Expand All @@ -168,24 +185,18 @@ test('Service', async ({ page, isMobile }) => {
} else {
await page.getByRole('link', { name, exact: true }).click()
}
})

test('Label', async ({ page, isMobile }) => {
name = 'pw-service ' + c.name()

// Create a label for the service
await page.getByRole('link', { name: 'Labels' }).click()
const key = `${c.word({ length: 4 })}/${c.word({ length: 3 })}`
let value = c.word({ length: 8 })
if (isMobile) {
await page.getByRole('button', { name: 'Add' }).click()
} else {
await page.getByTestId('create-label').click()
}

await page.getByLabel('Key', { exact: true }).fill(key)
await page.getByText('Create "' + key + '"').click()
await page.getByLabel('Value', { exact: true }).fill(value)
await page.click('[role=dialog] button[type=submit]')
await createService(page, name, description)

await expect(page.getByText(key)).toBeVisible()
await expect(page.getByText(value)).toBeVisible()
// Create a label for the service
await createLabel(page, key, value, isMobile)

// Edit the label, change the value, confirm new value is visible
value = c.word({ length: 8 })
Expand Down Expand Up @@ -224,20 +235,17 @@ test('Service', async ({ page, isMobile }) => {
} else {
await page.getByRole('link', { name, exact: true }).click()
}
})

test('Integration Keys', async ({ page, isMobile }) => {
name = 'pw-service ' + c.name()

// Make an integration key
const intKey = c.word({ length: 5 }) + ' Key'
await page.getByRole('link', { name: 'Integration Keys' }).click()
if (isMobile) {
await page.getByRole('button', { name: 'Create Integration Key' }).click()
} else {
await page.getByTestId('create-key').click()
}
await page.getByLabel('Name').fill(intKey)
await page.getByRole('button', { name: 'Submit' }).click()

await expect(page.getByText(intKey)).toBeVisible()
await expect(page.getByText('Generic Webhook URL')).toBeVisible()
await createService(page, name, description)

// Make an integration key
await createIntegrationKey(page, intKey, isMobile)

// Create a second integration key with a different type
const grafanaKey = c.word({ length: 5 }) + ' Key'
Expand All @@ -264,11 +272,51 @@ test('Service', async ({ page, isMobile }) => {

await expect(page.getByText(intKey, { exact: true })).toBeVisible()
await expect(page.getByText(grafanaKey, { exact: true })).toBeHidden()
})

test('Service Creation with Existing Label and Label Filtering', async ({
page,
isMobile,
}) => {
name = 'pw-service ' + c.name()

// Make another service
const key = `${c.word({ length: 4 })}/${c.word({ length: 3 })}`
const value = c.word({ length: 8 })
const intKey = c.word({ length: 5 }) + ' Key'
const diffName = 'pw-service ' + c.name()
const diffDescription = c.sentence()

// Create a service
await createService(page, name, description)

// Make an integration key
await createIntegrationKey(page, intKey, isMobile)

// Return to the service
if (isMobile) {
await page.getByRole('button', { name: 'Back' }).click()
} else {
await page.getByRole('link', { name, exact: true }).click()
}

// Create a label for the service
await page.getByRole('link', { name: 'Labels' }).click()

if (isMobile) {
await page.getByRole('button', { name: 'Add' }).click()
} else {
await page.getByTestId('create-label').click()
}

await page.getByLabel('Key', { exact: true }).fill(key)
await page.getByText('Create "' + key + '"').click()
await page.getByLabel('Value', { exact: true }).fill(value)
await page.click('[role=dialog] button[type=submit]')

await expect(page.getByText(key)).toBeVisible()
await expect(page.getByText(value)).toBeVisible()

// Create another service
await createService(page, diffName, diffDescription)

// Set the label with the existing key and a new value
Expand Down Expand Up @@ -344,6 +392,18 @@ test('Service', async ({ page, isMobile }) => {
await expect(
page.getByRole('link', { name: name + ' ' + description }),
).toBeVisible()
})

test('Service Search', async ({ page, isMobile }) => {
name = 'pw-service ' + c.name()

const key = `${c.word({ length: 4 })}/${c.word({ length: 3 })}`
const value = c.word({ length: 8 })

await createService(page, name, description)

// Create a label for the service
await createLabel(page, key, value, isMobile)

// Load in filters from URL, should find the service
await page.goto('./services?search=' + key + '=*')
Expand All @@ -369,6 +429,11 @@ test('Service', async ({ page, isMobile }) => {

// We should find the service in the list, lets go to it
await page.getByRole('link', { name }).click()
})

test('Maintenance Mode', async ({ page }) => {
name = 'pw-service ' + c.name()
await createService(page, name, description)

// Maintenance mode
await page.getByRole('button', { name: 'Maintenance Mode' }).click()
Expand All @@ -388,6 +453,11 @@ test('Service', async ({ page, isMobile }) => {

// We should be back on the details page, but with no more banner
await expect(page.getByRole('alert')).not.toBeVisible()
})

test('Service Deletion', async ({ page, isMobile }) => {
name = 'pw-service ' + c.name()
await createService(page, name, description)

// Finally, let's delete the service
await page.getByRole('button', { name: 'Delete' }).click()
Expand Down
4 changes: 4 additions & 0 deletions web/src/app/dialogs/FormDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function FormDialog(props) {
onBack,
fullHeight,
disableBackdropClose,
disablePortal,
...dialogProps
} = props

Expand Down Expand Up @@ -178,6 +179,7 @@ function FormDialog(props) {
const fs = fullScreen || (!isWideScreen && !confirm)
return (
<Dialog
disablePortal={disablePortal}
classes={classesProp}
fullScreen={fs}
maxWidth={maxWidth}
Expand Down Expand Up @@ -233,6 +235,8 @@ FormDialog.propTypes = {
confirm: p.bool,
maxWidth: p.string,

disablePortal: p.bool, // disable the portal behavior of the dialog

// overrides any of the main action button titles with this specific text
primaryActionLabel: p.string,

Expand Down
Loading

0 comments on commit 0459d65

Please sign in to comment.