import { storeToRefs } from "pinia"; import type { Ref } from "vue"; import { computed, ref, watch } from "vue"; import type { Creator } from "@/api/workflows"; import type { DatatypesMapperModel } from "@/components/Datatypes/model"; import { useWorkflowStores } from "@/composables/workflowStores"; import type { Steps } from "@/stores/workflowStepStore"; import { getDisconnectedInputs, getDuplicateLabels, getMissingMetadata, getUnlabeledOutputs, getUntypedParameters, } from "./linting"; import type { DisconnectedInputState, DuplicateLabelState, MetadataLintState, UnlabeledOuputState, UntypedParameterState, } from "./lintingTypes"; import { getUntypedWorkflowParameters, type UntypedParameters } from "./parameters"; export interface LintData { checkAnnotation: Ref; checkAnnotationLength: Ref; checkReadme: Ref; checkLicense: Ref; checkCreator: Ref; resolvedPriorityIssues: Ref; totalPriorityIssues: Ref; resolvedAttributeIssues: Ref; totalAttributeIssues: Ref; untypedParameters: Ref; untypedParameterWarnings: Ref; disconnectedInputs: Ref; duplicateLabels: Ref; unlabeledOutputs: Ref; missingMetadata: Ref; } export function useLintData( workflowId: Ref, steps: Ref, datatypesMapper: Ref, annotation?: Ref, readme?: Ref, license?: Ref, creator?: Ref, ): LintData { const workflowStores = useWorkflowStores(workflowId); const untypedParameters = ref(); const untypedParameterWarnings = ref([]); const disconnectedInputs = ref([]); const duplicateLabels = ref([]); const unlabeledOutputs = ref([]); const missingMetadata = ref([]); watch( () => [steps, datatypesMapper.value], () => { if (datatypesMapper.value) { untypedParameters.value = getUntypedWorkflowParameters(steps.value); untypedParameterWarnings.value = getUntypedParameters(untypedParameters.value); disconnectedInputs.value = getDisconnectedInputs(steps.value, datatypesMapper.value, workflowStores); duplicateLabels.value = getDuplicateLabels(steps.value, workflowStores); unlabeledOutputs.value = getUnlabeledOutputs(steps.value); missingMetadata.value = getMissingMetadata(steps.value); } }, { immediate: true, deep: true }, ); const checkAnnotation = computed(() => Boolean(annotation?.value)); const checkAnnotationLength = computed(() => Boolean(annotation?.value && annotation.value.length <= 250)); const checkReadme = computed(() => Boolean(readme?.value)); const checkLicense = computed(() => Boolean(license?.value)); const checkCreator = computed(() => (creator?.value ? creator.value.length > 0 : false)); const { stepStore } = workflowStores; const { hasActiveOutputs, hasInputSteps } = storeToRefs(stepStore); /** This computes the `LintSection`s, some of which are conditionally rendered * in `Lint.vue`. This is used to compute the total and resolved best practice issues. * * TODO: Maybe this could be used to decide which section is rendered in the parent component as well? */ const lintingSections = computed(() => [ { name: "annotation", exists: true, resolved: checkAnnotation.value, priority: "low", }, { name: "annotationLength", exists: checkAnnotation.value && !checkAnnotationLength.value, resolved: false, priority: "low", }, { name: "readme", exists: true, resolved: checkReadme.value, priority: "low", }, { name: "creator", exists: true, resolved: checkCreator.value, priority: "low", }, { name: "license", exists: true, resolved: checkLicense.value, priority: "low", }, { name: "untypedParameters", exists: Object.keys(steps.value).length > 0, resolved: untypedParameterWarnings.value.length === 0, priority: "high", }, { name: "disconnectedInputs", exists: Object.keys(steps.value).length > 0, resolved: disconnectedInputs.value.length === 0, priority: "high", }, { name: "missingMetadata", exists: hasInputSteps.value, resolved: missingMetadata.value.length === 0, priority: "high", }, { name: "duplicateLabels", exists: hasActiveOutputs.value, resolved: duplicateLabels.value.length === 0, priority: "high", }, { name: "unlabeledOutputs", exists: hasActiveOutputs.value, resolved: unlabeledOutputs.value.length === 0, priority: "high", }, { name: "noOutputs", exists: !hasActiveOutputs.value, resolved: false, priority: "high", }, ]); const totalPriorityIssues = computed( () => lintingSections.value.filter((section) => section.exists && section.priority === "high").length, ); const resolvedPriorityIssues = computed( () => lintingSections.value.filter((section) => section.exists && section.resolved && section.priority === "high") .length, ); const totalAttributeIssues = computed( () => lintingSections.value.filter((section) => section.exists && section.priority === "low").length, ); const resolvedAttributeIssues = computed( () => lintingSections.value.filter((section) => section.exists && section.resolved && section.priority === "low") .length, ); return { checkAnnotation, checkAnnotationLength, checkReadme, checkLicense, checkCreator, resolvedPriorityIssues, totalPriorityIssues, resolvedAttributeIssues, totalAttributeIssues, untypedParameters, untypedParameterWarnings, disconnectedInputs, duplicateLabels, unlabeledOutputs, missingMetadata, }; }