mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Client format.
This commit is contained in:
@@ -4,15 +4,15 @@ import { createMask } from "imask";
|
||||
export default {
|
||||
extends: BInput,
|
||||
props: {
|
||||
mask: {
|
||||
type: String,
|
||||
required: false,
|
||||
mask: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
masker() {
|
||||
return createMask({
|
||||
return createMask({
|
||||
mask: this.mask
|
||||
});
|
||||
}
|
||||
@@ -26,4 +26,4 @@ export default {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -17,15 +17,14 @@ localVue.use(BootstrapVue);
|
||||
localVue.filter("localize", value => _l(value));
|
||||
|
||||
describe("CloudAuth component", () => {
|
||||
|
||||
let stub, wrapper;
|
||||
|
||||
let mockSvc = {
|
||||
listCredentials: async () => null,
|
||||
listCredentials: async () => null
|
||||
};
|
||||
|
||||
|
||||
rewire.__Rewire__("svc", mockSvc);
|
||||
|
||||
|
||||
beforeEach(async () => {
|
||||
let creds = listCredentials.map(Credential.create);
|
||||
stub = sinon.stub(mockSvc, "listCredentials").resolves(creds);
|
||||
@@ -38,54 +37,51 @@ describe("CloudAuth component", () => {
|
||||
stub.restore();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
describe("initialization", () => {
|
||||
it("should render the initial list", () => {
|
||||
assert(wrapper);
|
||||
assert(wrapper.contains(CloudAuthItem));
|
||||
assert(wrapper.vm.items.length == 2);
|
||||
assert(wrapper.vm.filteredItems.length == 2);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe("text filter", () => {
|
||||
it("should show filter result by text match", () => {
|
||||
|
||||
let results;
|
||||
|
||||
wrapper.vm.filter = "aws";
|
||||
results = wrapper.vm.filteredItems;
|
||||
assert(wrapper.contains(CloudAuthItem));
|
||||
assert(results.length == 1, `Wrong number of items: ${results.length}`);
|
||||
|
||||
|
||||
wrapper.vm.filter = "azure";
|
||||
results = wrapper.vm.filteredItems;
|
||||
assert(results.length == 1, `Wrong number of items: ${results.length}`);
|
||||
|
||||
|
||||
wrapper.vm.filter = "";
|
||||
results = wrapper.vm.filteredItems;
|
||||
assert(results.length == 2, `Wrong number of items: ${results.length}`);
|
||||
})
|
||||
})
|
||||
assert(results.length == 2, `Wrong number of items: ${results.length}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("create button", () => {
|
||||
it("clicking create button should add a blank key", () => {
|
||||
|
||||
let results = wrapper.vm.filteredItems;
|
||||
assert(wrapper.contains(CloudAuthItem));
|
||||
assert(results.length == 2, `Wrong number of items: ${results.length}`);
|
||||
|
||||
let button = wrapper.find('button[name=createNewKey]');
|
||||
|
||||
let button = wrapper.find("button[name=createNewKey]");
|
||||
assert(button);
|
||||
button.trigger('click');
|
||||
button.trigger("click");
|
||||
|
||||
results = wrapper.vm.filteredItems;
|
||||
assert(results.length == 3, `Wrong number of items: ${results.length}`);
|
||||
|
||||
|
||||
let blank = results.find(i => i.id == null);
|
||||
assert(blank, "missing blank key");
|
||||
assert(blank.id == null);
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
<template>
|
||||
<section class="cloud-auth">
|
||||
<header>
|
||||
|
||||
<b-alert dismissible fade variant="warning"
|
||||
:show="errorMessage !== null"
|
||||
@dismissed="errorMessage = null">
|
||||
<b-alert dismissible fade variant="warning" :show="errorMessage !== null" @dismissed="errorMessage = null">
|
||||
{{ errorMessage }}
|
||||
</b-alert>
|
||||
|
||||
@@ -13,28 +10,34 @@
|
||||
<nav class="operations">
|
||||
<ul>
|
||||
<li class="cloudKeyHelp">
|
||||
<a :class="{ active: showHelp }"
|
||||
<a
|
||||
:class="{ active: showHelp }"
|
||||
@click.prevent="showHelp = !showHelp"
|
||||
v-b-tooltip.hover
|
||||
v-b-tooltip.hover
|
||||
aria-label="Instructions"
|
||||
title="Instructions">
|
||||
title="Instructions"
|
||||
>
|
||||
<span>Instructions</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="cloudKeyFilter">
|
||||
<a :class="{ active: showFilter }"
|
||||
<a
|
||||
:class="{ active: showFilter }"
|
||||
@click.prevent="showFilter = !showFilter"
|
||||
v-b-tooltip.hover
|
||||
aria-label="Filter Results"
|
||||
title="Filter Results">
|
||||
v-b-tooltip.hover
|
||||
aria-label="Filter Results"
|
||||
title="Filter Results"
|
||||
>
|
||||
<span>Filter</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="createCloudKey">
|
||||
<a @click.prevent="onCreate"
|
||||
v-b-tooltip.hover
|
||||
<a
|
||||
@click.prevent="onCreate"
|
||||
v-b-tooltip.hover
|
||||
aria-label="Create New Key"
|
||||
title="Create New Key">
|
||||
title="Create New Key"
|
||||
>
|
||||
<span>Create New Key</span>
|
||||
</a>
|
||||
</li>
|
||||
@@ -51,8 +54,7 @@
|
||||
<transition name="fade">
|
||||
<hgroup v-if="showFilter">
|
||||
<b-form-group :description="filterDescription">
|
||||
<b-form-input type="text" placeholder="Filter"
|
||||
v-model="filter" />
|
||||
<b-form-input type="text" placeholder="Filter" v-model="filter" />
|
||||
</b-form-group>
|
||||
</hgroup>
|
||||
</transition>
|
||||
@@ -62,36 +64,39 @@
|
||||
Create New Authorization Key
|
||||
</button>
|
||||
</b-button-group>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="scroll-container">
|
||||
<b-list-group>
|
||||
<transition-group name="fade">
|
||||
<cloud-auth-item v-for="credential in filteredItems"
|
||||
<cloud-auth-item
|
||||
v-for="credential in filteredItems"
|
||||
:key="credential.counter"
|
||||
:credential="credential"
|
||||
class="mb-1"
|
||||
@delete="onDelete"
|
||||
@save="onSave"
|
||||
@expand="expand(credential, $event)" />
|
||||
@expand="expand(credential, $event)"
|
||||
/>
|
||||
</transition-group>
|
||||
</b-list-group>
|
||||
</div>
|
||||
|
||||
<b-modal v-model="hasDoomed" centered
|
||||
id="deleteCredentialModal" ref="deleteModal"
|
||||
title="Delete Key?" size="sm"
|
||||
<b-modal
|
||||
v-model="hasDoomed"
|
||||
centered
|
||||
id="deleteCredentialModal"
|
||||
ref="deleteModal"
|
||||
title="Delete Key?"
|
||||
size="sm"
|
||||
@ok="deleteKey"
|
||||
@cancel="doomedItem = null">
|
||||
@cancel="doomedItem = null"
|
||||
>
|
||||
</b-modal>
|
||||
|
||||
</section>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import Vue from "vue";
|
||||
import BootstrapVue from "bootstrap-vue";
|
||||
import CloudAuthItem from "./CloudAuthItem";
|
||||
@@ -113,7 +118,7 @@ export default {
|
||||
loading: false,
|
||||
doomedItem: null,
|
||||
errorMessage: null
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
filteredItems() {
|
||||
@@ -123,10 +128,12 @@ export default {
|
||||
return `${this.filteredItems.length} matches out of ${this.items.length} items`;
|
||||
},
|
||||
deleteButtonVariant() {
|
||||
return this.showDeleted ? "primary": "secondary";
|
||||
return this.showDeleted ? "primary" : "secondary";
|
||||
},
|
||||
hasDoomed: {
|
||||
get() { return this.doomedItem !== null; },
|
||||
get() {
|
||||
return this.doomedItem !== null;
|
||||
},
|
||||
// This setter is here because vue-bootstrap modal
|
||||
// tries to set this property for unfathomable reasons
|
||||
set() {}
|
||||
@@ -141,9 +148,9 @@ export default {
|
||||
loadCredentials(params = {}) {
|
||||
this.loading = true;
|
||||
svc.listCredentials(params)
|
||||
.then(items => this.items = items)
|
||||
.then(items => (this.items = items))
|
||||
.catch(this.setError("Unable to load cloud keys."))
|
||||
.finally(() => this.loading = false);
|
||||
.finally(() => (this.loading = false));
|
||||
},
|
||||
onCreate() {
|
||||
let newItem = Credential.create({ expanded: true });
|
||||
@@ -160,7 +167,7 @@ export default {
|
||||
item.updateState();
|
||||
})
|
||||
.catch(this.setError("Unable to save key."))
|
||||
.finally(() => item.loading = false);
|
||||
.finally(() => (item.loading = false));
|
||||
},
|
||||
onDelete(doomed) {
|
||||
this.doomedItem = doomed;
|
||||
@@ -183,28 +190,26 @@ export default {
|
||||
expand(credential, { expanded }) {
|
||||
credential.expanded = expanded;
|
||||
if (expanded) {
|
||||
this.items.filter(i => i !== credential)
|
||||
.forEach(i => i.expanded = false);
|
||||
this.items.filter(i => i !== credential).forEach(i => (i.expanded = false));
|
||||
}
|
||||
},
|
||||
addItem(item) {
|
||||
this.items = [ ...this.items, item ];
|
||||
this.items = [...this.items, item];
|
||||
},
|
||||
removeItem(item) {
|
||||
this.items = this.items.filter(o => o !== item);
|
||||
},
|
||||
setError(msg) {
|
||||
return (err) => {
|
||||
return err => {
|
||||
this.errorMessage = msg;
|
||||
console.warn(err);
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadCredentials();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@@ -216,7 +221,6 @@ export default {
|
||||
@import "scss/theme/blue.scss";
|
||||
@import "scss/mixins";
|
||||
|
||||
|
||||
// TODO: build reusable icon menu? Maybe make into a component?
|
||||
// The existing one has lots of reusability problems
|
||||
|
||||
@@ -232,11 +236,9 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// General Layout
|
||||
|
||||
.cloud-auth {
|
||||
|
||||
// header sticks, bottom part scrolls
|
||||
@include scrollingListLayout("header", ".scroll-container");
|
||||
|
||||
@@ -246,22 +248,21 @@ export default {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
|
||||
|
||||
// top icon menu
|
||||
.operations {
|
||||
|
||||
li {
|
||||
a, a::before {
|
||||
a,
|
||||
a::before {
|
||||
font-size: 1rem;
|
||||
color: $gray-400;
|
||||
}
|
||||
a.active::before,
|
||||
a.active::before,
|
||||
a:hover::before {
|
||||
color: $brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.cloudKeyHelp a {
|
||||
@include fontawesome($fa-var-info-circle);
|
||||
}
|
||||
@@ -275,11 +276,9 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Single list item
|
||||
|
||||
.cloud-auth-key {
|
||||
|
||||
header hgroup {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -287,7 +286,7 @@ export default {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
|
||||
> * {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@@ -299,13 +298,14 @@ export default {
|
||||
|
||||
// removes wierd double arrows on select
|
||||
.custom-select {
|
||||
background: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
|
||||
// Undo butchering from base.css so side-by-side labels work
|
||||
.form-row {
|
||||
display: flex;
|
||||
input, select {
|
||||
input,
|
||||
select {
|
||||
max-width: none;
|
||||
}
|
||||
label {
|
||||
@@ -326,12 +326,11 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// icon menu
|
||||
|
||||
.operations {
|
||||
.save a {
|
||||
@include fontawesome($fa-var-save)
|
||||
@include fontawesome($fa-var-save);
|
||||
}
|
||||
.delete a {
|
||||
@include fontawesome($fa-var-times);
|
||||
@@ -344,7 +343,6 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// reverse icons when key is expanded
|
||||
|
||||
&.expanded .details a {
|
||||
@@ -353,23 +351,20 @@ export default {
|
||||
@include fontawesome($fa-var-window-minimize);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Transitions
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity .5s;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
// Delete modal
|
||||
|
||||
#deleteCredentialModal {
|
||||
@@ -377,7 +372,7 @@ export default {
|
||||
display: none;
|
||||
}
|
||||
.modal-dialog {
|
||||
max-width: 300px;
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
<template>
|
||||
<b-list-group-item class="cloud-auth-key"
|
||||
:class="statusClasses"
|
||||
:variant="variant"
|
||||
:button="!expanded">
|
||||
|
||||
<b-list-group-item class="cloud-auth-key" :class="statusClasses" :variant="variant" :button="!expanded">
|
||||
<header>
|
||||
<hgroup>
|
||||
<h4 @click.prevent="expand()">{{ credential.title }}</h4>
|
||||
@@ -11,27 +7,33 @@
|
||||
<ul>
|
||||
<transition name="fade">
|
||||
<li class="save" v-if="expanded && credential.dirty">
|
||||
<a @click.prevent="$emit('save', credential)"
|
||||
v-b-tooltip.hover
|
||||
<a
|
||||
@click.prevent="$emit('save', credential)"
|
||||
v-b-tooltip.hover
|
||||
aria-label="Save Key"
|
||||
title="Save Key">
|
||||
title="Save Key"
|
||||
>
|
||||
<span>Save Key</span>
|
||||
</a>
|
||||
</li>
|
||||
</transition>
|
||||
<li class="delete">
|
||||
<a @click.prevent="$emit('delete', credential)"
|
||||
v-b-tooltip.hover
|
||||
aria-label="Delete Key"
|
||||
title="Delete Key">
|
||||
<a
|
||||
@click.prevent="$emit('delete', credential)"
|
||||
v-b-tooltip.hover
|
||||
aria-label="Delete Key"
|
||||
title="Delete Key"
|
||||
>
|
||||
<span>Delete Key</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="details">
|
||||
<a @click.prevent="expand()"
|
||||
v-b-tooltip.hover
|
||||
<a
|
||||
@click.prevent="expand()"
|
||||
v-b-tooltip.hover
|
||||
aria-label="Show Details"
|
||||
title="Show Details">
|
||||
title="Show Details"
|
||||
>
|
||||
<span>Details</span>
|
||||
</a>
|
||||
</li>
|
||||
@@ -40,18 +42,18 @@
|
||||
</hgroup>
|
||||
</header>
|
||||
|
||||
<credential-form v-if="expanded"
|
||||
<credential-form
|
||||
v-if="expanded"
|
||||
class="border-top"
|
||||
v-model="credential"
|
||||
v-model="credential"
|
||||
@click.self="expand()"
|
||||
@save="$emit('save', credential)"
|
||||
@delete="$emit('delete', credential)" />
|
||||
|
||||
@delete="$emit('delete', credential)"
|
||||
/>
|
||||
</b-list-group-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { Credential } from "./model";
|
||||
import CredentialForm from "./CredentialForm";
|
||||
|
||||
@@ -85,10 +87,9 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
expand(forceState) {
|
||||
let expanded = (forceState !== undefined) ? forceState : !this.expanded;
|
||||
let expanded = forceState !== undefined ? forceState : !this.expanded;
|
||||
this.$emit("expand", { expanded });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-form-group v-for="(field, fieldName) in fieldDefs"
|
||||
<b-form-group
|
||||
v-for="(field, fieldName) in fieldDefs"
|
||||
:key="fieldName"
|
||||
:label="field.label || fieldName"
|
||||
:label-for="'config-' + fieldName"
|
||||
label-cols-lg="3"
|
||||
:state="config.fieldValid(fieldName)"
|
||||
:invalid-feedback="config.errorMessage(fieldName)">
|
||||
|
||||
<masked-input :id="'config-' + fieldName"
|
||||
:invalid-feedback="config.errorMessage(fieldName)"
|
||||
>
|
||||
<masked-input
|
||||
:id="'config-' + fieldName"
|
||||
v-model="config[fieldName]"
|
||||
:mask="field.mask"
|
||||
:mask="field.mask"
|
||||
:placeholder="field.placeholder"
|
||||
:maxlength="field.maxlength || Math.Infinity"
|
||||
:state="config.fieldValid(fieldName)"
|
||||
trim />
|
||||
|
||||
trim
|
||||
/>
|
||||
</b-form-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
import MaskedInput from "components/MaskedInput";
|
||||
|
||||
export default {
|
||||
@@ -40,6 +40,5 @@ export default {
|
||||
return this.value.constructor.fields;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,64 +1,60 @@
|
||||
<template>
|
||||
<b-form :validated="credential.isValid">
|
||||
|
||||
<!-- user label -->
|
||||
<b-form-group
|
||||
label="Description"
|
||||
label-for="credentialDescription"
|
||||
label-cols-lg="3">
|
||||
<b-form-input
|
||||
<b-form-group label="Description" label-for="credentialDescription" label-cols-lg="3">
|
||||
<b-form-input
|
||||
id="credentialDescription"
|
||||
v-model="credential.description"
|
||||
:state="credential.fieldValid('description')"
|
||||
placeholder="Description (optional)"
|
||||
trim />
|
||||
trim
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<!-- google-openidconnect -->
|
||||
<b-form-group
|
||||
label="Identity Provider"
|
||||
<b-form-group
|
||||
label="Identity Provider"
|
||||
label-for="identityProvider"
|
||||
label-cols-lg="3"
|
||||
v-if="identityProviders.length > 1">
|
||||
<b-form-select
|
||||
v-if="identityProviders.length > 1"
|
||||
>
|
||||
<b-form-select
|
||||
id="identityProvider"
|
||||
v-model="credential.authn_id"
|
||||
:state="credential.fieldValid('authn_id')"
|
||||
:options="identityProviders" />
|
||||
:options="identityProviders"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
|
||||
<!-- aws/azure, etc -->
|
||||
<b-form-group
|
||||
label="Resource Provider"
|
||||
label-for="resourceProvider"
|
||||
label-cols-lg="3">
|
||||
<b-form-select
|
||||
<b-form-group label="Resource Provider" label-for="resourceProvider" label-cols-lg="3">
|
||||
<b-form-select
|
||||
id="resourceProvider"
|
||||
v-model="credential.resourceProvider"
|
||||
:state="credential.fieldValid('provider')"
|
||||
:options="resourceProviderOptions" />
|
||||
:options="resourceProviderOptions"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
|
||||
<credential-config v-model="credential.config" />
|
||||
|
||||
<footer class="border-top">
|
||||
<b-button variant="secondary"
|
||||
@click.prevent="$emit('delete', credential)">
|
||||
<b-button variant="secondary" @click.prevent="$emit('delete', credential)">
|
||||
Delete Key
|
||||
</b-button>
|
||||
<b-button aria-label="Save Key"
|
||||
<b-button
|
||||
aria-label="Save Key"
|
||||
:variant="saveButtonVariant"
|
||||
:disabled="saveButtonDisabled"
|
||||
@click.prevent="$emit('save', credential)">
|
||||
@click.prevent="$emit('save', credential)"
|
||||
>
|
||||
{{ saveButtonTitle }}
|
||||
</b-button>
|
||||
</footer>
|
||||
|
||||
</b-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { Credential, ResourceProviders } from "./model";
|
||||
import { getIdentityProviders } from "./model/service";
|
||||
import CredentialConfig from "./CredentialConfig";
|
||||
@@ -73,7 +69,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
identityProviders: []
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
credential() {
|
||||
@@ -81,8 +77,7 @@ export default {
|
||||
},
|
||||
// transformed for bootstrap component
|
||||
resourceProviderOptions() {
|
||||
return Array.from(ResourceProviders.entries())
|
||||
.map(([value, o]) => ({ value, text: o.label }));
|
||||
return Array.from(ResourceProviders.entries()).map(([value, o]) => ({ value, text: o.label }));
|
||||
},
|
||||
config() {
|
||||
return this.credential.config;
|
||||
@@ -101,15 +96,13 @@ export default {
|
||||
}
|
||||
},
|
||||
created() {
|
||||
getIdentityProviders()
|
||||
.then(result => {
|
||||
if (!this.credential.authn_id && result.length == 1) {
|
||||
this.credential.authn_id = result[0].authn_id;
|
||||
this.credential.updateState();
|
||||
}
|
||||
this.identityProviders = result;
|
||||
});
|
||||
getIdentityProviders().then(result => {
|
||||
if (!this.credential.authn_id && result.length == 1) {
|
||||
this.credential.authn_id = result[0].authn_id;
|
||||
this.credential.updateState();
|
||||
}
|
||||
this.identityProviders = result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -19,9 +19,9 @@ AwsConfig.setValidator(function(model) {
|
||||
});
|
||||
|
||||
AwsConfig.fields = {
|
||||
"role_arn": {
|
||||
role_arn: {
|
||||
label: "Role ARN",
|
||||
description: "The Amazon resource name (ARN) of the role to be assumed by Galaxy.",
|
||||
placeholder: "arn:aws:iam::XXXXXXXXXXXX:role/XXXXXXXXXXX"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import { AwsConfig } from "./AwsConfig";
|
||||
|
||||
describe("AwsConfig", () => {
|
||||
|
||||
it("should instantiate", () => {
|
||||
let instance = new AwsConfig();
|
||||
assert(instance);
|
||||
assert(!instance.dirty);
|
||||
assert(!instance.valid);
|
||||
})
|
||||
});
|
||||
|
||||
it("should validate role_arn", () => {
|
||||
let instance = new AwsConfig();
|
||||
instance.role_arn = "abc";
|
||||
assert(instance.fieldValid("role_arn"));
|
||||
})
|
||||
});
|
||||
|
||||
it("should invalidate role_arn", () => {
|
||||
let instance = new AwsConfig();
|
||||
instance.role_arn = "";
|
||||
assert(!instance.fieldValid("role_arn"));
|
||||
})
|
||||
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,24 +37,25 @@ AzureConfig.setValidator(function(model) {
|
||||
});
|
||||
|
||||
AzureConfig.fields = {
|
||||
"tenant_id": {
|
||||
tenant_id: {
|
||||
label: "Tenant ID",
|
||||
mask: "********-****-****-****-************",
|
||||
placeholder: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||
maxlength: 36,
|
||||
description: "Your Tenant ID (or Directory ID) on Azure."
|
||||
},
|
||||
"client_id": {
|
||||
client_id: {
|
||||
label: "Client ID",
|
||||
mask: "********-****-****-****-************",
|
||||
placeholder: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||
maxlength: 36,
|
||||
description: "The Client ID (or Application ID) you defined for Galaxy on your Azure directory."
|
||||
},
|
||||
"client_secret": {
|
||||
client_secret: {
|
||||
label: "Client Secret",
|
||||
mask: "",
|
||||
placeholder: "Client Secret",
|
||||
description: "A secret string you obtained from Azure portal that Galaxy can use to prove its identity when requesting tokens to access your resources."
|
||||
description:
|
||||
"A secret string you obtained from Azure portal that Galaxy can use to prove its identity when requesting tokens to access your resources."
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,70 +1,58 @@
|
||||
import { AzureConfig } from "./AzureConfig";
|
||||
|
||||
describe("AzureConfig", () => {
|
||||
|
||||
it("should instantiate", () => {
|
||||
let instance = new AzureConfig();
|
||||
assert(instance);
|
||||
assert(!instance.dirty);
|
||||
assert(!instance.valid);
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe("client_secret", () => {
|
||||
|
||||
it("should validate client_secret", () => {
|
||||
let instance = new AzureConfig();
|
||||
instance.client_secret = "abc";
|
||||
assert(instance.fieldValid("client_secret"));
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
it("should invalidate client_secret", () => {
|
||||
let instance = new AzureConfig();
|
||||
instance.client_secret = "";
|
||||
assert(!instance.fieldValid("client_secret"));
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe("tenant_id", () => {
|
||||
|
||||
it("should validate tenant_id", () => {
|
||||
let validTenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
|
||||
let instance = new AzureConfig();
|
||||
instance.tenant_id = validTenantId;
|
||||
assert(instance.fieldValid("tenant_id"));
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
it("should invalidate tenant_id", () => {
|
||||
let instance = new AzureConfig();
|
||||
instance.tenant_id = "";
|
||||
assert(!instance.fieldValid("tenant_id"));
|
||||
instance.tenant_id = "asdfa";
|
||||
assert(!instance.fieldValid("tenant_id"));
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe("client_id", () => {
|
||||
|
||||
it("should validate client_id", () => {
|
||||
let validClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
|
||||
let instance = new AzureConfig();
|
||||
instance.client_id = validClientId;
|
||||
assert(instance.fieldValid("client_id"));
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
it("should invalidate client_id", () => {
|
||||
let instance = new AzureConfig();
|
||||
instance.client_id = "";
|
||||
assert(!instance.fieldValid("client_id"));
|
||||
instance.client_id = "asdf";
|
||||
assert(!instance.fieldValid("client_id"));
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,19 +7,16 @@ const counter = new WeakMap();
|
||||
let instanceCounter = 0;
|
||||
|
||||
export class BaseModel {
|
||||
|
||||
constructor() {
|
||||
counter.set(this, instanceCounter++);
|
||||
}
|
||||
|
||||
|
||||
/* object ID */
|
||||
|
||||
get counter() {
|
||||
return counter.get(this);
|
||||
}
|
||||
|
||||
|
||||
/* Dirty state tracking */
|
||||
|
||||
get dirty() {
|
||||
@@ -52,7 +49,7 @@ export class BaseModel {
|
||||
}
|
||||
return transients.get(key);
|
||||
}
|
||||
|
||||
|
||||
updateState() {
|
||||
lastState.set(this, this.state);
|
||||
}
|
||||
@@ -60,19 +57,16 @@ export class BaseModel {
|
||||
// Setting a property name as transient for a class means its
|
||||
// state will be caclulated without regard to that propety
|
||||
static setTransient(...fieldNames) {
|
||||
|
||||
let klass = this; // this will be a class
|
||||
if (!transients.has(klass)) {
|
||||
transients.set(klass, new Set());
|
||||
}
|
||||
|
||||
let fields = transients.get(klass);
|
||||
fieldNames.forEach(fieldName => fields.add(fieldName))
|
||||
fieldNames.forEach(fieldName => fields.add(fieldName));
|
||||
transients.set(klass, fields);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Validation */
|
||||
|
||||
get valid() {
|
||||
@@ -105,5 +99,4 @@ export class BaseModel {
|
||||
static setValidator(validationFunction) {
|
||||
validators.set(this, validationFunction);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { ResourceProviders } from "./ResourceProviders";
|
||||
import { BaseModel } from "./BaseModel";
|
||||
|
||||
export class Credential extends BaseModel {
|
||||
|
||||
constructor(props = {}) {
|
||||
super();
|
||||
|
||||
@@ -11,7 +10,7 @@ export class Credential extends BaseModel {
|
||||
this.description = "";
|
||||
this.authn_id = null; // identity provider
|
||||
this.provider = null; // resource provider
|
||||
|
||||
|
||||
// transient props, exclude from state
|
||||
this.expanded = false;
|
||||
this.loading = false;
|
||||
@@ -21,7 +20,7 @@ export class Credential extends BaseModel {
|
||||
safeAssign(this, options);
|
||||
|
||||
// init nested config
|
||||
this.config = new this.configClass(options.config);
|
||||
this.config = new this.configClass(options.config);
|
||||
|
||||
// initialize state
|
||||
this.updateState();
|
||||
@@ -35,7 +34,6 @@ export class Credential extends BaseModel {
|
||||
return super.valid && this.config.valid;
|
||||
}
|
||||
|
||||
|
||||
// Alias for provider also changes config object when updated
|
||||
// Set this when updating in the UI
|
||||
|
||||
@@ -48,28 +46,25 @@ export class Credential extends BaseModel {
|
||||
this.config = new this.configClass({});
|
||||
}
|
||||
|
||||
|
||||
// Polymorphic config class
|
||||
|
||||
get configClass() {
|
||||
return ResourceProviders.get(this.provider).klass;
|
||||
}
|
||||
|
||||
|
||||
// Methods
|
||||
|
||||
|
||||
match(searchText = "") {
|
||||
// TODO: more robust object matching?
|
||||
return searchText.length ? this.title.includes(searchText) : true;
|
||||
}
|
||||
|
||||
|
||||
// Statics
|
||||
|
||||
static get defaults() {
|
||||
return {
|
||||
authn_id: null,
|
||||
provider: 'aws',
|
||||
provider: "aws",
|
||||
expanded: false
|
||||
};
|
||||
}
|
||||
@@ -91,7 +86,6 @@ Credential.setTransient("expanded", "loading");
|
||||
* error messages.
|
||||
*/
|
||||
Credential.setValidator(function(model) {
|
||||
|
||||
let errors = {};
|
||||
|
||||
if (!model.provider) {
|
||||
@@ -107,4 +101,4 @@ Credential.setValidator(function(model) {
|
||||
}
|
||||
|
||||
return errors;
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,113 +1,106 @@
|
||||
import { Credential } from "./Credential";
|
||||
|
||||
describe("Credential model", () => {
|
||||
|
||||
describe("basic model props", () => {
|
||||
|
||||
it("should exist", () => {
|
||||
assert(Credential);
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
it("should build with defaults", () => {
|
||||
let instance = new Credential();
|
||||
assert(instance);
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
it("should build with props", () => {
|
||||
let description = "i am the test description";
|
||||
let props = { description };
|
||||
let instance = new Credential(props);
|
||||
assert(instance);
|
||||
assert(instance.description == description);
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
it("default config object should be AWS", () => {
|
||||
let instance = new Credential();
|
||||
assert('role_arn' in instance.config, "Missing role_arn prop");
|
||||
assert("role_arn" in instance.config, "Missing role_arn prop");
|
||||
assert(instance.config.constructor.name == "AwsConfig");
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
it("should load a different config object given the right provider", () => {
|
||||
let provider = 'azure';
|
||||
let provider = "azure";
|
||||
let props = { provider };
|
||||
let instance = new Credential(props);
|
||||
assert(instance.provider == provider);
|
||||
assert(instance.config.constructor.name == "AzureConfig");
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
it("should dynamically switch config objects as resourceProvider is changed", () => {
|
||||
let instance = new Credential();
|
||||
assert(instance.provider == 'aws', "Should default to AWS config' ");
|
||||
instance.resourceProvider = 'azure';
|
||||
assert(instance.provider == "aws", "Should default to AWS config' ");
|
||||
instance.resourceProvider = "azure";
|
||||
assert(instance.config.constructor.name == "AzureConfig", "Should have been AzureConfig");
|
||||
instance.resourceProvider = 'aws';
|
||||
instance.resourceProvider = "aws";
|
||||
assert(instance.config.constructor.name == "AwsConfig", "Should be AWS Config again");
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe("dirty state", () => {
|
||||
|
||||
it("should flag as dirty when a prop is changed", () => {
|
||||
let instance = new Credential();
|
||||
instance.description = "foo";
|
||||
assert(instance.dirty);
|
||||
})
|
||||
});
|
||||
|
||||
it("should flag as clean when a prop is restored", () => {
|
||||
let instance = new Credential();
|
||||
instance.description = 'foo';
|
||||
instance.description = "foo";
|
||||
assert(instance.dirty);
|
||||
instance.description = "";
|
||||
assert(!instance.dirty);
|
||||
})
|
||||
});
|
||||
|
||||
it("should not flag as dirty when a transient prop is changed", () => {
|
||||
let instance = new Credential();
|
||||
instance.loading = true;
|
||||
assert(!instance.dirty);
|
||||
})
|
||||
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe("validation", () => {
|
||||
|
||||
it("a new object should be invalid", () => {
|
||||
let instance = new Credential();
|
||||
assert(!instance.valid);
|
||||
})
|
||||
});
|
||||
|
||||
it("it should become valid when props are assigned", () => {
|
||||
let instance = new Credential();
|
||||
instance.authn_id = "floob";
|
||||
instance.resourceProvider = 'aws';
|
||||
instance.resourceProvider = "aws";
|
||||
instance.config.role_arn = "abc";
|
||||
assert(instance.valid);
|
||||
})
|
||||
});
|
||||
|
||||
it("it should be valid when initialized with correct props", () => {
|
||||
let props = {
|
||||
authn_id: "asdfasdf",
|
||||
provider: 'aws',
|
||||
provider: "aws",
|
||||
config: {
|
||||
role_arn: "floobar"
|
||||
}
|
||||
}
|
||||
};
|
||||
let instance = new Credential(props);
|
||||
assert(instance.valid);
|
||||
})
|
||||
});
|
||||
|
||||
it("should be invalid when bad props assigned", () => {
|
||||
let props = {
|
||||
authn_id: "asdfasdf",
|
||||
provider: 'aws',
|
||||
provider: "aws",
|
||||
config: {
|
||||
role_arn: ""
|
||||
}
|
||||
}
|
||||
};
|
||||
let instance = new Credential(props);
|
||||
assert(!instance.valid);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { safeAssign } from "utils/safeAssign";
|
||||
|
||||
export class IdentityProvider {
|
||||
|
||||
constructor(props = {}) {
|
||||
this.id = "";
|
||||
this.provider = "";
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { Credential } from "./Credential";
|
||||
export { IdentityProvider } from "./IdentityProvider";
|
||||
export { ResourceProviders } from "./ResourceProviders";
|
||||
export { ResourceProviders } from "./ResourceProviders";
|
||||
|
||||
@@ -37,7 +37,7 @@ export async function saveCredential(newItem) {
|
||||
|
||||
async function saveOrUpdate(model) {
|
||||
return model.id
|
||||
? axios.put(getUrl(`api/cloud/authz/${model.id}`), model)
|
||||
? axios.put(getUrl(`api/cloud/authz/${model.id}`), model)
|
||||
: axios.post(getUrl("api/cloud/authz"), model);
|
||||
}
|
||||
|
||||
@@ -69,10 +69,10 @@ export async function getIdentityProviders() {
|
||||
return identityProviders;
|
||||
}
|
||||
|
||||
export default {
|
||||
export default {
|
||||
listCredentials,
|
||||
getCredential,
|
||||
saveCredential,
|
||||
deleteCredential,
|
||||
getIdentityProviders
|
||||
}
|
||||
};
|
||||
|
||||
@@ -46,7 +46,7 @@ var ToolPanel = Backbone.View.extend({
|
||||
const panel_buttons = [this.upload_button];
|
||||
|
||||
// add favorite filter button
|
||||
if (Galaxy.user && Galaxy.user.id){
|
||||
if (Galaxy.user && Galaxy.user.id) {
|
||||
this.favorite_button = new Buttons.ButtonLink({
|
||||
cls: "panel-header-button",
|
||||
title: _l("Show favorites"),
|
||||
|
||||
@@ -29,7 +29,7 @@ export default Backbone.View.extend({
|
||||
onclick: function() {
|
||||
self._eventReset();
|
||||
}
|
||||
})
|
||||
});
|
||||
this.btnStart = new Ui.Button({
|
||||
title: _l("Start"),
|
||||
onclick: function() {
|
||||
@@ -63,7 +63,7 @@ export default Backbone.View.extend({
|
||||
self.collection.add({
|
||||
id: self.collection.size(),
|
||||
file_desc: item.description || item.name,
|
||||
optional: item.optional,
|
||||
optional: item.optional
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -113,7 +113,11 @@ export default Backbone.View.extend({
|
||||
this.select_genome.enable();
|
||||
this.select_extension.enable();
|
||||
}
|
||||
if (this.collection.where({ status: "ready" }).length + this.collection.where({ optional: true }).length == this.collection.length && this.collection.length > 0) {
|
||||
if (
|
||||
this.collection.where({ status: "ready" }).length + this.collection.where({ optional: true }).length ==
|
||||
this.collection.length &&
|
||||
this.collection.length > 0
|
||||
) {
|
||||
this.btnStart.enable();
|
||||
this.btnStart.$el.addClass("btn-primary");
|
||||
} else {
|
||||
|
||||
@@ -92,7 +92,9 @@ var Model = Backbone.Model.extend({
|
||||
},
|
||||
cloud_auth: {
|
||||
title: _l("Manage Cloud Authorization"),
|
||||
description: _l("Add or modify the configuration that grants Galaxy to access your cloud-based resources."),
|
||||
description: _l(
|
||||
"Add or modify the configuration that grants Galaxy to access your cloud-based resources."
|
||||
),
|
||||
icon: "fa-cloud",
|
||||
submit_title: "Create a new key",
|
||||
submit_icon: "fa-check"
|
||||
|
||||
@@ -7,5 +7,5 @@ export function safeAssign(target, source = {}) {
|
||||
}
|
||||
Object.keys(source)
|
||||
.filter(prop => target.hasOwnProperty(prop))
|
||||
.forEach(prop => target[prop] = source[prop]);
|
||||
.forEach(prop => (target[prop] = source[prop]));
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ $fa-font-path: "~font-awesome/fonts/";
|
||||
@import "~font-awesome/scss/font-awesome.scss";
|
||||
@import "~font-awesome/scss/_mixins";
|
||||
|
||||
|
||||
// Utility mixin expands to container edges
|
||||
@mixin fill() {
|
||||
position: relative;
|
||||
@@ -38,8 +37,8 @@ $fa-font-path: "~font-awesome/fonts/";
|
||||
@mixin scrollMe() {
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,14 +52,13 @@ $fa-font-path: "~font-awesome/fonts/";
|
||||
#{$fixedTop} {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
|
||||
#{$listRegion} {
|
||||
flex-grow: 1;
|
||||
@include scrollMe();
|
||||
@include scrollMe();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Removes browser formatting of lists
|
||||
|
||||
@mixin list_reset() {
|
||||
@@ -69,13 +67,12 @@ $fa-font-path: "~font-awesome/fonts/";
|
||||
margin: 0 0 0 0;
|
||||
}
|
||||
|
||||
|
||||
// font awesome as a mixin
|
||||
// icon variables come from ~font-awesome/scss/_variables.scss
|
||||
|
||||
@mixin fontawesome($icon) {
|
||||
@include fa-icon();
|
||||
&:before {
|
||||
&:before {
|
||||
content: $icon;
|
||||
}
|
||||
span {
|
||||
|
||||
Reference in New Issue
Block a user