Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
e9c24b9d
Commit
e9c24b9d
authored
Feb 23, 2021
by
Olena Horal-Koretska
Committed by
David O'Regan
Feb 23, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Integrate payload attribute mappings to cache
Handle data for integration load, updte, change, save
parent
70a9bd19
Changes
26
Hide whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
639 additions
and
332 deletions
+639
-332
app/assets/javascripts/alerts_settings/components/alert_mapping_builder.vue
...ipts/alerts_settings/components/alert_mapping_builder.vue
+20
-23
app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
...s/alerts_settings/components/alerts_integrations_list.vue
+8
-5
app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
...ripts/alerts_settings/components/alerts_settings_form.vue
+82
-58
app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
...ts/alerts_settings/components/alerts_settings_wrapper.vue
+68
-41
app/assets/javascripts/alerts_settings/components/mocks/parsedMapping.json
...ripts/alerts_settings/components/mocks/parsedMapping.json
+0
-95
app/assets/javascripts/alerts_settings/constants.js
app/assets/javascripts/alerts_settings/constants.js
+10
-5
app/assets/javascripts/alerts_settings/graphql.js
app/assets/javascripts/alerts_settings/graphql.js
+15
-1
app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql
.../graphql/fragments/http_integration_item.fragment.graphql
+7
-0
app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql
.../fragments/http_integration_payload_data.fragment.graphql
+14
-0
app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql
...raphql/mutations/create_http_integration.mutation.graphql
+2
-2
app/assets/javascripts/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql
...aphql/mutations/destroy_http_integration.mutation.graphql
+2
-2
app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql
...tings/graphql/mutations/reset_http_token.mutation.graphql
+2
-2
app/assets/javascripts/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql
...utations/update_current_http_integration.mutation.graphql
+25
-0
app/assets/javascripts/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql
...ns/update_current_prometheus_integration.mutation.graphql
+3
-1
app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql
...raphql/mutations/update_http_integration.mutation.graphql
+18
-4
app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql
...tings/graphql/queries/get_http_integrations.query.graphql
+13
-0
app/assets/javascripts/alerts_settings/graphql/queries/parse_sample_payload.query.graphql
...ttings/graphql/queries/parse_sample_payload.query.graphql
+9
-0
app/assets/javascripts/alerts_settings/utils/cache_updates.js
...assets/javascripts/alerts_settings/utils/cache_updates.js
+34
-0
app/assets/javascripts/alerts_settings/utils/mapping_transformations.js
...ascripts/alerts_settings/utils/mapping_transformations.js
+11
-17
spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js
.../alerts_settings/components/alert_mapping_builder_spec.js
+15
-13
spec/frontend/alerts_settings/components/alerts_settings_form_spec.js
...d/alerts_settings/components/alerts_settings_form_spec.js
+67
-29
spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
...lerts_settings/components/alerts_settings_wrapper_spec.js
+68
-7
spec/frontend/alerts_settings/components/mocks/apollo_mock.js
.../frontend/alerts_settings/components/mocks/apollo_mock.js
+14
-3
spec/frontend/alerts_settings/mocks/alert_fields.json
spec/frontend/alerts_settings/mocks/alert_fields.json
+0
-0
spec/frontend/alerts_settings/mocks/parsed_mapping.json
spec/frontend/alerts_settings/mocks/parsed_mapping.json
+122
-0
spec/frontend/alerts_settings/utils/mapping_transformations_spec.js
...end/alerts_settings/utils/mapping_transformations_spec.js
+10
-24
No files found.
app/assets/javascripts/alerts_settings/components/alert_mapping_builder.vue
View file @
e9c24b9d
...
...
@@ -7,15 +7,12 @@ import {
GlSearchBoxByType
,
GlTooltipDirective
as
GlTooltip
,
}
from
'
@gitlab/ui
'
;
import
{
cloneDeep
}
from
'
lodash
'
;
import
{
cloneDeep
,
isEqual
}
from
'
lodash
'
;
import
Vue
from
'
vue
'
;
import
{
capitalizeFirstCharacter
}
from
'
~/lib/utils/text_utility
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
import
{
getMappingData
,
getPayloadFields
,
transformForSave
,
}
from
'
../utils/mapping_transformations
'
;
import
{
mappingFields
}
from
'
../constants
'
;
import
{
getMappingData
,
transformForSave
}
from
'
../utils/mapping_transformations
'
;
export
const
i18n
=
{
columns
:
{
...
...
@@ -33,6 +30,7 @@ export const i18n = {
export
default
{
i18n
,
mappingFields
,
components
:
{
GlIcon
,
GlFormInput
,
...
...
@@ -73,18 +71,15 @@ export default {
};
},
computed
:
{
payloadFields
()
{
return
getPayloadFields
(
this
.
parsedPayload
);
},
mappingData
()
{
return
getMappingData
(
this
.
gitlabFields
,
this
.
pa
yloadFields
,
this
.
savedMapping
);
return
getMappingData
(
this
.
gitlabFields
,
this
.
pa
rsedPayload
,
this
.
savedMapping
);
},
hasFallbackColumn
()
{
return
this
.
gitlabFields
.
some
(({
numberOfFallbacks
})
=>
Boolean
(
numberOfFallbacks
));
},
},
methods
:
{
setMapping
(
gitlabKey
,
mappingKey
,
valueKey
)
{
setMapping
(
gitlabKey
,
mappingKey
,
valueKey
=
mappingFields
.
mapping
)
{
const
fieldIndex
=
this
.
gitlabFields
.
findIndex
((
field
)
=>
field
.
name
===
gitlabKey
);
const
updatedField
=
{
...
this
.
gitlabFields
[
fieldIndex
],
...{
[
valueKey
]:
mappingKey
}
};
Vue
.
set
(
this
.
gitlabFields
,
fieldIndex
,
updatedField
);
...
...
@@ -100,11 +95,11 @@ export default {
return
fields
.
filter
((
field
)
=>
field
.
label
.
toLowerCase
().
includes
(
search
));
},
isSelected
(
fieldValue
,
mapping
)
{
return
fieldValue
===
mapping
;
return
isEqual
(
fieldValue
,
mapping
)
;
},
selectedValue
(
name
)
{
selectedValue
(
mapping
)
{
return
(
this
.
pa
yloadFields
.
find
((
item
)
=>
item
.
name
===
name
)?.
label
||
this
.
pa
rsedPayload
.
find
((
item
)
=>
isEqual
(
item
.
path
,
mapping
)
)?.
label
||
this
.
$options
.
i18n
.
makeSelection
);
},
...
...
@@ -150,7 +145,7 @@ export default {
:key=
"gitlabField.name"
class=
"gl-display-table-row"
>
<div
class=
"gl-display-table-cell gl-py-3 gl-pr-3 w-30p gl-vertical-align-middle"
>
<div
class=
"gl-display-table-cell gl-py-3 gl-pr-3
gl-
w-30p gl-vertical-align-middle"
>
<gl-form-input
aria-labelledby=
"gitlabFieldsHeader"
disabled
...
...
@@ -164,7 +159,7 @@ export default {
</div>
</div>
<div
class=
"gl-display-table-cell gl-py-3 gl-pr-3 w-30p gl-vertical-align-middle"
>
<div
class=
"gl-display-table-cell gl-py-3 gl-pr-3
gl-
w-30p gl-vertical-align-middle"
>
<gl-dropdown
:disabled=
"!gitlabField.mappingFields.length"
aria-labelledby=
"parsedFieldsHeader"
...
...
@@ -175,10 +170,10 @@ export default {
<gl-search-box-by-type
@
input=
"setSearchTerm($event, 'searchTerm', gitlabField.name)"
/>
<gl-dropdown-item
v-for=
"mappingField in filterFields(gitlabField.searchTerm, gitlabField.mappingFields)"
:key=
"`$
{mappingField.
name
}__mapping`"
:is-checked="isSelected(gitlabField.mapping, mappingField.
name
)"
:key=
"`$
{mappingField.
path
}__mapping`"
:is-checked="isSelected(gitlabField.mapping, mappingField.
path
)"
is-check-item
@click="setMapping(gitlabField.name, mappingField.
name, 'mapping'
)"
@click="setMapping(gitlabField.name, mappingField.
path
)"
>
{{
mappingField
.
label
}}
</gl-dropdown-item>
...
...
@@ -188,7 +183,7 @@ export default {
</gl-dropdown>
</div>
<div
class=
"gl-display-table-cell gl-py-3 w-30p"
>
<div
class=
"gl-display-table-cell gl-py-3
gl-
w-30p"
>
<gl-dropdown
v-if=
"Boolean(gitlabField.numberOfFallbacks)"
:disabled=
"!gitlabField.mappingFields.length"
...
...
@@ -205,10 +200,12 @@ export default {
gitlabField.fallbackSearchTerm,
gitlabField.mappingFields,
)"
:key=
"`$
{mappingField.
name
}__fallback`"
:is-checked="isSelected(gitlabField.fallback, mappingField.
name
)"
:key=
"`$
{mappingField.
path
}__fallback`"
:is-checked="isSelected(gitlabField.fallback, mappingField.
path
)"
is-check-item
@click="setMapping(gitlabField.name, mappingField.name, 'fallback')"
@click="
setMapping(gitlabField.name, mappingField.path, $options.mappingFields.fallback)
"
>
{{
mappingField
.
label
}}
</gl-dropdown-item>
...
...
app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
View file @
e9c24b9d
...
...
@@ -120,14 +120,17 @@ export default {
const
{
category
,
action
}
=
trackAlertIntegrationsViewsOptions
;
Tracking
.
event
(
category
,
action
);
},
setIntegrationToDelete
({
name
,
id
})
{
this
.
integrationToDelete
.
id
=
id
;
this
.
integrationToDelete
.
name
=
name
;
setIntegrationToDelete
(
integration
)
{
this
.
integrationToDelete
=
integration
;
},
deleteIntegration
()
{
this
.
$emit
(
'
delete-integration
'
,
{
id
:
this
.
integrationToDelete
.
id
});
const
{
id
,
type
}
=
this
.
integrationToDelete
;
this
.
$emit
(
'
delete-integration
'
,
{
id
,
type
});
this
.
integrationToDelete
=
{
...
integrationToDeleteDefault
};
},
editIntegration
({
id
,
type
})
{
this
.
$emit
(
'
edit-integration
'
,
{
id
,
type
});
},
},
};
</
script
>
...
...
@@ -169,7 +172,7 @@ export default {
<
template
#cell(actions)=
"{ item }"
>
<gl-button-group
class=
"gl-ml-3"
>
<gl-button
icon=
"pencil"
@
click=
"
$emit('edit-integration',
{ id: item.id }
)" />
<gl-button
icon=
"pencil"
@
click=
"
editIntegration(item
)"
/>
<gl-button
v-gl-modal
.
deleteIntegration
:disabled=
"item.type === $options.typeSet.prometheus"
...
...
app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
View file @
e9c24b9d
...
...
@@ -12,6 +12,8 @@ import {
GlModalDirective
,
GlToggle
,
}
from
'
@gitlab/ui
'
;
import
*
as
Sentry
from
'
@sentry/browser
'
;
import
{
isEmpty
,
omit
}
from
'
lodash
'
;
import
{
s__
}
from
'
~/locale
'
;
import
ClipboardButton
from
'
~/vue_shared/components/clipboard_button.vue
'
;
import
glFeatureFlagsMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
...
...
@@ -22,12 +24,9 @@ import {
typeSet
,
}
from
'
../constants
'
;
import
getCurrentIntegrationQuery
from
'
../graphql/queries/get_current_integration.query.graphql
'
;
import
parseSamplePayloadQuery
from
'
../graphql/queries/parse_sample_payload.query.graphql
'
;
import
MappingBuilder
from
'
./alert_mapping_builder.vue
'
;
import
AlertSettingsFormHelpBlock
from
'
./alert_settings_form_help_block.vue
'
;
// Mocks will be removed when integrating with BE is ready
// data format is defined and will be the same as mocked (maybe with some minor changes)
// feature rollout plan - https://gitlab.com/gitlab-org/gitlab/-/issues/262707#note_442529171
import
mockedCustomMapping
from
'
./mocks/parsedMapping.json
'
;
export
const
i18n
=
{
integrationFormSteps
:
{
...
...
@@ -92,7 +91,6 @@ export const i18n = {
};
export
default
{
integrationTypes
,
placeholders
:
{
prometheus
:
targetPrometheusUrlPlaceholder
,
},
...
...
@@ -128,6 +126,9 @@ export default {
multiIntegrations
:
{
default
:
false
,
},
projectPath
:
{
default
:
''
,
},
},
props
:
{
loading
:
{
...
...
@@ -151,18 +152,19 @@ export default {
},
data
()
{
return
{
selectedIntegration
:
integrationTypes
[
0
].
value
,
integrationTypesOptions
:
Object
.
values
(
integrationTypes
),
selectedIntegration
:
integrationTypes
.
none
.
value
,
active
:
false
,
formVisible
:
false
,
integrationTestPayload
:
{
json
:
null
,
error
:
null
,
},
resetSamplePayloadConfirmed
:
false
,
customMapping
:
null
,
resetPayloadAndMappingConfirmed
:
false
,
mapping
:
[],
parsingPayload
:
false
,
currentIntegration
:
null
,
parsedPayload
:
[],
};
},
computed
:
{
...
...
@@ -210,17 +212,11 @@ export default {
this
.
alertFields
?.
length
);
},
parsedSamplePayload
()
{
return
this
.
customMapping
?.
samplePayload
?.
payloadAlerFields
?.
nodes
;
},
savedMapping
()
{
return
this
.
customMapping
?.
storedMapping
?.
nodes
;
},
hasSamplePayload
()
{
return
Boolean
(
this
.
customMapping
?.
samplePayload
);
return
this
.
isValidNonEmptyJSON
(
this
.
currentIntegration
?.
payloadExample
);
},
canEditPayload
()
{
return
this
.
hasSamplePayload
&&
!
this
.
reset
SamplePayload
Confirmed
;
return
this
.
hasSamplePayload
&&
!
this
.
reset
PayloadAndMapping
Confirmed
;
},
isResetAuthKeyDisabled
()
{
return
!
this
.
active
&&
!
this
.
integrationForm
.
token
!==
''
;
...
...
@@ -240,25 +236,52 @@ export default {
isSelectDisabled
()
{
return
this
.
currentIntegration
!==
null
||
!
this
.
canAddIntegration
;
},
savedMapping
()
{
return
this
.
mapping
;
},
},
watch
:
{
currentIntegration
(
val
)
{
if
(
val
===
null
)
{
return
this
.
reset
();
this
.
reset
();
return
;
}
const
{
type
,
active
,
payloadExample
,
payloadAlertFields
,
payloadAttributeMappings
}
=
val
;
this
.
selectedIntegration
=
type
;
this
.
active
=
active
;
if
(
type
===
typeSet
.
prometheus
)
{
this
.
integrationTestPayload
.
json
=
null
;
}
if
(
type
===
typeSet
.
http
&&
this
.
showMappingBuilder
)
{
this
.
parsedPayload
=
payloadAlertFields
;
this
.
integrationTestPayload
.
json
=
this
.
isValidNonEmptyJSON
(
payloadExample
)
?
payloadExample
:
null
;
const
mapping
=
payloadAttributeMappings
.
map
((
mappingItem
)
=>
omit
(
mappingItem
,
'
__typename
'
),
);
this
.
updateMapping
(
mapping
);
}
this
.
selectedIntegration
=
val
.
type
;
this
.
active
=
val
.
active
;
if
(
val
.
type
===
typeSet
.
http
&&
this
.
showMappingBuilder
)
this
.
getIntegrationMapping
(
val
.
id
);
return
this
.
integrationTypeSelect
();
this
.
toggleFormVisibility
();
},
},
methods
:
{
integrationTypeSelect
()
{
if
(
this
.
selectedIntegration
===
integrationTypes
[
0
].
value
)
{
this
.
formVisible
=
false
;
}
else
{
this
.
formVisible
=
true
;
isValidNonEmptyJSON
(
JSONString
)
{
if
(
JSONString
)
{
let
parsed
;
try
{
parsed
=
JSON
.
parse
(
JSONString
);
}
catch
(
error
)
{
Sentry
.
captureException
(
error
);
}
if
(
parsed
)
return
!
isEmpty
(
parsed
);
}
return
false
;
},
toggleFormVisibility
()
{
this
.
formVisible
=
this
.
selectedIntegration
!==
integrationTypes
.
none
.
value
;
},
submitWithTestPayload
()
{
this
.
$emit
(
'
set-test-alert-payload
'
,
this
.
testAlertPayload
);
...
...
@@ -269,20 +292,15 @@ export default {
const
customMappingVariables
=
this
.
glFeatures
.
multipleHttpIntegrationsCustomMapping
?
{
payloadAttributeMappings
:
this
.
mapping
,
payloadExample
:
this
.
integrationTestPayload
.
json
,
payloadExample
:
this
.
integrationTestPayload
.
json
||
'
{}
'
,
}
:
{};
const
variables
=
this
.
selectedIntegration
===
typeSet
.
http
?
{
name
,
active
:
this
.
active
,
...
customMappingVariables
,
}
?
{
name
,
active
:
this
.
active
,
...
customMappingVariables
}
:
{
apiUrl
,
active
:
this
.
active
};
const
integrationPayload
=
{
type
:
this
.
selectedIntegration
,
variables
};
if
(
this
.
currentIntegration
)
{
return
this
.
$emit
(
'
update-integration
'
,
integrationPayload
);
}
...
...
@@ -291,11 +309,12 @@ export default {
return
this
.
$emit
(
'
create-new-integration
'
,
integrationPayload
);
},
reset
()
{
this
.
selectedIntegration
=
integrationTypes
[
0
].
value
;
this
.
integrationTypeSelect
();
this
.
selectedIntegration
=
integrationTypes
.
none
.
value
;
this
.
toggleFormVisibility
();
this
.
resetPayloadAndMapping
();
if
(
this
.
currentIntegration
)
{
return
this
.
$emit
(
'
clear-current-integration
'
);
return
this
.
$emit
(
'
clear-current-integration
'
,
{
type
:
this
.
currentIntegration
.
type
}
);
}
return
this
.
resetFormValues
();
...
...
@@ -332,35 +351,40 @@ export default {
}
},
parseMapping
()
{
// TODO: replace with real BE mutation when ready;
this
.
parsingPayload
=
true
;
return
new
Promise
((
resolve
)
=>
{
setTimeout
(()
=>
resolve
(
mockedCustomMapping
),
1000
);
})
.
then
((
res
)
=>
{
const
mapping
=
{
...
res
};
delete
mapping
.
storedMapping
;
this
.
customMapping
=
res
;
this
.
integrationTestPayload
.
json
=
res
?.
samplePayload
.
body
;
this
.
resetSamplePayloadConfirmed
=
false
;
return
this
.
$apollo
.
query
({
query
:
parseSamplePayloadQuery
,
variables
:
{
projectPath
:
this
.
projectPath
,
payload
:
this
.
integrationTestPayload
.
json
},
})
.
then
(
({
data
:
{
project
:
{
alertManagementPayloadFields
},
},
})
=>
{
this
.
parsedPayload
=
alertManagementPayloadFields
;
this
.
resetPayloadAndMappingConfirmed
=
false
;
this
.
$toast
.
show
(
this
.
$options
.
i18n
.
integrationFormSteps
.
step4
.
payloadParsedSucessMsg
);
this
.
$toast
.
show
(
this
.
$options
.
i18n
.
integrationFormSteps
.
step4
.
payloadParsedSucessMsg
);
},
)
.
catch
(({
message
})
=>
{
this
.
integrationTestPayload
.
error
=
message
;
})
.
finally
(()
=>
{
this
.
parsingPayload
=
false
;
});
},
getIntegrationMapping
()
{
// TODO: replace with real BE mutation when ready;
return
Promise
.
resolve
(
mockedCustomMapping
).
then
((
res
)
=>
{
this
.
customMapping
=
res
;
this
.
integrationTestPayload
.
json
=
res
?.
samplePayload
.
body
;
});
},
updateMapping
(
mapping
)
{
this
.
mapping
=
mapping
;
},
resetPayloadAndMapping
()
{
this
.
resetPayloadAndMappingConfirmed
=
true
;
this
.
parsedPayload
=
[];
this
.
updateMapping
([]);
},
},
};
</
script
>
...
...
@@ -377,8 +401,8 @@ export default {
v-model=
"selectedIntegration"
:disabled=
"isSelectDisabled"
class=
"mw-100"
:options=
"
$options.integrationType
s"
@
change=
"
integrationTypeSelect
"
:options=
"
integrationTypesOption
s"
@
change=
"
toggleFormVisibility
"
/>
<div
v-if=
"!canAddIntegration"
class=
"gl-my-4"
data-testid=
"multi-integrations-not-supported"
>
...
...
@@ -551,7 +575,7 @@ export default {
:title=
"$options.i18n.integrationFormSteps.step4.resetHeader"
:ok-title=
"$options.i18n.integrationFormSteps.step4.resetOk"
ok-variant=
"danger"
@
ok=
"reset
SamplePayloadConfirmed = true
"
@
ok=
"reset
PayloadAndMapping
"
>
{{
$options
.
i18n
.
integrationFormSteps
.
step4
.
resetBody
}}
</gl-modal>
...
...
@@ -566,7 +590,7 @@ export default {
>
<span>
{{ $options.i18n.integrationFormSteps.step5.intro }}
</span>
<mapping-builder
:parsed-payload=
"parsed
Sample
Payload"
:parsed-payload=
"parsedPayload"
:saved-mapping=
"savedMapping"
:alert-fields=
"alertFields"
@
onMappingUpdate=
"updateMapping"
...
...
app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
View file @
e9c24b9d
...
...
@@ -8,15 +8,18 @@ import createPrometheusIntegrationMutation from '../graphql/mutations/create_pro
import
destroyHttpIntegrationMutation
from
'
../graphql/mutations/destroy_http_integration.mutation.graphql
'
;
import
resetHttpTokenMutation
from
'
../graphql/mutations/reset_http_token.mutation.graphql
'
;
import
resetPrometheusTokenMutation
from
'
../graphql/mutations/reset_prometheus_token.mutation.graphql
'
;
import
updateCurrentIntergrationMutation
from
'
../graphql/mutations/update_current_intergration.mutation.graphql
'
;
import
updateCurrentHttpIntegrationMutation
from
'
../graphql/mutations/update_current_http_integration.mutation.graphql
'
;
import
updateCurrentPrometheusIntegrationMutation
from
'
../graphql/mutations/update_current_prometheus_integration.mutation.graphql
'
;
import
updateHttpIntegrationMutation
from
'
../graphql/mutations/update_http_integration.mutation.graphql
'
;
import
updatePrometheusIntegrationMutation
from
'
../graphql/mutations/update_prometheus_integration.mutation.graphql
'
;
import
getCurrentIntegrationQuery
from
'
../graphql/queries/get_current_integration.query.graphql
'
;
import
getHttpIntegrationsQuery
from
'
../graphql/queries/get_http_integrations.query.graphql
'
;
import
getIntegrationsQuery
from
'
../graphql/queries/get_integrations.query.graphql
'
;
import
service
from
'
../services
'
;
import
{
updateStoreAfterIntegrationDelete
,
updateStoreAfterIntegrationAdd
,
updateStoreAfterHttpIntegrationAdd
,
}
from
'
../utils/cache_updates
'
;
import
{
DELETE_INTEGRATION_ERROR
,
...
...
@@ -84,6 +87,28 @@ export default {
createFlash
({
message
:
err
});
},
},
// TODO: we'll need to update the logic to request specific http integration by its id on edit
// when BE adds support for it https://gitlab.com/gitlab-org/gitlab/-/issues/321674
// currently the request for ALL http integrations is made and on specific integration edit we search it in the list
httpIntegrations
:
{
fetchPolicy
:
fetchPolicies
.
CACHE_AND_NETWORK
,
query
:
getHttpIntegrationsQuery
,
variables
()
{
return
{
projectPath
:
this
.
projectPath
,
};
},
update
(
data
)
{
const
{
alertManagementHttpIntegrations
:
{
nodes
:
list
=
[]
}
=
{}
}
=
data
.
project
||
{};
return
{
list
,
};
},
error
(
err
)
{
createFlash
({
message
:
err
});
},
},
currentIntegration
:
{
query
:
getCurrentIntegrationQuery
,
},
...
...
@@ -93,6 +118,7 @@ export default {
isUpdating
:
false
,
testAlertPayload
:
null
,
integrations
:
{},
httpIntegrations
:
{},
currentIntegration
:
null
,
};
},
...
...
@@ -105,22 +131,28 @@ export default {
},
},
methods
:
{
isHttp
(
type
)
{
return
type
===
typeSet
.
http
;
},
createNewIntegration
({
type
,
variables
})
{
const
{
projectPath
}
=
this
;
const
isHttp
=
this
.
isHttp
(
type
);
this
.
isUpdating
=
true
;
this
.
$apollo
.
mutate
({
mutation
:
type
===
this
.
$options
.
typeSet
.
http
?
createHttpIntegrationMutation
:
createPrometheusIntegrationMutation
,
mutation
:
isHttp
?
createHttpIntegrationMutation
:
createPrometheusIntegrationMutation
,
variables
:
{
...
variables
,
projectPath
,
},
update
(
store
,
{
data
})
{
updateStoreAfterIntegrationAdd
(
store
,
getIntegrationsQuery
,
data
,
{
projectPath
});
if
(
isHttp
)
{
updateStoreAfterHttpIntegrationAdd
(
store
,
getHttpIntegrationsQuery
,
data
,
{
projectPath
,
});
}
},
})
.
then
(({
data
:
{
httpIntegrationCreate
,
prometheusIntegrationCreate
}
=
{}
}
=
{})
=>
{
...
...
@@ -157,10 +189,9 @@ export default {
this
.
isUpdating
=
true
;
this
.
$apollo
.
mutate
({
mutation
:
type
===
this
.
$options
.
typeSet
.
http
?
updateHttpIntegrationMutation
:
updatePrometheusIntegrationMutation
,
mutation
:
this
.
isHttp
(
type
)
?
updateHttpIntegrationMutation
:
updatePrometheusIntegrationMutation
,
variables
:
{
...
variables
,
id
:
this
.
currentIntegration
.
id
,
...
...
@@ -176,7 +207,7 @@ export default {
return
this
.
validateAlertPayload
();
}
this
.
clearCurrentIntegration
();
this
.
clearCurrentIntegration
(
{
type
}
);
return
createFlash
({
message
:
this
.
$options
.
i18n
.
changesSaved
,
...
...
@@ -195,16 +226,13 @@ export default {
this
.
isUpdating
=
true
;
this
.
$apollo
.
mutate
({
mutation
:
type
===
this
.
$options
.
typeSet
.
http
?
resetHttpTokenMutation
:
resetPrometheusTokenMutation
,
mutation
:
this
.
isHttp
(
type
)
?
resetHttpTokenMutation
:
resetPrometheusTokenMutation
,
variables
,
})
.
then
(
({
data
:
{
httpIntegrationResetToken
,
prometheusIntegrationResetToken
}
=
{}
}
=
{})
=>
{
const
error
=
httpIntegrationResetToken
?.
errors
[
0
]
||
prometheusIntegrationResetToken
?.
errors
[
0
]
;
const
[
error
]
=
httpIntegrationResetToken
?.
errors
||
prometheusIntegrationResetToken
?.
errors
;
if
(
error
)
{
return
createFlash
({
message
:
error
});
}
...
...
@@ -214,10 +242,10 @@ export default {
prometheusIntegrationResetToken
?.
integration
;
this
.
$apollo
.
mutate
({
mutation
:
updateCurrentIntergrationMutation
,
variables
:
{
...
integr
ation
,
}
,
mutation
:
this
.
isHttp
(
type
)
?
updateCurrentHttpIntegrationMutation
:
updateCurrentPrometheusIntegrationMut
ation
,
variables
:
integration
,
});
return
createFlash
({
...
...
@@ -233,33 +261,30 @@ export default {
this
.
isUpdating
=
false
;
});
},
editIntegration
({
id
})
{
const
currentIntegration
=
this
.
integrations
.
list
.
find
(
(
integration
)
=>
integration
.
id
===
id
,
);
editIntegration
({
id
,
type
})
{
let
currentIntegration
=
this
.
integrations
.
list
.
find
((
integration
)
=>
integration
.
id
===
id
);
if
(
this
.
isHttp
(
type
))
{
const
httpIntegrationMappingData
=
this
.
httpIntegrations
.
list
.
find
(
(
integration
)
=>
integration
.
id
===
id
,
);
currentIntegration
=
{
...
currentIntegration
,
...
httpIntegrationMappingData
};
}
this
.
$apollo
.
mutate
({
mutation
:
updateCurrentIntergrationMutation
,
variables
:
{
id
:
currentIntegration
.
id
,
name
:
currentIntegration
.
name
,
active
:
currentIntegration
.
active
,
token
:
currentIntegration
.
token
,
type
:
currentIntegration
.
type
,
url
:
currentIntegration
.
url
,
apiUrl
:
currentIntegration
.
apiUrl
,
},
mutation
:
this
.
isHttp
(
type
)
?
updateCurrentHttpIntegrationMutation
:
updateCurrentPrometheusIntegrationMutation
,
variables
:
currentIntegration
,
});
},
deleteIntegration
({
id
})
{
deleteIntegration
({
id
,
type
})
{
const
{
projectPath
}
=
this
;
this
.
isUpdating
=
true
;
this
.
$apollo
.
mutate
({
mutation
:
destroyHttpIntegrationMutation
,
variables
:
{
id
,
},
variables
:
{
id
},
update
(
store
,
{
data
})
{
updateStoreAfterIntegrationDelete
(
store
,
getIntegrationsQuery
,
data
,
{
projectPath
});
},
...
...
@@ -269,7 +294,7 @@ export default {
if
(
error
)
{
return
createFlash
({
message
:
error
});
}
this
.
clearCurrentIntegration
();
this
.
clearCurrentIntegration
(
{
type
}
);
return
createFlash
({
message
:
this
.
$options
.
i18n
.
integrationRemoved
,
type
:
FLASH_TYPES
.
SUCCESS
,
...
...
@@ -282,9 +307,11 @@ export default {
this
.
isUpdating
=
false
;
});
},
clearCurrentIntegration
()
{
clearCurrentIntegration
(
{
type
}
)
{
this
.
$apollo
.
mutate
({
mutation
:
updateCurrentIntergrationMutation
,
mutation
:
this
.
isHttp
(
type
)
?
updateCurrentHttpIntegrationMutation
:
updateCurrentPrometheusIntegrationMutation
,
variables
:
{},
});
},
...
...
app/assets/javascripts/alerts_settings/components/mocks/parsedMapping.json
deleted
100644 → 0
View file @
70a9bd19
{
"samplePayload"
:
{
"body"
:
"{
\n
\"
dashboardId
\"
:1,
\n
\"
evalMatches
\"
:[
\n
{
\n
\"
value
\"
:1,
\n
\"
metric
\"
:
\"
Count
\"
,
\n
\"
tags
\"
:{}
\n
}
\n
],
\n
\"
imageUrl
\"
:
\"
https://grafana.com/static/assets/img/blog/mixed_styles.png
\"
,
\n
\"
message
\"
:
\"
Notification Message
\"
,
\n
\"
orgId
\"
:1,
\n
\"
panelId
\"
:2,
\n
\"
ruleId
\"
:1,
\n
\"
ruleName
\"
:
\"
Panel Title alert
\"
,
\n
\"
ruleUrl
\"
:
\"
http://localhost:3000/d/hZ7BuVbWz/test-dashboard?fullscreen
\\
u0026edit
\\
u0026tab=alert
\\
u0026panelId=2
\\
u0026orgId=1
\"
,
\n
\"
state
\"
:
\"
alerting
\"
,
\n
\"
tags
\"
:{
\n
\"
tag name
\"
:
\"
tag value
\"\n
},
\n
\"
title
\"
:
\"
[Alerting] Panel Title alert
\"\n
}
\n
"
,
"payloadAlerFields"
:
{
"nodes"
:
[
{
"path"
:
[
"dashboardId"
],
"label"
:
"Dashboard Id"
,
"type"
:
"string"
},
{
"path"
:
[
"evalMatches"
],
"label"
:
"Eval Matches"
,
"type"
:
"array"
},
{
"path"
:
[
"createdAt"
],
"label"
:
"Created At"
,
"type"
:
"datetime"
},
{
"path"
:
[
"imageUrl"
],
"label"
:
"Image Url"
,
"type"
:
"string"
},
{
"path"
:
[
"message"
],
"label"
:
"Message"
,
"type"
:
"string"
},
{
"path"
:
[
"orgId"
],
"label"
:
"Org Id"
,
"type"
:
"string"
},
{
"path"
:
[
"panelId"
],
"label"
:
"Panel Id"
,
"type"
:
"string"
},
{
"path"
:
[
"ruleId"
],
"label"
:
"Rule Id"
,
"type"
:
"string"
},
{
"path"
:
[
"ruleName"
],
"label"
:
"Rule Name"
,
"type"
:
"string"
},
{
"path"
:
[
"ruleUrl"
],
"label"
:
"Rule Url"
,
"type"
:
"string"
},
{
"path"
:
[
"state"
],
"label"
:
"State"
,
"type"
:
"string"
},
{
"path"
:
[
"title"
],
"label"
:
"Title"
,
"type"
:
"string"
},
{
"path"
:
[
"tags"
,
"tag"
],
"label"
:
"Tags"
,
"type"
:
"string"
}
]
}
},
"storedMapping"
:
{
"nodes"
:
[
{
"alertFieldName"
:
"title"
,
"payloadAlertPaths"
:
"title"
,
"fallbackAlertPaths"
:
"ruleUrl"
},
{
"alertFieldName"
:
"description"
,
"payloadAlertPaths"
:
"message"
},
{
"alertFieldName"
:
"hosts"
,
"payloadAlertPaths"
:
"evalMatches"
},
{
"alertFieldName"
:
"startTime"
,
"payloadAlertPaths"
:
"createdAt"
}
]
}
}
app/assets/javascripts/alerts_settings/constants.js
View file @
e9c24b9d
...
...
@@ -40,11 +40,11 @@ export const i18n = {
integration
:
s__
(
'
AlertSettings|Integration
'
),
};
export
const
integrationTypes
=
[
{
value
:
''
,
text
:
s__
(
'
AlertSettings|Select integration type
'
)
},
{
value
:
'
HTTP
'
,
text
:
s__
(
'
AlertSettings|HTTP Endpoint
'
)
},
{
value
:
'
PROMETHEUS
'
,
text
:
s__
(
'
AlertSettings|External Prometheus
'
)
},
]
;
export
const
integrationTypes
=
{
none
:
{
value
:
''
,
text
:
s__
(
'
AlertSettings|Select integration type
'
)
},
http
:
{
value
:
'
HTTP
'
,
text
:
s__
(
'
AlertSettings|HTTP Endpoint
'
)
},
prometheus
:
{
value
:
'
PROMETHEUS
'
,
text
:
s__
(
'
AlertSettings|External Prometheus
'
)
},
}
;
export
const
typeSet
=
{
http
:
'
HTTP
'
,
...
...
@@ -68,3 +68,8 @@ export const trackAlertIntegrationsViewsOptions = {
category
:
'
Alert Integrations
'
,
action
:
'
view_alert_integrations_list
'
,
};
export
const
mappingFields
=
{
mapping
:
'
mapping
'
,
fallback
:
'
fallback
'
,
};
app/assets/javascripts/alerts_settings/graphql.js
View file @
e9c24b9d
...
...
@@ -10,7 +10,18 @@ const resolvers = {
Mutation
:
{
updateCurrentIntegration
:
(
_
,
{
id
=
null
,
name
,
active
,
token
,
type
,
url
,
apiUrl
},
{
id
=
null
,
name
,
active
,
token
,
type
,
url
,
apiUrl
,
payloadExample
,
payloadAttributeMappings
,
payloadAlertFields
,
},
{
cache
},
)
=>
{
const
sourceData
=
cache
.
readQuery
({
query
:
getCurrentIntegrationQuery
});
...
...
@@ -28,6 +39,9 @@ const resolvers = {
type
,
url
,
apiUrl
,
payloadExample
,
payloadAttributeMappings
,
payloadAlertFields
,
};
}
});
...
...
app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql
0 → 100644
View file @
e9c24b9d
#import "./integration_item.fragment.graphql"
#import "./http_integration_payload_data.fragment.graphql"
fragment
HttpIntegrationItem
on
AlertManagementHttpIntegration
{
...
IntegrationItem
...
HttpIntegrationPayloadData
}
app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql
0 → 100644
View file @
e9c24b9d
fragment
HttpIntegrationPayloadData
on
AlertManagementHttpIntegration
{
payloadExample
payloadAttributeMappings
{
fieldName
path
type
label
}
payloadAlertFields
{
path
type
label
}
}
app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql
View file @
e9c24b9d
#import "../fragments/integration_item.fragment.graphql"
#import "../fragments/
http_
integration_item.fragment.graphql"
mutation
createHttpIntegration
(
$projectPath
:
ID
!
...
...
@@ -18,7 +18,7 @@ mutation createHttpIntegration(
)
{
errors
integration
{
...
IntegrationItem
...
Http
IntegrationItem
}
}
}
app/assets/javascripts/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql
View file @
e9c24b9d
#import "../fragments/integration_item.fragment.graphql"
#import "../fragments/
http_
integration_item.fragment.graphql"
mutation
destroyHttpIntegration
(
$id
:
ID
!)
{
httpIntegrationDestroy
(
input
:
{
id
:
$id
})
{
errors
integration
{
...
IntegrationItem
...
Http
IntegrationItem
}
}
}
app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql
View file @
e9c24b9d
#import "../fragments/integration_item.fragment.graphql"
#import "../fragments/
http_
integration_item.fragment.graphql"
mutation
resetHttpIntegrationToken
(
$id
:
ID
!)
{
httpIntegrationResetToken
(
input
:
{
id
:
$id
})
{
errors
integration
{
...
IntegrationItem
...
Http
IntegrationItem
}
}
}
app/assets/javascripts/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql
0 → 100644
View file @
e9c24b9d
mutation
updateCurrentHttpIntegration
(
$id
:
String
$name
:
String
$active
:
Boolean
$token
:
String
$type
:
String
$url
:
String
$apiUrl
:
String
$payloadExample
:
JsonString
$payloadAttributeMappings
:
[
AlertManagementPayloadAlertFieldInput
!]
$payloadAlertFields
:
[
AlertManagementPayloadAlertField
!]
)
{
updateCurrentIntegration
(
id
:
$id
name
:
$name
active
:
$active
token
:
$token
type
:
$type
url
:
$url
apiUrl
:
$apiUrl
payloadExample
:
$payloadExample
payloadAttributeMappings
:
$payloadAttributeMappings
payloadAlertFields
:
$payloadAlertFields
)
@client
}
app/assets/javascripts/alerts_settings/graphql/mutations/update_current_
inter
gration.mutation.graphql
→
app/assets/javascripts/alerts_settings/graphql/mutations/update_current_
prometheus_inte
gration.mutation.graphql
View file @
e9c24b9d
mutation
updateCurrentIntegration
(
mutation
updateCurrent
Prometheus
Integration
(
$id
:
String
$name
:
String
$active
:
Boolean
...
...
@@ -6,6 +6,7 @@ mutation updateCurrentIntegration(
$type
:
String
$url
:
String
$apiUrl
:
String
$samplePayload
:
String
)
{
updateCurrentIntegration
(
id
:
$id
...
...
@@ -15,5 +16,6 @@ mutation updateCurrentIntegration(
type
:
$type
url
:
$url
apiUrl
:
$apiUrl
samplePayload
:
$samplePayload
)
@client
}
app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql
View file @
e9c24b9d
#import "../fragments/integration_item.fragment.graphql"
#import "../fragments/
http_
integration_item.fragment.graphql"
mutation
updateHttpIntegration
(
$id
:
ID
!,
$name
:
String
!,
$active
:
Boolean
!)
{
httpIntegrationUpdate
(
input
:
{
id
:
$id
,
name
:
$name
,
active
:
$active
})
{
mutation
updateHttpIntegration
(
$id
:
ID
!
$name
:
String
!
$active
:
Boolean
!
$payloadExample
:
JsonString
$payloadAttributeMappings
:
[
AlertManagementPayloadAlertFieldInput
!]
)
{
httpIntegrationUpdate
(
input
:
{
id
:
$id
name
:
$name
active
:
$active
payloadExample
:
$payloadExample
payloadAttributeMappings
:
$payloadAttributeMappings
}
)
{
errors
integration
{
...
IntegrationItem
...
Http
IntegrationItem
}
}
}
app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql
0 → 100644
View file @
e9c24b9d
#import "../fragments/http_integration_payload_data.fragment.graphql"
# TODO: this query need to accept http integration id to request a sepcific integration
query
getHttpIntegrations
(
$projectPath
:
ID
!)
{
project
(
fullPath
:
$projectPath
)
{
alertManagementHttpIntegrations
{
nodes
{
id
...
HttpIntegrationPayloadData
}
}
}
}
app/assets/javascripts/alerts_settings/graphql/queries/parse_sample_payload.query.graphql
0 → 100644
View file @
e9c24b9d
query
parsePayloadFields
(
$projectPath
:
ID
!,
$payload
:
String
!)
{
project
(
fullPath
:
$projectPath
)
{
alertManagementPayloadFields
(
payloadExample
:
$payload
)
{
path
label
type
}
}
}
app/assets/javascripts/alerts_settings/utils/cache_updates.js
View file @
e9c24b9d
...
...
@@ -60,6 +60,32 @@ const addIntegrationToStore = (
});
};
const
addHttpIntegrationToStore
=
(
store
,
query
,
{
httpIntegrationCreate
},
variables
)
=>
{
const
integration
=
httpIntegrationCreate
?.
integration
;
if
(
!
integration
)
{
return
;
}
const
sourceData
=
store
.
readQuery
({
query
,
variables
,
});
const
data
=
produce
(
sourceData
,
(
draftData
)
=>
{
// eslint-disable-next-line no-param-reassign
draftData
.
project
.
alertManagementHttpIntegrations
.
nodes
=
[
integration
,
...
draftData
.
project
.
alertManagementHttpIntegrations
.
nodes
,
];
});
store
.
writeQuery
({
query
,
variables
,
data
,
});
};
const
onError
=
(
data
,
message
)
=>
{
createFlash
({
message
});
throw
new
Error
(
data
.
errors
);
...
...
@@ -82,3 +108,11 @@ export const updateStoreAfterIntegrationAdd = (store, query, data, variables) =>
addIntegrationToStore
(
store
,
query
,
data
,
variables
);
}
};
export
const
updateStoreAfterHttpIntegrationAdd
=
(
store
,
query
,
data
,
variables
)
=>
{
if
(
hasErrors
(
data
))
{
onError
(
data
,
ADD_INTEGRATION_ERROR
);
}
else
{
addHttpIntegrationToStore
(
store
,
query
,
data
,
variables
);
}
};
app/assets/javascripts/alerts_settings/utils/mapping_transformations.js
View file @
e9c24b9d
import
{
isEqual
}
from
'
lodash
'
;
/**
* Given data for GitLab alert fields, parsed payload fields data and previously stored mapping (if any)
* creates an object in a form convenient to build UI && interact with it
...
...
@@ -10,16 +11,19 @@
export
const
getMappingData
=
(
gitlabFields
,
payloadFields
,
savedMapping
)
=>
{
return
gitlabFields
.
map
((
gitlabField
)
=>
{
// find fields from payload that match gitlab alert field by type
const
mappingFields
=
payloadFields
.
filter
(({
type
})
=>
gitlabField
.
types
.
includes
(
type
));
const
mappingFields
=
payloadFields
.
filter
(({
type
})
=>
gitlabField
.
types
.
includes
(
type
.
toLowerCase
()),
);
// find the mapping that was previously stored
const
foundMapping
=
savedMapping
.
find
(({
fieldName
})
=>
fieldName
===
gitlabField
.
name
);
const
{
fallbackAlertPaths
,
payloadAlertPaths
}
=
foundMapping
||
{};
const
foundMapping
=
savedMapping
.
find
(
({
fieldName
})
=>
fieldName
.
toLowerCase
()
===
gitlabField
.
name
,
);
const
{
path
:
mapping
,
fallbackPath
:
fallback
}
=
foundMapping
||
{};
return
{
mapping
:
payloadAlertPaths
,
fallback
:
fallbackAlertPaths
,
mapping
,
fallback
,
searchTerm
:
''
,
fallbackSearchTerm
:
''
,
mappingFields
,
...
...
@@ -36,7 +40,7 @@ export const getMappingData = (gitlabFields, payloadFields, savedMapping) => {
*/
export
const
transformForSave
=
(
mappingData
)
=>
{
return
mappingData
.
reduce
((
acc
,
field
)
=>
{
const
mapped
=
field
.
mappingFields
.
find
(({
name
})
=>
name
===
field
.
mapping
);
const
mapped
=
field
.
mappingFields
.
find
(({
path
})
=>
isEqual
(
path
,
field
.
mapping
)
);
if
(
mapped
)
{
const
{
path
,
type
,
label
}
=
mapped
;
acc
.
push
({
...
...
@@ -49,13 +53,3 @@ export const transformForSave = (mappingData) => {
return
acc
;
},
[]);
};
/**
* Adds `name` prop to each provided by BE parsed payload field
* @param {Object} payload - parsed sample payload
*
* @return {Object} same as input with an extra `name` property which basically serves as a key to make a match
*/
export
const
getPayloadFields
=
(
payload
)
=>
{
return
payload
.
map
((
field
)
=>
({
...
field
,
name
:
field
.
path
.
join
(
'
_
'
)
}));
};
spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js
View file @
e9c24b9d
import
{
GlIcon
,
GlFormInput
,
GlDropdown
,
GlSearchBoxByType
,
GlDropdownItem
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
AlertMappingBuilder
,
{
i18n
}
from
'
~/alerts_settings/components/alert_mapping_builder.vue
'
;
import
parsedMapping
from
'
~/alerts_settings/components/mocks/parsedMapping.json
'
;
import
*
as
transformationUtils
from
'
~/alerts_settings/utils/mapping_transformations
'
;
import
{
capitalizeFirstCharacter
}
from
'
~/lib/utils/text_utility
'
;
import
alertFields
from
'
../mocks/alertFields.json
'
;
import
alertFields
from
'
../mocks/alert_fields.json
'
;
import
parsedMapping
from
'
../mocks/parsed_mapping.json
'
;
describe
(
'
AlertMappingBuilder
'
,
()
=>
{
let
wrapper
;
...
...
@@ -12,8 +12,8 @@ describe('AlertMappingBuilder', () => {
function
mountComponent
()
{
wrapper
=
shallowMount
(
AlertMappingBuilder
,
{
propsData
:
{
parsedPayload
:
parsedMapping
.
samplePayload
.
payloadAlerFields
.
node
s
,
savedMapping
:
parsedMapping
.
storedMapping
.
node
s
,
parsedPayload
:
parsedMapping
.
payloadAlerField
s
,
savedMapping
:
parsedMapping
.
payloadAttributeMapping
s
,
alertFields
,
},
});
...
...
@@ -33,6 +33,15 @@ describe('AlertMappingBuilder', () => {
const
findColumnInRow
=
(
row
,
column
)
=>
wrapper
.
findAll
(
'
.gl-display-table-row
'
).
at
(
row
).
findAll
(
'
.gl-display-table-cell
'
).
at
(
column
);
const
getDropdownContent
=
(
dropdown
,
types
)
=>
{
const
searchBox
=
dropdown
.
findComponent
(
GlSearchBoxByType
);
const
dropdownItems
=
dropdown
.
findAllComponents
(
GlDropdownItem
);
const
mappingOptions
=
parsedMapping
.
payloadAlerFields
.
filter
(({
type
})
=>
types
.
includes
(
type
),
);
return
{
searchBox
,
dropdownItems
,
mappingOptions
};
};
it
(
'
renders column captions
'
,
()
=>
{
expect
(
findColumnInRow
(
0
,
0
).
text
()).
toContain
(
i18n
.
columns
.
gitlabKeyTitle
);
expect
(
findColumnInRow
(
0
,
2
).
text
()).
toContain
(
i18n
.
columns
.
payloadKeyTitle
);
...
...
@@ -63,10 +72,7 @@ describe('AlertMappingBuilder', () => {
it
(
'
renders mapping dropdown for each field
'
,
()
=>
{
alertFields
.
forEach
(({
types
},
index
)
=>
{
const
dropdown
=
findColumnInRow
(
index
+
1
,
2
).
find
(
GlDropdown
);
const
searchBox
=
dropdown
.
findComponent
(
GlSearchBoxByType
);
const
dropdownItems
=
dropdown
.
findAllComponents
(
GlDropdownItem
);
const
{
nodes
}
=
parsedMapping
.
samplePayload
.
payloadAlerFields
;
const
mappingOptions
=
nodes
.
filter
(({
type
})
=>
types
.
includes
(
type
));
const
{
searchBox
,
dropdownItems
,
mappingOptions
}
=
getDropdownContent
(
dropdown
,
types
);
expect
(
dropdown
.
exists
()).
toBe
(
true
);
expect
(
searchBox
.
exists
()).
toBe
(
true
);
...
...
@@ -80,11 +86,7 @@ describe('AlertMappingBuilder', () => {
expect
(
dropdown
.
exists
()).
toBe
(
Boolean
(
numberOfFallbacks
));
if
(
numberOfFallbacks
)
{
const
searchBox
=
dropdown
.
findComponent
(
GlSearchBoxByType
);
const
dropdownItems
=
dropdown
.
findAllComponents
(
GlDropdownItem
);
const
{
nodes
}
=
parsedMapping
.
samplePayload
.
payloadAlerFields
;
const
mappingOptions
=
nodes
.
filter
(({
type
})
=>
types
.
includes
(
type
));
const
{
searchBox
,
dropdownItems
,
mappingOptions
}
=
getDropdownContent
(
dropdown
,
types
);
expect
(
searchBox
.
exists
()).
toBe
(
Boolean
(
numberOfFallbacks
));
expect
(
dropdownItems
).
toHaveLength
(
mappingOptions
.
length
);
}
...
...
spec/frontend/alerts_settings/components/alerts_settings_form_spec.js
View file @
e9c24b9d
...
...
@@ -11,7 +11,8 @@ import waitForPromises from 'helpers/wait_for_promises';
import
MappingBuilder
from
'
~/alerts_settings/components/alert_mapping_builder.vue
'
;
import
AlertsSettingsForm
from
'
~/alerts_settings/components/alerts_settings_form.vue
'
;
import
{
typeSet
}
from
'
~/alerts_settings/constants
'
;
import
alertFields
from
'
../mocks/alertFields.json
'
;
import
alertFields
from
'
../mocks/alert_fields.json
'
;
import
parsedMapping
from
'
../mocks/parsed_mapping.json
'
;
import
{
defaultAlertSettingsConfig
}
from
'
./util
'
;
describe
(
'
AlertsSettingsForm
'
,
()
=>
{
...
...
@@ -39,6 +40,9 @@ describe('AlertsSettingsForm', () => {
multiIntegrations
,
},
mocks
:
{
$apollo
:
{
query
:
jest
.
fn
(),
},
$toast
:
{
show
:
mockToastShow
,
},
...
...
@@ -146,7 +150,7 @@ describe('AlertsSettingsForm', () => {
enableIntegration
(
0
,
integrationName
);
const
sampleMapping
=
{
field
:
'
test
'
}
;
const
sampleMapping
=
parsedMapping
.
payloadAttributeMappings
;
findMappingBuilder
().
vm
.
$emit
(
'
onMappingUpdate
'
,
sampleMapping
);
findForm
().
trigger
(
'
submit
'
);
...
...
@@ -157,7 +161,7 @@ describe('AlertsSettingsForm', () => {
name
:
integrationName
,
active
:
true
,
payloadAttributeMappings
:
sampleMapping
,
payloadExample
:
null
,
payloadExample
:
'
{}
'
,
},
},
]);
...
...
@@ -275,34 +279,47 @@ describe('AlertsSettingsForm', () => {
});
describe
(
'
Test payload section for HTTP integration
'
,
()
=>
{
const
validSamplePayload
=
JSON
.
stringify
(
alertFields
);
const
emptySamplePayload
=
'
{}
'
;
beforeEach
(()
=>
{
createComponent
({
multipleHttpIntegrationsCustomMapping
:
true
,
props
:
{
data
:
{
currentIntegration
:
{
type
:
typeSet
.
http
,
payloadExample
:
validSamplePayload
,
payloadAttributeMappings
:
[],
},
alertFields
,
active
:
false
,
resetPayloadAndMappingConfirmed
:
false
,
},
props
:
{
alertFields
},
});
});
describe
.
each
`
active | resetSamplePayloadConfirmed | disabled
${
true
}
|
${
true
}
|
${
undefined
}
${
false
}
|
${
true
}
|
${
'
disabled
'
}
${
true
}
|
${
false
}
|
${
'
disabled
'
}
${
false
}
|
${
false
}
|
${
'
disabled
'
}
`
(
''
,
({
active
,
resetSamplePayloadConfirmed
,
disabled
})
=>
{
const
payloadResetMsg
=
resetSamplePayloadConfirmed
?
'
was confirmed
'
:
'
was not confirmed
'
;
active | resetPayloadAndMappingConfirmed | disabled
${
true
}
|
${
true
}
|
${
undefined
}
${
false
}
|
${
true
}
|
${
'
disabled
'
}
${
true
}
|
${
false
}
|
${
'
disabled
'
}
${
false
}
|
${
false
}
|
${
'
disabled
'
}
`
(
''
,
({
active
,
resetPayloadAndMappingConfirmed
,
disabled
})
=>
{
const
payloadResetMsg
=
resetPayloadAndMappingConfirmed
?
'
was confirmed
'
:
'
was not confirmed
'
;
const
enabledState
=
disabled
===
'
disabled
'
?
'
disabled
'
:
'
enabled
'
;
const
activeState
=
active
?
'
active
'
:
'
not active
'
;
it
(
`textarea should be
${
enabledState
}
when payload reset
${
payloadResetMsg
}
and current integration is
${
activeState
}
`
,
async
()
=>
{
wrapper
.
setData
({
customMapping
:
{
samplePayload
:
true
},
currentIntegration
:
{
type
:
typeSet
.
http
,
payloadExample
:
validSamplePayload
,
payloadAttributeMappings
:
[],
},
active
,
reset
SamplePayload
Confirmed
,
reset
PayloadAndMapping
Confirmed
,
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
findTestPayloadSection
().
find
(
GlFormTextarea
).
attributes
(
'
disabled
'
)).
toBe
(
disabled
);
...
...
@@ -311,20 +328,27 @@ describe('AlertsSettingsForm', () => {
describe
(
'
action buttons for sample payload
'
,
()
=>
{
describe
.
each
`
resetSamplePayloadConfirmed | samplePayload | caption
${
false
}
|
${
true
}
|
${
'
Edit payload
'
}
${
true
}
|
${
false
}
|
${
'
Submit payload
'
}
${
true
}
|
${
true
}
|
${
'
Submit payload
'
}
${
false
}
|
${
false
}
|
${
'
Submit payload
'
}
`
(
''
,
({
resetSamplePayloadConfirmed
,
samplePayload
,
caption
})
=>
{
const
samplePayloadMsg
=
samplePayload
?
'
was provided
'
:
'
was not provided
'
;
const
payloadResetMsg
=
resetSamplePayloadConfirmed
?
'
was confirmed
'
:
'
was not confirmed
'
;
resetPayloadAndMappingConfirmed | payloadExample | caption
${
false
}
|
${
validSamplePayload
}
|
${
'
Edit payload
'
}
${
true
}
|
${
emptySamplePayload
}
|
${
'
Submit payload
'
}
${
true
}
|
${
validSamplePayload
}
|
${
'
Submit payload
'
}
${
false
}
|
${
emptySamplePayload
}
|
${
'
Submit payload
'
}
`
(
''
,
({
resetPayloadAndMappingConfirmed
,
payloadExample
,
caption
})
=>
{
const
samplePayloadMsg
=
payloadExample
?
'
was provided
'
:
'
was not provided
'
;
const
payloadResetMsg
=
resetPayloadAndMappingConfirmed
?
'
was confirmed
'
:
'
was not confirmed
'
;
it
(
`shows
${
caption
}
button when sample payload
${
samplePayloadMsg
}
and payload reset
${
payloadResetMsg
}
`
,
async
()
=>
{
wrapper
.
setData
({
selectedIntegration
:
typeSet
.
http
,
customMapping
:
{
samplePayload
},
resetSamplePayloadConfirmed
,
currentIntegration
:
{
payloadExample
,
type
:
typeSet
.
http
,
active
:
true
,
payloadAttributeMappings
:
[],
},
resetPayloadAndMappingConfirmed
,
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
findActionBtn
().
text
()).
toBe
(
caption
);
...
...
@@ -333,16 +357,20 @@ describe('AlertsSettingsForm', () => {
});
describe
(
'
Parsing payload
'
,
()
=>
{
it
(
'
displays a toast message on successful parse
'
,
async
()
=>
{
jest
.
useFakeTimers
();
beforeEach
(()
=>
{
wrapper
.
setData
({
selectedIntegration
:
typeSet
.
http
,
customMapping
:
{
samplePayload
:
false
}
,
resetPayloadAndMappingConfirmed
:
true
,
});
await
wrapper
.
vm
.
$nextTick
(
);
}
);
it
(
'
displays a toast message on successful parse
'
,
async
()
=>
{
jest
.
spyOn
(
wrapper
.
vm
.
$apollo
,
'
query
'
).
mockResolvedValue
({
data
:
{
project
:
{
alertManagementPayloadFields
:
[]
},
},
});
findActionBtn
().
vm
.
$emit
(
'
click
'
);
jest
.
advanceTimersByTime
(
1000
);
await
waitForPromises
();
...
...
@@ -350,6 +378,16 @@ describe('AlertsSettingsForm', () => {
'
Sample payload has been parsed. You can now map the fields.
'
,
);
});
it
(
'
displays an error message under payload field on unsuccessful parse
'
,
async
()
=>
{
const
errorMessage
=
'
Error parsing paylod
'
;
jest
.
spyOn
(
wrapper
.
vm
.
$apollo
,
'
query
'
).
mockRejectedValue
({
message
:
errorMessage
});
findActionBtn
().
vm
.
$emit
(
'
click
'
);
await
waitForPromises
();
expect
(
findTestPayloadSection
().
find
(
'
.invalid-feedback
'
).
text
()).
toBe
(
errorMessage
);
});
});
});
...
...
spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
View file @
e9c24b9d
...
...
@@ -14,6 +14,8 @@ import createPrometheusIntegrationMutation from '~/alerts_settings/graphql/mutat
import
destroyHttpIntegrationMutation
from
'
~/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql
'
;
import
resetHttpTokenMutation
from
'
~/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql
'
;
import
resetPrometheusTokenMutation
from
'
~/alerts_settings/graphql/mutations/reset_prometheus_token.mutation.graphql
'
;
import
updateCurrentHttpIntegrationMutation
from
'
~/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql
'
;
import
updateCurrentPrometheusIntegrationMutation
from
'
~/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql
'
;
import
updateHttpIntegrationMutation
from
'
~/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql
'
;
import
updatePrometheusIntegrationMutation
from
'
~/alerts_settings/graphql/mutations/update_prometheus_integration.mutation.graphql
'
;
import
getIntegrationsQuery
from
'
~/alerts_settings/graphql/queries/get_integrations.query.graphql
'
;
...
...
@@ -31,7 +33,8 @@ import {
updateHttpVariables
,
createPrometheusVariables
,
updatePrometheusVariables
,
ID
,
HTTP_ID
,
PROMETHEUS_ID
,
errorMsg
,
getIntegrationsQueryResponse
,
destroyIntegrationResponse
,
...
...
@@ -50,8 +53,30 @@ describe('AlertsSettingsWrapper', () => {
let
fakeApollo
;
let
destroyIntegrationHandler
;
useMockIntersectionObserver
();
const
httpMappingData
=
{
payloadExample
:
'
{"test: : "field"}
'
,
payloadAttributeMappings
:
[],
payloadAlertFields
:
[],
};
const
httpIntegrations
=
{
list
:
[
{
id
:
mockIntegrations
[
0
].
id
,
...
httpMappingData
,
},
{
id
:
mockIntegrations
[
1
].
id
,
...
httpMappingData
,
},
{
id
:
mockIntegrations
[
2
].
id
,
httpMappingData
,
},
],
};
const
findLoader
=
()
=>
wrapper
.
find
(
IntegrationsList
).
find
(
GlLoadingIcon
);
const
findLoader
=
()
=>
wrapper
.
findComponent
(
IntegrationsList
).
findComponent
(
GlLoadingIcon
);
const
findIntegrationsList
=
()
=>
wrapper
.
findComponent
(
IntegrationsList
);
const
findIntegrations
=
()
=>
wrapper
.
find
(
IntegrationsList
).
findAll
(
'
table tbody tr
'
);
async
function
destroyHttpIntegration
(
localWrapper
)
{
...
...
@@ -197,13 +222,13 @@ describe('AlertsSettingsWrapper', () => {
});
wrapper
.
find
(
AlertsSettingsForm
).
vm
.
$emit
(
'
reset-token
'
,
{
type
:
typeSet
.
http
,
variables
:
{
id
:
ID
},
variables
:
{
id
:
HTTP_
ID
},
});
expect
(
wrapper
.
vm
.
$apollo
.
mutate
).
toHaveBeenCalledWith
({
mutation
:
resetHttpTokenMutation
,
variables
:
{
id
:
ID
,
id
:
HTTP_
ID
,
},
});
});
...
...
@@ -232,7 +257,7 @@ describe('AlertsSettingsWrapper', () => {
it
(
'
calls `$apollo.mutate` with `updatePrometheusIntegrationMutation`
'
,
()
=>
{
createComponent
({
data
:
{
integrations
:
{
list
:
mockIntegrations
},
currentIntegration
:
mockIntegrations
[
0
]
},
data
:
{
integrations
:
{
list
:
mockIntegrations
},
currentIntegration
:
mockIntegrations
[
3
]
},
loading
:
false
,
});
...
...
@@ -261,13 +286,13 @@ describe('AlertsSettingsWrapper', () => {
});
wrapper
.
find
(
AlertsSettingsForm
).
vm
.
$emit
(
'
reset-token
'
,
{
type
:
typeSet
.
prometheus
,
variables
:
{
id
:
ID
},
variables
:
{
id
:
PROMETHEUS_
ID
},
});
expect
(
wrapper
.
vm
.
$apollo
.
mutate
).
toHaveBeenCalledWith
({
mutation
:
resetPrometheusTokenMutation
,
variables
:
{
id
:
ID
,
id
:
PROMETHEUS_
ID
,
},
});
});
...
...
@@ -328,6 +353,42 @@ describe('AlertsSettingsWrapper', () => {
mock
.
restore
();
});
});
it
(
'
calls `$apollo.mutate` with `updateCurrentHttpIntegrationMutation` on HTTP integration edit
'
,
()
=>
{
createComponent
({
data
:
{
integrations
:
{
list
:
mockIntegrations
},
currentIntegration
:
mockIntegrations
[
0
],
httpIntegrations
,
},
loading
:
false
,
});
jest
.
spyOn
(
wrapper
.
vm
.
$apollo
,
'
mutate
'
);
findIntegrationsList
().
vm
.
$emit
(
'
edit-integration
'
,
updateHttpVariables
);
expect
(
wrapper
.
vm
.
$apollo
.
mutate
).
toHaveBeenCalledWith
({
mutation
:
updateCurrentHttpIntegrationMutation
,
variables
:
{
...
mockIntegrations
[
0
],
...
httpMappingData
},
});
});
it
(
'
calls `$apollo.mutate` with `updateCurrentPrometheusIntegrationMutation` on PROMETHEUS integration edit
'
,
()
=>
{
createComponent
({
data
:
{
integrations
:
{
list
:
mockIntegrations
},
currentIntegration
:
mockIntegrations
[
3
],
httpIntegrations
,
},
loading
:
false
,
});
jest
.
spyOn
(
wrapper
.
vm
.
$apollo
,
'
mutate
'
);
findIntegrationsList
().
vm
.
$emit
(
'
edit-integration
'
,
updatePrometheusVariables
);
expect
(
wrapper
.
vm
.
$apollo
.
mutate
).
toHaveBeenCalledWith
({
mutation
:
updateCurrentPrometheusIntegrationMutation
,
variables
:
mockIntegrations
[
3
],
});
});
});
describe
(
'
with mocked Apollo client
'
,
()
=>
{
...
...
spec/frontend/alerts_settings/components/mocks/apollo_mock.js
View file @
e9c24b9d
const
projectPath
=
''
;
export
const
ID
=
'
gid://gitlab/AlertManagement::HttpIntegration/7
'
;
export
const
HTTP_ID
=
'
gid://gitlab/AlertManagement::HttpIntegration/7
'
;
export
const
PROMETHEUS_ID
=
'
gid://gitlab/PrometheusService/12
'
;
export
const
errorMsg
=
'
Something went wrong
'
;
export
const
createHttpVariables
=
{
name
:
'
Test Pre
'
,
active
:
true
,
projectPath
,
type
:
'
HTTP
'
,
};
export
const
updateHttpVariables
=
{
name
:
'
Test Pre
'
,
active
:
true
,
id
:
ID
,
id
:
HTTP_ID
,
type
:
'
HTTP
'
,
};
export
const
createPrometheusVariables
=
{
apiUrl
:
'
https://test-pre.com
'
,
active
:
true
,
projectPath
,
type
:
'
PROMETHEUS
'
,
};
export
const
updatePrometheusVariables
=
{
apiUrl
:
'
https://test-pre.com
'
,
active
:
true
,
id
:
ID
,
id
:
PROMETHEUS_ID
,
type
:
'
PROMETHEUS
'
,
};
export
const
getIntegrationsQueryResponse
=
{
...
...
@@ -99,6 +104,9 @@ export const destroyIntegrationResponse = {
'
http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-5/d4875758e67334f3.json
'
,
token
:
'
89eb01df471d990ff5162a1c640408cf
'
,
apiUrl
:
null
,
payloadExample
:
'
{"field": "value"}
'
,
payloadAttributeMappings
:
[],
payloadAlertFields
:
[],
},
},
},
...
...
@@ -117,6 +125,9 @@ export const destroyIntegrationResponseWithErrors = {
'
http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-5/d4875758e67334f3.json
'
,
token
:
'
89eb01df471d990ff5162a1c640408cf
'
,
apiUrl
:
null
,
payloadExample
:
'
{"field": "value"}
'
,
payloadAttributeMappings
:
[],
payloadAlertFields
:
[],
},
},
},
...
...
spec/frontend/alerts_settings/mocks/alert
F
ields.json
→
spec/frontend/alerts_settings/mocks/alert
_f
ields.json
View file @
e9c24b9d
File moved
spec/frontend/alerts_settings/mocks/parsed_mapping.json
0 → 100644
View file @
e9c24b9d
{
"payloadAlerFields"
:
[
{
"path"
:
[
"dashboardId"
],
"label"
:
"Dashboard Id"
,
"type"
:
"string"
},
{
"path"
:
[
"evalMatches"
],
"label"
:
"Eval Matches"
,
"type"
:
"array"
},
{
"path"
:
[
"createdAt"
],
"label"
:
"Created At"
,
"type"
:
"datetime"
},
{
"path"
:
[
"imageUrl"
],
"label"
:
"Image Url"
,
"type"
:
"string"
},
{
"path"
:
[
"message"
],
"label"
:
"Message"
,
"type"
:
"string"
},
{
"path"
:
[
"orgId"
],
"label"
:
"Org Id"
,
"type"
:
"string"
},
{
"path"
:
[
"panelId"
],
"label"
:
"Panel Id"
,
"type"
:
"string"
},
{
"path"
:
[
"ruleId"
],
"label"
:
"Rule Id"
,
"type"
:
"string"
},
{
"path"
:
[
"ruleName"
],
"label"
:
"Rule Name"
,
"type"
:
"string"
},
{
"path"
:
[
"ruleUrl"
],
"label"
:
"Rule Url"
,
"type"
:
"string"
},
{
"path"
:
[
"state"
],
"label"
:
"State"
,
"type"
:
"string"
},
{
"path"
:
[
"title"
],
"label"
:
"Title"
,
"type"
:
"string"
},
{
"path"
:
[
"tags"
,
"tag"
],
"label"
:
"Tags"
,
"type"
:
"string"
}
],
"payloadAttributeMappings"
:
[
{
"fieldName"
:
"title"
,
"label"
:
"Title"
,
"type"
:
"STRING"
,
"path"
:
[
"title"
]
},
{
"fieldName"
:
"description"
,
"label"
:
"description"
,
"type"
:
"STRING"
,
"path"
:
[
"description"
]
},
{
"fieldName"
:
"hosts"
,
"label"
:
"Host"
,
"type"
:
"ARRAY"
,
"path"
:
[
"hosts"
,
"host"
]
},
{
"fieldName"
:
"startTime"
,
"label"
:
"Created Atd"
,
"type"
:
"STRING"
,
"path"
:
[
"time"
,
"createdAt"
]
}
]
}
spec/frontend/alerts_settings/utils/mapping_transformations_spec.js
View file @
e9c24b9d
import
parsedMapping
from
'
~/alerts_settings/components/mocks/parsedMapping.json
'
;
import
{
getMappingData
,
getPayloadFields
,
transformForSave
,
}
from
'
~/alerts_settings/utils/mapping_transformations
'
;
import
alertFields
from
'
../mocks/alertFields.json
'
;
import
{
getMappingData
,
transformForSave
}
from
'
~/alerts_settings/utils/mapping_transformations
'
;
import
alertFields
from
'
../mocks/alert_fields.json
'
;
import
parsedMapping
from
'
../mocks/parsed_mapping.json
'
;
describe
(
'
Mapping Transformation Utilities
'
,
()
=>
{
const
nameField
=
{
label
:
'
Name
'
,
path
:
[
'
alert
'
,
'
name
'
],
type
:
'
string
'
,
type
:
'
STRING
'
,
};
const
dashboardField
=
{
label
:
'
Dashboard Id
'
,
path
:
[
'
alert
'
,
'
dashboardId
'
],
type
:
'
string
'
,
type
:
'
STRING
'
,
};
describe
(
'
getMappingData
'
,
()
=>
{
it
(
'
should return mapping data
'
,
()
=>
{
const
result
=
getMappingData
(
alertFields
,
getPayloadFields
(
parsedMapping
.
samplePayload
.
payloadAlerFields
.
nodes
.
slice
(
0
,
3
)
),
parsedMapping
.
storedMapping
.
node
s
.
slice
(
0
,
3
),
parsedMapping
.
payloadAlerFields
.
slice
(
0
,
3
),
parsedMapping
.
payloadAttributeMapping
s
.
slice
(
0
,
3
),
);
result
.
forEach
((
data
,
index
)
=>
{
...
...
@@ -44,8 +40,8 @@ describe('Mapping Transformation Utilities', () => {
const
mockMappingData
=
[
{
name
:
fieldName
,
mapping
:
'
alert_name
'
,
mappingFields
:
getPayloadFields
([
dashboardField
,
nameField
])
,
mapping
:
[
'
alert
'
,
'
name
'
]
,
mappingFields
:
[
dashboardField
,
nameField
]
,
},
];
const
result
=
transformForSave
(
mockMappingData
);
...
...
@@ -61,21 +57,11 @@ describe('Mapping Transformation Utilities', () => {
{
name
:
fieldName
,
mapping
:
null
,
mappingFields
:
getPayloadFields
([
nameField
,
dashboardField
])
,
mappingFields
:
[
nameField
,
dashboardField
]
,
},
];
const
result
=
transformForSave
(
mockMappingData
);
expect
(
result
).
toEqual
([]);
});
});
describe
(
'
getPayloadFields
'
,
()
=>
{
it
(
'
should add name field to each payload field
'
,
()
=>
{
const
result
=
getPayloadFields
([
nameField
,
dashboardField
]);
expect
(
result
).
toEqual
([
{
...
nameField
,
name
:
'
alert_name
'
},
{
...
dashboardField
,
name
:
'
alert_dashboardId
'
},
]);
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment