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