Validation statuses polling
What does this MR do?
This introduces live validation status updates to the DAST site profiles library.
The logic is as follows:
- When the library loads, any profile that is currently being validated has its normalized URL[1] added to a stack.
- If the user manually triggers validation on any non-validated profile, that profile's normalized URL is added the above-mentioned stack.
- Whenever the normalized URLs stack contains item, a GraphQL smart query starts fetching up-to-date statuses and overwirites the data in the local cache to keep the list up-to-date in the UI.
- Once a validation ends (because it either failed or succeeded), the corresponding normalized URL is removed from the stack.
[1]: note that normalized URLs aren't supported yet, so the MR currently mocks the behavior by using the profiles' target URL.
How to test this?
- Enable the feature flag:
echo "Feature.enable(:security_on_demand_scans_site_validation)" | rails c
- Since the backend is currently not ready to support this feature, apply the following patch to mock the GraphQL queries client-side. This will mock the validations query by returning some random statuses, and the
targetUrl
will be used instead ofnormalizedTargetUrl
which doesn't exist yet.
Patch
diff --git a/ee/app/assets/javascripts/security_configuration/dast_profiles/components/dast_site_profiles_list.vue b/ee/app/assets/javascripts/security_configuration/dast_profiles/components/dast_site_profiles_list.vue
index aa60f7f90b6..2eeb7d987a7 100644
--- a/ee/app/assets/javascripts/security_configuration/dast_profiles/components/dast_site_profiles_list.vue
+++ b/ee/app/assets/javascripts/security_configuration/dast_profiles/components/dast_site_profiles_list.vue
@@ -88,12 +88,12 @@ export default {
if (!this.glFeatures.securityOnDemandScansSiteValidation) {
return;
}
- profiles.forEach(({ validationStatus, normalizedTargetUrl }) => {
+ profiles.forEach(({ validationStatus, targetUrl }) => {
if (
[PENDING, INPROGRESS].includes(validationStatus) &&
- !this.urlsPendingValidation.includes(normalizedTargetUrl)
+ !this.urlsPendingValidation.includes(targetUrl)
) {
- this.urlsPendingValidation.push(normalizedTargetUrl);
+ this.urlsPendingValidation.push(targetUrl);
}
});
},
@@ -118,10 +118,10 @@ export default {
this.showValidationModal();
});
},
- startValidatingProfile({ normalizedTargetUrl }) {
+ startValidatingProfile({ targetUrl }) {
updateSiteProfilesStatuses({
fullPath: this.fullPath,
- normalizedTargetUrl,
+ normalizedTargetUrl: targetUrl,
status: PENDING,
store: this.$apolloProvider.defaultClient,
});
diff --git a/ee/app/assets/javascripts/security_configuration/dast_profiles/graphql/cache_utils.js b/ee/app/assets/javascripts/security_configuration/dast_profiles/graphql/cache_utils.js
index 18037adc230..5eabcf981a3 100644
--- a/ee/app/assets/javascripts/security_configuration/dast_profiles/graphql/cache_utils.js
+++ b/ee/app/assets/javascripts/security_configuration/dast_profiles/graphql/cache_utils.js
@@ -69,7 +69,7 @@ export const updateSiteProfilesStatuses = ({ fullPath, normalizedTargetUrl, stat
const sourceData = store.readQuery(queryBody);
const profilesWithNormalizedTargetUrl = sourceData.project.siteProfiles.edges.flatMap(
- ({ node }) => (node.normalizedTargetUrl === normalizedTargetUrl ? node : []),
+ ({ node }) => (node.targetUrl === normalizedTargetUrl ? node : []),
);
profilesWithNormalizedTargetUrl.forEach(({ id }) => {
diff --git a/ee/app/assets/javascripts/security_configuration/dast_profiles/graphql/provider.js b/ee/app/assets/javascripts/security_configuration/dast_profiles/graphql/provider.js
index 80e16fe53a1..4f56820b1cc 100644
--- a/ee/app/assets/javascripts/security_configuration/dast_profiles/graphql/provider.js
+++ b/ee/app/assets/javascripts/security_configuration/dast_profiles/graphql/provider.js
@@ -1,14 +1,36 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
+import { random } from 'lodash';
import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo);
-export default new VueApollo({
- defaultClient: createDefaultClient(
- {},
- {
- assumeImmutableResults: true,
+const resolvers = {
+ Query: {
+ dastSiteValidations: (_, { normalizedTargetUrls }) => {
+ return {
+ nodes: normalizedTargetUrls.map(url => {
+ const randNumber = random(100);
+ let validationStatus = 'INPROGRESS_VALIDATION';
+ if (randNumber < 20) {
+ validationStatus = 'PASSED_VALIDATION';
+ } else if (randNumber > 80) {
+ validationStatus = 'FAILED_VALIDATION';
+ }
+ return {
+ normalizedTargetUrl: url,
+ status: validationStatus,
+ __typename: 'DastSiteValidation',
+ };
+ }),
+ __typename: 'DastSiteValidations',
+ };
},
- ),
+ },
+};
+
+export default new VueApollo({
+ defaultClient: createDefaultClient(resolvers, {
+ assumeImmutableResults: true,
+ }),
});
diff --git a/ee/app/assets/javascripts/security_configuration/dast_site_validation/graphql/dast_site_validations.query.graphql b/ee/app/assets/javascripts/security_configuration/dast_site_validation/graphql/dast_site_validations.query.graphql
index faf7db8e7e4..f72720ad9a6 100644
--- a/ee/app/assets/javascripts/security_configuration/dast_site_validation/graphql/dast_site_validations.query.graphql
+++ b/ee/app/assets/javascripts/security_configuration/dast_site_validation/graphql/dast_site_validations.query.graphql
@@ -1,10 +1,8 @@
query project($fullPath: ID!, $urls: [String!]) {
- project(fullPath: $fullPath) {
- validations: dastSiteValidations(normalizedTargetUrls: $urls) {
- nodes {
- normalizedTargetUrl
- status
- }
+ validations: dastSiteValidations(normalizedTargetUrls: $urls) @client {
+ nodes {
+ normalizedTargetUrl
+ status
}
}
}
- Navigate to the the site profiles library at
:group/:project/-/security/configuration/dast_profiles
. - Validate some profiles by clicking on the associated
Validate target site
button. - Reload the page to try again.
Screenshots (strongly suggested)
Does this MR meet the acceptance criteria?
Conformity
- [-] Changelog entry (not needed a this is behind a feature flag)
- [-] Documentation (if required)
-
Code review guidelines - [-] Merge request performance guidelines
-
Style guides - [-] Database guides
-
Separation of EE specific content
Availability and Testing
-
Review and add/update tests for this feature/bug. Consider all test levels. See the Test Planning Process. - [-] Tested in all supported browsers
- [-] Informed Infrastructure department of a default or new setting change, if applicable per definition of done
Related to #280570 (closed)
Edited by Paul Gascou-Vaillancourt