Align url handling for more consistency

This commit is contained in:
guerler
2022-10-05 11:47:11 -04:00
parent dd25dde39e
commit 2f06c940a6
24 changed files with 144 additions and 159 deletions
+3 -3
View File
@@ -25,7 +25,7 @@
</template>
<script>
import { getAppRoot } from "onload/loadConfig";
import { safePath } from "utils/redirect";
import { submitData } from "./services";
import { UrlDataProvider } from "components/providers/UrlDataProvider";
import { visitInputs } from "components/Form/utilities";
@@ -99,7 +99,7 @@ export default {
this.formData = formData;
},
onCancel() {
window.location = `${getAppRoot()}${this.cancelRedirect}`;
window.location = safePath(this.cancelRedirect);
},
onSubmit() {
submitData(this.url, this.formData).then((response) => {
@@ -115,7 +115,7 @@ export default {
}
if (this.redirect) {
const urlParams = new URLSearchParams(params);
window.location = `${getAppRoot()}${this.redirect}?${urlParams.toString()}`;
window.location = safePath(`${this.redirect}?${urlParams.toString()}`);
} else {
const replaceParams = {};
visitInputs(response.inputs, (input, name) => {
+2 -2
View File
@@ -1,10 +1,10 @@
import axios from "axios";
import { rethrowSimple } from "utils/simple-error";
import { getAppRoot } from "onload/loadConfig";
import { safePath } from "utils/redirect";
export async function submitData(url, payload) {
try {
const { data } = await axios.put(`${getAppRoot()}${url}`, payload);
const { data } = await axios.put(safePath(url), payload);
return data;
} catch (e) {
rethrowSimple(e);
@@ -140,7 +140,7 @@ export default {
return dataset && dataset.name;
},
datatypesUrl() {
return "api/datatypes/types_and_mapping";
return "/api/datatypes/types_and_mapping";
},
downloadUrl() {
return `${getAppRoot()}dataset/display?dataset_id=${this.args.history_dataset_id}`;
@@ -152,10 +152,10 @@ export default {
return `${getAppRoot()}dataset/imp?dataset_id=${this.args.history_dataset_id}`;
},
itemUrl() {
return `api/datasets/${this.args.history_dataset_id}/get_content_as_text`;
return `/api/datasets/${this.args.history_dataset_id}/get_content_as_text`;
},
metaUrl() {
return `api/datasets/${this.args.history_dataset_id}`;
return `/api/datasets/${this.args.history_dataset_id}`;
},
},
methods: {
@@ -10,7 +10,8 @@
role="button"
title="Download Workflow"
type="button"
class="py-0 px-1">
class="py-0 px-1"
data-description="workflow download">
<span class="fa fa-download" />
</b-button>
<b-button
@@ -20,18 +21,19 @@
variant="link"
title="Import Workflow"
type="button"
class="py-0 px-1">
class="py-0 px-1"
data-description="workflow import">
<span class="fa fa-file-import" />
</b-button>
</span>
<span>
<span>Workflow:</span>
<span class="font-weight-light">{{ workflowName }}</span>
<span class="font-weight-light" data-description="workflow name">{{ workflowName }}</span>
</span>
</b-card-header>
<b-card-body>
<LoadingSpan v-if="loading" message="Loading Workflow" />
<div v-else class="content-height">
<div v-else :class="!expanded && 'content-height'">
<div v-for="step in itemContent.steps" :key="step.order_index" class="mb-2">
<div>Step {{ step.order_index + 1 }}: {{ step.label }}</div>
<WorkflowTree :input="step" :skip-head="true" />
@@ -42,10 +44,10 @@
</template>
<script>
import { getAppRoot } from "onload/loadConfig";
import { safePath } from "utils/redirect";
import { urlData } from "utils/url";
import LoadingSpan from "components/LoadingSpan";
import WorkflowTree from "./WorkflowTree";
import axios from "axios";
export default {
components: {
LoadingSpan,
@@ -56,14 +58,14 @@ export default {
type: Object,
required: true,
},
workflows: {
type: Object,
required: true,
},
embedded: {
type: Boolean,
default: false,
},
expanded: {
type: Boolean,
default: false,
},
},
data() {
return {
@@ -73,35 +75,25 @@ export default {
},
computed: {
workflowName() {
const workflow = this.workflows[this.args.workflow_id];
return workflow && workflow.name;
return this.itemContent ? this.itemContent.name : "...";
},
downloadUrl() {
return `${getAppRoot()}api/workflows/${this.args.workflow_id}/download?format=json-download`;
return safePath("/api/workflows/${this.args.workflow_id}/download?format=json-download");
},
importUrl() {
return `${getAppRoot()}workflow/imp?id=${this.args.workflow_id}`;
return safePath("/workflow/imp?id=${this.args.workflow_id}");
},
itemUrl() {
return `${getAppRoot()}api/workflows/${this.args.workflow_id}/download?style=preview`;
return `/api/workflows/${this.args.workflow_id}/download?style=preview`;
},
},
created() {
this.getContent().then((data) => {
const url = this.itemUrl;
urlData({ url }).then((data) => {
this.itemContent = data;
this.loading = false;
});
},
methods: {
async getContent() {
try {
const response = await axios.get(this.itemUrl);
return response.data;
} catch (e) {
return `Failed to retrieve content. ${e}`;
}
},
},
};
</script>
<style scoped>
+22 -13
View File
@@ -23,11 +23,11 @@
<span class="float-left font-weight-light mb-3">
<small>Title: {{ markdownConfig.title || markdownConfig.model_class }}</small
><br />
<small>Username: {{ markdownConfig.username }}</small>
<small>Created by {{ markdownConfig.username }}</small>
</span>
</div>
<b-badge variant="info" class="w-100 rounded mb-3">
<div class="float-left m-1">Created with Galaxy {{ getVersion }} on {{ getTime }}</div>
<div class="float-left m-1">Published with Galaxy {{ getVersion }} on {{ getTime }}</div>
<div class="float-right m-1">Identifier {{ markdownConfig.id }}</div>
</b-badge>
<div>
@@ -206,20 +206,29 @@ export default {
},
},
watch: {
markdownConfig: function (config) {
const markdown = config.markdown;
this.markdownErrors = config.errors || [];
this.markdownObjects = this.splitMarkdown(markdown);
this.historyDatasets = config.history_datasets || {};
this.histories = config.histories || {};
this.historyDatasetCollections = config.history_dataset_collections || {};
this.workflows = config.workflows || {};
this.jobs = config.jobs || {};
this.invocations = config.invocations || {};
this.loading = false;
markdownConfig() {
this.initConfig();
},
},
created() {
this.initConfig();
},
methods: {
initConfig() {
if (Object.keys(this.markdownConfig).length) {
const config = this.markdownConfig;
const markdown = config.content || config.markdown;
this.markdownErrors = config.errors || [];
this.markdownObjects = this.splitMarkdown(markdown);
this.historyDatasets = config.history_datasets || {};
this.histories = config.histories || {};
this.historyDatasetCollections = config.history_dataset_collections || {};
this.workflows = config.workflows || {};
this.jobs = config.jobs || {};
this.invocations = config.invocations || {};
this.loading = false;
}
},
splitMarkdown(markdown) {
const sections = [];
let digest = markdown;
@@ -28,6 +28,7 @@ library.add(faDownload, faSpinner);
import ConfigProvider from "components/providers/ConfigProvider";
import { Toast } from "ui/toast";
import axios from "axios";
import { safePath } from "utils/redirect";
export default {
components: {
ConfigProvider,
@@ -67,7 +68,7 @@ export default {
onDownload(config) {
if (!config.enable_celery_tasks) {
console.log("celery tasks not enabled - setting href to fallback URL.");
window.location.assign(this.fallbackUrl);
window.location.assign(safePath(this.fallbackUrl));
return;
}
this.waiting = true;
@@ -2,7 +2,7 @@
<config-provider v-slot="{ config, loading }">
<markdown
v-if="!loading"
:markdown-config="markdownConfig"
:markdown-config="page"
:enable_beta_markdown_export="config.enable_beta_markdown_export"
:download-endpoint="stsUrl(config)"
:export-link="exportUrl"
@@ -11,8 +11,8 @@
</template>
<script>
import { getAppRoot } from "onload/loadConfig";
import axios from "axios";
import { safePath } from "utils/redirect";
import { urlData } from "utils/url";
import ConfigProvider from "components/providers/ConfigProvider";
import Markdown from "components/Markdown/Markdown.vue";
@@ -29,40 +29,32 @@ export default {
},
data() {
return {
markdownConfig: {},
page: {},
};
},
computed: {
dataUrl() {
return `${getAppRoot()}api/pages/${this.pageId}`;
return `/api/pages/${this.pageId}`;
},
exportUrl() {
return `${this.dataUrl}.pdf`;
},
editUrl() {
return `${getAppRoot()}page/edit_content?id=${this.pageId}`;
return `/page/edit_content?id=${this.pageId}`;
},
},
created() {
this.getContent().then((data) => {
this.markdownConfig = { ...data, markdown: data.content };
urlData({ url: this.dataUrl }).then((data) => {
this.page = { ...data };
});
},
methods: {
onEdit() {
window.location = this.editUrl;
window.location = safePath(this.editUrl);
},
stsUrl(config) {
return `${this.dataUrl}/prepare_download`;
},
async getContent() {
try {
const response = await axios.get(this.dataUrl);
return response.data;
} catch (e) {
return `Failed to retrieve content. ${e}`;
}
},
},
};
</script>
+1 -1
View File
@@ -47,7 +47,7 @@ export default {
},
created() {
this.root = getAppRoot();
urlData({ url: `api/tours` })
urlData({ url: `/api/tours` })
.then((response) => {
this.tours = response;
})
+1 -1
View File
@@ -1,5 +1,5 @@
<template>
<CenterFrame src="welcome" />
<CenterFrame src="/welcome" />
</template>
<script>
@@ -12,9 +12,9 @@ export const getUserPreferencesModel = (user_id) => {
description: config.enable_account_interface
? _l("Edit your email, addresses and custom parameters or change your public name.")
: _l("Edit your custom parameters."),
url: `api/users/${user_id}/information/inputs`,
url: `/api/users/${user_id}/information/inputs`,
icon: "fa-user",
redirect: "user",
redirect: "/user",
shouldRender: !config.use_remote_user,
},
password: {
@@ -22,9 +22,9 @@ export const getUserPreferencesModel = (user_id) => {
id: "edit-preferences-password",
description: _l("Allows you to change your login credentials."),
icon: "fa-unlock-alt",
url: `api/users/${user_id}/password/inputs`,
url: `/api/users/${user_id}/password/inputs`,
submit_title: "Save Password",
redirect: "user",
redirect: "/user",
shouldRender: !config.use_remote_user && config.enable_account_interface,
},
external_ids: {
@@ -42,10 +42,10 @@ export const getUserPreferencesModel = (user_id) => {
description: _l(
"Grant others default access to newly created histories. Changes made here will only affect histories created after these settings have been stored."
),
url: `api/users/${user_id}/permissions/inputs`,
url: `/api/users/${user_id}/permissions/inputs`,
icon: "fa-users",
submitTitle: "Save Permissions",
redirect: "user",
redirect: "/user",
shouldRender: !config.single_user,
},
make_data_private: {
@@ -59,7 +59,7 @@ export const getUserPreferencesModel = (user_id) => {
title: _l("Manage API Key"),
id: "edit-preferences-api-key",
description: _l("Access your current API key or create a new one."),
url: `api/users/${user_id}/api_key/inputs`,
url: `/api/users/${user_id}/api_key/inputs`,
icon: "fa-key",
submitTitle: "Create a new Key",
submitIcon: "fa-check",
@@ -77,10 +77,10 @@ export const getUserPreferencesModel = (user_id) => {
title: _l("Manage Toolbox Filters"),
id: "edit-preferences-toolbox-filters",
description: _l("Customize your Toolbox by displaying or omitting sets of Tools."),
url: `api/users/${user_id}/toolbox_filters/inputs`,
url: `/api/users/${user_id}/toolbox_filters/inputs`,
icon: "fa-filter",
submitTitle: "Save Filters",
redirect: "user",
redirect: "/user",
shouldRender: !!config.has_user_tool_filters,
},
custom_builds: {
@@ -10,12 +10,11 @@
</template>
<script>
import axios from "axios";
import { safePath } from "utils/redirect";
import { urlData } from "utils/url";
import { Toast } from "ui/toast";
import { getAppRoot } from "onload/loadConfig";
import { rethrowSimple } from "utils/simple-error";
import ConfigProvider from "components/providers/ConfigProvider";
import Markdown from "components/Markdown/Markdown.vue";
import Markdown from "components/Markdown/Markdown";
import Vue from "vue";
import BootstrapVue from "bootstrap-vue";
@@ -40,14 +39,15 @@ export default {
},
computed: {
dataUrl() {
return `${getAppRoot()}api/invocations/${this.invocationId}/report`;
return `/api/invocations/${this.invocationId}/report`;
},
exportUrl() {
return `${this.dataUrl}.pdf`;
},
},
created() {
this.getMarkdown()
const url = this.dataUrl;
urlData({ url })
.then((response) => {
this.markdownConfig = response;
this.invocationMarkdown = response.invocation_markdown;
@@ -58,16 +58,7 @@ export default {
},
methods: {
onEdit() {
window.location = `${getAppRoot()}pages/create?invocation_id=${this.invocationId}`;
},
/** Markdown data request helper **/
async getMarkdown() {
try {
const { data } = await axios.get(this.dataUrl);
return data;
} catch (e) {
rethrowSimple(e);
}
window.location = safePath(`/pages/create?invocation_id=${this.invocationId}`);
},
},
};
@@ -100,7 +100,7 @@ export default {
},
methods: {
getWorkflow() {
const url = `api/workflows/${this.id}`;
const url = `/api/workflows/${this.id}`;
urlData({ url })
.then((workflow) => {
this.workflow = workflow;
@@ -9,7 +9,7 @@
<div class="center-container">
<CenterFrame v-show="showCenter" id="galaxy_main" @load="onLoad" />
<div v-show="!showCenter" class="center-panel" style="display: block">
<router-view :key="$route.fullPath" />
<router-view class="h-100" :key="$route.fullPath" />
</div>
</div>
</div>
@@ -1,15 +1,17 @@
<template>
<iframe
:id="id"
:name="id"
:src="srcWithRoot"
frameborder="0"
class="center-frame"
title="galaxy frame"
:name="id"
:src="srcWithRoot"
width="100%"
height="100%"
@load="onLoad" />
</template>
<script>
import { getAppRoot } from "onload";
import { safePath } from "utils/redirect";
export default {
props: {
id: {
@@ -23,14 +25,11 @@ export default {
},
computed: {
srcWithRoot() {
if (this.src) {
return `${getAppRoot()}${this.src}`;
}
return undefined;
return safePath(this.src);
},
},
methods: {
onLoad: function (ev) {
onLoad(ev) {
const iframe = ev.currentTarget;
const location = iframe.contentWindow && iframe.contentWindow.location;
try {
+1 -1
View File
@@ -3,7 +3,7 @@
<ToolForm v-if="isTool && !isUpload" v-bind="toolParams" />
<WorkflowRun v-else-if="isWorkflow" v-bind="workflowParams" />
<div v-else-if="isController" :src="controllerParams" />
<CenterFrame v-else src="welcome" />
<CenterFrame v-else src="/welcome" />
</div>
</template>
@@ -28,7 +28,7 @@ export default {
},
methods: {
async getState() {
this.editorConfig = await urlData({ url: `workflow/editor?id=${this.workflowId}` });
this.editorConfig = await urlData({ url: `/workflow/editor?id=${this.workflowId}` });
},
},
};
+14 -14
View File
@@ -142,7 +142,7 @@ export function getRouter(Galaxy) {
path: "datasets/:datasetId/preview",
component: CenterFrame,
props: (route) => ({
src: `datasets/${route.params.datasetId}/display/?preview=True`,
src: `/datasets/${route.params.datasetId}/display/?preview=True`,
}),
},
{
@@ -176,8 +176,8 @@ export function getRouter(Galaxy) {
path: "histories/rename",
component: FormGeneric,
props: (route) => ({
url: `history/rename?id=${route.query.id}`,
redirect: "histories/list",
url: `/history/rename?id=${route.query.id}`,
redirect: "/histories/list",
}),
},
{
@@ -193,8 +193,8 @@ export function getRouter(Galaxy) {
path: "histories/permissions",
component: FormGeneric,
props: (route) => ({
url: `history/permissions?id=${route.query.id}`,
redirect: "histories/list",
url: `/history/permissions?id=${route.query.id}`,
redirect: "/histories/list",
}),
},
{
@@ -236,14 +236,14 @@ export function getRouter(Galaxy) {
path: "pages/create",
component: FormGeneric,
props: (route) => {
let url = "page/create";
let url = "/page/create";
const invocation_id = route.query.invocation_id;
if (invocation_id) {
url += `?invocation_id=${invocation_id}`;
}
return {
url: url,
redirect: "pages/list",
redirect: "/pages/list",
active_tab: "user",
};
},
@@ -252,8 +252,8 @@ export function getRouter(Galaxy) {
path: "pages/edit",
component: FormGeneric,
props: (route) => ({
url: `page/edit?id=${route.query.id}`,
redirect: "pages/list",
url: `/page/edit?id=${route.query.id}`,
redirect: "/pages/list",
active_tab: "user",
}),
},
@@ -338,8 +338,8 @@ export function getRouter(Galaxy) {
path: "visualizations/edit",
component: FormGeneric,
props: (route) => ({
url: `visualization/edit?id=${route.query.id}`,
redirect: "visualizations/list",
url: `/visualization/edit?id=${route.query.id}`,
redirect: "/visualizations/list",
active_tab: "visualization",
}),
},
@@ -369,12 +369,12 @@ export function getRouter(Galaxy) {
path: "workflows/create",
component: FormGeneric,
props: {
url: "workflow/create",
redirect: "workflows/edit",
url: "/workflow/create",
redirect: "/workflows/edit",
active_tab: "workflow",
submitTitle: "Create",
submitIcon: "fa-check",
cancelRedirect: "workflows/list",
cancelRedirect: "/workflows/list",
},
},
{
@@ -142,51 +142,51 @@ export default [
component: FormGeneric,
props: (route) => ({
title: "Reset passwords",
url: `admin/reset_user_password?id=${route.query.id}`,
url: `/admin/reset_user_password?id=${route.query.id}`,
icon: "fa-user",
submitTitle: "Save new password",
redirect: "admin/users",
redirect: "/admin/users",
}),
},
{
path: "form/manage_roles_and_groups_for_user",
component: FormGeneric,
props: (route) => ({
url: `admin/manage_roles_and_groups_for_user?id=${route.query.id}`,
url: `/admin/manage_roles_and_groups_for_user?id=${route.query.id}`,
icon: "fa-users",
redirect: "admin/users",
redirect: "/admin/users",
}),
},
{
path: "form/manage_users_and_groups_for_role",
component: FormGeneric,
props: (route) => ({
url: `admin/manage_users_and_groups_for_role?id=${route.query.id}`,
redirect: "admin/users",
url: `/admin/manage_users_and_groups_for_role?id=${route.query.id}`,
redirect: "/admin/users",
}),
},
{
path: "form/manage_users_and_roles_for_group",
component: FormGeneric,
props: (route) => ({
url: `admin/manage_users_and_roles_for_group?id=${route.query.id}`,
redirect: "admin/users",
url: `/admin/manage_users_and_roles_for_group?id=${route.query.id}`,
redirect: "/admin/users",
}),
},
{
path: "form/manage_users_and_groups_for_quota",
component: FormGeneric,
props: (route) => ({
url: `admin/manage_users_and_groups_for_quota?id=${route.query.id}`,
redirect: "admin/quotas",
url: `/admin/manage_users_and_groups_for_quota?id=${route.query.id}`,
redirect: "/admin/quotas",
}),
},
{
path: "form/create_role",
component: FormGeneric,
props: {
url: "admin/create_role",
redirect: "admin/roles",
url: "/admin/create_role",
redirect: "/admin/roles",
},
},
{
@@ -201,64 +201,64 @@ export default [
path: "form/create_quota",
component: FormGeneric,
props: {
url: "admin/create_quota",
redirect: "admin/quotas",
url: "/admin/create_quota",
redirect: "/admin/quotas",
},
},
{
path: "form/rename_role",
component: FormGeneric,
props: (route) => ({
url: `admin/rename_role?id=${route.query.id}`,
redirect: "admin/roles",
url: `/admin/rename_role?id=${route.query.id}`,
redirect: "/admin/roles",
}),
},
{
path: "form/rename_group",
component: FormGeneric,
props: (route) => ({
url: `admin/rename_group?id=${route.query.id}`,
redirect: "admin/groups",
url: `/admin/rename_group?id=${route.query.id}`,
redirect: "/admin/groups",
}),
},
{
path: "form/rename_quota",
component: FormGeneric,
props: (route) => ({
url: `admin/rename_quota?id=${route.query.id}`,
redirect: "admin/quotas",
url: `/admin/rename_quota?id=${route.query.id}`,
redirect: "/admin/quotas",
}),
},
{
path: "form/edit_quota",
component: FormGeneric,
props: (route) => ({
url: `admin/edit_quota?id=${route.query.id}`,
redirect: "admin/quotas",
url: `/admin/edit_quota?id=${route.query.id}`,
redirect: "/admin/quotas",
}),
},
{
path: "form/set_quota_default",
component: FormGeneric,
props: (route) => ({
url: `admin/set_quota_default?id=${route.query.id}`,
redirect: "admin/quotas",
url: `/admin/set_quota_default?id=${route.query.id}`,
redirect: "/admin/quotas",
}),
},
{
path: "form/create_form",
component: FormGeneric,
props: {
url: "forms/create_form",
redirect: "admin/forms",
url: "/forms/create_form",
redirect: "/admin/forms",
},
},
{
path: "form/edit_form",
component: FormGeneric,
props: (route) => ({
url: `forms/edit_form?id=${route.query.id}`,
redirect: "admin/forms",
url: `/forms/edit_form?id=${route.query.id}`,
redirect: "/admin/forms",
}),
},
],
@@ -45,7 +45,7 @@ const getters = {
const actions = {
fetchCollectionElements: async ({ commit }, { id, contentsUrl, offset }) => {
const url = `${contentsUrl}?offset=${offset}&limit=${limit}`;
const url = `/${contentsUrl}?offset=${offset}&limit=${limit}`;
await queue.enqueue(urlData, { url }).then((payload) => {
commit("saveCollectionElements", { id, payload });
});
@@ -16,7 +16,7 @@ const getters = {
const actions = {
fetchDataset: async ({ state, commit }, { id }) => {
if (!state.items[id]) {
const url = `api/datasets/${id}`;
const url = `/api/datasets/${id}`;
const dataset = await urlData({ url });
commit("saveDataset", { id, dataset });
}
@@ -55,7 +55,7 @@ const actions = {
fetchHistoryItems: async ({ commit }, { historyId, filterText, offset }) => {
const queryString = getQueryString(filterText);
const params = `v=dev&order=hid&offset=${offset}&limit=${limit}`;
const url = `api/histories/${historyId}/contents?${params}&${queryString}`;
const url = `/api/histories/${historyId}/contents?${params}&${queryString}`;
const headers = { accept: "application/vnd.galaxy.history.contents.stats+json" };
await queue.enqueue(urlData, { url, headers }, historyId).then((data) => {
const stats = data.stats;
@@ -63,7 +63,7 @@ export async function watchHistoryOnce(store) {
if (detailedIds.length) {
params["details"] = detailedIds.join(",");
}
const url = `api/histories/${historyId}/contents`;
const url = `/api/histories/${historyId}/contents`;
lastRequestDate = new Date();
const payload = await urlData({ url, params });
// show warning that not all changes have been obtained
+8 -7
View File
@@ -116,15 +116,16 @@ $ui-margin-horizontal-large: $margin-v * 2;
.ui-thumbnails-item {
cursor: pointer;
.ui-thumbnails-image {
padding: 10px;
width: 100px;
height: 86px;
padding: 1rem;
width: 5rem;
height: 4.3rem;
}
.ui-thumbnails-icon {
width: 100px;
height: 86px;
font-size: 4em;
padding: 20px 26px;
padding: 1rem;
width: 5rem;
height: 4.3rem;
font-size: 2em;
text-align: center;
color: $text-color;
}
.ui-thumbnails-text {
+2 -2
View File
@@ -1,5 +1,5 @@
import axios from "axios";
import { getAppRoot } from "onload/loadConfig";
import { safePath } from "utils/redirect";
import { rethrowSimple } from "utils/simple-error";
export async function urlData({ url, headers, params }) {
@@ -7,7 +7,7 @@ export async function urlData({ url, headers, params }) {
console.debug("Requesting data from: ", url);
headers = headers || {};
params = params || {};
const { data } = await axios.get(`${getAppRoot()}${url}`, { headers, params });
const { data } = await axios.get(safePath(url), { headers, params });
return data;
} catch (e) {
rethrowSimple(e);