Client format.

This commit is contained in:
Dannon Baker
2019-05-14 16:20:04 -04:00
parent 7ca27f1436
commit 837b40aac1
21 changed files with 234 additions and 281 deletions
@@ -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"
+1 -1
View File
@@ -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]));
}
+5 -8
View File
@@ -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 {