@backstage-community/plugin-servicenow-backend 1.10.0 → 1.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @backstage-community/plugin-servicenow-backend
2
2
 
3
+ ## 1.11.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 85f6e69: Bump axios to 1.16.0 to include different security fixes.
8
+
9
+ ## 1.11.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 8754ccb: Backstage version bump to v1.50.2
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies [8754ccb]
18
+ - @backstage-community/plugin-servicenow-common@1.10.0
19
+
3
20
  ## 1.10.0
4
21
 
5
22
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"config.cjs.js","sources":["../../src/config/config.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { InputError } from '@backstage/errors';\nimport type {\n BasicAuthConfig,\n OAuthConfig,\n ServiceNowConfig,\n} from '../../config';\n\n/**\n * Reads the ServiceNow configuration from the provided Config object.\n *\n * @param config - The Backstage configuration object.\n * @returns The parsed ServiceNow configuration, or undefined if not found.\n * @throws InputError if the configuration is invalid.\n * @public\n */\nexport function readServiceNowConfig(\n config: Config,\n): ServiceNowConfig | undefined {\n const serviceNowConfig = config.getOptionalConfig('servicenow');\n if (!serviceNowConfig) {\n return undefined;\n }\n\n const instanceUrl = serviceNowConfig.getString('instanceUrl');\n if (!instanceUrl) {\n throw new InputError(\n 'Missing required config value at servicenow.instanceUrl',\n );\n }\n\n let oauth: OAuthConfig | undefined = undefined;\n const oauthConfig = serviceNowConfig.getOptionalConfig('oauth');\n\n let basicAuth: BasicAuthConfig | undefined = undefined;\n const basicAuthConfig = serviceNowConfig.getOptionalConfig('basicAuth');\n\n if (oauthConfig && basicAuthConfig) {\n throw new InputError(\n 'Both servicenow.oauth and servicenow.basicAuth are configured. Please use only one.',\n );\n }\n\n if (oauthConfig) {\n const grantType = oauthConfig.getString('grantType');\n if (grantType !== 'client_credentials' && grantType !== 'password') {\n throw new Error(`Unsupported OAuth grantType: ${grantType}`);\n }\n const clientId = oauthConfig.getString('clientId');\n const clientSecret = oauthConfig.getString('clientSecret');\n const tokenUrl = oauthConfig.getOptionalString('tokenUrl');\n\n if (!clientId) {\n throw new InputError(\n 'Missing required config value at servicenow.oauth.clientId',\n );\n }\n if (!clientSecret) {\n throw new InputError(\n 'Missing required config value at servicenow.oauth.clientSecret',\n );\n }\n\n if (grantType === 'client_credentials') {\n oauth = {\n grantType,\n clientId,\n clientSecret,\n tokenUrl,\n };\n } else if (grantType === 'password') {\n const username = oauthConfig.getString('username');\n const password = oauthConfig.getString('password');\n if (!username) {\n throw new InputError(\n \"Missing required config value at servicenow.oauth.username for 'password' grant type\",\n );\n }\n if (!password) {\n throw new InputError(\n \"Missing required config value at servicenow.oauth.password for 'password' grant type\",\n );\n }\n oauth = {\n grantType,\n clientId,\n clientSecret,\n username,\n password,\n tokenUrl,\n };\n } else {\n throw new InputError(\n `Invalid or missing 'grantType' in servicenow.oauth configuration. Must be 'client_credentials' or 'password'. Received: ${grantType}`,\n );\n }\n }\n\n if (basicAuthConfig) {\n const username = basicAuthConfig.getString('username');\n const password = basicAuthConfig.getString('password');\n if (!username) {\n throw new InputError(\n 'Missing required config value at servicenow.basicAuth.username',\n );\n }\n if (!password) {\n throw new InputError(\n 'Missing required config value at servicenow.basicAuth.password',\n );\n }\n basicAuth = {\n username,\n password,\n };\n }\n\n return {\n servicenow: {\n instanceUrl,\n oauth,\n basicAuth,\n },\n };\n}\n"],"names":["InputError"],"mappings":"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"}
1
+ {"version":3,"file":"config.cjs.js","sources":["../../src/config/config.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { InputError } from '@backstage/errors';\nimport type {\n BasicAuthConfig,\n OAuthConfig,\n ServiceNowConfig,\n} from '../../config';\n\n/**\n * Reads the ServiceNow configuration from the provided Config object.\n *\n * @param config - The Backstage configuration object.\n * @returns The parsed ServiceNow configuration, or undefined if not found.\n * @throws InputError if the configuration is invalid.\n * @public\n */\nexport function readServiceNowConfig(\n config: Config,\n): ServiceNowConfig | undefined {\n const serviceNowConfig = config.getOptionalConfig('servicenow');\n if (!serviceNowConfig) {\n return undefined;\n }\n\n const instanceUrl = serviceNowConfig.getString('instanceUrl');\n if (!instanceUrl) {\n throw new InputError(\n 'Missing required config value at servicenow.instanceUrl',\n );\n }\n\n let oauth: OAuthConfig | undefined = undefined;\n const oauthConfig = serviceNowConfig.getOptionalConfig('oauth');\n\n let basicAuth: BasicAuthConfig | undefined = undefined;\n const basicAuthConfig = serviceNowConfig.getOptionalConfig('basicAuth');\n\n if (oauthConfig && basicAuthConfig) {\n throw new InputError(\n 'Both servicenow.oauth and servicenow.basicAuth are configured. Please use only one.',\n );\n }\n\n if (oauthConfig) {\n const grantType = oauthConfig.getString('grantType');\n if (grantType !== 'client_credentials' && grantType !== 'password') {\n throw new Error(`Unsupported OAuth grantType: ${grantType}`);\n }\n const clientId = oauthConfig.getString('clientId');\n const clientSecret = oauthConfig.getString('clientSecret');\n const tokenUrl = oauthConfig.getOptionalString('tokenUrl');\n\n if (!clientId) {\n throw new InputError(\n 'Missing required config value at servicenow.oauth.clientId',\n );\n }\n if (!clientSecret) {\n throw new InputError(\n 'Missing required config value at servicenow.oauth.clientSecret',\n );\n }\n\n if (grantType === 'client_credentials') {\n oauth = {\n grantType,\n clientId,\n clientSecret,\n tokenUrl,\n };\n } else if (grantType === 'password') {\n const username = oauthConfig.getString('username');\n const password = oauthConfig.getString('password');\n if (!username) {\n throw new InputError(\n \"Missing required config value at servicenow.oauth.username for 'password' grant type\",\n );\n }\n if (!password) {\n throw new InputError(\n \"Missing required config value at servicenow.oauth.password for 'password' grant type\",\n );\n }\n oauth = {\n grantType,\n clientId,\n clientSecret,\n username,\n password,\n tokenUrl,\n };\n } else {\n throw new InputError(\n `Invalid or missing 'grantType' in servicenow.oauth configuration. Must be 'client_credentials' or 'password'. Received: ${grantType}`,\n );\n }\n }\n\n if (basicAuthConfig) {\n const username = basicAuthConfig.getString('username');\n const password = basicAuthConfig.getString('password');\n if (!username) {\n throw new InputError(\n 'Missing required config value at servicenow.basicAuth.username',\n );\n }\n if (!password) {\n throw new InputError(\n 'Missing required config value at servicenow.basicAuth.password',\n );\n }\n basicAuth = {\n username,\n password,\n };\n }\n\n return {\n servicenow: {\n instanceUrl,\n oauth,\n basicAuth,\n },\n };\n}\n"],"names":["InputError"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './service';\nimport { readServiceNowConfig } from './config';\nimport { ServiceNowConfig } from '../config';\n\n/**\n * servicenowPlugin backend plugin\n *\n * @public\n */\nexport const servicenowPlugin = createBackendPlugin({\n pluginId: 'servicenow',\n register(env) {\n env.registerInit({\n deps: {\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n httpRouter: coreServices.httpRouter,\n httpAuth: coreServices.httpAuth,\n },\n async init({ logger, config, httpRouter, httpAuth }) {\n const servicenowConfig: ServiceNowConfig | undefined =\n readServiceNowConfig(config);\n\n if (!servicenowConfig) {\n logger.error(\n 'ServiceNow plugin configuration is missing. The plugin will not be initialized.',\n );\n return;\n }\n\n httpRouter.use(\n await createRouter({\n logger,\n servicenowConfig,\n httpAuth,\n }),\n );\n },\n });\n },\n});\n"],"names":["createBackendPlugin","coreServices","config","readServiceNowConfig","createRouter"],"mappings":";;;;;;AA4BO,MAAM,mBAAmBA,oCAAoB,CAAA;AAAA,EAClD,QAAU,EAAA,YAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,QAAQC,6BAAa,CAAA,MAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,UAAA;AAAA,QACrB,YAAYA,6BAAa,CAAA,UAAA;AAAA,QACzB,UAAUA,6BAAa,CAAA;AAAA,OACzB;AAAA,MACA,MAAM,IAAK,CAAA,EAAE,gBAAQC,QAAQ,EAAA,UAAA,EAAY,UAAY,EAAA;AACnD,QAAM,MAAA,gBAAA,GACJC,4BAAqBD,QAAM,CAAA;AAE7B,QAAA,IAAI,CAAC,gBAAkB,EAAA;AACrB,UAAO,MAAA,CAAA,KAAA;AAAA,YACL;AAAA,WACF;AACA,UAAA;AAAA;AAGF,QAAW,UAAA,CAAA,GAAA;AAAA,UACT,MAAME,mBAAa,CAAA;AAAA,YACjB,MAAA;AAAA,YACA,gBAAA;AAAA,YACA;AAAA,WACD;AAAA,SACH;AAAA;AACF,KACD,CAAA;AAAA;AAEL,CAAC;;;;"}
1
+ {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './service';\nimport { readServiceNowConfig } from './config';\nimport { ServiceNowConfig } from '../config';\n\n/**\n * servicenowPlugin backend plugin\n *\n * @public\n */\nexport const servicenowPlugin = createBackendPlugin({\n pluginId: 'servicenow',\n register(env) {\n env.registerInit({\n deps: {\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n httpRouter: coreServices.httpRouter,\n httpAuth: coreServices.httpAuth,\n },\n async init({ logger, config, httpRouter, httpAuth }) {\n const servicenowConfig: ServiceNowConfig | undefined =\n readServiceNowConfig(config);\n\n if (!servicenowConfig) {\n logger.error(\n 'ServiceNow plugin configuration is missing. The plugin will not be initialized.',\n );\n return;\n }\n\n httpRouter.use(\n await createRouter({\n logger,\n servicenowConfig,\n httpAuth,\n }),\n );\n },\n });\n },\n});\n"],"names":["createBackendPlugin","coreServices","config","readServiceNowConfig","createRouter"],"mappings":";;;;;;AA4BO,MAAM,mBAAmBA,oCAAA,CAAoB;AAAA,EAClD,QAAA,EAAU,YAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,QAAQC,6BAAA,CAAa,MAAA;AAAA,QACrB,QAAQA,6BAAA,CAAa,UAAA;AAAA,QACrB,YAAYA,6BAAA,CAAa,UAAA;AAAA,QACzB,UAAUA,6BAAA,CAAa;AAAA,OACzB;AAAA,MACA,MAAM,IAAA,CAAK,EAAE,gBAAQC,QAAA,EAAQ,UAAA,EAAY,UAAS,EAAG;AACnD,QAAA,MAAM,gBAAA,GACJC,4BAAqBD,QAAM,CAAA;AAE7B,QAAA,IAAI,CAAC,gBAAA,EAAkB;AACrB,UAAA,MAAA,CAAO,KAAA;AAAA,YACL;AAAA,WACF;AACA,UAAA;AAAA,QACF;AAEA,QAAA,UAAA,CAAW,GAAA;AAAA,UACT,MAAME,mBAAA,CAAa;AAAA,YACjB,MAAA;AAAA,YACA,gBAAA;AAAA,YACA;AAAA,WACD;AAAA,SACH;AAAA,MACF;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { InputError } from '@backstage/errors';\nimport { validateIncidentQueryParams } from './validator';\nimport { HttpAuthService, LoggerService } from '@backstage/backend-plugin-api';\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport { DefaultServiceNowClient } from '../service-now-rest/client';\nimport { ServiceNowConfig } from '../../config';\nimport { ServiceNowSchemaChecker } from '../service-now-rest/schema-checker';\nimport { ServiceNowConnection } from '../service-now-rest/connection';\n\nexport interface RouterOptions {\n logger: LoggerService;\n servicenowConfig: ServiceNowConfig;\n httpAuth: HttpAuthService;\n}\n\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const { logger, servicenowConfig, httpAuth } = options;\n\n logger.info(\n `Creating router for ServiceNow with instance URL: ${servicenowConfig.servicenow?.instanceUrl}`,\n );\n\n const conn = new ServiceNowConnection(\n servicenowConfig,\n logger.child({ service: 'servicenow-connection' }),\n );\n const client = new DefaultServiceNowClient(\n conn,\n logger.child({ service: 'servicenow-client' }),\n );\n const schemaChecker = new ServiceNowSchemaChecker(conn);\n const router = Router();\n router.use(express.json());\n\n router.get('/health', (_, response) => {\n logger.info('PONG!');\n response.json({ status: 'ok' });\n });\n\n router.get('/incidents', async (req, res) => {\n await httpAuth.credentials(req, {\n allow: ['user'],\n });\n\n const validatedParams = await validateIncidentQueryParams(\n req.query,\n schemaChecker,\n );\n\n try {\n const incidents = await client.fetchIncidents(validatedParams);\n res.json(incidents);\n } catch (error) {\n if (error instanceof InputError) {\n throw error;\n }\n\n // Log the full error and throw a generic one\n logger.error('Failed to fetch incidents from ServiceNow', error);\n throw new Error('Failed to fetch incidents from ServiceNow');\n }\n });\n\n return router;\n}\n"],"names":["ServiceNowConnection","client","DefaultServiceNowClient","schemaChecker","ServiceNowSchemaChecker","Router","express","validateIncidentQueryParams","InputError"],"mappings":";;;;;;;;;;;;;;;AA+BA,eAAsB,aACpB,OACyB,EAAA;AACzB,EAAA,MAAM,EAAE,MAAA,EAAQ,gBAAkB,EAAA,QAAA,EAAa,GAAA,OAAA;AAE/C,EAAO,MAAA,CAAA,IAAA;AAAA,IACL,CAAA,kDAAA,EAAqD,gBAAiB,CAAA,UAAA,EAAY,WAAW,CAAA;AAAA,GAC/F;AAEA,EAAA,MAAM,OAAO,IAAIA,+BAAA;AAAA,IACf,gBAAA;AAAA,IACA,MAAO,CAAA,KAAA,CAAM,EAAE,OAAA,EAAS,yBAAyB;AAAA,GACnD;AACA,EAAA,MAAMC,WAAS,IAAIC,8BAAA;AAAA,IACjB,IAAA;AAAA,IACA,MAAO,CAAA,KAAA,CAAM,EAAE,OAAA,EAAS,qBAAqB;AAAA,GAC/C;AACA,EAAM,MAAAC,eAAA,GAAgB,IAAIC,qCAAA,CAAwB,IAAI,CAAA;AACtD,EAAA,MAAM,SAASC,uBAAO,EAAA;AACtB,EAAO,MAAA,CAAA,GAAA,CAAIC,wBAAQ,CAAA,IAAA,EAAM,CAAA;AAEzB,EAAA,MAAA,CAAO,GAAI,CAAA,SAAA,EAAW,CAAC,CAAA,EAAG,QAAa,KAAA;AACrC,IAAA,MAAA,CAAO,KAAK,OAAO,CAAA;AACnB,IAAA,QAAA,CAAS,IAAK,CAAA,EAAE,MAAQ,EAAA,IAAA,EAAM,CAAA;AAAA,GAC/B,CAAA;AAED,EAAA,MAAA,CAAO,GAAI,CAAA,YAAA,EAAc,OAAO,GAAA,EAAK,GAAQ,KAAA;AAC3C,IAAM,MAAA,QAAA,CAAS,YAAY,GAAK,EAAA;AAAA,MAC9B,KAAA,EAAO,CAAC,MAAM;AAAA,KACf,CAAA;AAED,IAAA,MAAM,kBAAkB,MAAMC,qCAAA;AAAA,MAC5B,GAAI,CAAA,KAAA;AAAA,MACJJ;AAAA,KACF;AAEA,IAAI,IAAA;AACF,MAAA,MAAM,SAAY,GAAA,MAAMF,QAAO,CAAA,cAAA,CAAe,eAAe,CAAA;AAC7D,MAAA,GAAA,CAAI,KAAK,SAAS,CAAA;AAAA,aACX,KAAO,EAAA;AACd,MAAA,IAAI,iBAAiBO,iBAAY,EAAA;AAC/B,QAAM,MAAA,KAAA;AAAA;AAIR,MAAO,MAAA,CAAA,KAAA,CAAM,6CAA6C,KAAK,CAAA;AAC/D,MAAM,MAAA,IAAI,MAAM,2CAA2C,CAAA;AAAA;AAC7D,GACD,CAAA;AAED,EAAO,OAAA,MAAA;AACT;;;;"}
1
+ {"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { InputError } from '@backstage/errors';\nimport { validateIncidentQueryParams } from './validator';\nimport { HttpAuthService, LoggerService } from '@backstage/backend-plugin-api';\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport { DefaultServiceNowClient } from '../service-now-rest/client';\nimport { ServiceNowConfig } from '../../config';\nimport { ServiceNowSchemaChecker } from '../service-now-rest/schema-checker';\nimport { ServiceNowConnection } from '../service-now-rest/connection';\n\nexport interface RouterOptions {\n logger: LoggerService;\n servicenowConfig: ServiceNowConfig;\n httpAuth: HttpAuthService;\n}\n\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const { logger, servicenowConfig, httpAuth } = options;\n\n logger.info(\n `Creating router for ServiceNow with instance URL: ${servicenowConfig.servicenow?.instanceUrl}`,\n );\n\n const conn = new ServiceNowConnection(\n servicenowConfig,\n logger.child({ service: 'servicenow-connection' }),\n );\n const client = new DefaultServiceNowClient(\n conn,\n logger.child({ service: 'servicenow-client' }),\n );\n const schemaChecker = new ServiceNowSchemaChecker(conn);\n const router = Router();\n router.use(express.json());\n\n router.get('/health', (_, response) => {\n logger.info('PONG!');\n response.json({ status: 'ok' });\n });\n\n router.get('/incidents', async (req, res) => {\n await httpAuth.credentials(req, {\n allow: ['user'],\n });\n\n const validatedParams = await validateIncidentQueryParams(\n req.query,\n schemaChecker,\n );\n\n try {\n const incidents = await client.fetchIncidents(validatedParams);\n res.json(incidents);\n } catch (error) {\n if (error instanceof InputError) {\n throw error;\n }\n\n // Log the full error and throw a generic one\n logger.error('Failed to fetch incidents from ServiceNow', error);\n throw new Error('Failed to fetch incidents from ServiceNow');\n }\n });\n\n return router;\n}\n"],"names":["ServiceNowConnection","client","DefaultServiceNowClient","schemaChecker","ServiceNowSchemaChecker","Router","express","validateIncidentQueryParams","InputError"],"mappings":";;;;;;;;;;;;;;;AA+BA,eAAsB,aACpB,OAAA,EACyB;AACzB,EAAA,MAAM,EAAE,MAAA,EAAQ,gBAAA,EAAkB,QAAA,EAAS,GAAI,OAAA;AAE/C,EAAA,MAAA,CAAO,IAAA;AAAA,IACL,CAAA,kDAAA,EAAqD,gBAAA,CAAiB,UAAA,EAAY,WAAW,CAAA;AAAA,GAC/F;AAEA,EAAA,MAAM,OAAO,IAAIA,+BAAA;AAAA,IACf,gBAAA;AAAA,IACA,MAAA,CAAO,KAAA,CAAM,EAAE,OAAA,EAAS,yBAAyB;AAAA,GACnD;AACA,EAAA,MAAMC,WAAS,IAAIC,8BAAA;AAAA,IACjB,IAAA;AAAA,IACA,MAAA,CAAO,KAAA,CAAM,EAAE,OAAA,EAAS,qBAAqB;AAAA,GAC/C;AACA,EAAA,MAAMC,eAAA,GAAgB,IAAIC,qCAAA,CAAwB,IAAI,CAAA;AACtD,EAAA,MAAM,SAASC,uBAAA,EAAO;AACtB,EAAA,MAAA,CAAO,GAAA,CAAIC,wBAAA,CAAQ,IAAA,EAAM,CAAA;AAEzB,EAAA,MAAA,CAAO,GAAA,CAAI,SAAA,EAAW,CAAC,CAAA,EAAG,QAAA,KAAa;AACrC,IAAA,MAAA,CAAO,KAAK,OAAO,CAAA;AACnB,IAAA,QAAA,CAAS,IAAA,CAAK,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA;AAAA,EAChC,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,GAAA,CAAI,YAAA,EAAc,OAAO,GAAA,EAAK,GAAA,KAAQ;AAC3C,IAAA,MAAM,QAAA,CAAS,YAAY,GAAA,EAAK;AAAA,MAC9B,KAAA,EAAO,CAAC,MAAM;AAAA,KACf,CAAA;AAED,IAAA,MAAM,kBAAkB,MAAMC,qCAAA;AAAA,MAC5B,GAAA,CAAI,KAAA;AAAA,MACJJ;AAAA,KACF;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,SAAA,GAAY,MAAMF,QAAA,CAAO,cAAA,CAAe,eAAe,CAAA;AAC7D,MAAA,GAAA,CAAI,KAAK,SAAS,CAAA;AAAA,IACpB,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,iBAAiBO,iBAAA,EAAY;AAC/B,QAAA,MAAM,KAAA;AAAA,MACR;AAGA,MAAA,MAAA,CAAO,KAAA,CAAM,6CAA6C,KAAK,CAAA;AAC/D,MAAA,MAAM,IAAI,MAAM,2CAA2C,CAAA;AAAA,IAC7D;AAAA,EACF,CAAC,CAAA;AAED,EAAA,OAAO,MAAA;AACT;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"validator.cjs.js","sources":["../../src/service/validator.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { InputError } from '@backstage/errors';\nimport type { ParsedQs } from 'qs';\nimport type { Order } from '@backstage-community/plugin-servicenow-common';\nimport { IncidentFieldEnum } from '@backstage-community/plugin-servicenow-common';\nimport { IncidentQueryParams } from '../service-now-rest';\nimport { ServiceNowSchemaChecker } from '../service-now-rest/schema-checker';\n\nconst ALLOWED_STATES: readonly string[] = ['1', '2', '3', '6', '7', '8'];\nconst ALLOWED_PRIORITIES: readonly string[] = ['1', '2', '3', '4', '5'];\nconst ALLOWED_ORDER_BY_FIELDS: string[] = Object.values(IncidentFieldEnum);\n\nfunction parseAndValidateMultiValueQueryParam(\n paramQueryValue: string | string[] | ParsedQs | ParsedQs[] | undefined,\n allowedValues: readonly string[],\n paramName: string,\n): string | undefined {\n if (paramQueryValue === undefined || paramQueryValue === null) {\n return undefined;\n }\n\n if (Array.isArray(paramQueryValue) || typeof paramQueryValue === 'object') {\n throw new InputError(\n `Query parameter '${paramName}' must be a single string.`,\n );\n }\n\n const valueAsString = String(paramQueryValue);\n\n if (valueAsString.trim() === '') {\n return undefined;\n }\n\n if (!valueAsString.startsWith('IN')) {\n throw new InputError(\n `Query parameter '${paramName}' must use the 'IN' prefix format (e.g., 'INvalue1,value2' or 'INvalue').`,\n );\n }\n\n // Extract the values after the 'IN' prefix\n const valuesStr = valueAsString.substring(2);\n if (!valuesStr) {\n throw new InputError(\n `Query parameter '${paramName}' with 'IN' prefix cannot have empty values.`,\n );\n }\n\n const items = valuesStr.split(',');\n for (const item of items) {\n const trimmedItem = item.trim();\n if (!trimmedItem) {\n throw new InputError(\n `Invalid ${paramName} format: empty value found in '${valueAsString}'. Allowed values for 'IN' list are: ${allowedValues.join(\n ', ',\n )}.`,\n );\n }\n\n const isValid = allowedValues.includes(trimmedItem);\n\n if (!isValid) {\n throw new InputError(\n `Invalid value '${trimmedItem}' for query parameter '${paramName}' in 'IN' list '${valueAsString}'. Allowed values are: ${allowedValues.join(\n ', ',\n )}.`,\n );\n }\n }\n return valueAsString;\n}\n\nexport async function validateIncidentQueryParams(\n query: ParsedQs,\n schemaChecker: ServiceNowSchemaChecker,\n): Promise<IncidentQueryParams> {\n const {\n state: stateQuery,\n priority: priorityQuery,\n search: searchQuery,\n limit: limitQuery,\n offset: offsetQuery,\n order: orderQuery,\n orderBy: orderByQuery,\n userEmail: userEmailQuery,\n entityId: entityIdQuery,\n ...rest\n } = query;\n const validatedParams: IncidentQueryParams = { ...rest };\n if (entityIdQuery !== undefined) {\n if (typeof entityIdQuery !== 'string' || !entityIdQuery.trim()) {\n throw new InputError('entityId must be a non-empty string.');\n }\n validatedParams.u_backstage_entity_id = entityIdQuery.trim();\n }\n\n const annotationFields = Object.keys(rest);\n if (validatedParams.u_backstage_entity_id) {\n annotationFields.push('u_backstage_entity_id');\n }\n const allFieldsExist = await schemaChecker.fieldExists(...annotationFields);\n if (!allFieldsExist) {\n throw new InputError(\n `One or more annotation fields do not exist in the ServiceNow incidents schema: ${annotationFields.join(\n ', ',\n )}`,\n );\n }\n\n // userEmail validation\n if (userEmailQuery !== undefined) {\n if (typeof userEmailQuery !== 'string' || !userEmailQuery.trim()) {\n throw new InputError('userEmail must be a non-empty string.');\n }\n // Basic email format validation\n if (!/.+@.+\\..+/.test(userEmailQuery)) {\n throw new InputError('userEmail must be a valid email address.');\n }\n validatedParams.userEmail = userEmailQuery.trim();\n }\n\n // limit validation\n if (limitQuery !== undefined) {\n if (typeof limitQuery !== 'string') {\n throw new InputError('limit must be a string with number value.');\n }\n const parsedLimit = parseInt(limitQuery, 10);\n if (isNaN(parsedLimit) || parsedLimit < 0) {\n throw new InputError('limit must be a non-negative integer.');\n }\n validatedParams.limit = parsedLimit;\n }\n\n // offset validation\n if (offsetQuery !== undefined) {\n if (typeof offsetQuery !== 'string') {\n throw new InputError('offset must be a string with number value.');\n }\n const parsedOffset = parseInt(offsetQuery, 10);\n if (isNaN(parsedOffset) || parsedOffset < 0) {\n throw new InputError('offset must be a non-negative integer.');\n }\n validatedParams.offset = parsedOffset;\n }\n\n // order validation\n if (orderQuery !== undefined) {\n if (typeof orderQuery !== 'string') {\n throw new InputError('order must be a string.');\n }\n if (orderQuery !== 'asc' && orderQuery !== 'desc') {\n throw new InputError(\"order must be 'asc' or 'desc'.\");\n }\n validatedParams.order = orderQuery as Order;\n }\n\n // state validation\n validatedParams.state = parseAndValidateMultiValueQueryParam(\n stateQuery as string | undefined,\n ALLOWED_STATES,\n 'state',\n );\n\n // priority validation\n validatedParams.priority = parseAndValidateMultiValueQueryParam(\n priorityQuery as string | undefined,\n ALLOWED_PRIORITIES,\n 'priority',\n );\n\n // search validation\n if (searchQuery !== undefined) {\n if (typeof searchQuery !== 'string') {\n throw new InputError(`search must be a string.`);\n }\n validatedParams.search = searchQuery;\n }\n\n // orderBy validation\n if (orderByQuery !== undefined) {\n if (typeof orderByQuery !== 'string') {\n throw new InputError('orderBy must be a string.');\n }\n if (!ALLOWED_ORDER_BY_FIELDS.includes(orderByQuery)) {\n throw new InputError(\n `Invalid orderBy field. Allowed values are: ${ALLOWED_ORDER_BY_FIELDS.join(\n ', ',\n )}`,\n );\n }\n validatedParams.orderBy = orderByQuery;\n }\n\n return validatedParams;\n}\n"],"names":["IncidentFieldEnum","InputError"],"mappings":"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"}
1
+ {"version":3,"file":"validator.cjs.js","sources":["../../src/service/validator.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { InputError } from '@backstage/errors';\nimport type { ParsedQs } from 'qs';\nimport type { Order } from '@backstage-community/plugin-servicenow-common';\nimport { IncidentFieldEnum } from '@backstage-community/plugin-servicenow-common';\nimport { IncidentQueryParams } from '../service-now-rest';\nimport { ServiceNowSchemaChecker } from '../service-now-rest/schema-checker';\n\nconst ALLOWED_STATES: readonly string[] = ['1', '2', '3', '6', '7', '8'];\nconst ALLOWED_PRIORITIES: readonly string[] = ['1', '2', '3', '4', '5'];\nconst ALLOWED_ORDER_BY_FIELDS: string[] = Object.values(IncidentFieldEnum);\n\nfunction parseAndValidateMultiValueQueryParam(\n paramQueryValue: string | string[] | ParsedQs | ParsedQs[] | undefined,\n allowedValues: readonly string[],\n paramName: string,\n): string | undefined {\n if (paramQueryValue === undefined || paramQueryValue === null) {\n return undefined;\n }\n\n if (Array.isArray(paramQueryValue) || typeof paramQueryValue === 'object') {\n throw new InputError(\n `Query parameter '${paramName}' must be a single string.`,\n );\n }\n\n const valueAsString = String(paramQueryValue);\n\n if (valueAsString.trim() === '') {\n return undefined;\n }\n\n if (!valueAsString.startsWith('IN')) {\n throw new InputError(\n `Query parameter '${paramName}' must use the 'IN' prefix format (e.g., 'INvalue1,value2' or 'INvalue').`,\n );\n }\n\n // Extract the values after the 'IN' prefix\n const valuesStr = valueAsString.substring(2);\n if (!valuesStr) {\n throw new InputError(\n `Query parameter '${paramName}' with 'IN' prefix cannot have empty values.`,\n );\n }\n\n const items = valuesStr.split(',');\n for (const item of items) {\n const trimmedItem = item.trim();\n if (!trimmedItem) {\n throw new InputError(\n `Invalid ${paramName} format: empty value found in '${valueAsString}'. Allowed values for 'IN' list are: ${allowedValues.join(\n ', ',\n )}.`,\n );\n }\n\n const isValid = allowedValues.includes(trimmedItem);\n\n if (!isValid) {\n throw new InputError(\n `Invalid value '${trimmedItem}' for query parameter '${paramName}' in 'IN' list '${valueAsString}'. Allowed values are: ${allowedValues.join(\n ', ',\n )}.`,\n );\n }\n }\n return valueAsString;\n}\n\nexport async function validateIncidentQueryParams(\n query: ParsedQs,\n schemaChecker: ServiceNowSchemaChecker,\n): Promise<IncidentQueryParams> {\n const {\n state: stateQuery,\n priority: priorityQuery,\n search: searchQuery,\n limit: limitQuery,\n offset: offsetQuery,\n order: orderQuery,\n orderBy: orderByQuery,\n userEmail: userEmailQuery,\n entityId: entityIdQuery,\n ...rest\n } = query;\n const validatedParams: IncidentQueryParams = { ...rest };\n if (entityIdQuery !== undefined) {\n if (typeof entityIdQuery !== 'string' || !entityIdQuery.trim()) {\n throw new InputError('entityId must be a non-empty string.');\n }\n validatedParams.u_backstage_entity_id = entityIdQuery.trim();\n }\n\n const annotationFields = Object.keys(rest);\n if (validatedParams.u_backstage_entity_id) {\n annotationFields.push('u_backstage_entity_id');\n }\n const allFieldsExist = await schemaChecker.fieldExists(...annotationFields);\n if (!allFieldsExist) {\n throw new InputError(\n `One or more annotation fields do not exist in the ServiceNow incidents schema: ${annotationFields.join(\n ', ',\n )}`,\n );\n }\n\n // userEmail validation\n if (userEmailQuery !== undefined) {\n if (typeof userEmailQuery !== 'string' || !userEmailQuery.trim()) {\n throw new InputError('userEmail must be a non-empty string.');\n }\n // Basic email format validation\n if (!/.+@.+\\..+/.test(userEmailQuery)) {\n throw new InputError('userEmail must be a valid email address.');\n }\n validatedParams.userEmail = userEmailQuery.trim();\n }\n\n // limit validation\n if (limitQuery !== undefined) {\n if (typeof limitQuery !== 'string') {\n throw new InputError('limit must be a string with number value.');\n }\n const parsedLimit = parseInt(limitQuery, 10);\n if (isNaN(parsedLimit) || parsedLimit < 0) {\n throw new InputError('limit must be a non-negative integer.');\n }\n validatedParams.limit = parsedLimit;\n }\n\n // offset validation\n if (offsetQuery !== undefined) {\n if (typeof offsetQuery !== 'string') {\n throw new InputError('offset must be a string with number value.');\n }\n const parsedOffset = parseInt(offsetQuery, 10);\n if (isNaN(parsedOffset) || parsedOffset < 0) {\n throw new InputError('offset must be a non-negative integer.');\n }\n validatedParams.offset = parsedOffset;\n }\n\n // order validation\n if (orderQuery !== undefined) {\n if (typeof orderQuery !== 'string') {\n throw new InputError('order must be a string.');\n }\n if (orderQuery !== 'asc' && orderQuery !== 'desc') {\n throw new InputError(\"order must be 'asc' or 'desc'.\");\n }\n validatedParams.order = orderQuery as Order;\n }\n\n // state validation\n validatedParams.state = parseAndValidateMultiValueQueryParam(\n stateQuery as string | undefined,\n ALLOWED_STATES,\n 'state',\n );\n\n // priority validation\n validatedParams.priority = parseAndValidateMultiValueQueryParam(\n priorityQuery as string | undefined,\n ALLOWED_PRIORITIES,\n 'priority',\n );\n\n // search validation\n if (searchQuery !== undefined) {\n if (typeof searchQuery !== 'string') {\n throw new InputError(`search must be a string.`);\n }\n validatedParams.search = searchQuery;\n }\n\n // orderBy validation\n if (orderByQuery !== undefined) {\n if (typeof orderByQuery !== 'string') {\n throw new InputError('orderBy must be a string.');\n }\n if (!ALLOWED_ORDER_BY_FIELDS.includes(orderByQuery)) {\n throw new InputError(\n `Invalid orderBy field. Allowed values are: ${ALLOWED_ORDER_BY_FIELDS.join(\n ', ',\n )}`,\n );\n }\n validatedParams.orderBy = orderByQuery;\n }\n\n return validatedParams;\n}\n"],"names":["IncidentFieldEnum","InputError"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"client.cjs.js","sources":["../../src/service-now-rest/client.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport {\n IncidentPick,\n PaginatedIncidents,\n} from '@backstage-community/plugin-servicenow-common';\nimport { ServiceNowConnection } from './connection';\n\nconst INCIDENT_QUERY_KEYS_ARRAY = [\n 'userEmail',\n 'u_backstage_entity_id',\n 'state',\n 'priority',\n 'search',\n 'limit',\n 'offset',\n 'order',\n 'orderBy',\n] as const;\n\nexport type IncidentQueryKeyString = (typeof INCIDENT_QUERY_KEYS_ARRAY)[number];\n\n// Plugin UI specific query parameters (pagination, search, ordering, tickets filter)\nexport type PredefinedIncidentQueryParams = {\n [K in IncidentQueryKeyString]?: K extends 'order'\n ? 'asc' | 'desc'\n : K extends 'limit' | 'offset'\n ? number\n : string;\n};\n\n// UI-specific query parameters joined with user's custom incident table fields query parameters\nexport type IncidentQueryParams = PredefinedIncidentQueryParams & {\n [key: string]: any;\n};\n\nexport type IncidentQueryKeys = keyof IncidentQueryParams;\n\nexport function isPredefinedIncidentQueryKey(keyName: string): boolean {\n return INCIDENT_QUERY_KEYS_ARRAY.includes(keyName as IncidentQueryKeyString);\n}\n\nexport interface ServiceNowClient {\n fetchIncidents(options: IncidentQueryParams): Promise<PaginatedIncidents>;\n}\n\nexport class DefaultServiceNowClient implements ServiceNowClient {\n constructor(\n private readonly conn: ServiceNowConnection,\n private readonly logger: LoggerService,\n ) {}\n\n async fetchIncidents(options: IncidentQueryParams): Promise<{\n items: IncidentPick[];\n totalCount: number;\n }> {\n const authHeaders = await this.conn.getAuthHeaders();\n const queryParts: string[] = [];\n\n const responseFields = [\n 'sys_id',\n 'number',\n 'short_description',\n 'description',\n 'sys_created_on',\n 'priority',\n 'incident_state',\n ];\n for (const [key, value] of Object.entries(options)) {\n if (!isPredefinedIncidentQueryKey(key)) {\n queryParts.push(`${key}=${value}`);\n responseFields.push(key);\n }\n }\n\n if (options.userEmail) {\n const id = await this.getUserSysIdByEmail(options.userEmail);\n queryParts.push(`caller_id=${id}^ORopened_by=${id}^ORassigned_to=${id}`);\n }\n\n if (options.state) queryParts.push(`state${options.state}`);\n if (options.priority) queryParts.push(`priority${options.priority}`);\n\n if (options.search) {\n const searchTerm = options.search;\n queryParts.push(\n `numberLIKE${searchTerm}^ORshort_descriptionLIKE${searchTerm}^ORdescriptionLIKE${searchTerm}`,\n );\n }\n\n if (options.orderBy) {\n queryParts.push(\n `${options.order === 'desc' ? 'ORDERBYDESC' : 'ORDERBY'}${\n options.orderBy\n }`,\n );\n }\n\n const sysparmQuery = queryParts.join('^');\n\n const params = new URLSearchParams();\n if (sysparmQuery) params.append('sysparm_query', sysparmQuery);\n if (options.limit !== undefined)\n params.append('sysparm_limit', String(options.limit));\n if (options.offset !== undefined)\n params.append('sysparm_offset', String(options.offset));\n\n params.append('sysparm_fields', responseFields.join(','));\n params.append('sysparm_count', 'true');\n\n const requestUrl = `api/now/table/incident?${params.toString()}`;\n this.logger.info(`Fetching incidents from ServiceNow: ${requestUrl}`);\n\n try {\n const response = await this.conn.getAxiosInstance().get(requestUrl, {\n headers: {\n ...authHeaders,\n Accept: 'application/json',\n },\n timeout: 30000,\n });\n\n const countHeader =\n response.headers['x-total-count'] ?? response.headers['X-Total-Count'];\n const totalCount = Number(countHeader ?? 0);\n\n const instanceUrl = this.conn.getInstanceUrl();\n const items =\n response.data?.result?.map((incident: any) => ({\n ...incident,\n url: `${instanceUrl}/nav_to.do?uri=incident.do?sys_id=${incident.sys_id}`,\n })) ?? [];\n\n return { items, totalCount };\n } catch (error: any) {\n this.logger.error(`Failed to fetch incidents: ${error.message}`, {\n error,\n });\n throw new Error(`Failed to fetch incidents: ${error.message}`);\n }\n }\n\n private async getUserSysIdByEmail(email: string): Promise<string | null> {\n const authHeaders = await this.conn.getAuthHeaders();\n const url = `api/now/table/sys_user?sysparm_query=email=${encodeURIComponent(\n email,\n )}&sysparm_fields=sys_id`;\n const response = await this.conn.getAxiosInstance().get(url, {\n headers: {\n ...authHeaders,\n Accept: 'application/json',\n },\n });\n const users = response.data?.result;\n if (users && users.length > 0) return users[0].sys_id;\n\n throw new Error(`User with email ${email} not found in ServiceNow.`);\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.cjs.js","sources":["../../src/service-now-rest/client.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport {\n IncidentPick,\n PaginatedIncidents,\n} from '@backstage-community/plugin-servicenow-common';\nimport { ServiceNowConnection } from './connection';\n\nconst INCIDENT_QUERY_KEYS_ARRAY = [\n 'userEmail',\n 'u_backstage_entity_id',\n 'state',\n 'priority',\n 'search',\n 'limit',\n 'offset',\n 'order',\n 'orderBy',\n] as const;\n\nexport type IncidentQueryKeyString = (typeof INCIDENT_QUERY_KEYS_ARRAY)[number];\n\n// Plugin UI specific query parameters (pagination, search, ordering, tickets filter)\nexport type PredefinedIncidentQueryParams = {\n [K in IncidentQueryKeyString]?: K extends 'order'\n ? 'asc' | 'desc'\n : K extends 'limit' | 'offset'\n ? number\n : string;\n};\n\n// UI-specific query parameters joined with user's custom incident table fields query parameters\nexport type IncidentQueryParams = PredefinedIncidentQueryParams & {\n [key: string]: any;\n};\n\nexport type IncidentQueryKeys = keyof IncidentQueryParams;\n\nexport function isPredefinedIncidentQueryKey(keyName: string): boolean {\n return INCIDENT_QUERY_KEYS_ARRAY.includes(keyName as IncidentQueryKeyString);\n}\n\nexport interface ServiceNowClient {\n fetchIncidents(options: IncidentQueryParams): Promise<PaginatedIncidents>;\n}\n\nexport class DefaultServiceNowClient implements ServiceNowClient {\n constructor(\n private readonly conn: ServiceNowConnection,\n private readonly logger: LoggerService,\n ) {}\n\n async fetchIncidents(options: IncidentQueryParams): Promise<{\n items: IncidentPick[];\n totalCount: number;\n }> {\n const authHeaders = await this.conn.getAuthHeaders();\n const queryParts: string[] = [];\n\n const responseFields = [\n 'sys_id',\n 'number',\n 'short_description',\n 'description',\n 'sys_created_on',\n 'priority',\n 'incident_state',\n ];\n for (const [key, value] of Object.entries(options)) {\n if (!isPredefinedIncidentQueryKey(key)) {\n queryParts.push(`${key}=${value}`);\n responseFields.push(key);\n }\n }\n\n if (options.userEmail) {\n const id = await this.getUserSysIdByEmail(options.userEmail);\n queryParts.push(`caller_id=${id}^ORopened_by=${id}^ORassigned_to=${id}`);\n }\n\n if (options.state) queryParts.push(`state${options.state}`);\n if (options.priority) queryParts.push(`priority${options.priority}`);\n\n if (options.search) {\n const searchTerm = options.search;\n queryParts.push(\n `numberLIKE${searchTerm}^ORshort_descriptionLIKE${searchTerm}^ORdescriptionLIKE${searchTerm}`,\n );\n }\n\n if (options.orderBy) {\n queryParts.push(\n `${options.order === 'desc' ? 'ORDERBYDESC' : 'ORDERBY'}${\n options.orderBy\n }`,\n );\n }\n\n const sysparmQuery = queryParts.join('^');\n\n const params = new URLSearchParams();\n if (sysparmQuery) params.append('sysparm_query', sysparmQuery);\n if (options.limit !== undefined)\n params.append('sysparm_limit', String(options.limit));\n if (options.offset !== undefined)\n params.append('sysparm_offset', String(options.offset));\n\n params.append('sysparm_fields', responseFields.join(','));\n params.append('sysparm_count', 'true');\n\n const requestUrl = `api/now/table/incident?${params.toString()}`;\n this.logger.info(`Fetching incidents from ServiceNow: ${requestUrl}`);\n\n try {\n const response = await this.conn.getAxiosInstance().get(requestUrl, {\n headers: {\n ...authHeaders,\n Accept: 'application/json',\n },\n timeout: 30000,\n });\n\n const countHeader =\n response.headers['x-total-count'] ?? response.headers['X-Total-Count'];\n const totalCount = Number(countHeader ?? 0);\n\n const instanceUrl = this.conn.getInstanceUrl();\n const items =\n response.data?.result?.map((incident: any) => ({\n ...incident,\n url: `${instanceUrl}/nav_to.do?uri=incident.do?sys_id=${incident.sys_id}`,\n })) ?? [];\n\n return { items, totalCount };\n } catch (error: any) {\n this.logger.error(`Failed to fetch incidents: ${error.message}`, {\n error,\n });\n throw new Error(`Failed to fetch incidents: ${error.message}`);\n }\n }\n\n private async getUserSysIdByEmail(email: string): Promise<string | null> {\n const authHeaders = await this.conn.getAuthHeaders();\n const url = `api/now/table/sys_user?sysparm_query=email=${encodeURIComponent(\n email,\n )}&sysparm_fields=sys_id`;\n const response = await this.conn.getAxiosInstance().get(url, {\n headers: {\n ...authHeaders,\n Accept: 'application/json',\n },\n });\n const users = response.data?.result;\n if (users && users.length > 0) return users[0].sys_id;\n\n throw new Error(`User with email ${email} not found in ServiceNow.`);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"connection.cjs.js","sources":["../../src/service-now-rest/connection.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { OAuthConfig, ServiceNowConfig } from '../../config';\nimport {\n ClientCredentials,\n ResourceOwnerPassword,\n ModuleOptions,\n AccessToken,\n} from 'simple-oauth2';\nimport axios, { AxiosInstance } from 'axios';\nimport https from 'https';\n\nexport class ServiceNowConnection {\n private oauthClient?: ClientCredentials | ResourceOwnerPassword;\n private readonly instanceUrl: string;\n private readonly config: ServiceNowConfig;\n private readonly logger: LoggerService;\n private axiosInstance: AxiosInstance;\n\n constructor(config: ServiceNowConfig, logger: LoggerService) {\n this.config = config;\n this.logger = logger;\n\n if (!config.servicenow?.instanceUrl) {\n logger.error('ServiceNow instance url is missing. Please configure it.');\n throw new Error(\n 'ServiceNow instance url is missing. Please configure it.',\n );\n }\n this.instanceUrl = config.servicenow?.instanceUrl.replace(/\\/$/, '');\n\n if (!config.servicenow?.oauth && !config.servicenow?.basicAuth) {\n logger.error(\n 'ServiceNow authentication configuration is missing. Please configure either OAuth or Basic Auth.',\n );\n throw new Error(\n 'ServiceNow authentication configuration is missing. Please configure either OAuth or Basic Auth.',\n );\n }\n\n if (config.servicenow?.oauth) {\n this.setupOAuthClient(config.servicenow.oauth);\n }\n\n if (config.servicenow?.basicAuth) {\n logger.warn(\n 'Basic authentication is configured for ServiceNow. This is not recommended for production environments.',\n );\n }\n\n this.axiosInstance = axios.create({\n baseURL: this.instanceUrl,\n httpsAgent: new https.Agent({ keepAlive: true, maxSockets: 10 }),\n timeout: 10000,\n });\n }\n\n getAxiosInstance(): AxiosInstance {\n return this.axiosInstance;\n }\n\n getInstanceUrl(): string {\n return this.instanceUrl;\n }\n\n private setupOAuthClient(oauth: OAuthConfig) {\n const determinedTokenUrl = `${this.instanceUrl}/oauth_token.do`;\n\n let tokenHost: string;\n let tokenPath: string;\n try {\n const parsedTokenUrl = new URL(determinedTokenUrl);\n tokenHost = parsedTokenUrl.origin;\n tokenPath = parsedTokenUrl.pathname;\n } catch (e: any) {\n this.logger.error(\n `Invalid tokenUrl constructed or provided: ${determinedTokenUrl}. Error: ${e.message}`,\n );\n throw new Error(`Invalid tokenUrl: ${determinedTokenUrl}`);\n }\n\n const oauthModuleOptions: ModuleOptions = {\n client: {\n id: oauth.clientId,\n secret: oauth.clientSecret,\n },\n auth: {\n tokenHost: tokenHost,\n tokenPath: tokenPath,\n },\n options: {\n authorizationMethod: 'body',\n },\n };\n\n if (oauth.grantType === 'client_credentials') {\n this.oauthClient = new ClientCredentials(oauthModuleOptions);\n } else if (oauth.grantType === 'password') {\n if (!oauth.username || !oauth.password) {\n this.logger.error(\n \"Username and/or password missing for 'password' grant type in ServiceNow OAuth config.\",\n );\n throw new Error(\n \"Username and/or password missing for 'password' grant type.\",\n );\n }\n this.oauthClient = new ResourceOwnerPassword(oauthModuleOptions);\n } else {\n const grantType = (oauth as any).grantType;\n this.logger.error(`Unsupported OAuth grantType: ${grantType}`);\n throw new Error(`Unsupported OAuth grantType: ${grantType}`);\n }\n }\n\n async getAuthHeaders(): Promise<{ Authorization: string }> {\n if (this.config.servicenow?.basicAuth) {\n const { username, password } = this.config.servicenow.basicAuth;\n const encodedCredentials = Buffer.from(\n `${username}:${password}`,\n ).toString('base64');\n return { Authorization: `Basic ${encodedCredentials}` };\n }\n\n if (this.config.servicenow?.oauth && this.oauthClient) {\n let accessToken: AccessToken;\n try {\n if (this.config.servicenow.oauth.grantType === 'client_credentials') {\n accessToken = await (this.oauthClient as ClientCredentials).getToken(\n {},\n );\n } else if (this.config.servicenow.oauth.grantType === 'password') {\n if (\n !this.config.servicenow.oauth.username ||\n !this.config.servicenow.oauth.password\n ) {\n throw new Error(\n \"Username or password missing for 'password' grant type during token acquisition.\",\n );\n }\n accessToken = await (\n this.oauthClient as ResourceOwnerPassword\n ).getToken({\n username: this.config.servicenow.oauth.username,\n password: this.config.servicenow.oauth.password,\n });\n } else {\n throw new Error(\n `Unsupported grantType in getAuthHeaders: ${\n (this.config.servicenow.oauth as any).grantType\n }`,\n );\n }\n\n const tokenData = accessToken.token;\n if (\n !tokenData ||\n typeof tokenData.access_token !== 'string' ||\n !tokenData.access_token\n ) {\n throw new Error(\n 'Failed to obtain access_token string (token data is invalid or missing).',\n );\n }\n return { Authorization: `Bearer ${tokenData.access_token}` };\n } catch (error: any) {\n this.logger.error(`Error fetching ServiceNow token: ${error.message}`, {\n error: error.stack || error,\n });\n if (error.isAxiosError && error.response) {\n this.logger.error(\n `OAuth2 token error details: Status ${\n error.response.status\n }, Data: ${JSON.stringify(error.response.data)}`,\n );\n } else if (error.data && error.data.payload) {\n this.logger.error(\n `OAuth2 token error payload: ${JSON.stringify(error.data.payload)}`,\n );\n }\n throw new Error(`Failed to obtain access token: ${error.message}`);\n }\n }\n\n throw new Error('No authentication method configured.');\n }\n}\n"],"names":["axios","https","ClientCredentials","ResourceOwnerPassword"],"mappings":"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"}
1
+ {"version":3,"file":"connection.cjs.js","sources":["../../src/service-now-rest/connection.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { OAuthConfig, ServiceNowConfig } from '../../config';\nimport {\n ClientCredentials,\n ResourceOwnerPassword,\n ModuleOptions,\n AccessToken,\n} from 'simple-oauth2';\nimport axios, { AxiosInstance } from 'axios';\nimport https from 'https';\n\nexport class ServiceNowConnection {\n private oauthClient?: ClientCredentials | ResourceOwnerPassword;\n private readonly instanceUrl: string;\n private readonly config: ServiceNowConfig;\n private readonly logger: LoggerService;\n private axiosInstance: AxiosInstance;\n\n constructor(config: ServiceNowConfig, logger: LoggerService) {\n this.config = config;\n this.logger = logger;\n\n if (!config.servicenow?.instanceUrl) {\n logger.error('ServiceNow instance url is missing. Please configure it.');\n throw new Error(\n 'ServiceNow instance url is missing. Please configure it.',\n );\n }\n this.instanceUrl = config.servicenow?.instanceUrl.replace(/\\/$/, '');\n\n if (!config.servicenow?.oauth && !config.servicenow?.basicAuth) {\n logger.error(\n 'ServiceNow authentication configuration is missing. Please configure either OAuth or Basic Auth.',\n );\n throw new Error(\n 'ServiceNow authentication configuration is missing. Please configure either OAuth or Basic Auth.',\n );\n }\n\n if (config.servicenow?.oauth) {\n this.setupOAuthClient(config.servicenow.oauth);\n }\n\n if (config.servicenow?.basicAuth) {\n logger.warn(\n 'Basic authentication is configured for ServiceNow. This is not recommended for production environments.',\n );\n }\n\n this.axiosInstance = axios.create({\n baseURL: this.instanceUrl,\n httpsAgent: new https.Agent({ keepAlive: true, maxSockets: 10 }),\n timeout: 10000,\n });\n }\n\n getAxiosInstance(): AxiosInstance {\n return this.axiosInstance;\n }\n\n getInstanceUrl(): string {\n return this.instanceUrl;\n }\n\n private setupOAuthClient(oauth: OAuthConfig) {\n const determinedTokenUrl = `${this.instanceUrl}/oauth_token.do`;\n\n let tokenHost: string;\n let tokenPath: string;\n try {\n const parsedTokenUrl = new URL(determinedTokenUrl);\n tokenHost = parsedTokenUrl.origin;\n tokenPath = parsedTokenUrl.pathname;\n } catch (e: any) {\n this.logger.error(\n `Invalid tokenUrl constructed or provided: ${determinedTokenUrl}. Error: ${e.message}`,\n );\n throw new Error(`Invalid tokenUrl: ${determinedTokenUrl}`);\n }\n\n const oauthModuleOptions: ModuleOptions = {\n client: {\n id: oauth.clientId,\n secret: oauth.clientSecret,\n },\n auth: {\n tokenHost: tokenHost,\n tokenPath: tokenPath,\n },\n options: {\n authorizationMethod: 'body',\n },\n };\n\n if (oauth.grantType === 'client_credentials') {\n this.oauthClient = new ClientCredentials(oauthModuleOptions);\n } else if (oauth.grantType === 'password') {\n if (!oauth.username || !oauth.password) {\n this.logger.error(\n \"Username and/or password missing for 'password' grant type in ServiceNow OAuth config.\",\n );\n throw new Error(\n \"Username and/or password missing for 'password' grant type.\",\n );\n }\n this.oauthClient = new ResourceOwnerPassword(oauthModuleOptions);\n } else {\n const grantType = (oauth as any).grantType;\n this.logger.error(`Unsupported OAuth grantType: ${grantType}`);\n throw new Error(`Unsupported OAuth grantType: ${grantType}`);\n }\n }\n\n async getAuthHeaders(): Promise<{ Authorization: string }> {\n if (this.config.servicenow?.basicAuth) {\n const { username, password } = this.config.servicenow.basicAuth;\n const encodedCredentials = Buffer.from(\n `${username}:${password}`,\n ).toString('base64');\n return { Authorization: `Basic ${encodedCredentials}` };\n }\n\n if (this.config.servicenow?.oauth && this.oauthClient) {\n let accessToken: AccessToken;\n try {\n if (this.config.servicenow.oauth.grantType === 'client_credentials') {\n accessToken = await (this.oauthClient as ClientCredentials).getToken(\n {},\n );\n } else if (this.config.servicenow.oauth.grantType === 'password') {\n if (\n !this.config.servicenow.oauth.username ||\n !this.config.servicenow.oauth.password\n ) {\n throw new Error(\n \"Username or password missing for 'password' grant type during token acquisition.\",\n );\n }\n accessToken = await (\n this.oauthClient as ResourceOwnerPassword\n ).getToken({\n username: this.config.servicenow.oauth.username,\n password: this.config.servicenow.oauth.password,\n });\n } else {\n throw new Error(\n `Unsupported grantType in getAuthHeaders: ${\n (this.config.servicenow.oauth as any).grantType\n }`,\n );\n }\n\n const tokenData = accessToken.token;\n if (\n !tokenData ||\n typeof tokenData.access_token !== 'string' ||\n !tokenData.access_token\n ) {\n throw new Error(\n 'Failed to obtain access_token string (token data is invalid or missing).',\n );\n }\n return { Authorization: `Bearer ${tokenData.access_token}` };\n } catch (error: any) {\n this.logger.error(`Error fetching ServiceNow token: ${error.message}`, {\n error: error.stack || error,\n });\n if (error.isAxiosError && error.response) {\n this.logger.error(\n `OAuth2 token error details: Status ${\n error.response.status\n }, Data: ${JSON.stringify(error.response.data)}`,\n );\n } else if (error.data && error.data.payload) {\n this.logger.error(\n `OAuth2 token error payload: ${JSON.stringify(error.data.payload)}`,\n );\n }\n throw new Error(`Failed to obtain access token: ${error.message}`);\n }\n }\n\n throw new Error('No authentication method configured.');\n }\n}\n"],"names":["axios","https","ClientCredentials","ResourceOwnerPassword"],"mappings":"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"}
@@ -26,8 +26,8 @@ class ServiceNowSchemaChecker {
26
26
  if (response.status !== 200) {
27
27
  throw new Error(`ServiceNow API returned status ${response.status}`);
28
28
  }
29
- const contentType = response.headers["content-type"] ?? "";
30
- if (!contentType.includes("application/json")) {
29
+ const contentType = response.headers["content-type"];
30
+ if (!contentType || typeof contentType === "string" && !contentType.includes("application/json") || Array.isArray(contentType) && !contentType.some((ct) => ct.includes("application/json")) || !Array.isArray(contentType) && typeof contentType === "object" && !contentType.hasContentType("application/json")) {
31
31
  throw new Error(
32
32
  `Expected JSON response but got Content-Type: ${contentType}`
33
33
  );
@@ -1 +1 @@
1
- {"version":3,"file":"schema-checker.cjs.js","sources":["../../src/service-now-rest/schema-checker.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ServiceNowConnection } from './connection';\n\ntype CachedSchema = {\n fields: Set<string>;\n fetchedAt: number;\n};\n\nconst INCIDENT_TABLE_NAME = 'incident';\nconst SCHEMA_TTL_MS = 5 * 60 * 1000; // 5 minutes cache timeout\n\nexport class ServiceNowSchemaChecker {\n private cache?: CachedSchema;\n\n constructor(private readonly conn: ServiceNowConnection) {}\n\n private async fetchIncidentSchema(): Promise<Set<string>> {\n try {\n const authHeaders = await this.conn.getAuthHeaders();\n const url = `api/now/table/sys_dictionary`;\n\n const response = await this.conn.getAxiosInstance().get(url, {\n headers: {\n ...authHeaders,\n Accept: 'application/json',\n },\n params: {\n sysparm_query: `name=${INCIDENT_TABLE_NAME}`,\n sysparm_fields: 'element',\n sysparm_limit: 500,\n },\n responseType: 'json',\n });\n\n if (response.status !== 200) {\n throw new Error(`ServiceNow API returned status ${response.status}`);\n }\n\n const contentType = response.headers['content-type'] ?? '';\n if (!contentType.includes('application/json')) {\n throw new Error(\n `Expected JSON response but got Content-Type: ${contentType}`,\n );\n }\n\n const data = response.data;\n if (!data || typeof data !== 'object') {\n throw new Error('ServiceNow API returned invalid JSON response');\n }\n\n if (data.error) {\n throw new Error(`ServiceNow API Error: ${data.error.message}`);\n }\n\n const result = data.result ?? [];\n return new Set(result.map((r: any) => r.element));\n } catch (e) {\n if (e instanceof Error) {\n throw new Error(`Failed to fetch incident schema: ${e.message}`);\n }\n throw e;\n }\n }\n\n private async getIncidentFields(): Promise<Set<string>> {\n const now = Date.now();\n\n if (this.cache && now - this.cache.fetchedAt < SCHEMA_TTL_MS) {\n return this.cache.fields;\n }\n\n const fields = await this.fetchIncidentSchema();\n\n this.cache = {\n fields,\n fetchedAt: now,\n };\n\n return fields;\n }\n\n public async fieldExists(...fields: string[]): Promise<boolean> {\n if (fields.length === 0) {\n return true;\n }\n const incidentFields = await this.getIncidentFields();\n return fields.every(field => incidentFields.has(field));\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema-checker.cjs.js","sources":["../../src/service-now-rest/schema-checker.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ServiceNowConnection } from './connection';\n\ntype CachedSchema = {\n fields: Set<string>;\n fetchedAt: number;\n};\n\nconst INCIDENT_TABLE_NAME = 'incident';\nconst SCHEMA_TTL_MS = 5 * 60 * 1000; // 5 minutes cache timeout\n\nexport class ServiceNowSchemaChecker {\n private cache?: CachedSchema;\n\n constructor(private readonly conn: ServiceNowConnection) {}\n\n private async fetchIncidentSchema(): Promise<Set<string>> {\n try {\n const authHeaders = await this.conn.getAuthHeaders();\n const url = `api/now/table/sys_dictionary`;\n\n const response = await this.conn.getAxiosInstance().get(url, {\n headers: {\n ...authHeaders,\n Accept: 'application/json',\n },\n params: {\n sysparm_query: `name=${INCIDENT_TABLE_NAME}`,\n sysparm_fields: 'element',\n sysparm_limit: 500,\n },\n responseType: 'json',\n });\n\n if (response.status !== 200) {\n throw new Error(`ServiceNow API returned status ${response.status}`);\n }\n\n const contentType = response.headers['content-type'];\n if (\n !contentType ||\n (typeof contentType === 'string' &&\n !contentType.includes('application/json')) ||\n (Array.isArray(contentType) &&\n !contentType.some(ct => ct.includes('application/json'))) ||\n (!Array.isArray(contentType) &&\n typeof contentType === 'object' &&\n !contentType.hasContentType('application/json'))\n ) {\n throw new Error(\n `Expected JSON response but got Content-Type: ${contentType}`,\n );\n }\n\n const data = response.data;\n if (!data || typeof data !== 'object') {\n throw new Error('ServiceNow API returned invalid JSON response');\n }\n\n if (data.error) {\n throw new Error(`ServiceNow API Error: ${data.error.message}`);\n }\n\n const result = data.result ?? [];\n return new Set(result.map((r: any) => r.element));\n } catch (e) {\n if (e instanceof Error) {\n throw new Error(`Failed to fetch incident schema: ${e.message}`);\n }\n throw e;\n }\n }\n\n private async getIncidentFields(): Promise<Set<string>> {\n const now = Date.now();\n\n if (this.cache && now - this.cache.fetchedAt < SCHEMA_TTL_MS) {\n return this.cache.fields;\n }\n\n const fields = await this.fetchIncidentSchema();\n\n this.cache = {\n fields,\n fetchedAt: now,\n };\n\n return fields;\n }\n\n public async fieldExists(...fields: string[]): Promise<boolean> {\n if (fields.length === 0) {\n return true;\n }\n const incidentFields = await this.getIncidentFields();\n return fields.every(field => incidentFields.has(field));\n }\n}\n"],"names":[],"mappings":";;AAuBA,MAAM,mBAAA,GAAsB,UAAA;AAC5B,MAAM,aAAA,GAAgB,IAAI,EAAA,GAAK,GAAA;AAExB,MAAM,uBAAA,CAAwB;AAAA,EAGnC,YAA6B,IAAA,EAA4B;AAA5B,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAA6B;AAAA,EAFlD,KAAA;AAAA,EAIR,MAAc,mBAAA,GAA4C;AACxD,IAAA,IAAI;AACF,MAAA,MAAM,WAAA,GAAc,MAAM,IAAA,CAAK,IAAA,CAAK,cAAA,EAAe;AACnD,MAAA,MAAM,GAAA,GAAM,CAAA,4BAAA,CAAA;AAEZ,MAAA,MAAM,WAAW,MAAM,IAAA,CAAK,KAAK,gBAAA,EAAiB,CAAE,IAAI,GAAA,EAAK;AAAA,QAC3D,OAAA,EAAS;AAAA,UACP,GAAG,WAAA;AAAA,UACH,MAAA,EAAQ;AAAA,SACV;AAAA,QACA,MAAA,EAAQ;AAAA,UACN,aAAA,EAAe,QAAQ,mBAAmB,CAAA,CAAA;AAAA,UAC1C,cAAA,EAAgB,SAAA;AAAA,UAChB,aAAA,EAAe;AAAA,SACjB;AAAA,QACA,YAAA,EAAc;AAAA,OACf,CAAA;AAED,MAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,+BAAA,EAAkC,QAAA,CAAS,MAAM,CAAA,CAAE,CAAA;AAAA,MACrE;AAEA,MAAA,MAAM,WAAA,GAAc,QAAA,CAAS,OAAA,CAAQ,cAAc,CAAA;AACnD,MAAA,IACE,CAAC,WAAA,IACA,OAAO,WAAA,KAAgB,YACtB,CAAC,WAAA,CAAY,QAAA,CAAS,kBAAkB,KACzC,KAAA,CAAM,OAAA,CAAQ,WAAW,CAAA,IACxB,CAAC,WAAA,CAAY,IAAA,CAAK,CAAA,EAAA,KAAM,EAAA,CAAG,SAAS,kBAAkB,CAAC,CAAA,IACxD,CAAC,MAAM,OAAA,CAAQ,WAAW,CAAA,IACzB,OAAO,gBAAgB,QAAA,IACvB,CAAC,WAAA,CAAY,cAAA,CAAe,kBAAkB,CAAA,EAChD;AACA,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,gDAAgD,WAAW,CAAA;AAAA,SAC7D;AAAA,MACF;AAEA,MAAA,MAAM,OAAO,QAAA,CAAS,IAAA;AACtB,MAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AACrC,QAAA,MAAM,IAAI,MAAM,+CAA+C,CAAA;AAAA,MACjE;AAEA,MAAA,IAAI,KAAK,KAAA,EAAO;AACd,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyB,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,MAC/D;AAEA,MAAA,MAAM,MAAA,GAAS,IAAA,CAAK,MAAA,IAAU,EAAC;AAC/B,MAAA,OAAO,IAAI,IAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAW,CAAA,CAAE,OAAO,CAAC,CAAA;AAAA,IAClD,SAAS,CAAA,EAAG;AACV,MAAA,IAAI,aAAa,KAAA,EAAO;AACtB,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,iCAAA,EAAoC,CAAA,CAAE,OAAO,CAAA,CAAE,CAAA;AAAA,MACjE;AACA,MAAA,MAAM,CAAA;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAc,iBAAA,GAA0C;AACtD,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AAErB,IAAA,IAAI,KAAK,KAAA,IAAS,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,YAAY,aAAA,EAAe;AAC5D,MAAA,OAAO,KAAK,KAAA,CAAM,MAAA;AAAA,IACpB;AAEA,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,mBAAA,EAAoB;AAE9C,IAAA,IAAA,CAAK,KAAA,GAAQ;AAAA,MACX,MAAA;AAAA,MACA,SAAA,EAAW;AAAA,KACb;AAEA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,MAAa,eAAe,MAAA,EAAoC;AAC9D,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,MAAM,cAAA,GAAiB,MAAM,IAAA,CAAK,iBAAA,EAAkB;AACpD,IAAA,OAAO,OAAO,KAAA,CAAM,CAAA,KAAA,KAAS,cAAA,CAAe,GAAA,CAAI,KAAK,CAAC,CAAA;AAAA,EACxD;AACF;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage-community/plugin-servicenow-backend",
3
- "version": "1.10.0",
3
+ "version": "1.11.1",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/index.cjs.js",
6
6
  "types": "dist/index.d.ts",
@@ -36,21 +36,21 @@
36
36
  "postpack": "backstage-cli package postpack"
37
37
  },
38
38
  "dependencies": {
39
- "@backstage-community/plugin-servicenow-common": "^1.9.0",
40
- "@backstage/backend-defaults": "^0.16.0",
41
- "@backstage/backend-plugin-api": "^1.8.0",
42
- "@backstage/config": "^1.3.6",
43
- "@backstage/errors": "^1.2.7",
44
- "@backstage/plugin-catalog-node": "^2.1.0",
45
- "axios": "^1.9.0",
39
+ "@backstage-community/plugin-servicenow-common": "^1.10.0",
40
+ "@backstage/backend-defaults": "^0.17.0",
41
+ "@backstage/backend-plugin-api": "^1.9.0",
42
+ "@backstage/config": "^1.3.7",
43
+ "@backstage/errors": "^1.3.0",
44
+ "@backstage/plugin-catalog-node": "^2.2.0",
45
+ "axios": "^1.16.0",
46
46
  "express": "^4.17.1",
47
47
  "express-promise-router": "^4.1.0",
48
48
  "simple-oauth2": "^5.1.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@backstage/backend-test-utils": "^1.11.1",
52
- "@backstage/cli": "^0.36.0",
53
- "@backstage/plugin-catalog-node": "^2.1.0",
51
+ "@backstage/backend-test-utils": "^1.11.2",
52
+ "@backstage/cli": "^0.36.1",
53
+ "@backstage/plugin-catalog-node": "^2.2.0",
54
54
  "@types/express": "^4.17.6",
55
55
  "@types/qs": "^6",
56
56
  "@types/simple-oauth2": "^5",