diff --git a/.eslintrc.js b/.eslintrc.js
index b9ce54679c..f77d6b15dc 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -22,6 +22,7 @@ module.exports = {
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
+ 'plugin:storybook/recommended',
],
rules: {
'no-else-return': ['error', { allowElseIf: false }],
diff --git a/.gitignore b/.gitignore
index 6d373e1d6f..0a101c8438 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@
*.zip
/test-results
/test/coverage/
+/storybook-static
/cypress/
@@ -72,4 +73,7 @@ smoketest_db_dump
/.yarn
/.pnp.*
-
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
diff --git a/.storybook/main.ts b/.storybook/main.ts
new file mode 100644
index 0000000000..41d284e7ef
--- /dev/null
+++ b/.storybook/main.ts
@@ -0,0 +1,28 @@
+import type { StorybookConfig } from '@storybook/react-vite'
+
+import { join, dirname } from 'path'
+
+/**
+ * This function is used to resolve the absolute path of a package.
+ * It is needed in projects that use Yarn PnP or are set up within a monorepo.
+ */
+function getAbsolutePath(value: string): any {
+ return dirname(require.resolve(join(value, 'package.json')))
+}
+const config: StorybookConfig = {
+ staticDirs: ['./static'],
+ stories: ['../web/src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
+ addons: [
+ getAbsolutePath('@storybook/addon-links'),
+ getAbsolutePath('@storybook/addon-essentials'),
+ getAbsolutePath('@storybook/addon-interactions'),
+ ],
+ framework: {
+ name: getAbsolutePath('@storybook/react-vite'),
+ options: {},
+ },
+ docs: {
+ autodocs: 'tag',
+ },
+}
+export default config
diff --git a/.storybook/msw.ts b/.storybook/msw.ts
new file mode 100644
index 0000000000..ea03360bd0
--- /dev/null
+++ b/.storybook/msw.ts
@@ -0,0 +1,3 @@
+import { setupWorker } from 'msw'
+
+export const worker = setupWorker()
diff --git a/.storybook/preview.ts b/.storybook/preview.ts
new file mode 100644
index 0000000000..efad841cd4
--- /dev/null
+++ b/.storybook/preview.ts
@@ -0,0 +1,27 @@
+import type { Preview } from '@storybook/react'
+import DefaultDecorator from '../web/src/app/storybook/decorators'
+import { initialize, mswLoader } from 'msw-storybook-addon'
+import { handleDefaultConfig } from '../web/src/app/storybook/graphql'
+
+initialize({
+ onUnhandledRequest: 'bypass',
+})
+
+const preview: Preview = {
+ parameters: {
+ actions: { argTypesRegex: '^on[A-Z].*' },
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/i,
+ },
+ },
+ msw: {
+ handlers: [handleDefaultConfig],
+ },
+ },
+ decorators: [DefaultDecorator],
+ loaders: [mswLoader],
+}
+
+export default preview
diff --git a/.storybook/static/mockServiceWorker.js b/.storybook/static/mockServiceWorker.js
new file mode 100644
index 0000000000..e369128ec0
--- /dev/null
+++ b/.storybook/static/mockServiceWorker.js
@@ -0,0 +1,287 @@
+/* eslint-disable */
+/* tslint:disable */
+
+/**
+ * Mock Service Worker (2.0.11).
+ * @see https://github.com/mswjs/msw
+ * - Please do NOT modify this file.
+ * - Please do NOT serve this file on production.
+ */
+
+const INTEGRITY_CHECKSUM = 'c5f7f8e188b673ea4e677df7ea3c5a39'
+const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
+const activeClientIds = new Set()
+
+self.addEventListener('install', function () {
+ self.skipWaiting()
+})
+
+self.addEventListener('activate', function (event) {
+ event.waitUntil(self.clients.claim())
+})
+
+self.addEventListener('message', async function (event) {
+ const clientId = event.source.id
+
+ if (!clientId || !self.clients) {
+ return
+ }
+
+ const client = await self.clients.get(clientId)
+
+ if (!client) {
+ return
+ }
+
+ const allClients = await self.clients.matchAll({
+ type: 'window',
+ })
+
+ switch (event.data) {
+ case 'KEEPALIVE_REQUEST': {
+ sendToClient(client, {
+ type: 'KEEPALIVE_RESPONSE',
+ })
+ break
+ }
+
+ case 'INTEGRITY_CHECK_REQUEST': {
+ sendToClient(client, {
+ type: 'INTEGRITY_CHECK_RESPONSE',
+ payload: INTEGRITY_CHECKSUM,
+ })
+ break
+ }
+
+ case 'MOCK_ACTIVATE': {
+ activeClientIds.add(clientId)
+
+ sendToClient(client, {
+ type: 'MOCKING_ENABLED',
+ payload: true,
+ })
+ break
+ }
+
+ case 'MOCK_DEACTIVATE': {
+ activeClientIds.delete(clientId)
+ break
+ }
+
+ case 'CLIENT_CLOSED': {
+ activeClientIds.delete(clientId)
+
+ const remainingClients = allClients.filter((client) => {
+ return client.id !== clientId
+ })
+
+ // Unregister itself when there are no more clients
+ if (remainingClients.length === 0) {
+ self.registration.unregister()
+ }
+
+ break
+ }
+ }
+})
+
+self.addEventListener('fetch', function (event) {
+ const { request } = event
+
+ // Bypass navigation requests.
+ if (request.mode === 'navigate') {
+ return
+ }
+
+ // Opening the DevTools triggers the "only-if-cached" request
+ // that cannot be handled by the worker. Bypass such requests.
+ if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
+ return
+ }
+
+ // Bypass all requests when there are no active clients.
+ // Prevents the self-unregistered worked from handling requests
+ // after it's been deleted (still remains active until the next reload).
+ if (activeClientIds.size === 0) {
+ return
+ }
+
+ // Generate unique request ID.
+ const requestId = crypto.randomUUID()
+ event.respondWith(handleRequest(event, requestId))
+})
+
+async function handleRequest(event, requestId) {
+ const client = await resolveMainClient(event)
+ const response = await getResponse(event, client, requestId)
+
+ // Send back the response clone for the "response:*" life-cycle events.
+ // Ensure MSW is active and ready to handle the message, otherwise
+ // this message will pend indefinitely.
+ if (client && activeClientIds.has(client.id)) {
+ ;(async function () {
+ const responseClone = response.clone()
+
+ sendToClient(
+ client,
+ {
+ type: 'RESPONSE',
+ payload: {
+ requestId,
+ isMockedResponse: IS_MOCKED_RESPONSE in response,
+ type: responseClone.type,
+ status: responseClone.status,
+ statusText: responseClone.statusText,
+ body: responseClone.body,
+ headers: Object.fromEntries(responseClone.headers.entries()),
+ },
+ },
+ [responseClone.body],
+ )
+ })()
+ }
+
+ return response
+}
+
+// Resolve the main client for the given event.
+// Client that issues a request doesn't necessarily equal the client
+// that registered the worker. It's with the latter the worker should
+// communicate with during the response resolving phase.
+async function resolveMainClient(event) {
+ const client = await self.clients.get(event.clientId)
+
+ if (client?.frameType === 'top-level') {
+ return client
+ }
+
+ const allClients = await self.clients.matchAll({
+ type: 'window',
+ })
+
+ return allClients
+ .filter((client) => {
+ // Get only those clients that are currently visible.
+ return client.visibilityState === 'visible'
+ })
+ .find((client) => {
+ // Find the client ID that's recorded in the
+ // set of clients that have registered the worker.
+ return activeClientIds.has(client.id)
+ })
+}
+
+async function getResponse(event, client, requestId) {
+ const { request } = event
+
+ // Clone the request because it might've been already used
+ // (i.e. its body has been read and sent to the client).
+ const requestClone = request.clone()
+
+ function passthrough() {
+ const headers = Object.fromEntries(requestClone.headers.entries())
+
+ // Remove internal MSW request header so the passthrough request
+ // complies with any potential CORS preflight checks on the server.
+ // Some servers forbid unknown request headers.
+ delete headers['x-msw-intention']
+
+ return fetch(requestClone, { headers })
+ }
+
+ // Bypass mocking when the client is not active.
+ if (!client) {
+ return passthrough()
+ }
+
+ // Bypass initial page load requests (i.e. static assets).
+ // The absence of the immediate/parent client in the map of the active clients
+ // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
+ // and is not ready to handle requests.
+ if (!activeClientIds.has(client.id)) {
+ return passthrough()
+ }
+
+ // Bypass requests with the explicit bypass header.
+ // Such requests can be issued by "ctx.fetch()".
+ const mswIntention = request.headers.get('x-msw-intention')
+ if (['bypass', 'passthrough'].includes(mswIntention)) {
+ return passthrough()
+ }
+
+ // Notify the client that a request has been intercepted.
+ const requestBuffer = await request.arrayBuffer()
+ const clientMessage = await sendToClient(
+ client,
+ {
+ type: 'REQUEST',
+ payload: {
+ id: requestId,
+ url: request.url,
+ mode: request.mode,
+ method: request.method,
+ headers: Object.fromEntries(request.headers.entries()),
+ cache: request.cache,
+ credentials: request.credentials,
+ destination: request.destination,
+ integrity: request.integrity,
+ redirect: request.redirect,
+ referrer: request.referrer,
+ referrerPolicy: request.referrerPolicy,
+ body: requestBuffer,
+ keepalive: request.keepalive,
+ },
+ },
+ [requestBuffer],
+ )
+
+ switch (clientMessage.type) {
+ case 'MOCK_RESPONSE': {
+ return respondWithMock(clientMessage.data)
+ }
+
+ case 'MOCK_NOT_FOUND': {
+ return passthrough()
+ }
+ }
+
+ return passthrough()
+}
+
+function sendToClient(client, message, transferrables = []) {
+ return new Promise((resolve, reject) => {
+ const channel = new MessageChannel()
+
+ channel.port1.onmessage = (event) => {
+ if (event.data && event.data.error) {
+ return reject(event.data.error)
+ }
+
+ resolve(event.data)
+ }
+
+ client.postMessage(
+ message,
+ [channel.port2].concat(transferrables.filter(Boolean)),
+ )
+ })
+}
+
+async function respondWithMock(response) {
+ // Setting response status code to 0 is a no-op.
+ // However, when responding with a "Response.error()", the produced Response
+ // instance will have status code set to 0. Since it's not possible to create
+ // a Response instance with status code 0, handle that use-case separately.
+ if (response.status === 0) {
+ return Response.error()
+ }
+
+ const mockedResponse = new Response(response.body, response)
+
+ Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
+ value: true,
+ enumerable: true,
+ })
+
+ return mockedResponse
+}
diff --git a/.yarnrc.yml b/.yarnrc.yml
index e838e851d2..608c03d221 100644
--- a/.yarnrc.yml
+++ b/.yarnrc.yml
@@ -1,8 +1,13 @@
defaultSemverRangePrefix: ''
-yarnPath: .yarn/releases/yarn-3.6.3.cjs
-
packageExtensions:
- 'markdown-it@*':
+ jest-playwright-preset@*:
+ dependencies:
+ playwright: 1.40.1
+ markdown-it@*:
dependencies:
- 'punycode': '2.3.0'
+ punycode: 2.3.0
+
+pnpEnableEsmLoader: true
+
+yarnPath: .yarn/releases/yarn-3.6.3.cjs
diff --git a/Makefile b/Makefile
index 90b32ab927..7185323c30 100644
--- a/Makefile
+++ b/Makefile
@@ -233,11 +233,20 @@ generate: $(NODE_DEPS) pkg/sysapi/sysapi.pb.go pkg/sysapi/sysapi_grpc.pb.go $(BI
go generate ./...
-test-all: test-unit test-smoke test-integration
+test-all: test-unit test-components test-smoke test-integration
test-integration: playwright-run cy-wide-prod-run cy-mobile-prod-run
test-smoke: smoketest
test-unit: test
+test-components: $(NODE_DEPS) bin/waitfor
+ yarn build-storybook --test --quiet 2>/dev/null
+ yarn concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
+ "yarn http-server storybook-static -a 127.0.0.1 --port 6008 --silent" \
+ "./bin/waitfor tcp://localhost:6008 && yarn test-storybook --ci --url http://127.0.0.1:6008"
+
+storybook: ensure-yarn $(NODE_DEPS) # Start the Storybook UI
+ yarn storybook
+
bin/MailHog: go.mod go.sum
go build -o bin/MailHog github.com/mailhog/MailHog
@@ -331,7 +340,7 @@ resetdb: config.json.bak ## Recreate the database leaving it empty (no migration
go run ./devtools/resetdb --no-migrate
clean: ## Clean up build artifacts
- rm -rf bin node_modules web/src/node_modules .pnp.cjs .pnp.loader.mjs web/src/build/static .yarn/cache .yarn/install-state.gz .yarn/unplugged
+ rm -rf bin node_modules web/src/node_modules .pnp.cjs .pnp.loader.mjs web/src/build/static .yarn/cache .yarn/install-state.gz .yarn/unplugged storybook-static
new-migration:
@test "$(NAME)" != "" || (echo "NAME is required" && false)
@@ -339,11 +348,9 @@ new-migration:
@echo "-- +migrate Up\n\n\n-- +migrate Down\n" >migrate/migrations/$(shell date +%Y%m%d%H%M%S)-$(NAME).sql
@echo "Created: migrate/migrations/$(shell date +%Y%m%d%H%M%S)-$(NAME).sql"
-.yarn/sdks/integrations.yml: $(NODE_DEPS)
+vscode: $(NODE_DEPS)
yarn dlx @yarnpkg/sdks vscode
-vscode: .yarn/sdks/integrations.yml ## Setup vscode integrations
-
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs: $(NODE_DEPS)
yarn plugin import interactive-tools
diff --git a/config/config.go b/config/config.go
index 0dca547a27..3b917066ac 100644
--- a/config/config.go
+++ b/config/config.go
@@ -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)."`
@@ -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,
@@ -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),
diff --git a/engine/compatmanager/update.go b/engine/compatmanager/update.go
index f32f31e775..679b67530c 100644
--- a/engine/compatmanager/update.go
+++ b/engine/compatmanager/update.go
@@ -117,13 +117,13 @@ func (db *DB) updateContactMethods(ctx context.Context) error {
// but we need to store the contact method id in the format "team_id:subject_id"
teamID := strings.TrimPrefix(s.ProviderID, "slack:")
value := s.SubjectID
- name, err := db.cs.TeamName(ctx, teamID)
+ team, err := db.cs.Team(ctx, teamID)
if err != nil {
log.Log(ctx, err)
continue
}
- _, err = tx.StmtContext(ctx, db.insertCM).ExecContext(ctx, uuid.New(), name, "SLACK_DM", value, s.UserID)
+ _, err = tx.StmtContext(ctx, db.insertCM).ExecContext(ctx, uuid.New(), team.Name, "SLACK_DM", value, s.UserID)
if err != nil {
return fmt.Errorf("insert cm: %w", err)
}
diff --git a/go.mod b/go.mod
index d084c91d61..a774183fa0 100644
--- a/go.mod
+++ b/go.mod
@@ -3,12 +3,12 @@ module github.com/target/goalert
go 1.21
require (
- github.com/99designs/gqlgen v0.17.41
- github.com/brianvoe/gofakeit/v6 v6.26.3
+ github.com/99designs/gqlgen v0.17.42
+ github.com/brianvoe/gofakeit/v6 v6.26.4
github.com/coreos/go-oidc/v3 v3.9.0
github.com/creack/pty/v2 v2.0.1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
- github.com/emersion/go-smtp v0.19.0
+ github.com/emersion/go-smtp v0.20.1
github.com/fatih/color v1.16.0
github.com/felixge/httpsnoop v1.0.4
github.com/fullstorydev/grpcui v1.3.3
@@ -19,7 +19,7 @@ require (
github.com/gordonklaus/ineffassign v0.1.0
github.com/hashicorp/yamux v0.1.1
github.com/jackc/pgtype v1.14.0
- github.com/jackc/pgx/v5 v5.5.1
+ github.com/jackc/pgx/v5 v5.5.2
github.com/jmespath/go-jmespath v0.4.0
github.com/joho/godotenv v1.5.1
github.com/kffl/speedbump v1.1.0
@@ -34,7 +34,7 @@ require (
github.com/oauth2-proxy/mockoidc v0.0.0-20220308204021-b9169deeb282
github.com/pelletier/go-toml/v2 v2.1.1
github.com/pkg/errors v0.9.1
- github.com/prometheus/client_golang v1.17.0
+ github.com/prometheus/client_golang v1.18.0
github.com/rubenv/sql-migrate v1.6.0
github.com/sirupsen/logrus v1.9.3
github.com/slack-go/slack v0.12.3
@@ -43,10 +43,10 @@ require (
github.com/sqlc-dev/pqtype v0.3.0
github.com/stretchr/testify v1.8.4
github.com/vektah/gqlparser/v2 v2.5.10
- golang.org/x/crypto v0.17.0
- golang.org/x/oauth2 v0.15.0
- golang.org/x/sys v0.15.0
- golang.org/x/term v0.15.0
+ golang.org/x/crypto v0.18.0
+ golang.org/x/oauth2 v0.16.0
+ golang.org/x/sys v0.16.0
+ golang.org/x/term v0.16.0
golang.org/x/tools v0.16.1
google.golang.org/grpc v1.60.1
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
@@ -115,7 +115,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
- github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
+ github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
@@ -124,9 +124,9 @@ require (
github.com/philhofer/fwd v1.1.2 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
- github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
- github.com/prometheus/common v0.44.0 // indirect
- github.com/prometheus/procfs v0.11.1 // indirect
+ github.com/prometheus/client_model v0.5.0 // indirect
+ github.com/prometheus/common v0.45.0 // indirect
+ github.com/prometheus/procfs v0.12.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
@@ -150,7 +150,7 @@ require (
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 // indirect
golang.org/x/exp/typeparams v0.0.0-20230626212559-97b1e661b5df // indirect
golang.org/x/mod v0.14.0 // indirect
- golang.org/x/net v0.19.0 // indirect
+ golang.org/x/net v0.20.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
diff --git a/go.sum b/go.sum
index abd32c8e7b..3273ca927d 100644
--- a/go.sum
+++ b/go.sum
@@ -598,8 +598,8 @@ cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcP
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
-github.com/99designs/gqlgen v0.17.41 h1:C1/zYMhGVP5TWNCNpmZ9Mb6CqT1Vr5SHEWoTOEJ3v3I=
-github.com/99designs/gqlgen v0.17.41/go.mod h1:GQ6SyMhwFbgHR0a8r2Wn8fYgEwPxxmndLFPhU63+cJE=
+github.com/99designs/gqlgen v0.17.42 h1:BVWDOb2VVHQC5k3m6oa0XhDnxltLLrU4so7x/u39Zu4=
+github.com/99designs/gqlgen v0.17.42/go.mod h1:GQ6SyMhwFbgHR0a8r2Wn8fYgEwPxxmndLFPhU63+cJE=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
@@ -650,8 +650,8 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4Yn
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
-github.com/brianvoe/gofakeit/v6 v6.26.3 h1:3ljYrjPwsUNAUFdUIr2jVg5EhKdcke/ZLop7uVg1Er8=
-github.com/brianvoe/gofakeit/v6 v6.26.3/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs=
+github.com/brianvoe/gofakeit/v6 v6.26.4 h1:+7JwTAXxw46Hdo1hA/F92Wi7x8vTwbjdFtBWYdm8eII=
+github.com/brianvoe/gofakeit/v6 v6.26.4/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs=
github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY=
github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -704,8 +704,8 @@ github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZi
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpzYfLAAfl63E8Le6F2+EL/cdhlkyRJY=
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
-github.com/emersion/go-smtp v0.19.0 h1:iVCDtR2/JY3RpKoaZ7u6I/sb52S3EzfNHO1fAWVHgng=
-github.com/emersion/go-smtp v0.19.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
+github.com/emersion/go-smtp v0.20.1 h1:kW8Nkzomjk6W1ebfUYy6wAYThukCzD9MFOkFnragOHc=
+github.com/emersion/go-smtp v0.20.1/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -967,8 +967,8 @@ github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQ
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0=
github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE=
-github.com/jackc/pgx/v5 v5.5.1 h1:5I9etrGkLrN+2XPCsi6XLlV5DITbSL/xBZdmAxFcXPI=
-github.com/jackc/pgx/v5 v5.5.1/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
+github.com/jackc/pgx/v5 v5.5.2 h1:iLlpgp4Cp/gC9Xuscl7lFL1PhhW+ZLtXZcrfCt4C3tA=
+github.com/jackc/pgx/v5 v5.5.2/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
@@ -1064,8 +1064,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
-github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
+github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
@@ -1104,17 +1104,17 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
-github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
+github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
+github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
-github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
-github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
-github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
-github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
-github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
-github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
+github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
+github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
+github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
+github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
+github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
+github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
@@ -1268,8 +1268,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
-golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
-golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
+golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1397,8 +1397,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
-golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
-golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
+golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
+golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1428,8 +1428,8 @@ golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
-golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
-golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
+golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
+golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1536,8 +1536,8 @@ golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
+golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1547,8 +1547,8 @@ golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
-golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
-golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
+golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
+golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/graphql2/compatdestinput.go b/graphql2/compatdestinput.go
new file mode 100644
index 0000000000..1370fd8581
--- /dev/null
+++ b/graphql2/compatdestinput.go
@@ -0,0 +1,19 @@
+package graphql2
+
+func (d DestinationInput) FieldValue(id string) string {
+ for _, f := range d.Values {
+ if f.FieldID == id {
+ return f.Value
+ }
+ }
+ return ""
+}
+
+func (d Destination) FieldValuePair(id string) FieldValuePair {
+ for _, f := range d.Values {
+ if f.FieldID == id {
+ return f
+ }
+ }
+ return FieldValuePair{}
+}
diff --git a/graphql2/generated.go b/graphql2/generated.go
index 2c6cf00dfe..b65aa54fdc 100644
--- a/graphql2/generated.go
+++ b/graphql2/generated.go
@@ -68,6 +68,7 @@ type ResolverRoot interface {
AlertMetric() AlertMetricResolver
EscalationPolicy() EscalationPolicyResolver
EscalationPolicyStep() EscalationPolicyStepResolver
+ FieldValuePair() FieldValuePairResolver
GQLAPIKey() GQLAPIKeyResolver
HeartbeatMonitor() HeartbeatMonitorResolver
IntegrationKey() IntegrationKeyResolver
@@ -215,11 +216,19 @@ type ComplexityRoot struct {
}
Destination struct {
+ Display func(childComplexity int) int
Type func(childComplexity int) int
TypeInfo func(childComplexity int) int
Values func(childComplexity int) int
}
+ DestinationDisplayInfo struct {
+ IconAltText func(childComplexity int) int
+ IconURL func(childComplexity int) int
+ LinkURL func(childComplexity int) int
+ Text func(childComplexity int) int
+ }
+
DestinationFieldConfig struct {
FieldID func(childComplexity int) int
Hint func(childComplexity int) int
@@ -271,9 +280,16 @@ type ComplexityRoot struct {
Targets func(childComplexity int) int
}
+ FieldValueConnection struct {
+ Nodes func(childComplexity int) int
+ PageInfo func(childComplexity int) int
+ }
+
FieldValuePair struct {
- FieldID func(childComplexity int) int
- Value func(childComplexity int) int
+ FieldID func(childComplexity int) int
+ IsFavorite func(childComplexity int) int
+ Label func(childComplexity int) int
+ Value func(childComplexity int) int
}
GQLAPIKey struct {
@@ -450,50 +466,53 @@ type ComplexityRoot struct {
}
Query struct {
- Alert func(childComplexity int, id int) int
- Alerts func(childComplexity int, input *AlertSearchOptions) int
- AuthSubjectsForProvider func(childComplexity int, first *int, after *string, providerID string) int
- CalcRotationHandoffTimes func(childComplexity int, input *CalcRotationHandoffTimesInput) int
- Config func(childComplexity int, all *bool) int
- ConfigHints func(childComplexity int) int
- DebugMessageStatus func(childComplexity int, input DebugMessageStatusInput) int
- DebugMessages func(childComplexity int, input *DebugMessagesInput) int
- DestinationFieldValidate func(childComplexity int, input DestinationFieldValidateInput) int
- DestinationTypes func(childComplexity int) int
- EscalationPolicies func(childComplexity int, input *EscalationPolicySearchOptions) int
- EscalationPolicy func(childComplexity int, id string) int
- ExperimentalFlags func(childComplexity int) int
- GenerateSlackAppManifest func(childComplexity int) int
- GqlAPIKeys func(childComplexity int) int
- HeartbeatMonitor func(childComplexity int, id string) int
- IntegrationKey func(childComplexity int, id string) int
- IntegrationKeyTypes func(childComplexity int) int
- IntegrationKeys func(childComplexity int, input *IntegrationKeySearchOptions) int
- LabelKeys func(childComplexity int, input *LabelKeySearchOptions) int
- LabelValues func(childComplexity int, input *LabelValueSearchOptions) int
- Labels func(childComplexity int, input *LabelSearchOptions) int
- LinkAccountInfo func(childComplexity int, token string) int
- MessageLogs func(childComplexity int, input *MessageLogSearchOptions) int
- PhoneNumberInfo func(childComplexity int, number string) int
- Rotation func(childComplexity int, id string) int
- Rotations func(childComplexity int, input *RotationSearchOptions) int
- Schedule func(childComplexity int, id string) int
- Schedules func(childComplexity int, input *ScheduleSearchOptions) int
- Service func(childComplexity int, id string) int
- Services func(childComplexity int, input *ServiceSearchOptions) int
- SlackChannel func(childComplexity int, id string) int
- SlackChannels func(childComplexity int, input *SlackChannelSearchOptions) int
- SlackUserGroup func(childComplexity int, id string) int
- SlackUserGroups func(childComplexity int, input *SlackUserGroupSearchOptions) int
- SwoStatus func(childComplexity int) int
- SystemLimits func(childComplexity int) int
- TimeZones func(childComplexity int, input *TimeZoneSearchOptions) int
- User func(childComplexity int, id *string) int
- UserCalendarSubscription func(childComplexity int, id string) int
- UserContactMethod func(childComplexity int, id string) int
- UserOverride func(childComplexity int, id string) int
- UserOverrides func(childComplexity int, input *UserOverrideSearchOptions) int
- Users func(childComplexity int, input *UserSearchOptions, first *int, after *string, search *string) int
+ Alert func(childComplexity int, id int) int
+ Alerts func(childComplexity int, input *AlertSearchOptions) int
+ AuthSubjectsForProvider func(childComplexity int, first *int, after *string, providerID string) int
+ CalcRotationHandoffTimes func(childComplexity int, input *CalcRotationHandoffTimesInput) int
+ Config func(childComplexity int, all *bool) int
+ ConfigHints func(childComplexity int) int
+ DebugMessageStatus func(childComplexity int, input DebugMessageStatusInput) int
+ DebugMessages func(childComplexity int, input *DebugMessagesInput) int
+ DestinationDisplayInfo func(childComplexity int, input DestinationInput) int
+ DestinationFieldSearch func(childComplexity int, input DestinationFieldSearchInput) int
+ DestinationFieldValidate func(childComplexity int, input DestinationFieldValidateInput) int
+ DestinationFieldValueName func(childComplexity int, input DestinationFieldValidateInput) int
+ DestinationTypes func(childComplexity int) int
+ EscalationPolicies func(childComplexity int, input *EscalationPolicySearchOptions) int
+ EscalationPolicy func(childComplexity int, id string) int
+ ExperimentalFlags func(childComplexity int) int
+ GenerateSlackAppManifest func(childComplexity int) int
+ GqlAPIKeys func(childComplexity int) int
+ HeartbeatMonitor func(childComplexity int, id string) int
+ IntegrationKey func(childComplexity int, id string) int
+ IntegrationKeyTypes func(childComplexity int) int
+ IntegrationKeys func(childComplexity int, input *IntegrationKeySearchOptions) int
+ LabelKeys func(childComplexity int, input *LabelKeySearchOptions) int
+ LabelValues func(childComplexity int, input *LabelValueSearchOptions) int
+ Labels func(childComplexity int, input *LabelSearchOptions) int
+ LinkAccountInfo func(childComplexity int, token string) int
+ MessageLogs func(childComplexity int, input *MessageLogSearchOptions) int
+ PhoneNumberInfo func(childComplexity int, number string) int
+ Rotation func(childComplexity int, id string) int
+ Rotations func(childComplexity int, input *RotationSearchOptions) int
+ Schedule func(childComplexity int, id string) int
+ Schedules func(childComplexity int, input *ScheduleSearchOptions) int
+ Service func(childComplexity int, id string) int
+ Services func(childComplexity int, input *ServiceSearchOptions) int
+ SlackChannel func(childComplexity int, id string) int
+ SlackChannels func(childComplexity int, input *SlackChannelSearchOptions) int
+ SlackUserGroup func(childComplexity int, id string) int
+ SlackUserGroups func(childComplexity int, input *SlackUserGroupSearchOptions) int
+ SwoStatus func(childComplexity int) int
+ SystemLimits func(childComplexity int) int
+ TimeZones func(childComplexity int, input *TimeZoneSearchOptions) int
+ User func(childComplexity int, id *string) int
+ UserCalendarSubscription func(childComplexity int, id string) int
+ UserContactMethod func(childComplexity int, id string) int
+ UserOverride func(childComplexity int, id string) int
+ UserOverrides func(childComplexity int, input *UserOverrideSearchOptions) int
+ Users func(childComplexity int, input *UserSearchOptions, first *int, after *string, search *string) int
}
Rotation struct {
@@ -771,6 +790,9 @@ type EscalationPolicyStepResolver interface {
Targets(ctx context.Context, obj *escalation.Step) ([]assignment.RawTarget, error)
EscalationPolicy(ctx context.Context, obj *escalation.Step) (*escalation.Policy, error)
}
+type FieldValuePairResolver interface {
+ Label(ctx context.Context, obj *FieldValuePair) (string, error)
+}
type GQLAPIKeyResolver interface {
CreatedBy(ctx context.Context, obj *GQLAPIKey) (*user.User, error)
@@ -894,6 +916,9 @@ type QueryResolver interface {
SwoStatus(ctx context.Context) (*SWOStatus, error)
DestinationTypes(ctx context.Context) ([]DestinationTypeInfo, error)
DestinationFieldValidate(ctx context.Context, input DestinationFieldValidateInput) (bool, error)
+ DestinationFieldSearch(ctx context.Context, input DestinationFieldSearchInput) (*FieldValueConnection, error)
+ DestinationFieldValueName(ctx context.Context, input DestinationFieldValidateInput) (string, error)
+ DestinationDisplayInfo(ctx context.Context, input DestinationInput) (*DestinationDisplayInfo, error)
GqlAPIKeys(ctx context.Context) ([]GQLAPIKey, error)
}
type RotationResolver interface {
@@ -1480,6 +1505,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.DebugSendSMSInfo.ProviderURL(childComplexity), true
+ case "Destination.display":
+ if e.complexity.Destination.Display == nil {
+ break
+ }
+
+ return e.complexity.Destination.Display(childComplexity), true
+
case "Destination.type":
if e.complexity.Destination.Type == nil {
break
@@ -1501,6 +1533,34 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Destination.Values(childComplexity), true
+ case "DestinationDisplayInfo.iconAltText":
+ if e.complexity.DestinationDisplayInfo.IconAltText == nil {
+ break
+ }
+
+ return e.complexity.DestinationDisplayInfo.IconAltText(childComplexity), true
+
+ case "DestinationDisplayInfo.iconURL":
+ if e.complexity.DestinationDisplayInfo.IconURL == nil {
+ break
+ }
+
+ return e.complexity.DestinationDisplayInfo.IconURL(childComplexity), true
+
+ case "DestinationDisplayInfo.linkURL":
+ if e.complexity.DestinationDisplayInfo.LinkURL == nil {
+ break
+ }
+
+ return e.complexity.DestinationDisplayInfo.LinkURL(childComplexity), true
+
+ case "DestinationDisplayInfo.text":
+ if e.complexity.DestinationDisplayInfo.Text == nil {
+ break
+ }
+
+ return e.complexity.DestinationDisplayInfo.Text(childComplexity), true
+
case "DestinationFieldConfig.fieldID":
if e.complexity.DestinationFieldConfig.FieldID == nil {
break
@@ -1753,6 +1813,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.EscalationPolicyStep.Targets(childComplexity), true
+ case "FieldValueConnection.nodes":
+ if e.complexity.FieldValueConnection.Nodes == nil {
+ break
+ }
+
+ return e.complexity.FieldValueConnection.Nodes(childComplexity), true
+
+ case "FieldValueConnection.pageInfo":
+ if e.complexity.FieldValueConnection.PageInfo == nil {
+ break
+ }
+
+ return e.complexity.FieldValueConnection.PageInfo(childComplexity), true
+
case "FieldValuePair.fieldID":
if e.complexity.FieldValuePair.FieldID == nil {
break
@@ -1760,6 +1834,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.FieldValuePair.FieldID(childComplexity), true
+ case "FieldValuePair.isFavorite":
+ if e.complexity.FieldValuePair.IsFavorite == nil {
+ break
+ }
+
+ return e.complexity.FieldValuePair.IsFavorite(childComplexity), true
+
+ case "FieldValuePair.label":
+ if e.complexity.FieldValuePair.Label == nil {
+ break
+ }
+
+ return e.complexity.FieldValuePair.Label(childComplexity), true
+
case "FieldValuePair.value":
if e.complexity.FieldValuePair.Value == nil {
break
@@ -2951,6 +3039,30 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Query.DebugMessages(childComplexity, args["input"].(*DebugMessagesInput)), true
+ case "Query.destinationDisplayInfo":
+ if e.complexity.Query.DestinationDisplayInfo == nil {
+ break
+ }
+
+ args, err := ec.field_Query_destinationDisplayInfo_args(context.TODO(), rawArgs)
+ if err != nil {
+ return 0, false
+ }
+
+ return e.complexity.Query.DestinationDisplayInfo(childComplexity, args["input"].(DestinationInput)), true
+
+ case "Query.destinationFieldSearch":
+ if e.complexity.Query.DestinationFieldSearch == nil {
+ break
+ }
+
+ args, err := ec.field_Query_destinationFieldSearch_args(context.TODO(), rawArgs)
+ if err != nil {
+ return 0, false
+ }
+
+ return e.complexity.Query.DestinationFieldSearch(childComplexity, args["input"].(DestinationFieldSearchInput)), true
+
case "Query.destinationFieldValidate":
if e.complexity.Query.DestinationFieldValidate == nil {
break
@@ -2963,6 +3075,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Query.DestinationFieldValidate(childComplexity, args["input"].(DestinationFieldValidateInput)), true
+ case "Query.destinationFieldValueName":
+ if e.complexity.Query.DestinationFieldValueName == nil {
+ break
+ }
+
+ args, err := ec.field_Query_destinationFieldValueName_args(context.TODO(), rawArgs)
+ if err != nil {
+ return 0, false
+ }
+
+ return e.complexity.Query.DestinationFieldValueName(childComplexity, args["input"].(DestinationFieldValidateInput)), true
+
case "Query.destinationTypes":
if e.complexity.Query.DestinationTypes == nil {
break
@@ -4455,6 +4579,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
ec.unmarshalInputDebugMessageStatusInput,
ec.unmarshalInputDebugMessagesInput,
ec.unmarshalInputDebugSendSMSInput,
+ ec.unmarshalInputDestinationFieldSearchInput,
ec.unmarshalInputDestinationFieldValidateInput,
ec.unmarshalInputDestinationInput,
ec.unmarshalInputEscalationPolicySearchOptions,
@@ -5569,6 +5694,36 @@ func (ec *executionContext) field_Query_debugMessages_args(ctx context.Context,
return args, nil
}
+func (ec *executionContext) field_Query_destinationDisplayInfo_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
+ var err error
+ args := map[string]interface{}{}
+ var arg0 DestinationInput
+ if tmp, ok := rawArgs["input"]; ok {
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input"))
+ arg0, err = ec.unmarshalNDestinationInput2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationInput(ctx, tmp)
+ if err != nil {
+ return nil, err
+ }
+ }
+ args["input"] = arg0
+ return args, nil
+}
+
+func (ec *executionContext) field_Query_destinationFieldSearch_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
+ var err error
+ args := map[string]interface{}{}
+ var arg0 DestinationFieldSearchInput
+ if tmp, ok := rawArgs["input"]; ok {
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input"))
+ arg0, err = ec.unmarshalNDestinationFieldSearchInput2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationFieldSearchInput(ctx, tmp)
+ if err != nil {
+ return nil, err
+ }
+ }
+ args["input"] = arg0
+ return args, nil
+}
+
func (ec *executionContext) field_Query_destinationFieldValidate_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
@@ -5584,6 +5739,21 @@ func (ec *executionContext) field_Query_destinationFieldValidate_args(ctx contex
return args, nil
}
+func (ec *executionContext) field_Query_destinationFieldValueName_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
+ var err error
+ args := map[string]interface{}{}
+ var arg0 DestinationFieldValidateInput
+ if tmp, ok := rawArgs["input"]; ok {
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input"))
+ arg0, err = ec.unmarshalNDestinationFieldValidateInput2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationFieldValidateInput(ctx, tmp)
+ if err != nil {
+ return nil, err
+ }
+ }
+ args["input"] = arg0
+ return args, nil
+}
+
func (ec *executionContext) field_Query_escalationPolicies_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
@@ -9361,6 +9531,10 @@ func (ec *executionContext) fieldContext_Destination_values(ctx context.Context,
return ec.fieldContext_FieldValuePair_fieldID(ctx, field)
case "value":
return ec.fieldContext_FieldValuePair_value(ctx, field)
+ case "label":
+ return ec.fieldContext_FieldValuePair_label(ctx, field)
+ case "isFavorite":
+ return ec.fieldContext_FieldValuePair_isFavorite(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type FieldValuePair", field.Name)
},
@@ -9436,6 +9610,236 @@ func (ec *executionContext) fieldContext_Destination_typeInfo(ctx context.Contex
return fc, nil
}
+func (ec *executionContext) _Destination_display(ctx context.Context, field graphql.CollectedField, obj *Destination) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_Destination_display(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.Display, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(*DestinationDisplayInfo)
+ fc.Result = res
+ return ec.marshalNDestinationDisplayInfo2ᚖgithubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationDisplayInfo(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_Destination_display(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "Destination",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "text":
+ return ec.fieldContext_DestinationDisplayInfo_text(ctx, field)
+ case "iconURL":
+ return ec.fieldContext_DestinationDisplayInfo_iconURL(ctx, field)
+ case "iconAltText":
+ return ec.fieldContext_DestinationDisplayInfo_iconAltText(ctx, field)
+ case "linkURL":
+ return ec.fieldContext_DestinationDisplayInfo_linkURL(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type DestinationDisplayInfo", field.Name)
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _DestinationDisplayInfo_text(ctx context.Context, field graphql.CollectedField, obj *DestinationDisplayInfo) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_DestinationDisplayInfo_text(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.Text, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(string)
+ fc.Result = res
+ return ec.marshalNString2string(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_DestinationDisplayInfo_text(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "DestinationDisplayInfo",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type String does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _DestinationDisplayInfo_iconURL(ctx context.Context, field graphql.CollectedField, obj *DestinationDisplayInfo) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_DestinationDisplayInfo_iconURL(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.IconURL, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(string)
+ fc.Result = res
+ return ec.marshalNString2string(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_DestinationDisplayInfo_iconURL(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "DestinationDisplayInfo",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type String does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _DestinationDisplayInfo_iconAltText(ctx context.Context, field graphql.CollectedField, obj *DestinationDisplayInfo) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_DestinationDisplayInfo_iconAltText(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.IconAltText, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(string)
+ fc.Result = res
+ return ec.marshalNString2string(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_DestinationDisplayInfo_iconAltText(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "DestinationDisplayInfo",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type String does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _DestinationDisplayInfo_linkURL(ctx context.Context, field graphql.CollectedField, obj *DestinationDisplayInfo) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_DestinationDisplayInfo_linkURL(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.LinkURL, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(string)
+ fc.Result = res
+ return ec.marshalNString2string(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_DestinationDisplayInfo_linkURL(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "DestinationDisplayInfo",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type String does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
func (ec *executionContext) _DestinationFieldConfig_fieldID(ctx context.Context, field graphql.CollectedField, obj *DestinationFieldConfig) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_DestinationFieldConfig_fieldID(ctx, field)
if err != nil {
@@ -11117,6 +11521,110 @@ func (ec *executionContext) fieldContext_EscalationPolicyStep_escalationPolicy(c
return fc, nil
}
+func (ec *executionContext) _FieldValueConnection_nodes(ctx context.Context, field graphql.CollectedField, obj *FieldValueConnection) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_FieldValueConnection_nodes(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.Nodes, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.([]FieldValuePair)
+ fc.Result = res
+ return ec.marshalNFieldValuePair2ᚕgithubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐFieldValuePairᚄ(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_FieldValueConnection_nodes(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "FieldValueConnection",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "fieldID":
+ return ec.fieldContext_FieldValuePair_fieldID(ctx, field)
+ case "value":
+ return ec.fieldContext_FieldValuePair_value(ctx, field)
+ case "label":
+ return ec.fieldContext_FieldValuePair_label(ctx, field)
+ case "isFavorite":
+ return ec.fieldContext_FieldValuePair_isFavorite(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type FieldValuePair", field.Name)
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _FieldValueConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *FieldValueConnection) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_FieldValueConnection_pageInfo(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.PageInfo, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(*PageInfo)
+ fc.Result = res
+ return ec.marshalNPageInfo2ᚖgithubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐPageInfo(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_FieldValueConnection_pageInfo(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "FieldValueConnection",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "endCursor":
+ return ec.fieldContext_PageInfo_endCursor(ctx, field)
+ case "hasNextPage":
+ return ec.fieldContext_PageInfo_hasNextPage(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name)
+ },
+ }
+ return fc, nil
+}
+
func (ec *executionContext) _FieldValuePair_fieldID(ctx context.Context, field graphql.CollectedField, obj *FieldValuePair) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_FieldValuePair_fieldID(ctx, field)
if err != nil {
@@ -11205,6 +11713,94 @@ func (ec *executionContext) fieldContext_FieldValuePair_value(ctx context.Contex
return fc, nil
}
+func (ec *executionContext) _FieldValuePair_label(ctx context.Context, field graphql.CollectedField, obj *FieldValuePair) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_FieldValuePair_label(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.FieldValuePair().Label(rctx, obj)
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(string)
+ fc.Result = res
+ return ec.marshalNString2string(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_FieldValuePair_label(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "FieldValuePair",
+ Field: field,
+ IsMethod: true,
+ IsResolver: true,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type String does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _FieldValuePair_isFavorite(ctx context.Context, field graphql.CollectedField, obj *FieldValuePair) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_FieldValuePair_isFavorite(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.IsFavorite, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(bool)
+ fc.Result = res
+ return ec.marshalNBoolean2bool(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_FieldValuePair_isFavorite(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "FieldValuePair",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type Boolean does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
func (ec *executionContext) _GQLAPIKey_id(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_GQLAPIKey_id(ctx, field)
if err != nil {
@@ -20240,6 +20836,211 @@ func (ec *executionContext) fieldContext_Query_destinationFieldValidate(ctx cont
return fc, nil
}
+func (ec *executionContext) _Query_destinationFieldSearch(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_Query_destinationFieldSearch(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.Query().DestinationFieldSearch(rctx, fc.Args["input"].(DestinationFieldSearchInput))
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(*FieldValueConnection)
+ fc.Result = res
+ return ec.marshalNFieldValueConnection2ᚖgithubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐFieldValueConnection(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_Query_destinationFieldSearch(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "Query",
+ Field: field,
+ IsMethod: true,
+ IsResolver: true,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "nodes":
+ return ec.fieldContext_FieldValueConnection_nodes(ctx, field)
+ case "pageInfo":
+ return ec.fieldContext_FieldValueConnection_pageInfo(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type FieldValueConnection", field.Name)
+ },
+ }
+ defer func() {
+ if r := recover(); r != nil {
+ err = ec.Recover(ctx, r)
+ ec.Error(ctx, err)
+ }
+ }()
+ ctx = graphql.WithFieldContext(ctx, fc)
+ if fc.Args, err = ec.field_Query_destinationFieldSearch_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
+ ec.Error(ctx, err)
+ return fc, err
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _Query_destinationFieldValueName(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_Query_destinationFieldValueName(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.Query().DestinationFieldValueName(rctx, fc.Args["input"].(DestinationFieldValidateInput))
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(string)
+ fc.Result = res
+ return ec.marshalNString2string(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_Query_destinationFieldValueName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "Query",
+ Field: field,
+ IsMethod: true,
+ IsResolver: true,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type String does not have child fields")
+ },
+ }
+ defer func() {
+ if r := recover(); r != nil {
+ err = ec.Recover(ctx, r)
+ ec.Error(ctx, err)
+ }
+ }()
+ ctx = graphql.WithFieldContext(ctx, fc)
+ if fc.Args, err = ec.field_Query_destinationFieldValueName_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
+ ec.Error(ctx, err)
+ return fc, err
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _Query_destinationDisplayInfo(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_Query_destinationDisplayInfo(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ directive0 := func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.Query().DestinationDisplayInfo(rctx, fc.Args["input"].(DestinationInput))
+ }
+ directive1 := func(ctx context.Context) (interface{}, error) {
+ flagName, err := ec.unmarshalNString2string(ctx, "dest-types")
+ if err != nil {
+ return nil, err
+ }
+ if ec.directives.Experimental == nil {
+ return nil, errors.New("directive experimental is not implemented")
+ }
+ return ec.directives.Experimental(ctx, nil, directive0, flagName)
+ }
+
+ tmp, err := directive1(rctx)
+ if err != nil {
+ return nil, graphql.ErrorOnPath(ctx, err)
+ }
+ if tmp == nil {
+ return nil, nil
+ }
+ if data, ok := tmp.(*DestinationDisplayInfo); ok {
+ return data, nil
+ }
+ return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/target/goalert/graphql2.DestinationDisplayInfo`, tmp)
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(*DestinationDisplayInfo)
+ fc.Result = res
+ return ec.marshalNDestinationDisplayInfo2ᚖgithubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationDisplayInfo(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_Query_destinationDisplayInfo(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "Query",
+ Field: field,
+ IsMethod: true,
+ IsResolver: true,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "text":
+ return ec.fieldContext_DestinationDisplayInfo_text(ctx, field)
+ case "iconURL":
+ return ec.fieldContext_DestinationDisplayInfo_iconURL(ctx, field)
+ case "iconAltText":
+ return ec.fieldContext_DestinationDisplayInfo_iconAltText(ctx, field)
+ case "linkURL":
+ return ec.fieldContext_DestinationDisplayInfo_linkURL(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type DestinationDisplayInfo", field.Name)
+ },
+ }
+ defer func() {
+ if r := recover(); r != nil {
+ err = ec.Recover(ctx, r)
+ ec.Error(ctx, err)
+ }
+ }()
+ ctx = graphql.WithFieldContext(ctx, fc)
+ if fc.Args, err = ec.field_Query_destinationDisplayInfo_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
+ ec.Error(ctx, err)
+ return fc, err
+ }
+ return fc, nil
+}
+
func (ec *executionContext) _Query_gqlAPIKeys(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Query_gqlAPIKeys(ctx, field)
if err != nil {
@@ -30917,6 +31718,72 @@ func (ec *executionContext) unmarshalInputDebugSendSMSInput(ctx context.Context,
return it, nil
}
+func (ec *executionContext) unmarshalInputDestinationFieldSearchInput(ctx context.Context, obj interface{}) (DestinationFieldSearchInput, error) {
+ var it DestinationFieldSearchInput
+ asMap := map[string]interface{}{}
+ for k, v := range obj.(map[string]interface{}) {
+ asMap[k] = v
+ }
+
+ if _, present := asMap["first"]; !present {
+ asMap["first"] = 15
+ }
+
+ fieldsInOrder := [...]string{"destType", "fieldID", "search", "omit", "after", "first"}
+ for _, k := range fieldsInOrder {
+ v, ok := asMap[k]
+ if !ok {
+ continue
+ }
+ switch k {
+ case "destType":
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("destType"))
+ data, err := ec.unmarshalNDestinationType2string(ctx, v)
+ if err != nil {
+ return it, err
+ }
+ it.DestType = data
+ case "fieldID":
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fieldID"))
+ data, err := ec.unmarshalNID2string(ctx, v)
+ if err != nil {
+ return it, err
+ }
+ it.FieldID = data
+ case "search":
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("search"))
+ data, err := ec.unmarshalOString2ᚖstring(ctx, v)
+ if err != nil {
+ return it, err
+ }
+ it.Search = data
+ case "omit":
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("omit"))
+ data, err := ec.unmarshalOString2ᚕstringᚄ(ctx, v)
+ if err != nil {
+ return it, err
+ }
+ it.Omit = data
+ case "after":
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("after"))
+ data, err := ec.unmarshalOString2ᚖstring(ctx, v)
+ if err != nil {
+ return it, err
+ }
+ it.After = data
+ case "first":
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("first"))
+ data, err := ec.unmarshalOInt2ᚖint(ctx, v)
+ if err != nil {
+ return it, err
+ }
+ it.First = data
+ }
+ }
+
+ return it, nil
+}
+
func (ec *executionContext) unmarshalInputDestinationFieldValidateInput(ctx context.Context, obj interface{}) (DestinationFieldValidateInput, error) {
var it DestinationFieldValidateInput
asMap := map[string]interface{}{}
@@ -34592,6 +35459,65 @@ func (ec *executionContext) _Destination(ctx context.Context, sel ast.SelectionS
if out.Values[i] == graphql.Null {
out.Invalids++
}
+ case "display":
+ out.Values[i] = ec._Destination_display(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ default:
+ panic("unknown field " + strconv.Quote(field.Name))
+ }
+ }
+ out.Dispatch(ctx)
+ if out.Invalids > 0 {
+ return graphql.Null
+ }
+
+ atomic.AddInt32(&ec.deferred, int32(len(deferred)))
+
+ for label, dfs := range deferred {
+ ec.processDeferredGroup(graphql.DeferredGroup{
+ Label: label,
+ Path: graphql.GetPath(ctx),
+ FieldSet: dfs,
+ Context: ctx,
+ })
+ }
+
+ return out
+}
+
+var destinationDisplayInfoImplementors = []string{"DestinationDisplayInfo"}
+
+func (ec *executionContext) _DestinationDisplayInfo(ctx context.Context, sel ast.SelectionSet, obj *DestinationDisplayInfo) graphql.Marshaler {
+ fields := graphql.CollectFields(ec.OperationContext, sel, destinationDisplayInfoImplementors)
+
+ out := graphql.NewFieldSet(fields)
+ deferred := make(map[string]*graphql.FieldSet)
+ for i, field := range fields {
+ switch field.Name {
+ case "__typename":
+ out.Values[i] = graphql.MarshalString("DestinationDisplayInfo")
+ case "text":
+ out.Values[i] = ec._DestinationDisplayInfo_text(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ case "iconURL":
+ out.Values[i] = ec._DestinationDisplayInfo_iconURL(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ case "iconAltText":
+ out.Values[i] = ec._DestinationDisplayInfo_iconAltText(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ case "linkURL":
+ out.Values[i] = ec._DestinationDisplayInfo_linkURL(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
default:
panic("unknown field " + strconv.Quote(field.Name))
}
@@ -35148,6 +36074,50 @@ func (ec *executionContext) _EscalationPolicyStep(ctx context.Context, sel ast.S
return out
}
+var fieldValueConnectionImplementors = []string{"FieldValueConnection"}
+
+func (ec *executionContext) _FieldValueConnection(ctx context.Context, sel ast.SelectionSet, obj *FieldValueConnection) graphql.Marshaler {
+ fields := graphql.CollectFields(ec.OperationContext, sel, fieldValueConnectionImplementors)
+
+ out := graphql.NewFieldSet(fields)
+ deferred := make(map[string]*graphql.FieldSet)
+ for i, field := range fields {
+ switch field.Name {
+ case "__typename":
+ out.Values[i] = graphql.MarshalString("FieldValueConnection")
+ case "nodes":
+ out.Values[i] = ec._FieldValueConnection_nodes(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ case "pageInfo":
+ out.Values[i] = ec._FieldValueConnection_pageInfo(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ out.Invalids++
+ }
+ default:
+ panic("unknown field " + strconv.Quote(field.Name))
+ }
+ }
+ out.Dispatch(ctx)
+ if out.Invalids > 0 {
+ return graphql.Null
+ }
+
+ atomic.AddInt32(&ec.deferred, int32(len(deferred)))
+
+ for label, dfs := range deferred {
+ ec.processDeferredGroup(graphql.DeferredGroup{
+ Label: label,
+ Path: graphql.GetPath(ctx),
+ FieldSet: dfs,
+ Context: ctx,
+ })
+ }
+
+ return out
+}
+
var fieldValuePairImplementors = []string{"FieldValuePair"}
func (ec *executionContext) _FieldValuePair(ctx context.Context, sel ast.SelectionSet, obj *FieldValuePair) graphql.Marshaler {
@@ -35162,12 +36132,53 @@ func (ec *executionContext) _FieldValuePair(ctx context.Context, sel ast.Selecti
case "fieldID":
out.Values[i] = ec._FieldValuePair_fieldID(ctx, field, obj)
if out.Values[i] == graphql.Null {
- out.Invalids++
+ atomic.AddUint32(&out.Invalids, 1)
}
case "value":
out.Values[i] = ec._FieldValuePair_value(ctx, field, obj)
if out.Values[i] == graphql.Null {
- out.Invalids++
+ atomic.AddUint32(&out.Invalids, 1)
+ }
+ case "label":
+ field := field
+
+ innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._FieldValuePair_label(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&fs.Invalids, 1)
+ }
+ return res
+ }
+
+ if field.Deferrable != nil {
+ dfs, ok := deferred[field.Deferrable.Label]
+ di := 0
+ if ok {
+ dfs.AddField(field)
+ di = len(dfs.Values) - 1
+ } else {
+ dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
+ deferred[field.Deferrable.Label] = dfs
+ }
+ dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
+ return innerFunc(ctx, dfs)
+ })
+
+ // don't run the out.Concurrently() call below
+ out.Values[i] = graphql.Null
+ continue
+ }
+
+ out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
+ case "isFavorite":
+ out.Values[i] = ec._FieldValuePair_isFavorite(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ atomic.AddUint32(&out.Invalids, 1)
}
default:
panic("unknown field " + strconv.Quote(field.Name))
@@ -37662,6 +38673,72 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
}
+ out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) })
+ case "destinationFieldSearch":
+ field := field
+
+ innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._Query_destinationFieldSearch(ctx, field)
+ if res == graphql.Null {
+ atomic.AddUint32(&fs.Invalids, 1)
+ }
+ return res
+ }
+
+ rrm := func(ctx context.Context) graphql.Marshaler {
+ return ec.OperationContext.RootResolverMiddleware(ctx,
+ func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
+ }
+
+ out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) })
+ case "destinationFieldValueName":
+ field := field
+
+ innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._Query_destinationFieldValueName(ctx, field)
+ if res == graphql.Null {
+ atomic.AddUint32(&fs.Invalids, 1)
+ }
+ return res
+ }
+
+ rrm := func(ctx context.Context) graphql.Marshaler {
+ return ec.OperationContext.RootResolverMiddleware(ctx,
+ func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
+ }
+
+ out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) })
+ case "destinationDisplayInfo":
+ field := field
+
+ innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._Query_destinationDisplayInfo(ctx, field)
+ if res == graphql.Null {
+ atomic.AddUint32(&fs.Invalids, 1)
+ }
+ return res
+ }
+
+ rrm := func(ctx context.Context) graphql.Marshaler {
+ return ec.OperationContext.RootResolverMiddleware(ctx,
+ func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
+ }
+
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) })
case "gqlAPIKeys":
field := field
@@ -41828,6 +42905,20 @@ func (ec *executionContext) unmarshalNDebugSendSMSInput2githubᚗcomᚋtargetᚋ
return res, graphql.ErrorOnPath(ctx, err)
}
+func (ec *executionContext) marshalNDestinationDisplayInfo2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationDisplayInfo(ctx context.Context, sel ast.SelectionSet, v DestinationDisplayInfo) graphql.Marshaler {
+ return ec._DestinationDisplayInfo(ctx, sel, &v)
+}
+
+func (ec *executionContext) marshalNDestinationDisplayInfo2ᚖgithubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationDisplayInfo(ctx context.Context, sel ast.SelectionSet, v *DestinationDisplayInfo) graphql.Marshaler {
+ if v == nil {
+ if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
+ ec.Errorf(ctx, "the requested element is null which the schema does not allow")
+ }
+ return graphql.Null
+ }
+ return ec._DestinationDisplayInfo(ctx, sel, v)
+}
+
func (ec *executionContext) marshalNDestinationFieldConfig2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationFieldConfig(ctx context.Context, sel ast.SelectionSet, v DestinationFieldConfig) graphql.Marshaler {
return ec._DestinationFieldConfig(ctx, sel, &v)
}
@@ -41876,11 +42967,21 @@ func (ec *executionContext) marshalNDestinationFieldConfig2ᚕgithubᚗcomᚋtar
return ret
}
+func (ec *executionContext) unmarshalNDestinationFieldSearchInput2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationFieldSearchInput(ctx context.Context, v interface{}) (DestinationFieldSearchInput, error) {
+ res, err := ec.unmarshalInputDestinationFieldSearchInput(ctx, v)
+ return res, graphql.ErrorOnPath(ctx, err)
+}
+
func (ec *executionContext) unmarshalNDestinationFieldValidateInput2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationFieldValidateInput(ctx context.Context, v interface{}) (DestinationFieldValidateInput, error) {
res, err := ec.unmarshalInputDestinationFieldValidateInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)
}
+func (ec *executionContext) unmarshalNDestinationInput2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationInput(ctx context.Context, v interface{}) (DestinationInput, error) {
+ res, err := ec.unmarshalInputDestinationInput(ctx, v)
+ return res, graphql.ErrorOnPath(ctx, err)
+}
+
func (ec *executionContext) unmarshalNDestinationType2string(ctx context.Context, v interface{}) (string, error) {
res, err := graphql.UnmarshalString(v)
return res, graphql.ErrorOnPath(ctx, err)
@@ -42064,6 +43165,20 @@ func (ec *executionContext) marshalNEscalationPolicyStep2ᚕgithubᚗcomᚋtarge
return ret
}
+func (ec *executionContext) marshalNFieldValueConnection2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐFieldValueConnection(ctx context.Context, sel ast.SelectionSet, v FieldValueConnection) graphql.Marshaler {
+ return ec._FieldValueConnection(ctx, sel, &v)
+}
+
+func (ec *executionContext) marshalNFieldValueConnection2ᚖgithubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐFieldValueConnection(ctx context.Context, sel ast.SelectionSet, v *FieldValueConnection) graphql.Marshaler {
+ if v == nil {
+ if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
+ ec.Errorf(ctx, "the requested element is null which the schema does not allow")
+ }
+ return graphql.Null
+ }
+ return ec._FieldValueConnection(ctx, sel, v)
+}
+
func (ec *executionContext) unmarshalNFieldValueInput2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐFieldValueInput(ctx context.Context, v interface{}) (FieldValueInput, error) {
res, err := ec.unmarshalInputFieldValueInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)
diff --git a/graphql2/graph/destinations.graphqls b/graphql2/graph/destinations.graphqls
index c2a6dddf44..782c5a291b 100644
--- a/graphql2/graph/destinations.graphqls
+++ b/graphql2/graph/destinations.graphqls
@@ -1,14 +1,23 @@
extend type Query {
-
# destinationTypes returns a list of destination types that can be used for
# notifications.
destinationTypes: [DestinationTypeInfo!]! @experimental(flagName: "dest-types")
-
# destinationFieldValidate validates a destination field value as valid or invalid.
#
# It does not guarantee that the value is valid for the destination type, only
# that it is valid for the field (i.e., syntax/formatting).
destinationFieldValidate(input: DestinationFieldValidateInput!): Boolean! @experimental(flagName: "dest-types")
+ destinationFieldSearch(input: DestinationFieldSearchInput!): FieldValueConnection!
+ destinationFieldValueName(input: DestinationFieldValidateInput!): String!
+
+ # destinationDisplayInfo returns the display information for a destination.
+ destinationDisplayInfo(input: DestinationInput!): DestinationDisplayInfo! @experimental(flagName: "dest-types")
+}
+
+# FieldValueConnection is a connection to a list of FieldValuePairs.
+type FieldValueConnection {
+ nodes: [FieldValuePair!]!
+ pageInfo: PageInfo!
}
input DestinationFieldValidateInput {
@@ -21,17 +30,38 @@ input DestinationFieldValidateInput {
# notifications.
scalar DestinationType
+input DestinationFieldSearchInput {
+ destType: DestinationType! # the type of destination to search for
+ fieldID: ID! # the ID of the input field to search for
+ search: String # search string to match against
+ omit: [String!] # values/ids to omit from results
+ after: String # cursor to start search from
+ first: Int = 15 # number of results to return
+}
+
# Destination represents a destination that can be used for notifications.
type Destination {
type: DestinationType!
values: [FieldValuePair!]!
typeInfo: DestinationTypeInfo!
+ display: DestinationDisplayInfo!
+}
+
+# DestinationDisplayInfo provides information for displaying a destination.
+type DestinationDisplayInfo {
+ text: String! # user-friendly text to display for this destination
+ iconURL: String! # URL to an icon to display for this destination
+ iconAltText: String! # alt text for the icon
+ linkURL: String! # URL to link to for more information about this destination
}
type FieldValuePair {
fieldID: ID! # The ID of the input field that this value is for.
value: String! # The value of the input field.
+ label: String! @goField(forceResolver: true) # The user-friendly text for this value of the input field (e.g., if the value is a user ID, label would be the user's name).
+
+ isFavorite: Boolean! # if true, this value is a favorite for the user, only set for search results
}
input DestinationInput {
diff --git a/graphql2/graphqlapp/destinationdisplayinfo.go b/graphql2/graphqlapp/destinationdisplayinfo.go
new file mode 100644
index 0000000000..3157bacb66
--- /dev/null
+++ b/graphql2/graphqlapp/destinationdisplayinfo.go
@@ -0,0 +1,157 @@
+package graphqlapp
+
+import (
+ "context"
+ "net/mail"
+ "net/url"
+
+ "github.com/nyaruka/phonenumbers"
+ "github.com/target/goalert/config"
+ "github.com/target/goalert/graphql2"
+ "github.com/target/goalert/validation"
+)
+
+func (a *Query) DestinationDisplayInfo(ctx context.Context, dest graphql2.DestinationInput) (*graphql2.DestinationDisplayInfo, error) {
+ app := (*App)(a)
+ cfg := config.FromContext(ctx)
+ switch dest.Type {
+ case destTwilioSMS:
+ n, err := phonenumbers.Parse(dest.FieldValue(fieldPhoneNumber), "")
+ if err != nil {
+ return nil, validation.WrapError(err)
+ }
+
+ return &graphql2.DestinationDisplayInfo{
+ IconURL: "builtin://phone-text",
+ IconAltText: "Text Message",
+ Text: phonenumbers.Format(n, phonenumbers.INTERNATIONAL),
+ }, nil
+ case destTwilioVoice:
+ n, err := phonenumbers.Parse(dest.FieldValue(fieldPhoneNumber), "")
+ if err != nil {
+ return nil, validation.WrapError(err)
+ }
+ return &graphql2.DestinationDisplayInfo{
+ IconURL: "builtin://phone-voice",
+ IconAltText: "Voice Call",
+ Text: phonenumbers.Format(n, phonenumbers.INTERNATIONAL),
+ }, nil
+ case destSMTP:
+ e, err := mail.ParseAddress(dest.FieldValue(fieldEmailAddress))
+ if err != nil {
+ return nil, validation.WrapError(err)
+ }
+ return &graphql2.DestinationDisplayInfo{
+ IconURL: "builtin://email",
+ IconAltText: "Email",
+ Text: e.Address,
+ }, nil
+ case destRotation:
+ r, err := app.FindOneRotation(ctx, dest.FieldValue(fieldRotationID))
+ if err != nil {
+ return nil, err
+ }
+ return &graphql2.DestinationDisplayInfo{
+ IconURL: "builtin://rotation",
+ IconAltText: "Rotation",
+ LinkURL: cfg.CallbackURL("/rotations/" + r.ID),
+ Text: r.Name,
+ }, nil
+ case destSchedule:
+ s, err := app.FindOneSchedule(ctx, dest.FieldValue(fieldScheduleID))
+ if err != nil {
+ return nil, err
+ }
+ return &graphql2.DestinationDisplayInfo{
+ IconURL: "builtin://schedule",
+ IconAltText: "Schedule",
+ LinkURL: cfg.CallbackURL("/schedules/" + s.ID),
+ Text: s.Name,
+ }, nil
+ case destUser:
+ u, err := app.FindOneUser(ctx, dest.FieldValue(fieldUserID))
+ if err != nil {
+ return nil, err
+ }
+ return &graphql2.DestinationDisplayInfo{
+ IconURL: cfg.CallbackURL("/api/v2/user-avatar/" + u.ID),
+ IconAltText: "User",
+ LinkURL: cfg.CallbackURL("/users/" + u.ID),
+ Text: u.Name,
+ }, nil
+
+ case destWebhook:
+ u, err := url.Parse(dest.FieldValue(fieldWebhookURL))
+ if err != nil {
+ return nil, validation.WrapError(err)
+ }
+ return &graphql2.DestinationDisplayInfo{
+ IconURL: "builtin://webhook",
+ IconAltText: "Webhook",
+ Text: u.Hostname(),
+ }, nil
+ case destSlackDM:
+ u, err := app.SlackStore.User(ctx, dest.FieldValue(fieldSlackUserID))
+ if err != nil {
+ return nil, err
+ }
+
+ team, err := app.SlackStore.Team(ctx, u.TeamID)
+ if err != nil {
+ return nil, err
+ }
+
+ if team.IconURL == "" {
+ team.IconURL = "builtin://slack"
+ }
+
+ return &graphql2.DestinationDisplayInfo{
+ IconURL: team.IconURL,
+ IconAltText: team.Name,
+ LinkURL: team.UserLink(u.ID),
+ Text: u.Name,
+ }, nil
+ case destSlackChan:
+ ch, err := app.SlackStore.Channel(ctx, dest.FieldValue(fieldSlackChanID))
+ if err != nil {
+ return nil, err
+ }
+
+ team, err := app.SlackStore.Team(ctx, ch.TeamID)
+ if err != nil {
+ return nil, err
+ }
+
+ if team.IconURL == "" {
+ team.IconURL = "builtin://slack"
+ }
+ return &graphql2.DestinationDisplayInfo{
+ IconURL: team.IconURL,
+ IconAltText: team.Name,
+ LinkURL: team.ChannelLink(ch.ID),
+ Text: ch.Name,
+ }, nil
+
+ case destSlackUG:
+ ug, err := app.SlackStore.UserGroup(ctx, dest.FieldValue(fieldSlackUGID))
+ if err != nil {
+ return nil, err
+ }
+
+ team, err := app.SlackStore.Team(ctx, ug.TeamID)
+ if err != nil {
+ return nil, err
+ }
+
+ if team.IconURL == "" {
+ team.IconURL = "builtin://slack"
+ }
+ return &graphql2.DestinationDisplayInfo{
+ IconURL: team.IconURL,
+ IconAltText: team.Name,
+ Text: ug.Handle,
+ }, nil
+ }
+
+ return nil, validation.NewGenericError("unsupported data type")
+}
diff --git a/graphql2/graphqlapp/destinationtypes.go b/graphql2/graphqlapp/destinationtypes.go
index d06d30b0df..89c52f581c 100644
--- a/graphql2/graphqlapp/destinationtypes.go
+++ b/graphql2/graphqlapp/destinationtypes.go
@@ -35,6 +35,130 @@ const (
fieldScheduleID = "schedule-id"
)
+type FieldValuePair App
+type DestinationDisplayInfo App
+
+func (a *App) FieldValuePair() graphql2.FieldValuePairResolver { return (*FieldValuePair)(a) }
+
+func (a *FieldValuePair) Label(ctx context.Context, fvp *graphql2.FieldValuePair) (string, error) {
+ if fvp.Label != "" {
+ return fvp.Label, nil
+ }
+
+ app := (*App)(a)
+ switch fvp.FieldID {
+ case fieldSlackChanID:
+ ch, err := app.SlackStore.Channel(ctx, fvp.Value)
+ if err != nil {
+ return "", err
+ }
+ return ch.Name, nil
+ case fieldSlackUGID:
+ ug, err := app.SlackStore.UserGroup(ctx, fvp.Value)
+ if err != nil {
+ return "", err
+ }
+
+ return ug.Handle, nil
+ case fieldUserID:
+ u, err := app.FindOneUser(ctx, fvp.Value)
+ if err != nil {
+ return "", err
+ }
+ return u.Name, nil
+ case fieldRotationID:
+ r, err := app.FindOneRotation(ctx, fvp.Value)
+ if err != nil {
+ return "", err
+ }
+ return r.Name, nil
+ case fieldScheduleID:
+ s, err := app.FindOneSchedule(ctx, fvp.Value)
+ if err != nil {
+ return "", err
+ }
+ return s.Name, nil
+ }
+
+ return "", validation.NewGenericError("unsupported fieldID")
+}
+
+func (q *Query) DestinationFieldValueName(ctx context.Context, input graphql2.DestinationFieldValidateInput) (string, error) {
+ switch input.FieldID {
+ case fieldSlackChanID:
+ ch, err := q.SlackChannel(ctx, input.Value)
+ if err != nil {
+ return "", err
+ }
+
+ return ch.Name, nil
+ case fieldSlackUGID:
+ ug, err := q.SlackUserGroup(ctx, input.Value)
+ if err != nil {
+ return "", err
+ }
+
+ return ug.Handle, nil
+ }
+
+ return "", validation.NewGenericError("unsupported fieldID")
+}
+
+func (q *Query) DestinationFieldSearch(ctx context.Context, input graphql2.DestinationFieldSearchInput) (*graphql2.FieldValueConnection, error) {
+ switch input.FieldID {
+ case fieldSlackChanID:
+ res, err := q.SlackChannels(ctx, &graphql2.SlackChannelSearchOptions{
+ Omit: input.Omit,
+ First: input.First,
+ Search: input.Search,
+ After: input.After,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ var nodes []graphql2.FieldValuePair
+ for _, c := range res.Nodes {
+ nodes = append(nodes, graphql2.FieldValuePair{
+ FieldID: input.FieldID,
+ Value: c.ID,
+ Label: c.Name,
+ })
+ }
+
+ return &graphql2.FieldValueConnection{
+ Nodes: nodes,
+ PageInfo: res.PageInfo,
+ }, nil
+ case fieldSlackUGID:
+ res, err := q.SlackUserGroups(ctx, &graphql2.SlackUserGroupSearchOptions{
+ Omit: input.Omit,
+ First: input.First,
+ Search: input.Search,
+ After: input.After,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ var nodes []graphql2.FieldValuePair
+ for _, ug := range res.Nodes {
+ nodes = append(nodes, graphql2.FieldValuePair{
+ FieldID: input.FieldID,
+ Value: ug.ID,
+ Label: ug.Handle,
+ })
+ }
+
+ return &graphql2.FieldValueConnection{
+ Nodes: nodes,
+ PageInfo: res.PageInfo,
+ }, nil
+ }
+
+ return nil, validation.NewGenericError("unsupported fieldID")
+}
+
func (q *Query) DestinationFieldValidate(ctx context.Context, input graphql2.DestinationFieldValidateInput) (bool, error) {
switch input.DestType {
case destTwilioSMS, destTwilioVoice:
diff --git a/graphql2/mapconfig.go b/graphql2/mapconfig.go
index ab870c14cd..746aacf7ea 100644
--- a/graphql2/mapconfig.go
+++ b/graphql2/mapconfig.go
@@ -34,6 +34,7 @@ func MapConfigValues(cfg config.Config) []ConfigValue {
{ID: "General.DisableSMSLinks", Type: ConfigTypeBoolean, Description: "If set, SMS messages will not contain a URL pointing to GoAlert.", Value: fmt.Sprintf("%t", cfg.General.DisableSMSLinks)},
{ID: "General.DisableLabelCreation", Type: ConfigTypeBoolean, Description: "Disables the ability to create new labels for services.", Value: fmt.Sprintf("%t", cfg.General.DisableLabelCreation)},
{ID: "General.DisableCalendarSubscriptions", Type: ConfigTypeBoolean, Description: "If set, disables all active calendar subscriptions as well as the ability to create new calendar subscriptions.", Value: fmt.Sprintf("%t", cfg.General.DisableCalendarSubscriptions)},
+ {ID: "Services.RequiredLabels", Type: ConfigTypeStringList, Description: "List of label names to require new services to define.", Value: strings.Join(cfg.Services.RequiredLabels, "\n")},
{ID: "Maintenance.AlertCleanupDays", Type: ConfigTypeInteger, Description: "Closed alerts will be deleted after this many days (0 means disable cleanup).", Value: fmt.Sprintf("%d", cfg.Maintenance.AlertCleanupDays)},
{ID: "Maintenance.AlertAutoCloseDays", Type: ConfigTypeInteger, Description: "Unacknowledged alerts will automatically be closed after this many days of inactivity. (0 means disable auto-close).", Value: fmt.Sprintf("%d", cfg.Maintenance.AlertAutoCloseDays)},
{ID: "Maintenance.APIKeyExpireDays", Type: ConfigTypeInteger, Description: "Unused calendar API keys will be disabled after this many days (0 means disable cleanup).", Value: fmt.Sprintf("%d", cfg.Maintenance.APIKeyExpireDays)},
@@ -103,6 +104,7 @@ func MapPublicConfigValues(cfg config.Config) []ConfigValue {
{ID: "General.DisableSMSLinks", Type: ConfigTypeBoolean, Description: "If set, SMS messages will not contain a URL pointing to GoAlert.", Value: fmt.Sprintf("%t", cfg.General.DisableSMSLinks)},
{ID: "General.DisableLabelCreation", Type: ConfigTypeBoolean, Description: "Disables the ability to create new labels for services.", Value: fmt.Sprintf("%t", cfg.General.DisableLabelCreation)},
{ID: "General.DisableCalendarSubscriptions", Type: ConfigTypeBoolean, Description: "If set, disables all active calendar subscriptions as well as the ability to create new calendar subscriptions.", Value: fmt.Sprintf("%t", cfg.General.DisableCalendarSubscriptions)},
+ {ID: "Services.RequiredLabels", Type: ConfigTypeStringList, Description: "List of label names to require new services to define.", Value: strings.Join(cfg.Services.RequiredLabels, "\n")},
{ID: "Maintenance.AlertCleanupDays", Type: ConfigTypeInteger, Description: "Closed alerts will be deleted after this many days (0 means disable cleanup).", Value: fmt.Sprintf("%d", cfg.Maintenance.AlertCleanupDays)},
{ID: "Maintenance.AlertAutoCloseDays", Type: ConfigTypeInteger, Description: "Unacknowledged alerts will automatically be closed after this many days of inactivity. (0 means disable auto-close).", Value: fmt.Sprintf("%d", cfg.Maintenance.AlertAutoCloseDays)},
{ID: "Maintenance.APIKeyExpireDays", Type: ConfigTypeInteger, Description: "Unused calendar API keys will be disabled after this many days (0 means disable cleanup).", Value: fmt.Sprintf("%d", cfg.Maintenance.APIKeyExpireDays)},
@@ -188,6 +190,8 @@ func ApplyConfigValues(cfg config.Config, vals []ConfigValueInput) (config.Confi
return cfg, err
}
cfg.General.DisableCalendarSubscriptions = val
+ case "Services.RequiredLabels":
+ cfg.Services.RequiredLabels = parseStringList(v.Value)
case "Maintenance.AlertCleanupDays":
val, err := parseInt(v.ID, v.Value)
if err != nil {
diff --git a/graphql2/models_gen.go b/graphql2/models_gen.go
index 8d5d49a1be..e26571c3cc 100644
--- a/graphql2/models_gen.go
+++ b/graphql2/models_gen.go
@@ -293,9 +293,17 @@ type DebugSendSMSInput struct {
}
type Destination struct {
- Type string `json:"type"`
- Values []FieldValuePair `json:"values"`
- TypeInfo *DestinationTypeInfo `json:"typeInfo"`
+ Type string `json:"type"`
+ Values []FieldValuePair `json:"values"`
+ TypeInfo *DestinationTypeInfo `json:"typeInfo"`
+ Display *DestinationDisplayInfo `json:"display"`
+}
+
+type DestinationDisplayInfo struct {
+ Text string `json:"text"`
+ IconURL string `json:"iconURL"`
+ IconAltText string `json:"iconAltText"`
+ LinkURL string `json:"linkURL"`
}
type DestinationFieldConfig struct {
@@ -311,6 +319,15 @@ type DestinationFieldConfig struct {
SupportsValidation bool `json:"supportsValidation"`
}
+type DestinationFieldSearchInput struct {
+ DestType string `json:"destType"`
+ FieldID string `json:"fieldID"`
+ Search *string `json:"search,omitempty"`
+ Omit []string `json:"omit,omitempty"`
+ After *string `json:"after,omitempty"`
+ First *int `json:"first,omitempty"`
+}
+
type DestinationFieldValidateInput struct {
DestType string `json:"destType"`
FieldID string `json:"fieldID"`
@@ -350,14 +367,21 @@ type EscalationPolicySearchOptions struct {
FavoritesFirst *bool `json:"favoritesFirst,omitempty"`
}
+type FieldValueConnection struct {
+ Nodes []FieldValuePair `json:"nodes"`
+ PageInfo *PageInfo `json:"pageInfo"`
+}
+
type FieldValueInput struct {
FieldID string `json:"fieldID"`
Value string `json:"value"`
}
type FieldValuePair struct {
- FieldID string `json:"fieldID"`
- Value string `json:"value"`
+ FieldID string `json:"fieldID"`
+ Value string `json:"value"`
+ Label string `json:"label"`
+ IsFavorite bool `json:"isFavorite"`
}
type GQLAPIKey struct {
@@ -448,6 +472,9 @@ type MessageLogSearchOptions struct {
Omit []string `json:"omit,omitempty"`
}
+type Mutation struct {
+}
+
type NotificationState struct {
Details string `json:"details"`
Status *NotificationStatus `json:"status,omitempty"`
@@ -468,6 +495,9 @@ type PhoneNumberInfo struct {
Error string `json:"error"`
}
+type Query struct {
+}
+
type RotationConnection struct {
Nodes []rotation.Rotation `json:"nodes"`
PageInfo *PageInfo `json:"pageInfo"`
diff --git a/notification/slack/channel.go b/notification/slack/channel.go
index ea4e371216..37ce074cb2 100644
--- a/notification/slack/channel.go
+++ b/notification/slack/channel.go
@@ -3,6 +3,7 @@ package slack
import (
"context"
"fmt"
+ "net/url"
"strings"
"sync"
"time"
@@ -83,6 +84,34 @@ type User struct {
TeamID string
}
+// Team contains information about a Slack team.
+type Team struct {
+ ID string
+ Domain string
+ Name string
+ IconURL string
+}
+
+func (t Team) ChannelLink(id string) string {
+ var u url.URL
+
+ u.Host = t.Domain + ".slack.com"
+ u.Scheme = "https"
+ u.Path = "/archives/" + url.PathEscape(id)
+
+ return u.String()
+}
+
+func (t Team) UserLink(id string) string {
+ var u url.URL
+
+ u.Host = t.Domain + ".slack.com"
+ u.Scheme = "https"
+ u.Path = "/team/" + url.PathEscape(id)
+
+ return u.String()
+}
+
func rootMsg(err error) string {
unwrapped := errors.Unwrap(err)
if unwrapped == nil {
@@ -129,13 +158,19 @@ func (s *ChannelSender) Channel(ctx context.Context, channelID string) (*Channel
return res, nil
}
-func (s *ChannelSender) TeamName(ctx context.Context, id string) (name string, err error) {
+func (s *ChannelSender) Team(ctx context.Context, id string) (t *Team, err error) {
s.teamInfoMx.Lock()
defer s.teamInfoMx.Unlock()
info, ok := s.teamInfoCache.Get(id)
if ok {
- return info.Name, nil
+ url, _ := info.Icon["image_44"].(string)
+ return &Team{
+ ID: info.ID,
+ Name: info.Name,
+ IconURL: url,
+ Domain: info.Domain,
+ }, nil
}
err = s.withClient(ctx, func(c *slack.Client) error {
@@ -144,12 +179,19 @@ func (s *ChannelSender) TeamName(ctx context.Context, id string) (name string, e
return err
}
- name = info.Name
+ url, _ := info.Icon["image_44"].(string)
+ t = &Team{
+ ID: info.ID,
+ Name: info.Name,
+ IconURL: url,
+ Domain: info.Domain,
+ }
+
s.teamInfoCache.Add(id, info)
return nil
})
- return name, err
+ return t, err
}
func (s *ChannelSender) TeamID(ctx context.Context) (string, error) {
diff --git a/notification/slack/usergroup.go b/notification/slack/usergroup.go
index 837cf6a851..001e6ae02d 100644
--- a/notification/slack/usergroup.go
+++ b/notification/slack/usergroup.go
@@ -11,6 +11,7 @@ import (
type UserGroup struct {
ID string
+ TeamID string
Name string
Handle string
}
@@ -72,6 +73,7 @@ func (s *ChannelSender) ListUserGroups(ctx context.Context) ([]UserGroup, error)
ID: g.ID,
Name: g.Name,
Handle: "@" + g.Handle,
+ TeamID: g.TeamID,
}
res = append(res, grp)
s.ugInfoCache.Add(g.ID, grp)
diff --git a/package.json b/package.json
index ab67a6ea56..9081cda152 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,9 @@
"fmt": "prettier -l --write '**/*.{js,jsx,yml,yaml,json,css,ts,tsx,html}'",
"check": "tsc -p web/src/app/tsconfig.json && tsc -p web/src/cypress/tsconfig.json",
"esbuild": "./web/src/esbuild.config.js",
- "esbuild-cy": "./web/src/esbuild.cypress.js"
+ "esbuild-cy": "./web/src/esbuild.cypress.js",
+ "storybook": "storybook dev -p 6006",
+ "build-storybook": "storybook build"
},
"jest": {
"transform": {
@@ -44,32 +46,47 @@
"@mui/material": "5.14.10",
"@mui/styles": "5.14.16",
"@mui/system": "5.14.10",
- "@mui/x-data-grid": "6.18.2",
- "@playwright/test": "1.40.0",
+ "@mui/x-data-grid": "6.18.7",
+ "@playwright/test": "1.40.1",
+ "@storybook/addon-essentials": "7.6.7",
+ "@storybook/addon-interactions": "7.6.7",
+ "@storybook/addon-links": "7.6.7",
+ "@storybook/addons": "7.6.7",
+ "@storybook/blocks": "7.6.7",
+ "@storybook/jest": "0.2.3",
+ "@storybook/preview-api": "7.6.7",
+ "@storybook/react": "7.6.7",
+ "@storybook/react-vite": "7.6.7",
+ "@storybook/test-runner": "0.16.0",
+ "@storybook/testing-library": "0.2.2",
+ "@storybook/types": "7.6.7",
"@types/chance": "1.1.4",
"@types/diff": "5.0.8",
"@types/glob": "8.1.0",
"@types/jest": "29.5.10",
"@types/lodash": "4.14.202",
"@types/luxon": "3.3.4",
+ "@types/node": "20.10.5",
"@types/prop-types": "15.7.9",
- "@types/react": "18.2.22",
+ "@types/react": "18.2.46",
"@types/react-big-calendar": "1.6.5",
- "@types/react-dom": "18.2.17",
+ "@types/react-dom": "18.2.18",
"@types/react-transition-group": "4.4.7",
- "@types/react-virtualized-auto-sizer": "1.0.1",
- "@typescript-eslint/eslint-plugin": "6.8.0",
+ "@types/react-virtualized-auto-sizer": "1.0.4",
+ "@typescript-eslint/eslint-plugin": "6.18.1",
"@typescript-eslint/parser": "6.15.0",
"@urql/exchange-retry": "1.2.0",
"bowser": "2.11.0",
"chance": "1.1.11",
"classnames": "2.3.2",
+ "concurrently": "8.2.2",
"cypress": "13.3.0",
+ "detect-package-manager": "3.0.1",
"diff": "5.1.0",
"esbuild": "0.19.10",
"esbuild-jest": "0.5.0",
- "eslint": "8.52.0",
- "eslint-config-prettier": "9.0.0",
+ "eslint": "8.56.0",
+ "eslint-config-prettier": "9.1.0",
"eslint-config-standard": "17.1.0",
"eslint-config-standard-jsx": "11.0.0",
"eslint-plugin-cypress": "2.15.1",
@@ -81,15 +98,19 @@
"eslint-plugin-promise": "6.1.1",
"eslint-plugin-react": "7.33.2",
"eslint-plugin-react-hooks": "4.6.0",
+ "eslint-plugin-storybook": "0.6.15",
"fuse.js": "7.0.0",
"glob": "10.3.10",
"graphiql": "3.0.10",
"graphql": "16.8.1",
+ "http-server": "14.1.1",
"jest": "29.7.0",
"lodash": "4.17.21",
"luxon": "3.4.3",
"mdast-util-find-and-replace": "3.0.1",
"mdi-material-ui": "7.7.0",
+ "msw": "2.0.11",
+ "msw-storybook-addon": "2.0.0--canary.122.b3ed3b1.0",
"prettier": "3.1.0",
"prettier-plugin-go-template": "0.0.15",
"prop-types": "15.8.1",
@@ -99,6 +120,7 @@
"react-colorful": "5.6.1",
"react-countdown": "2.3.5",
"react-dom": "18.2.0",
+ "react-error-boundary": "4.0.12",
"react-ga4": "2.1.0",
"react-infinite-scroll-component": "6.1.0",
"react-markdown": "9.0.0",
@@ -111,10 +133,14 @@
"redux-thunk": "2.4.2",
"remark-breaks": "4.0.0",
"remark-gfm": "4.0.0",
+ "semver": "7.5.4",
+ "storybook": "7.6.7",
+ "storybook-addon-mock": "4.3.0",
"stylelint": "15.11.0",
"stylelint-config-standard": "34.0.0",
"typescript": "5.2.2",
"urql": "4.0.6",
+ "vite": "5.0.10",
"wonka": "6.3.4",
"wouter": "2.12.1"
},
@@ -126,5 +152,8 @@
"prettier-plugin-go-template@0.0.15": {
"unplugged": true
}
+ },
+ "msw": {
+ "workerDirectory": ".storybook/static"
}
}
diff --git a/playwright.config.ts b/playwright.config.ts
index a0f1558be2..1403cef52e 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -28,6 +28,8 @@ const config = {
'./test/integration/setup/global-teardown.ts',
),
retries: process.env.CI ? 3 : 0,
+ forbidOnly: !!process.env.CI, // fail CI if .only() is used
+ workers: process.env.CI ? 2 : undefined,
use: {
trace: 'on-first-retry',
baseURL: 'http://localhost:6130',
diff --git a/tsconfig.json b/tsconfig.json
index ebf9f2c231..e682c65ae0 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"allowJs": true,
+ "jsx": "react-jsx",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
diff --git a/web/index.html b/web/index.html
index 958afd03c4..1e0a62587a 100644
--- a/web/index.html
+++ b/web/index.html
@@ -31,6 +31,12 @@
sizes="64x64"
href="{{ .Prefix }}/static/favicon-64.png"
/>
+
| JSX.Element
+ labels?: Array