Skip to content

Commit

Permalink
SALTO-6422: add AQL placeholder change validator (#6362)
Browse files Browse the repository at this point in the history
* add assetsObjectFieldConfigurationAqlValidator
  • Loading branch information
shayc331 authored Aug 8, 2024
1 parent e052a8f commit 9344c2a
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright 2024 Salto Labs Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {
ChangeValidator,
getChangeData,
InstanceElement,
isAdditionOrModificationChange,
isInstanceChange,
} from '@salto-io/adapter-api'
import _ from 'lodash'
import { getParent } from '@salto-io/adapter-utils'
import JiraClient from '../../client/client'
import { FIELD_CONTEXT_TYPE_NAME } from '../../filters/fields/constants'
import { removeCustomFieldPrefix } from '../../filters/jql/template_expression_generator'

const PLACEHOLDER_PATTERN = /\$\{(.*)\}/

const isAqlHasPlaceholder = (aql: string): boolean => {
const matches = aql.match(PLACEHOLDER_PATTERN)
return matches !== null && matches.length > 0
}

const getFieldContextsUrl = (instance: InstanceElement, client: JiraClient): URL => {
const customFieldId = removeCustomFieldPrefix(getParent(instance).value.id)
const fieldContextsUrlSuffix = `secure/admin/ConfigureCustomField!default.jspa?customFieldId=${customFieldId}`
return new URL(fieldContextsUrlSuffix, client.baseUrl)
}

export const assetsObjectFieldConfigurationAqlValidator: (client: JiraClient) => ChangeValidator =
client => async changes =>
changes
.filter(isInstanceChange)
.filter(isAdditionOrModificationChange)
.map(getChangeData)
.filter(instance => instance.elemID.typeName === FIELD_CONTEXT_TYPE_NAME)
.filter(instance => _.isString(instance.value.assetsObjectFieldConfiguration?.issueScopeFilterQuery))
.filter(instance => isAqlHasPlaceholder(instance.value.assetsObjectFieldConfiguration.issueScopeFilterQuery))
.map(instance => ({
elemID: instance.elemID.createNestedID('assetsObjectFieldConfiguration', 'issueScopeFilterQuery'),
severity: 'Warning',
message: 'AQL placeholders are not supported.',
detailedMessage:
'This AQL expression will be deployed as is. You may need to manually edit the ids later to match the target environment.',
deployActions: {
postAction: {
title: 'Edit AQL placeholders manually',
subActions: [
`Go to ${getFieldContextsUrl(instance, client)}`,
`Under the context "${instance.value.name}", click on "Edit Assets object/s field configuration"`,
'Inside "Filter issue scope" section, fix the placeholder with the correct value',
'Click "Save"',
],
},
},
}))
2 changes: 2 additions & 0 deletions packages/jira-adapter/src/change_validators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import { referencedWorkflowDeletionChangeValidator } from './workflowsV2/referen
import { missingExtensionsTransitionRulesChangeValidator } from './workflowsV2/missing_extensions_transition_rules'
import { fieldContextOptionsValidator } from './field_contexts/field_context_options'
import { ISSUE_TYPE_NAME, PORTAL_GROUP_TYPE, PROJECT_TYPE, SLA_TYPE_NAME } from '../constants'
import { assetsObjectFieldConfigurationAqlValidator } from './field_contexts/assets_object_field_configuration_aql'

const { deployTypesNotSupportedValidator, createChangeValidator, uniqueFieldsChangeValidatorCreator } =
deployment.changeValidators
Expand Down Expand Up @@ -147,6 +148,7 @@ export default (client: JiraClient, config: JiraConfig, paginator: clientUtils.P
deleteLabelAtttribute: deleteLabelAtttributeValidator(config),
jsmPermissions: jsmPermissionsValidator(config, client),
fieldContextOptions: fieldContextOptionsValidator,
assetsObjectFieldConfigurationAql: assetsObjectFieldConfigurationAqlValidator(client),
}

return createChangeValidator({
Expand Down
2 changes: 2 additions & 0 deletions packages/jira-adapter/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ export type ChangeValidatorName =
| 'jsmPermissions'
| 'fieldContextOptions'
| 'uniqueFields'
| 'assetsObjectFieldConfigurationAql'

type ChangeValidatorConfig = Partial<Record<ChangeValidatorName, boolean>>

Expand Down Expand Up @@ -328,6 +329,7 @@ const changeValidatorConfigType = createMatchingObjectType<ChangeValidatorConfig
jsmPermissions: { refType: BuiltinTypes.BOOLEAN },
fieldContextOptions: { refType: BuiltinTypes.BOOLEAN },
uniqueFields: { refType: BuiltinTypes.BOOLEAN },
assetsObjectFieldConfigurationAql: { refType: BuiltinTypes.BOOLEAN },
},
annotations: {
[CORE_ANNOTATIONS.ADDITIONAL_PROPERTIES]: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright 2024 Salto Labs Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { CORE_ANNOTATIONS, InstanceElement, ReferenceExpression, toChange } from '@salto-io/adapter-api'
import { createEmptyType, mockClient } from '../../utils'
import { assetsObjectFieldConfigurationAqlValidator } from '../../../src/change_validators/field_contexts/assets_object_field_configuration_aql'
import JiraClient from '../../../src/client/client'

describe('assetsObjectFieldConfigurationAql', () => {
let contextInstance: InstanceElement
let fieldInstance: InstanceElement
let client: JiraClient

beforeEach(() => {
const mockCli = mockClient()
client = mockCli.client
fieldInstance = new InstanceElement('field', createEmptyType('Field'), { id: 'customfield_10000' })
contextInstance = new InstanceElement(
'context',
createEmptyType('CustomFieldContext'),
{
name: 'context',
assetsObjectFieldConfiguration: {
issueScopeFilterQuery:
// eslint-disable-next-line no-template-curly-in-string
'object HAVING inboundReferences(objecttype = Server AND objectId IN (${customfield_14168${0}}))',
},
},
undefined,
{
[CORE_ANNOTATIONS.PARENT]: [new ReferenceExpression(fieldInstance.elemID, fieldInstance)],
},
)
})

it('should return warning for AQL with placeholder', async () => {
const result = await assetsObjectFieldConfigurationAqlValidator(client)([toChange({ after: contextInstance })])
expect(result).toHaveLength(1)
expect(result[0]).toEqual({
elemID: contextInstance.elemID.createNestedID('assetsObjectFieldConfiguration', 'issueScopeFilterQuery'),
severity: 'Warning',
message: 'AQL placeholders are not supported.',
detailedMessage:
'This AQL expression will be deployed as is. You may need to manually edit the ids later to match the target environment.',
deployActions: {
postAction: {
title: 'Edit AQL placeholders manually',
subActions: [
'Go to https://ori-salto-test.atlassian.net/secure/admin/ConfigureCustomField!default.jspa?customFieldId=10000',
'Under the context "context", click on "Edit Assets object/s field configuration"',
'Inside "Filter issue scope" section, fix the placeholder with the correct value',
'Click "Save"',
],
},
},
})
})

it('should not return warning for AQL without placeholder', async () => {
contextInstance.value.assetsObjectFieldConfiguration.issueScopeFilterQuery =
'object HAVING inboundReferences(objecttype = Server AND objectId IN (customfield_14168))'
const result = await assetsObjectFieldConfigurationAqlValidator(client)([toChange({ after: contextInstance })])
expect(result).toHaveLength(0)
})

it('should do nothing for non context changes', async () => {
const result = await assetsObjectFieldConfigurationAqlValidator(client)([
toChange({ after: createEmptyType('Field') }),
])
expect(result).toHaveLength(0)
})

it('should do nothing for context without assetsObjectFieldConfiguration', async () => {
contextInstance.value.assetsObjectFieldConfiguration = undefined
const result = await assetsObjectFieldConfigurationAqlValidator(client)([toChange({ after: contextInstance })])
expect(result).toHaveLength(0)
})

it('should do nothing for context without issueScopeFilterQuery', async () => {
contextInstance.value.assetsObjectFieldConfiguration.issueScopeFilterQuery = undefined
const result = await assetsObjectFieldConfigurationAqlValidator(client)([toChange({ after: contextInstance })])
expect(result).toHaveLength(0)
})
})

0 comments on commit 9344c2a

Please sign in to comment.