Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ todo.md

rhythm_api/
rhythm/rhythm_api

signet/signet

db/mongo/data
db/postgres/data
db/db_postgres
4 changes: 2 additions & 2 deletions api/src/routes/datasets/uploads.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ router.get(
};
}
where.audit_log = {
user : {
user: {
username: req.params.username,
}
},
};

const filter_query = {
Expand Down
17 changes: 16 additions & 1 deletion api/src/routes/workflows.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,33 @@ const prisma = require('@/db');
const isPermittedTo = accessControl('workflow');
const router = express.Router();

router.get(
'/names',
isPermittedTo('read'),
asyncHandler(
async (req, res, next) => {
const workflows_names = Object.keys(config.workflow_registry);
res.json(workflows_names);
},
),
);

router.get(
'/',
isPermittedTo('read'),
validate([
query('dataset_id').isInt().toInt().optional(),
query('dataset_name').isString().optional().notEmpty(),
query('workflow_id').isString().optional().notEmpty(),
query('workflow_name').isString().optional().notEmpty(),
]),
asyncHandler(
async (req, res, next) => {
// #swagger.tags = ['Workflow']

const { dataset_id, dataset_name, workflow_id } = req.query;
const {
dataset_id, dataset_name, workflow_id, workflow_name,
} = req.query;
let workflow_ids = null;

// if workflow_id is provided, then ignore dataset_id and dataset_name
Expand Down Expand Up @@ -76,6 +90,7 @@ router.get(
skip: req.query.skip,
limit: req.query.limit,
workflow_ids,
workflow_name,
});

// for each workflow, get initiator details from the app db
Expand Down
2 changes: 2 additions & 0 deletions api/src/services/workflow.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ function getAll({
skip = null,
limit = null,
workflow_ids = null,
workflow_name = null,
} = {}) {
return wfApi.get('/workflows', {
params: {
Expand All @@ -24,6 +25,7 @@ function getAll({
skip,
limit,
workflow_id: workflow_ids,
workflow_name,
},
paramsSerializer: {
// to create workflow_id=123&workflow_id=456
Expand Down
96 changes: 89 additions & 7 deletions ui/src/components/runs/WorkflowSearchInputFilter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,56 @@
>
</va-select>

<va-input
class="flex-1"
v-model="search_text"
:placeholder="`Search by ${getSearchByLabel(search_by)}`"
clearable
:type="search_by === 'dataset_id' ? 'number' : 'text'"
/>
<div class="flex-1">
<!-- Dropdown for search by Workflow Name -->
<va-select
v-if="search_by === 'workflow_name'"
class="w-full"
v-model="search_text"
:options="workflow_names"
text-by="label"
value-by="key"
placeholder="Select Workflow"
clearable
/>
<!-- Autocomplete for search by Dataset Name -->
<DatasetSelectAutoComplete
v-else-if="search_by === 'dataset_name'"
class="w-full"
v-model:selected="selected_dataset"
v-model:search-term="dataset_search_text"
@clear="resetDatasetSearch"
@close="onDatasetSearchClose"
:placeholder="`Search by ${getSearchByLabel(search_by)}`"
/>
<!-- Inputs for search by Workflow ID / Dataset Id -->
<va-input
v-else
class="w-full"
v-model="search_text"
:placeholder="`Search by ${getSearchByLabel(search_by)}`"
clearable
:type="search_by === 'dataset_id' ? 'number' : 'text'"
/>
</div>
</div>
</template>

<script setup>
import workflowService from "@/services/workflow";

const search_by = defineModel("search_by", { type: String, required: true });
const search_text = defineModel("search_text", {
type: String,
required: true,
});
// const props = defineProps({});

const selected_dataset = ref(null);
const dataset_search_text = ref("");

const workflow_names = ref([]);

const search_by_options = [
{
label: "Dataset Name",
Expand All @@ -44,14 +76,64 @@ const search_by_options = [
label: "Workflow ID",
key: "workflow_id",
},
{
label: "Workflow Name",
key: "workflow_name",
},
];

function getSearchByLabel(key) {
return search_by_options.find((o) => o.key === key)?.label;
}

function resetDatasetSearch() {
selected_dataset.value = null;
dataset_search_text.value = "";
}

function onDatasetSearchClose() {
if (!selected_dataset.value) {
dataset_search_text.value = "";
} else {
dataset_search_text.value = selected_dataset.value?.name || "";
}
}

function toTitleCase(text) {
return text
.replace(/_/g, " ")
.split(" ")
.filter((segment) => segment.length > 0)
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join(" ");
}

// clear search text when user selects a new search by option
watch(search_by, () => {
// clear search-field's search text
search_text.value = "";
// clear Dataset-Search attributes
selected_dataset.value = null;
dataset_search_text.value = "";
});

onMounted(() => {
workflowService
.getWorkflowNames()
.then((res) => {
workflow_names.value = res.data.map((wf_name) => {
return {
key: wf_name,
label: toTitleCase(wf_name),
};
});
})
.catch(() => {
// console.error("error fetching workflow names");
});
});

watch(selected_dataset, () => {
search_text.value = selected_dataset.value?.name || "";
});
</script>
3 changes: 1 addition & 2 deletions ui/src/services/datetime.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,5 @@ export {
fromNow,
getMidnightNextDay,
readableDuration,
time
time,
};

3 changes: 1 addition & 2 deletions ui/src/services/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,5 @@ export {
navigateBackSafely,
setIntersection,
union,
validateEmail
validateEmail,
};

6 changes: 6 additions & 0 deletions ui/src/services/workflow.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ import api from "./api";
const DONE_STATUSES = ["REVOKED", "FAILURE", "SUCCESS"];

class WorkflowService {
getWorkflowNames() {
return api.get("/workflows/names");
}

getAll({
last_task_run = false,
prev_task_runs = false,
workflow_id = null,
dataset_id = null,
dataset_name = null,
workflow_name = null,
status = null,
skip = 0,
limit = 10,
Expand All @@ -24,6 +29,7 @@ class WorkflowService {
limit,
dataset_id,
dataset_name,
workflow_name,
initiator,
},
paramsSerializer: {
Expand Down