-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SALTO-7399: advanced fields references #7209
Changes from all commits
35341cd
872de59
fe71705
ac59d56
7f89b0f
5e25404
78722bc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Copyright 2025 Salto Labs Ltd. | ||
* Licensed under the Salto Terms of Use (the "License"); | ||
* You may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.salto.io/terms-of-use | ||
* | ||
* CERTAIN THIRD PARTY SOFTWARE MAY BE CONTAINED IN PORTIONS OF THE SOFTWARE. See NOTICE FILE AT https://github.com/salto-io/salto/blob/main/NOTICES | ||
*/ | ||
import { WALK_NEXT_STEP, WalkOnFunc, walkOnValue } from '@salto-io/adapter-utils' | ||
import { InstanceElement, Value } from '@salto-io/adapter-api' | ||
import { AutomationInstance, isAutomationInstance } from './smart_values/smart_value_reference_filter' | ||
|
||
const FIELDS_TO_WALK_ON = (): string[] => ['trigger', 'components'] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO it should be a constant and not a function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is something I learned from Seggev, they use it in Netsuite. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice! |
||
const ADVANCED_FIELDS = 'advancedFields' | ||
|
||
export const walkOnAutomation = ({ instance, func }: { instance: AutomationInstance; func: WalkOnFunc }): void => { | ||
FIELDS_TO_WALK_ON().forEach(fieldName => { | ||
if (instance.value[fieldName] !== undefined) { | ||
walkOnValue({ | ||
elemId: instance.elemID.createNestedID(fieldName), | ||
value: instance.value[fieldName], | ||
func, | ||
}) | ||
} | ||
}) | ||
} | ||
|
||
export const walkOnAutomations = (instances: InstanceElement[], func: WalkOnFunc): void => { | ||
instances.filter(isAutomationInstance).forEach(instance => walkOnAutomation({ instance, func })) | ||
} | ||
|
||
export const advancedFieldsReferenceFunc = | ||
(func: (value: Value, fieldName: string) => void): WalkOnFunc => | ||
({ value }): WALK_NEXT_STEP => { | ||
if (value == null) { | ||
return WALK_NEXT_STEP.SKIP | ||
} | ||
if (value[ADVANCED_FIELDS] !== undefined) { | ||
func(value, ADVANCED_FIELDS) | ||
return WALK_NEXT_STEP.SKIP | ||
} | ||
return WALK_NEXT_STEP.RECURSE | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,29 +5,52 @@ | |
* | ||
* CERTAIN THIRD PARTY SOFTWARE MAY BE CONTAINED IN PORTIONS OF THE SOFTWARE. See NOTICE FILE AT https://github.com/salto-io/salto/blob/main/NOTICES | ||
*/ | ||
import { Element, isInstanceElement } from '@salto-io/adapter-api' | ||
import { InstanceElement, isInstanceElement } from '@salto-io/adapter-api' | ||
import { references as referenceUtils } from '@salto-io/adapter-components' | ||
import _ from 'lodash' | ||
import { referencesRules, JiraFieldReferenceResolver, contextStrategyLookup } from '../reference_mapping' | ||
import { FilterCreator } from '../filter' | ||
import { FIELD_CONFIGURATION_TYPE_NAME, PROJECT_COMPONENT_TYPE } from '../constants' | ||
import { AUTOMATION_TYPE, FIELD_CONFIGURATION_TYPE_NAME, PROJECT_COMPONENT_TYPE } from '../constants' | ||
import { JiraConfig } from '../config/config' | ||
import { FIELD_TYPE_NAME } from './fields/constants' | ||
import { advancedFieldsReferenceFunc, walkOnAutomations } from './automation/walk_on_automation' | ||
import { addFieldsTemplateReferences } from './fields/reference_to_fields' | ||
|
||
/** | ||
* Convert field values into references, based on predefined rules. | ||
*/ | ||
|
||
const noReferencesTypes = [PROJECT_COMPONENT_TYPE, FIELD_CONFIGURATION_TYPE_NAME] | ||
const NO_REFERENCES_TYPES = (): string[] => [PROJECT_COMPONENT_TYPE, FIELD_CONFIGURATION_TYPE_NAME] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need it as a function? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as I answered in the previous comment |
||
|
||
const addWalkOnReferences = (instances: InstanceElement[], config: JiraConfig): void => { | ||
if (!config.fetch.walkOnReferences) { | ||
return | ||
} | ||
const fieldInstances = instances.filter(instance => instance.elemID.typeName === FIELD_TYPE_NAME) | ||
const fieldInstancesById = new Map( | ||
fieldInstances | ||
.filter(instance => typeof instance.value.id === 'string') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tend to go with a natural solution over a package one |
||
.map(instance => [instance.value.id, instance] as [string, InstanceElement]), | ||
) | ||
walkOnAutomations( | ||
instances.filter(instance => instance.elemID.typeName === AUTOMATION_TYPE), | ||
advancedFieldsReferenceFunc( | ||
addFieldsTemplateReferences(fieldInstancesById, config.fetch.enableMissingReferences ?? true), | ||
), | ||
) | ||
} | ||
|
||
const filter: FilterCreator = ({ config }) => ({ | ||
name: 'fieldReferencesFilter', | ||
onFetch: async (elements: Element[]) => { | ||
onFetch: async elements => { | ||
const instances = elements.filter(isInstanceElement) | ||
addWalkOnReferences(instances, config) | ||
|
||
const fixedDefs = referencesRules.map(def => | ||
config.fetch.enableMissingReferences ? def : _.omit(def, 'missingRefStrategy'), | ||
) | ||
// Remove once SALTO-6889 is done: ProjectComponents have no references, so don't need to scan them | ||
const relevantElements = elements | ||
.filter(isInstanceElement) | ||
.filter(instance => !noReferencesTypes.includes(instance.elemID.typeName)) | ||
const relevantElements = instances.filter(instance => !NO_REFERENCES_TYPES().includes(instance.elemID.typeName)) | ||
await referenceUtils.addReferences({ | ||
elements: relevantElements, | ||
contextElements: elements, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright 2025 Salto Labs Ltd. | ||
* Licensed under the Salto Terms of Use (the "License"); | ||
* You may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.salto.io/terms-of-use | ||
* | ||
* CERTAIN THIRD PARTY SOFTWARE MAY BE CONTAINED IN PORTIONS OF THE SOFTWARE. See NOTICE FILE AT https://github.com/salto-io/salto/blob/main/NOTICES | ||
*/ | ||
|
||
import { ElemID, InstanceElement, ReferenceExpression, TemplateExpression, Value } from '@salto-io/adapter-api' | ||
import { references as referenceUtils } from '@salto-io/adapter-components' | ||
import { extractTemplate } from '@salto-io/adapter-utils' | ||
import { referenceFunc } from '../script_runner/walk_on_scripts' | ||
import { JIRA } from '../../constants' | ||
import { FIELD_TYPE_NAME } from './constants' | ||
|
||
const CUSTOM_FIELD_PATTERN = /(customfield_\d+)/ | ||
|
||
const referenceCustomFields = ({ | ||
text, | ||
fieldInstancesById, | ||
enableMissingReferences, | ||
}: { | ||
text: string | ||
fieldInstancesById: Map<string, InstanceElement> | ||
enableMissingReferences: boolean | ||
}): TemplateExpression | string => | ||
extractTemplate(text, [CUSTOM_FIELD_PATTERN], expression => { | ||
if (!expression.match(CUSTOM_FIELD_PATTERN)) { | ||
return expression | ||
} | ||
const instance = fieldInstancesById.get(expression) | ||
if (instance !== undefined) { | ||
return new ReferenceExpression(instance.elemID, instance) | ||
} | ||
return enableMissingReferences | ||
? referenceUtils.createMissingValueReference(new ElemID(JIRA, FIELD_TYPE_NAME, 'instance'), expression) | ||
: expression | ||
}) | ||
|
||
export const addFieldsTemplateReferences = | ||
(fieldInstancesById: Map<string, InstanceElement>, enableMissingReferences: boolean): referenceFunc => | ||
(value: Value, fieldName: string): void => { | ||
if (typeof value[fieldName] === 'string') { | ||
value[fieldName] = referenceCustomFields({ text: value[fieldName], fieldInstancesById, enableMissingReferences }) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you explain this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a part of the E2E where we clean old instances that were not deleted due to aborted E2E. We had problems with DC so we omitted it, but there is no reason as we do not fail the E2E if this stage fails