/* * Copyright 2020 EPAM Systems * * 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 React, { useState } from "react"; import { Button, Panel, Icons, Spinner, composeValidators, numericLimits, positiveInteger, Form, Formik, useCloseModal, } from "@drill4j/ui-kit"; import tw, { styled } from "twin.macro"; import { ConditionSetting, ConditionSettingByType, QualityGate, QualityGateStatus as Status, } from "types/quality-gate-type"; import { KEY_METRICS_EVENT_NAMES, sendKeyMetricsEvent } from "common/analytic"; import { useAdminConnection, useAgentRouteParams, useFilteredData } from "hooks"; import { AnalyticsInfo } from "types"; import { QualityGateStatus } from "./quality-gate-status"; import { QualityGateSettings } from "./quality-gate-settings"; import { updateQualityGateSettings } from "./api"; const validateQualityGate = (formValues: ConditionSettingByType) => composeValidators( formValues.coverage?.enabled ? numericLimits({ fieldName: "coverage.condition.value", fieldAlias: "Build coverage", min: 0.1, max: 100, }) : () => undefined, formValues.risks?.enabled ? positiveInteger("risks.condition.value", "The field") : () => undefined, formValues.tests?.enabled ? positiveInteger("tests.condition.value", "The field") : () => undefined, )(formValues); export const QualityGatePane = () => { const { pluginId = "", agentId = "" } = useAgentRouteParams(); const [isEditing, setIsEditing] = useState(false); const closeModal = useCloseModal(); const { isAnalyticsDisabled } = useAdminConnection("/api/analytics/info") || {}; const handleOnToggle = () => { closeModal(); setIsEditing(false); }; const { status = "FAILED", results = { coverage: false, risks: false, tests: false }, } = useFilteredData("/data/quality-gate") || {}; const conditionSettings = useFilteredData("/data/quality-gate-settings") || []; const conditionSettingByType = conditionSettings.reduce( (conditionSetting, measureType) => ({ ...conditionSetting, [measureType.condition.measure]: measureType, }), {} as ConditionSettingByType, ); const StatusIcon = Icons[status]; const configured = conditionSettings.some(({ enabled }) => enabled); return ( { await updateQualityGateSettings(agentId, pluginId)(values as ConditionSettingByType); setIsEditing(false); const { coverage, risks, tests } = values as ConditionSettingByType; const label = []; coverage?.enabled && label.push("Build coverage"); risks?.enabled && label.push("Risks"); tests?.enabled && label.push("Tests to run"); !isAnalyticsDisabled && sendKeyMetricsEvent({ name: KEY_METRICS_EVENT_NAMES.CLICK_ON_SAVE_BUTTON_IN_QG_PANEL, label: label.join("#"), }); }} initialValues={conditionSettingByType} validate={validateQualityGate} enableReinitialize > {({ values, isValid, dirty, isSubmitting, initialValues, resetForm, }) => { const isCheckboxesPristine = Object.entries(values) .every(([key, value]) => conditionSettingByType[key].enabled === value.enabled); return (
Quality Gate
{configured && !isEditing && ( )}
{configured && !isEditing ? "Meet all conditions to pass the quality gate." : "Choose the metrics and define their threshold."} {configured && !isEditing ? : } {configured && !isEditing ? ( ) : ( )} {configured && isEditing && ( )}
); }}
); }; const StatusIconWrapper = styled.div(({ status }: { status: Status }) => [ tw`flex items-center`, status === "PASSED" && tw`text-green-default`, status === "FAILED" && tw`text-red-default`, ]); const ActionsPanel = styled.div` ${tw`grid gap-4 items-center`}; grid-template-columns: max-content max-content max-content; `;