mirror of
https://github.com/ansible/awx.git
synced 2026-05-14 04:47:44 -02:30
add input source creation ui
This commit is contained in:
@@ -11,10 +11,13 @@ function AddCredentialsController (
|
|||||||
Wait,
|
Wait,
|
||||||
$filter,
|
$filter,
|
||||||
CredentialType,
|
CredentialType,
|
||||||
|
GetBasePath,
|
||||||
|
Rest,
|
||||||
) {
|
) {
|
||||||
const vm = this || {};
|
const vm = this || {};
|
||||||
|
|
||||||
const { me, credential, credentialType, organization } = models;
|
const { me, credential, credentialType, organization } = models;
|
||||||
|
const isExternal = credentialType.get('kind') === 'external';
|
||||||
|
|
||||||
vm.mode = 'add';
|
vm.mode = 'add';
|
||||||
vm.strings = strings;
|
vm.strings = strings;
|
||||||
@@ -44,44 +47,6 @@ function AddCredentialsController (
|
|||||||
vm.form.credential_type._placeholder = strings.get('inputs.CREDENTIAL_TYPE_PLACEHOLDER');
|
vm.form.credential_type._placeholder = strings.get('inputs.CREDENTIAL_TYPE_PLACEHOLDER');
|
||||||
vm.isTestable = credentialType.get('kind') === 'external';
|
vm.isTestable = credentialType.get('kind') === 'external';
|
||||||
|
|
||||||
vm.inputSources = {
|
|
||||||
field: null,
|
|
||||||
credentialId: null,
|
|
||||||
credentialTypeId: null,
|
|
||||||
credentialTypeName: null,
|
|
||||||
tabs: {
|
|
||||||
credential: {
|
|
||||||
_active: true,
|
|
||||||
_disabled: false,
|
|
||||||
},
|
|
||||||
metadata: {
|
|
||||||
_active: false,
|
|
||||||
_disabled: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
metadata: {},
|
|
||||||
form: {
|
|
||||||
inputs: {
|
|
||||||
_get: () => vm.inputSources.metadata,
|
|
||||||
_reference: 'vm.form.inputs',
|
|
||||||
_key: 'inputs',
|
|
||||||
_source: { _value: {} },
|
|
||||||
}
|
|
||||||
},
|
|
||||||
items: [],
|
|
||||||
};
|
|
||||||
vm.externalTest = {
|
|
||||||
metadata: null,
|
|
||||||
form: {
|
|
||||||
inputs: {
|
|
||||||
_get: () => vm.externalTest.metadata,
|
|
||||||
_reference: 'vm.form.inputs',
|
|
||||||
_key: 'inputs',
|
|
||||||
_source: { _value: {} },
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const gceFileInputSchema = {
|
const gceFileInputSchema = {
|
||||||
id: 'gce_service_account_key',
|
id: 'gce_service_account_key',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
@@ -106,12 +71,20 @@ function AddCredentialsController (
|
|||||||
become._isDynamic = true;
|
become._isDynamic = true;
|
||||||
become._choices = Array.from(apiConfig.become_methods, method => method[0]);
|
become._choices = Array.from(apiConfig.become_methods, method => method[0]);
|
||||||
}
|
}
|
||||||
vm.isTestable = credentialType.get('kind') === 'external';
|
vm.isTestable = (credentialType.get('kind') === 'external');
|
||||||
vm.getSubmitData = getSubmitData;
|
vm.getSubmitData = getSubmitData;
|
||||||
|
|
||||||
|
vm.inputSources.items = [];
|
||||||
|
const linkedFieldNames = vm.inputSources.items
|
||||||
|
.map(({ input_field_name }) => input_field_name);
|
||||||
|
|
||||||
fields = fields.map((field) => {
|
fields = fields.map((field) => {
|
||||||
if (credentialType.get('kind') !== 'external') {
|
field.tagMode = credentialType.get('kind') !== 'external';
|
||||||
field.tagMode = true;
|
if (linkedFieldNames.includes(field.id)) {
|
||||||
|
field.asTag = true;
|
||||||
|
const { summary_fields } = vm.inputSources.items
|
||||||
|
.find(({ input_field_name }) => input_field_name === field.id);
|
||||||
|
field._value = summary_fields.source_credential.name;
|
||||||
}
|
}
|
||||||
return field;
|
return field;
|
||||||
});
|
});
|
||||||
@@ -129,120 +102,184 @@ function AddCredentialsController (
|
|||||||
_key: 'inputs'
|
_key: 'inputs'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vm.externalTest = {
|
||||||
|
form: {
|
||||||
|
inputs: {
|
||||||
|
_get: () => vm.externalTest.metadataInputs,
|
||||||
|
_reference: 'vm.form.inputs',
|
||||||
|
_key: 'inputs',
|
||||||
|
_source: { _value: {} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
metadataInputs: null,
|
||||||
|
};
|
||||||
|
vm.inputSources = {
|
||||||
|
tabs: {
|
||||||
|
credential: {
|
||||||
|
_active: true,
|
||||||
|
_disabled: false,
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
_active: false,
|
||||||
|
_disabled: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
inputs: {
|
||||||
|
_get: () => vm.inputSources.metadataInputs,
|
||||||
|
_reference: 'vm.form.inputs',
|
||||||
|
_key: 'inputs',
|
||||||
|
_source: { _value: {} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
field: null,
|
||||||
|
credentialTypeId: null,
|
||||||
|
credentialTypeName: null,
|
||||||
|
credentialId: null,
|
||||||
|
credentialName: null,
|
||||||
|
metadataInputs: null,
|
||||||
|
initialItems: credential.get('related.input_sources.results'),
|
||||||
|
items: credential.get('related.input_sources.results'),
|
||||||
|
};
|
||||||
|
|
||||||
vm.onInputSourceClear = (field) => {
|
vm.onInputSourceClear = (field) => {
|
||||||
vm.form[field].tagMode = true;
|
vm.form[field].tagMode = true;
|
||||||
vm.form[field].asTag = false;
|
vm.form[field].asTag = false;
|
||||||
|
vm.form[field]._value = '';
|
||||||
|
vm.inputSources.items = vm.inputSources.items
|
||||||
|
.filter(({ input_field_name }) => input_field_name !== field);
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.setTab = (name) => {
|
function setInputSourceTab (name) {
|
||||||
const metaIsActive = name === 'metadata';
|
const metaIsActive = name === 'metadata';
|
||||||
vm.inputSources.tabs.credential._active = !metaIsActive;
|
vm.inputSources.tabs.credential._active = !metaIsActive;
|
||||||
vm.inputSources.tabs.credential._disabled = false;
|
vm.inputSources.tabs.credential._disabled = false;
|
||||||
vm.inputSources.tabs.metadata._active = metaIsActive;
|
vm.inputSources.tabs.metadata._active = metaIsActive;
|
||||||
vm.inputSources.tabs.metadata._disabled = false;
|
vm.inputSources.tabs.metadata._disabled = false;
|
||||||
};
|
}
|
||||||
|
|
||||||
vm.unsetTabs = () => {
|
function unsetInputSourceTabs () {
|
||||||
vm.inputSources.tabs.credential._active = false;
|
vm.inputSources.tabs.credential._active = false;
|
||||||
vm.inputSources.tabs.credential._disabled = false;
|
vm.inputSources.tabs.credential._disabled = false;
|
||||||
vm.inputSources.tabs.metadata._active = false;
|
vm.inputSources.tabs.metadata._active = false;
|
||||||
vm.inputSources.tabs.metadata._disabled = false;
|
vm.inputSources.tabs.metadata._disabled = false;
|
||||||
};
|
}
|
||||||
|
|
||||||
vm.onInputSourceOpen = (field) => {
|
vm.onInputSourceOpen = (field) => {
|
||||||
vm.inputSources.field = field;
|
|
||||||
vm.setTab('credential');
|
|
||||||
const sourceItem = vm.inputSources.items
|
const sourceItem = vm.inputSources.items
|
||||||
.find(({ input_field_name }) => input_field_name === field);
|
.find(({ input_field_name }) => input_field_name === field);
|
||||||
if (sourceItem) {
|
if (sourceItem) {
|
||||||
const { source_credential, summary_fields } = sourceItem;
|
const { source_credential, summary_fields } = sourceItem;
|
||||||
const { source_credential: { credential_type_id } } = summary_fields;
|
const { source_credential: { credential_type_id, name } } = summary_fields;
|
||||||
vm.inputSources.credentialId = source_credential;
|
vm.inputSources.credentialId = source_credential;
|
||||||
|
vm.inputSources.credentialName = name;
|
||||||
vm.inputSources.credentialTypeId = credential_type_id;
|
vm.inputSources.credentialTypeId = credential_type_id;
|
||||||
vm.inputSources._value = credential_type_id;
|
vm.inputSources._value = credential_type_id;
|
||||||
}
|
}
|
||||||
|
setInputSourceTab('credential');
|
||||||
|
vm.inputSources.field = field;
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.onInputSourceClose = () => {
|
vm.onInputSourceClose = () => {
|
||||||
vm.inputSources.field = null;
|
vm.inputSources.field = null;
|
||||||
vm.inputSources.metadata = null;
|
vm.inputSources.credentialId = null;
|
||||||
vm.unsetTabs();
|
vm.inputSources.credentialName = null;
|
||||||
|
vm.inputSources.metadataInputs = null;
|
||||||
|
unsetInputSourceTabs();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the current set of input values from the metadata form and reshape them to a
|
||||||
|
* metadata object that can be sent to the api later or reloaded when re-opening the form.
|
||||||
|
*/
|
||||||
|
function getMetadataFormSubmitData ({ inputs }) {
|
||||||
|
const metadata = Object.assign({}, ...inputs._group
|
||||||
|
.filter(({ _value }) => _value !== undefined)
|
||||||
|
.map(({ id, _value }) => ({ [id]: _value })));
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
vm.onInputSourceNext = () => {
|
vm.onInputSourceNext = () => {
|
||||||
const { field, credentialId, credentialTypeId } = vm.inputSources;
|
const { field, credentialId, credentialTypeId } = vm.inputSources;
|
||||||
Wait('start');
|
Wait('start');
|
||||||
new CredentialType('get', credentialTypeId)
|
new CredentialType('get', credentialTypeId)
|
||||||
.then(model => {
|
.then(model => {
|
||||||
model.mergeInputProperties('metadata');
|
model.mergeInputProperties('metadata');
|
||||||
vm.inputSources.metadata = model.get('inputs.metadata');
|
vm.inputSources.metadataInputs = model.get('inputs.metadata');
|
||||||
vm.inputSources.credentialTypeName = model.get('name');
|
vm.inputSources.credentialTypeName = model.get('name');
|
||||||
const [metavals] = vm.inputSources.items
|
const [metavals] = vm.inputSources.items
|
||||||
.filter(({ input_field_name }) => input_field_name === field)
|
.filter(({ input_field_name }) => input_field_name === field)
|
||||||
.filter(({ source_credential }) => source_credential === credentialId)
|
.filter(({ source_credential }) => source_credential === credentialId)
|
||||||
.map(({ metadata }) => metadata);
|
.map(({ metadata }) => metadata);
|
||||||
Object.keys(metavals || {}).forEach(key => {
|
Object.keys(metavals || {}).forEach(key => {
|
||||||
const obj = vm.inputSources.metadata.find(o => o.id === key);
|
const obj = vm.inputSources.metadataInputs.find(o => o.id === key);
|
||||||
if (obj) obj._value = metavals[key];
|
if (obj) obj._value = metavals[key];
|
||||||
});
|
});
|
||||||
vm.setTab('metadata');
|
setInputSourceTab('metadata');
|
||||||
})
|
})
|
||||||
.finally(() => Wait('stop'));
|
.finally(() => Wait('stop'));
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.onInputSourceSelect = () => {
|
vm.onInputSourceSelect = () => {
|
||||||
const { field, credentialId } = vm.inputSources;
|
const { field, credentialId, credentialName, credentialTypeId } = vm.inputSources;
|
||||||
|
const metadata = getMetadataFormSubmitData(vm.inputSources.form);
|
||||||
vm.inputSources.items = vm.inputSources.items
|
vm.inputSources.items = vm.inputSources.items
|
||||||
.filter(({ input_field_name }) => input_field_name !== field)
|
.filter(({ input_field_name }) => input_field_name !== field)
|
||||||
.concat([{
|
.concat([{
|
||||||
|
metadata,
|
||||||
input_field_name: field,
|
input_field_name: field,
|
||||||
source_credential: credentialId,
|
source_credential: credentialId,
|
||||||
target_credential: credential.get('id'),
|
target_credential: credential.get('id'),
|
||||||
|
summary_fields: {
|
||||||
|
source_credential: {
|
||||||
|
name: credentialName,
|
||||||
|
credential_type_id: credentialTypeId
|
||||||
|
}
|
||||||
|
},
|
||||||
}]);
|
}]);
|
||||||
vm.inputSources.field = null;
|
vm.inputSources.field = null;
|
||||||
vm.inputSources.metadata = null;
|
vm.inputSources.metadataInputs = null;
|
||||||
vm.unsetTabs();
|
unsetInputSourceTabs();
|
||||||
|
vm.form[field]._value = credentialName;
|
||||||
|
vm.form[field].asTag = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.onInputSourceTabSelect = (name) => {
|
vm.onInputSourceTabSelect = (name) => {
|
||||||
if (name === 'metadata') {
|
if (name === 'metadata') {
|
||||||
vm.onInputSourceNext();
|
vm.onInputSourceNext();
|
||||||
} else {
|
} else {
|
||||||
vm.setTab('credential');
|
setInputSourceTab('credential');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.onInputSourceRowClick = ({ id, credential_type }) => {
|
vm.onInputSourceRowClick = ({ id, credential_type, name }) => {
|
||||||
vm.inputSources.credentialId = id;
|
vm.inputSources.credentialId = id;
|
||||||
|
vm.inputSources.credentialName = name;
|
||||||
vm.inputSources.credentialTypeId = credential_type;
|
vm.inputSources.credentialTypeId = credential_type;
|
||||||
vm.inputSources._value = credential_type;
|
vm.inputSources._value = credential_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.onInputSourceTest = () => {
|
vm.onInputSourceTest = () => {
|
||||||
const metadata = Object.assign({}, ...vm.inputSources.form.inputs._group
|
const metadata = getMetadataFormSubmitData(vm.inputSources.form);
|
||||||
.filter(({ _value }) => _value !== undefined)
|
|
||||||
.map(({ id, _value }) => ({ [id]: _value })));
|
|
||||||
const name = $filter('sanitize')(vm.inputSources.credentialTypeName);
|
const name = $filter('sanitize')(vm.inputSources.credentialTypeName);
|
||||||
const endpoint = `${vm.inputSources.credentialId}/test/`;
|
const endpoint = `${vm.inputSources.credentialId}/test/`;
|
||||||
|
return runTest({ name, model: credential, endpoint, data: { metadata } });
|
||||||
return vm.runTest({ name, model: credential, endpoint, data: { metadata } });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.onExternalTestClick = () => {
|
function onExternalTestOpen () {
|
||||||
credentialType.mergeInputProperties('metadata');
|
credentialType.mergeInputProperties('metadata');
|
||||||
vm.externalTest.metadata = credentialType.get('inputs.metadata');
|
vm.externalTest.metadataInputs = credentialType.get('inputs.metadata');
|
||||||
};
|
}
|
||||||
|
vm.form.secondary = onExternalTestOpen;
|
||||||
|
|
||||||
vm.onExternalTestClose = () => {
|
vm.onExternalTestClose = () => {
|
||||||
vm.externalTest.metadata = null;
|
vm.externalTest.metadataInputs = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.onExternalTest = () => {
|
vm.onExternalTest = () => {
|
||||||
const name = $filter('sanitize')(credentialType.get('name'));
|
const name = $filter('sanitize')(credentialType.get('name'));
|
||||||
const { inputs } = vm.getSubmitData();
|
const { inputs } = vm.getSubmitData();
|
||||||
const metadata = Object.assign({}, ...vm.externalTest.form.inputs._group
|
const metadata = getMetadataFormSubmitData(vm.externalTest.form);
|
||||||
.filter(({ _value }) => _value !== undefined)
|
|
||||||
.map(({ id, _value }) => ({ [id]: _value })));
|
|
||||||
|
|
||||||
let model;
|
let model;
|
||||||
if (credential.get('credential_type') !== credentialType.get('id')) {
|
if (credential.get('credential_type') !== credentialType.get('id')) {
|
||||||
@@ -252,49 +289,57 @@ function AddCredentialsController (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const endpoint = `${model.get('id')}/test/`;
|
const endpoint = `${model.get('id')}/test/`;
|
||||||
return vm.runTest({ name, model, endpoint, data: { inputs, metadata } });
|
return runTest({ name, model, endpoint, data: { inputs, metadata } });
|
||||||
};
|
};
|
||||||
vm.form.secondary = vm.onExternalTestClick;
|
|
||||||
|
|
||||||
vm.runTest = ({ name, model, endpoint, data: { inputs, metadata } }) => {
|
vm.filterInputSourceCredentialResults = (data) => {
|
||||||
|
if (isExternal) {
|
||||||
|
data.results = data.results.filter(({ id }) => id !== credential.get('id'));
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
function runTest ({ name, model, endpoint, data: { inputs, metadata } }) {
|
||||||
return model.http.post({ url: endpoint, data: { inputs, metadata }, replace: false })
|
return model.http.post({ url: endpoint, data: { inputs, metadata }, replace: false })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
const icon = 'fa-check-circle';
|
||||||
|
const msg = strings.get('edit.TEST_PASSED');
|
||||||
|
const content = buildTestNotificationContent({ name, icon, msg });
|
||||||
ngToast.success({
|
ngToast.success({
|
||||||
content: vm.buildTestNotificationContent({
|
content,
|
||||||
name,
|
|
||||||
icon: 'fa-check-circle',
|
|
||||||
msg: strings.get('edit.TEST_PASSED'),
|
|
||||||
}),
|
|
||||||
dismissButton: false,
|
dismissButton: false,
|
||||||
dismissOnTimeout: true
|
dismissOnTimeout: true
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(({ data }) => {
|
.catch(({ data }) => {
|
||||||
const msg = data.inputs
|
const icon = 'fa-exclamation-triangle';
|
||||||
? `${$filter('sanitize')(data.inputs)}`
|
const msg = data.inputs || strings.get('edit.TEST_FAILED');
|
||||||
: strings.get('edit.TEST_FAILED');
|
const content = buildTestNotificationContent({ name, icon, msg });
|
||||||
ngToast.danger({
|
ngToast.danger({
|
||||||
content: vm.buildTestNotificationContent({
|
content,
|
||||||
name,
|
|
||||||
msg,
|
|
||||||
icon: 'fa-exclamation-triangle'
|
|
||||||
}),
|
|
||||||
dismissButton: false,
|
dismissButton: false,
|
||||||
dismissOnTimeout: true
|
dismissOnTimeout: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
vm.buildTestNotificationContent = ({ name, msg, icon }) => (
|
function buildTestNotificationContent ({ name, msg, icon }) {
|
||||||
`<div class="Toast-wrapper">
|
const sanitize = $filter('sanitize');
|
||||||
|
const content = `<div class="Toast-wrapper">
|
||||||
<div class="Toast-icon">
|
<div class="Toast-icon">
|
||||||
<i class="fa ${icon} Toast-successIcon"></i>
|
<i class="fa ${icon} Toast-successIcon"></i>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<b>${name}:</b> ${msg}
|
<b>${sanitize(name)}:</b> ${sanitize(msg)}
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`;
|
||||||
);
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createInputSource (data) {
|
||||||
|
Rest.setUrl(GetBasePath('credential_input_sources'));
|
||||||
|
return Rest.post(data);
|
||||||
|
}
|
||||||
|
|
||||||
vm.form.save = data => {
|
vm.form.save = data => {
|
||||||
data.user = me.get('id');
|
data.user = me.get('id');
|
||||||
@@ -303,14 +348,25 @@ function AddCredentialsController (
|
|||||||
delete data.inputs[gceFileInputSchema.id];
|
delete data.inputs[gceFileInputSchema.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredInputs = _.omit(data.inputs, (value) => value === '');
|
const updatedLinkedFieldNames = vm.inputSources.items
|
||||||
|
.map(({ input_field_name }) => input_field_name);
|
||||||
|
const sourcesToAssociate = [...vm.inputSources.items];
|
||||||
|
|
||||||
|
// remove inputs with empty string values
|
||||||
|
let filteredInputs = _.omit(data.inputs, (value) => value === '');
|
||||||
|
// remove inputs that are to be linked to an external credential
|
||||||
|
filteredInputs = _.omit(filteredInputs, updatedLinkedFieldNames);
|
||||||
data.inputs = filteredInputs;
|
data.inputs = filteredInputs;
|
||||||
|
|
||||||
return credential.request('post', { data });
|
return credential.request('post', { data })
|
||||||
|
.then(() => {
|
||||||
|
sourcesToAssociate.forEach(obj => { obj.target_credential = credential.get('id'); });
|
||||||
|
return Promise.all(sourcesToAssociate.map(createInputSource));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.form.onSaveSuccess = res => {
|
vm.form.onSaveSuccess = () => {
|
||||||
$state.go('credentials.edit', { credential_id: res.data.id }, { reload: true });
|
$state.go('credentials.edit', { credential_id: credential.get('id') }, { reload: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.gceOnFileInputChanged = (value, oldValue) => {
|
vm.gceOnFileInputChanged = (value, oldValue) => {
|
||||||
@@ -381,6 +437,8 @@ AddCredentialsController.$inject = [
|
|||||||
'Wait',
|
'Wait',
|
||||||
'$filter',
|
'$filter',
|
||||||
'CredentialTypeModel',
|
'CredentialTypeModel',
|
||||||
|
'GetBasePath',
|
||||||
|
'Rest',
|
||||||
];
|
];
|
||||||
|
|
||||||
export default AddCredentialsController;
|
export default AddCredentialsController;
|
||||||
|
|||||||
@@ -54,9 +54,10 @@
|
|||||||
on-tab-select="vm.onInputSourceTabSelect"
|
on-tab-select="vm.onInputSourceTabSelect"
|
||||||
on-row-click="vm.onInputSourceRowClick"
|
on-row-click="vm.onInputSourceRowClick"
|
||||||
on-test="vm.onInputSourceTest"
|
on-test="vm.onInputSourceTest"
|
||||||
|
results-filter="vm.filterInputSourceCredentialResults"
|
||||||
/>
|
/>
|
||||||
<at-external-credential-test
|
<at-external-credential-test
|
||||||
ng-if="vm.externalTest.metadata"
|
ng-if="vm.externalTest.metadataInputs"
|
||||||
on-close="vm.onExternalTestClose"
|
on-close="vm.onExternalTestClose"
|
||||||
on-submit="vm.onExternalTest"
|
on-submit="vm.onExternalTest"
|
||||||
form="vm.externalTest.form"
|
form="vm.externalTest.form"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ function CredentialsStrings (BaseString) {
|
|||||||
|
|
||||||
ns.tab = {
|
ns.tab = {
|
||||||
DETAILS: t.s('Details'),
|
DETAILS: t.s('Details'),
|
||||||
PERMISSIONS: t.s('Permissions')
|
PERMISSIONS: t.s('Permissions'),
|
||||||
};
|
};
|
||||||
|
|
||||||
ns.inputs = {
|
ns.inputs = {
|
||||||
@@ -22,6 +22,18 @@ function CredentialsStrings (BaseString) {
|
|||||||
GCE_FILE_INPUT_HELP_TEXT: t.s('Provide account information using Google Compute Engine JSON credentials file.')
|
GCE_FILE_INPUT_HELP_TEXT: t.s('Provide account information using Google Compute Engine JSON credentials file.')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ns.externalTest = {
|
||||||
|
TITLE: t.s('Test External Credential')
|
||||||
|
};
|
||||||
|
|
||||||
|
ns.inputSources = {
|
||||||
|
TITLE: t.s('Set Input Source'),
|
||||||
|
CREDENTIAL: t.s('CREDENTIAL'),
|
||||||
|
METADATA: t.s('METADATA'),
|
||||||
|
NO_MATCH: t.s('No records matched your search.'),
|
||||||
|
NO_RECORDS: t.s('No external credentials available.'),
|
||||||
|
};
|
||||||
|
|
||||||
ns.add = {
|
ns.add = {
|
||||||
PANEL_TITLE: t.s('NEW CREDENTIAL')
|
PANEL_TITLE: t.s('NEW CREDENTIAL')
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ function EditCredentialsController (
|
|||||||
Wait,
|
Wait,
|
||||||
$filter,
|
$filter,
|
||||||
CredentialType,
|
CredentialType,
|
||||||
|
GetBasePath,
|
||||||
|
Rest,
|
||||||
) {
|
) {
|
||||||
const vm = this || {};
|
const vm = this || {};
|
||||||
const {
|
const {
|
||||||
@@ -23,6 +25,7 @@ function EditCredentialsController (
|
|||||||
|
|
||||||
const omit = ['user', 'team', 'inputs'];
|
const omit = ['user', 'team', 'inputs'];
|
||||||
const isEditable = credential.isEditable();
|
const isEditable = credential.isEditable();
|
||||||
|
const isExternal = credentialType.get('kind') === 'external';
|
||||||
|
|
||||||
vm.mode = 'edit';
|
vm.mode = 'edit';
|
||||||
vm.strings = strings;
|
vm.strings = strings;
|
||||||
@@ -96,44 +99,6 @@ function EditCredentialsController (
|
|||||||
vm.form.credential_type._placeholder = strings.get('inputs.CREDENTIAL_TYPE_PLACEHOLDER');
|
vm.form.credential_type._placeholder = strings.get('inputs.CREDENTIAL_TYPE_PLACEHOLDER');
|
||||||
vm.isTestable = (isEditable && credentialType.get('kind') === 'external');
|
vm.isTestable = (isEditable && credentialType.get('kind') === 'external');
|
||||||
|
|
||||||
vm.inputSources = {
|
|
||||||
field: null,
|
|
||||||
credentialId: null,
|
|
||||||
credentialTypeId: null,
|
|
||||||
credentialTypeName: null,
|
|
||||||
tabs: {
|
|
||||||
credential: {
|
|
||||||
_active: true,
|
|
||||||
_disabled: false,
|
|
||||||
},
|
|
||||||
metadata: {
|
|
||||||
_active: false,
|
|
||||||
_disabled: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
metadata: {},
|
|
||||||
form: {
|
|
||||||
inputs: {
|
|
||||||
_get: () => vm.inputSources.metadata,
|
|
||||||
_reference: 'vm.form.inputs',
|
|
||||||
_key: 'inputs',
|
|
||||||
_source: { _value: {} },
|
|
||||||
}
|
|
||||||
},
|
|
||||||
items: credential.get('related.input_sources.results'),
|
|
||||||
};
|
|
||||||
vm.externalTest = {
|
|
||||||
metadata: null,
|
|
||||||
form: {
|
|
||||||
inputs: {
|
|
||||||
_get: () => vm.externalTest.metadata,
|
|
||||||
_reference: 'vm.form.inputs',
|
|
||||||
_key: 'inputs',
|
|
||||||
_source: { _value: {} },
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const gceFileInputSchema = {
|
const gceFileInputSchema = {
|
||||||
id: 'gce_service_account_key',
|
id: 'gce_service_account_key',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
@@ -175,16 +140,30 @@ function EditCredentialsController (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vm.isTestable = (isEditable && credentialType.get('kind') === 'external');
|
||||||
|
vm.getSubmitData = getSubmitData;
|
||||||
|
|
||||||
|
vm.inputSources.initialItems = credential.get('related.input_sources.results');
|
||||||
|
if (credential.get('credential_type') !== credentialType.get('id')) {
|
||||||
|
vm.inputSources.items = [];
|
||||||
|
} else {
|
||||||
|
vm.inputSources.items = credential.get('related.input_sources.results');
|
||||||
|
}
|
||||||
|
|
||||||
|
const linkedFieldNames = vm.inputSources.items
|
||||||
|
.map(({ input_field_name }) => input_field_name);
|
||||||
|
|
||||||
fields = fields.map((field) => {
|
fields = fields.map((field) => {
|
||||||
if (isEditable && credentialType.get('kind') !== 'external') {
|
field.tagMode = isEditable && credentialType.get('kind') !== 'external';
|
||||||
field.tagMode = true;
|
if (linkedFieldNames.includes(field.id)) {
|
||||||
|
field.asTag = true;
|
||||||
|
const { summary_fields } = vm.inputSources.items
|
||||||
|
.find(({ input_field_name }) => input_field_name === field.id);
|
||||||
|
field._value = summary_fields.source_credential.name;
|
||||||
}
|
}
|
||||||
return field;
|
return field;
|
||||||
});
|
});
|
||||||
|
|
||||||
vm.isTestable = (isEditable && credentialType.get('kind') === 'external');
|
|
||||||
vm.getSubmitData = getSubmitData;
|
|
||||||
|
|
||||||
return fields;
|
return fields;
|
||||||
},
|
},
|
||||||
_onRemoveTag ({ id }) {
|
_onRemoveTag ({ id }) {
|
||||||
@@ -200,121 +179,228 @@ function EditCredentialsController (
|
|||||||
title: true,
|
title: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vm.externalTest = {
|
||||||
|
form: {
|
||||||
|
inputs: {
|
||||||
|
_get: () => vm.externalTest.metadataInputs,
|
||||||
|
_reference: 'vm.form.inputs',
|
||||||
|
_key: 'inputs',
|
||||||
|
_source: { _value: {} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
metadataInputs: null,
|
||||||
|
};
|
||||||
|
vm.inputSources = {
|
||||||
|
tabs: {
|
||||||
|
credential: {
|
||||||
|
_active: true,
|
||||||
|
_disabled: false,
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
_active: false,
|
||||||
|
_disabled: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
inputs: {
|
||||||
|
_get: () => vm.inputSources.metadataInputs,
|
||||||
|
_reference: 'vm.form.inputs',
|
||||||
|
_key: 'inputs',
|
||||||
|
_source: { _value: {} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
field: null,
|
||||||
|
credentialTypeId: null,
|
||||||
|
credentialTypeName: null,
|
||||||
|
credentialId: null,
|
||||||
|
credentialName: null,
|
||||||
|
metadataInputs: null,
|
||||||
|
initialItems: credential.get('related.input_sources.results'),
|
||||||
|
items: credential.get('related.input_sources.results'),
|
||||||
|
};
|
||||||
|
|
||||||
vm.onInputSourceClear = (field) => {
|
vm.onInputSourceClear = (field) => {
|
||||||
vm.form[field].tagMode = true;
|
vm.form[field].tagMode = true;
|
||||||
vm.form[field].asTag = false;
|
vm.form[field].asTag = false;
|
||||||
|
vm.form[field]._value = '';
|
||||||
|
vm.inputSources.items = vm.inputSources.items
|
||||||
|
.filter(({ input_field_name }) => input_field_name !== field);
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.setTab = (name) => {
|
function setInputSourceTab (name) {
|
||||||
const metaIsActive = name === 'metadata';
|
const metaIsActive = name === 'metadata';
|
||||||
vm.inputSources.tabs.credential._active = !metaIsActive;
|
vm.inputSources.tabs.credential._active = !metaIsActive;
|
||||||
vm.inputSources.tabs.credential._disabled = false;
|
vm.inputSources.tabs.credential._disabled = false;
|
||||||
vm.inputSources.tabs.metadata._active = metaIsActive;
|
vm.inputSources.tabs.metadata._active = metaIsActive;
|
||||||
vm.inputSources.tabs.metadata._disabled = false;
|
vm.inputSources.tabs.metadata._disabled = false;
|
||||||
};
|
}
|
||||||
|
|
||||||
vm.unsetTabs = () => {
|
function unsetInputSourceTabs () {
|
||||||
vm.inputSources.tabs.credential._active = false;
|
vm.inputSources.tabs.credential._active = false;
|
||||||
vm.inputSources.tabs.credential._disabled = false;
|
vm.inputSources.tabs.credential._disabled = false;
|
||||||
vm.inputSources.tabs.metadata._active = false;
|
vm.inputSources.tabs.metadata._active = false;
|
||||||
vm.inputSources.tabs.metadata._disabled = false;
|
vm.inputSources.tabs.metadata._disabled = false;
|
||||||
};
|
}
|
||||||
|
|
||||||
vm.onInputSourceOpen = (field) => {
|
vm.onInputSourceOpen = (field) => {
|
||||||
vm.inputSources.field = field;
|
// We get here when the input source lookup modal for a field is opened. If source
|
||||||
vm.setTab('credential');
|
// credential and metadata values for this field already exist in the initial API data
|
||||||
|
// or from it being set during a prior visit to the lookup, we initialize the lookup with
|
||||||
|
// these values here before opening it.
|
||||||
const sourceItem = vm.inputSources.items
|
const sourceItem = vm.inputSources.items
|
||||||
.find(({ input_field_name }) => input_field_name === field);
|
.find(({ input_field_name }) => input_field_name === field);
|
||||||
if (sourceItem) {
|
if (sourceItem) {
|
||||||
const { source_credential, summary_fields } = sourceItem;
|
const { source_credential, summary_fields } = sourceItem;
|
||||||
const { source_credential: { credential_type_id } } = summary_fields;
|
const { source_credential: { credential_type_id, name } } = summary_fields;
|
||||||
vm.inputSources.credentialId = source_credential;
|
vm.inputSources.credentialId = source_credential;
|
||||||
|
vm.inputSources.credentialName = name;
|
||||||
vm.inputSources.credentialTypeId = credential_type_id;
|
vm.inputSources.credentialTypeId = credential_type_id;
|
||||||
vm.inputSources._value = credential_type_id;
|
vm.inputSources._value = credential_type_id;
|
||||||
}
|
}
|
||||||
|
setInputSourceTab('credential');
|
||||||
|
vm.inputSources.field = field;
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.onInputSourceClose = () => {
|
vm.onInputSourceClose = () => {
|
||||||
|
// We get here if the lookup was closed or canceled so we clear the state for the lookup
|
||||||
|
// and metadata form without storing any changes.
|
||||||
vm.inputSources.field = null;
|
vm.inputSources.field = null;
|
||||||
vm.inputSources.metadata = null;
|
vm.inputSources.credentialId = null;
|
||||||
vm.unsetTabs();
|
vm.inputSources.credentialName = null;
|
||||||
|
vm.inputSources.metadataInputs = null;
|
||||||
|
unsetInputSourceTabs();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the current set of input values from the metadata form and reshape them to a
|
||||||
|
* metadata object that can be sent to the api later or reloaded when re-opening the form.
|
||||||
|
*/
|
||||||
|
function getMetadataFormSubmitData ({ inputs }) {
|
||||||
|
const metadata = Object.assign({}, ...inputs._group
|
||||||
|
.filter(({ _value }) => _value !== undefined)
|
||||||
|
.map(({ id, _value }) => ({ [id]: _value })));
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
vm.onInputSourceNext = () => {
|
vm.onInputSourceNext = () => {
|
||||||
const { field, credentialId, credentialTypeId } = vm.inputSources;
|
const { field, credentialId, credentialTypeId } = vm.inputSources;
|
||||||
Wait('start');
|
Wait('start');
|
||||||
new CredentialType('get', credentialTypeId)
|
new CredentialType('get', credentialTypeId)
|
||||||
.then(model => {
|
.then(model => {
|
||||||
model.mergeInputProperties('metadata');
|
model.mergeInputProperties('metadata');
|
||||||
vm.inputSources.metadata = model.get('inputs.metadata');
|
vm.inputSources.metadataInputs = model.get('inputs.metadata');
|
||||||
vm.inputSources.credentialTypeName = model.get('name');
|
vm.inputSources.credentialTypeName = model.get('name');
|
||||||
|
// Pre-populate the input values for the metadata form if state for this specific
|
||||||
|
// field_name->source_credential link already exists. This occurs one of two ways:
|
||||||
|
//
|
||||||
|
// 1. This field->source_credential link already exists in the API and so we're
|
||||||
|
// reflecting the current state as it exists on the backend.
|
||||||
|
// 2. The metadata form for this specific field->source_credential combination was
|
||||||
|
// set during a prior visit to this lookup and so we're reflecting the most
|
||||||
|
// recent set of (unsaved) metadata values provided by the user for this field.
|
||||||
|
//
|
||||||
|
// Note: Prior state for a given credential input field is only set for one source
|
||||||
|
// credential at a time. Linking a field to a source credential will remove all
|
||||||
|
// other prior input state for that field.
|
||||||
const [metavals] = vm.inputSources.items
|
const [metavals] = vm.inputSources.items
|
||||||
.filter(({ input_field_name }) => input_field_name === field)
|
.filter(({ input_field_name }) => input_field_name === field)
|
||||||
.filter(({ source_credential }) => source_credential === credentialId)
|
.filter(({ source_credential }) => source_credential === credentialId)
|
||||||
.map(({ metadata }) => metadata);
|
.map(({ metadata }) => metadata);
|
||||||
Object.keys(metavals || {}).forEach(key => {
|
Object.keys(metavals || {}).forEach(key => {
|
||||||
const obj = vm.inputSources.metadata.find(o => o.id === key);
|
const obj = vm.inputSources.metadataInputs.find(o => o.id === key);
|
||||||
if (obj) obj._value = metavals[key];
|
if (obj) obj._value = metavals[key];
|
||||||
});
|
});
|
||||||
vm.setTab('metadata');
|
setInputSourceTab('metadata');
|
||||||
})
|
})
|
||||||
.finally(() => Wait('stop'));
|
.finally(() => Wait('stop'));
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.onInputSourceSelect = () => {
|
vm.onInputSourceSelect = () => {
|
||||||
const { field, credentialId } = vm.inputSources;
|
const { field, credentialId, credentialName, credentialTypeId } = vm.inputSources;
|
||||||
|
const metadata = getMetadataFormSubmitData(vm.inputSources.form);
|
||||||
|
// Remove any input source objects already stored for this field then store the metadata
|
||||||
|
// and currently selected source credential as a valid credential input source object that
|
||||||
|
// can be sent to the api later or reloaded into the form if it is reopened.
|
||||||
vm.inputSources.items = vm.inputSources.items
|
vm.inputSources.items = vm.inputSources.items
|
||||||
.filter(({ input_field_name }) => input_field_name !== field)
|
.filter(({ input_field_name }) => input_field_name !== field)
|
||||||
.concat([{
|
.concat([{
|
||||||
|
metadata,
|
||||||
input_field_name: field,
|
input_field_name: field,
|
||||||
source_credential: credentialId,
|
source_credential: credentialId,
|
||||||
target_credential: credential.get('id'),
|
target_credential: credential.get('id'),
|
||||||
|
summary_fields: {
|
||||||
|
source_credential: {
|
||||||
|
name: credentialName,
|
||||||
|
credential_type_id: credentialTypeId
|
||||||
|
}
|
||||||
|
},
|
||||||
}]);
|
}]);
|
||||||
|
// Now that we've extracted and stored the selected source credential and metadata values
|
||||||
|
// for this field, we clear the state for the source credential lookup and metadata form.
|
||||||
vm.inputSources.field = null;
|
vm.inputSources.field = null;
|
||||||
vm.inputSources.metadata = null;
|
vm.inputSources.metadataInputs = null;
|
||||||
vm.unsetTabs();
|
unsetInputSourceTabs();
|
||||||
|
// We've linked this field to a credential, so display value as a credential tag
|
||||||
|
vm.form[field]._value = credentialName;
|
||||||
|
vm.form[field].asTag = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.onInputSourceTabSelect = (name) => {
|
vm.onInputSourceTabSelect = (name) => {
|
||||||
if (name === 'metadata') {
|
if (name === 'metadata') {
|
||||||
|
// Clicking on the metadata tab should have identical behavior to clicking the 'next'
|
||||||
|
// button, so we pass-through to the same handler here.
|
||||||
vm.onInputSourceNext();
|
vm.onInputSourceNext();
|
||||||
} else {
|
} else {
|
||||||
vm.setTab('credential');
|
setInputSourceTab('credential');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.onInputSourceRowClick = ({ id, credential_type }) => {
|
vm.onInputSourceRowClick = ({ id, credential_type, name }) => {
|
||||||
vm.inputSources.credentialId = id;
|
vm.inputSources.credentialId = id;
|
||||||
|
vm.inputSources.credentialName = name;
|
||||||
vm.inputSources.credentialTypeId = credential_type;
|
vm.inputSources.credentialTypeId = credential_type;
|
||||||
vm.inputSources._value = credential_type;
|
vm.inputSources._value = credential_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.onInputSourceTest = () => {
|
vm.onInputSourceTest = () => {
|
||||||
const metadata = Object.assign({}, ...vm.inputSources.form.inputs._group
|
// We get here if the test button on the metadata form for the field of a non-external
|
||||||
.filter(({ _value }) => _value !== undefined)
|
// credential was used. All input values for the external credential are already stored
|
||||||
.map(({ id, _value }) => ({ [id]: _value })));
|
// on the backend, so we are only testing how it works with a set of metadata before
|
||||||
|
// linking it.
|
||||||
|
const metadata = getMetadataFormSubmitData(vm.inputSources.form);
|
||||||
const name = $filter('sanitize')(vm.inputSources.credentialTypeName);
|
const name = $filter('sanitize')(vm.inputSources.credentialTypeName);
|
||||||
const endpoint = `${vm.inputSources.credentialId}/test/`;
|
const endpoint = `${vm.inputSources.credentialId}/test/`;
|
||||||
|
return runTest({ name, model: credential, endpoint, data: { metadata } });
|
||||||
return vm.runTest({ name, model: credential, endpoint, data: { metadata } });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.onExternalTestClick = () => {
|
function onExternalTestOpen () {
|
||||||
|
// We get here if test button on the top-level form for an external credential type was
|
||||||
|
// used. We load the metadata schema for this particular external credential type and
|
||||||
|
// use it to generate and open a form for submitting test values.
|
||||||
credentialType.mergeInputProperties('metadata');
|
credentialType.mergeInputProperties('metadata');
|
||||||
vm.externalTest.metadata = credentialType.get('inputs.metadata');
|
vm.externalTest.metadataInputs = credentialType.get('inputs.metadata');
|
||||||
};
|
}
|
||||||
|
vm.form.secondary = onExternalTestOpen;
|
||||||
|
|
||||||
vm.onExternalTestClose = () => {
|
vm.onExternalTestClose = () => {
|
||||||
vm.externalTest.metadata = null;
|
// We get here if the metadata test form for an external credential type was canceled or
|
||||||
|
// closed so we clear the form state and close without submitting any data to the test api,
|
||||||
|
vm.externalTest.metadataInputs = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.onExternalTest = () => {
|
vm.onExternalTest = () => {
|
||||||
const name = $filter('sanitize')(credentialType.get('name'));
|
const name = $filter('sanitize')(credentialType.get('name'));
|
||||||
const { inputs } = vm.getSubmitData();
|
const { inputs } = vm.getSubmitData();
|
||||||
const metadata = Object.assign({}, ...vm.externalTest.form.inputs._group
|
const metadata = getMetadataFormSubmitData(vm.externalTest.form);
|
||||||
.filter(({ _value }) => _value !== undefined)
|
// We get here if the test button on the top-level form for an external credential type was
|
||||||
.map(({ id, _value }) => ({ [id]: _value })));
|
// used. We need to see if the currently selected credential type is the one loaded from
|
||||||
|
// the api when we initialized the view or if its type was changed on the form and hasn't
|
||||||
|
// been saved. If the credential type hasn't been changed, it means some of the input
|
||||||
|
// values for the credential may be stored in the backend and not in the form, so we need
|
||||||
|
// to use the test endpoint for the credential. If the credential type has been changed,
|
||||||
|
// the user must provide a complete set of input values for the credential to save their
|
||||||
|
// changes, so we use the generic test endpoint for the credental type as if we were
|
||||||
|
// testing a completely new and unsaved credential.
|
||||||
let model;
|
let model;
|
||||||
if (credential.get('credential_type') !== credentialType.get('id')) {
|
if (credential.get('credential_type') !== credentialType.get('id')) {
|
||||||
model = credentialType;
|
model = credentialType;
|
||||||
@@ -323,49 +409,65 @@ function EditCredentialsController (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const endpoint = `${model.get('id')}/test/`;
|
const endpoint = `${model.get('id')}/test/`;
|
||||||
return vm.runTest({ name, model, endpoint, data: { inputs, metadata } });
|
return runTest({ name, model, endpoint, data: { inputs, metadata } });
|
||||||
};
|
};
|
||||||
vm.form.secondary = vm.onExternalTestClick;
|
|
||||||
|
|
||||||
vm.runTest = ({ name, model, endpoint, data: { inputs, metadata } }) => {
|
vm.filterInputSourceCredentialResults = (data) => {
|
||||||
|
// If an external credential is changed to have a non-external `credential_type` while
|
||||||
|
// editing, we avoid showing a self-reference in the list of selectable external
|
||||||
|
// credentials for input fields by filtering it out here.
|
||||||
|
if (isExternal) {
|
||||||
|
data.results = data.results.filter(({ id }) => id !== credential.get('id'));
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
function runTest ({ name, model, endpoint, data: { inputs, metadata } }) {
|
||||||
return model.http.post({ url: endpoint, data: { inputs, metadata }, replace: false })
|
return model.http.post({ url: endpoint, data: { inputs, metadata }, replace: false })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
const icon = 'fa-check-circle';
|
||||||
|
const msg = strings.get('edit.TEST_PASSED');
|
||||||
|
const content = buildTestNotificationContent({ name, icon, msg });
|
||||||
ngToast.success({
|
ngToast.success({
|
||||||
content: vm.buildTestNotificationContent({
|
content,
|
||||||
name,
|
|
||||||
icon: 'fa-check-circle',
|
|
||||||
msg: strings.get('edit.TEST_PASSED'),
|
|
||||||
}),
|
|
||||||
dismissButton: false,
|
dismissButton: false,
|
||||||
dismissOnTimeout: true
|
dismissOnTimeout: true
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(({ data }) => {
|
.catch(({ data }) => {
|
||||||
const msg = data.inputs
|
const icon = 'fa-exclamation-triangle';
|
||||||
? `${$filter('sanitize')(data.inputs)}`
|
const msg = data.inputs || strings.get('edit.TEST_FAILED');
|
||||||
: strings.get('edit.TEST_FAILED');
|
const content = buildTestNotificationContent({ name, icon, msg });
|
||||||
ngToast.danger({
|
ngToast.danger({
|
||||||
content: vm.buildTestNotificationContent({
|
content,
|
||||||
name,
|
|
||||||
msg,
|
|
||||||
icon: 'fa-exclamation-triangle'
|
|
||||||
}),
|
|
||||||
dismissButton: false,
|
dismissButton: false,
|
||||||
dismissOnTimeout: true
|
dismissOnTimeout: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
vm.buildTestNotificationContent = ({ name, msg, icon }) => (
|
function buildTestNotificationContent ({ name, msg, icon }) {
|
||||||
`<div class="Toast-wrapper">
|
const sanitize = $filter('sanitize');
|
||||||
|
const content = `<div class="Toast-wrapper">
|
||||||
<div class="Toast-icon">
|
<div class="Toast-icon">
|
||||||
<i class="fa ${icon} Toast-successIcon"></i>
|
<i class="fa ${icon} Toast-successIcon"></i>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<b>${name}:</b> ${msg}
|
<b>${sanitize(name)}:</b> ${sanitize(msg)}
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`;
|
||||||
);
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteInputSource ({ id }) {
|
||||||
|
Rest.setUrl(`${GetBasePath('credential_input_sources')}${id}/`);
|
||||||
|
return Rest.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createInputSource (data) {
|
||||||
|
Rest.setUrl(GetBasePath('credential_input_sources'));
|
||||||
|
return Rest.post(data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a credential's `credential_type` is changed while editing, the inputs associated with
|
* If a credential's `credential_type` is changed while editing, the inputs associated with
|
||||||
@@ -380,10 +482,32 @@ function EditCredentialsController (
|
|||||||
delete data.inputs[gceFileInputSchema.id];
|
delete data.inputs[gceFileInputSchema.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredInputs = _.omit(data.inputs, (value) => value === '');
|
const initialLinkedFieldNames = vm.inputSources.initialItems
|
||||||
|
.map(({ input_field_name }) => input_field_name);
|
||||||
|
const updatedLinkedFieldNames = vm.inputSources.items
|
||||||
|
.map(({ input_field_name }) => input_field_name);
|
||||||
|
|
||||||
|
const fieldsToDisassociate = [...initialLinkedFieldNames]
|
||||||
|
.filter(name => !updatedLinkedFieldNames.includes(name));
|
||||||
|
const fieldsToAssociate = [...updatedLinkedFieldNames]
|
||||||
|
.filter(name => !initialLinkedFieldNames.includes(name));
|
||||||
|
|
||||||
|
const sourcesToDisassociate = [...fieldsToDisassociate]
|
||||||
|
.map(name => vm.inputSources.initialItems
|
||||||
|
.find(({ input_field_name }) => input_field_name === name));
|
||||||
|
const sourcesToAssociate = [...fieldsToAssociate]
|
||||||
|
.map(name => vm.inputSources.items
|
||||||
|
.find(({ input_field_name }) => input_field_name === name));
|
||||||
|
|
||||||
|
// remove inputs with empty string values
|
||||||
|
let filteredInputs = _.omit(data.inputs, (value) => value === '');
|
||||||
|
// remove inputs that are to be linked to an external credential
|
||||||
|
filteredInputs = _.omit(filteredInputs, updatedLinkedFieldNames);
|
||||||
data.inputs = filteredInputs;
|
data.inputs = filteredInputs;
|
||||||
|
|
||||||
return credential.request('put', { data });
|
return Promise.all(sourcesToDisassociate.map(deleteInputSource))
|
||||||
|
.then(() => credential.request('put', { data }))
|
||||||
|
.then(() => Promise.all(sourcesToAssociate.map(createInputSource)));
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.form.onSaveSuccess = () => {
|
vm.form.onSaveSuccess = () => {
|
||||||
@@ -450,6 +574,8 @@ EditCredentialsController.$inject = [
|
|||||||
'Wait',
|
'Wait',
|
||||||
'$filter',
|
'$filter',
|
||||||
'CredentialTypeModel',
|
'CredentialTypeModel',
|
||||||
|
'GetBasePath',
|
||||||
|
'Rest',
|
||||||
];
|
];
|
||||||
|
|
||||||
export default EditCredentialsController;
|
export default EditCredentialsController;
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
const templateUrl = require('~features/credentials/external-test-modal.partial.html');
|
||||||
|
|
||||||
|
function ExternalTestModalController (strings) {
|
||||||
|
const vm = this || {};
|
||||||
|
|
||||||
|
vm.strings = strings;
|
||||||
|
vm.title = strings.get('externalTest.TITLE');
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalTestModalController.$inject = [
|
||||||
|
'CredentialsStrings',
|
||||||
|
];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
templateUrl,
|
||||||
|
controller: ExternalTestModalController,
|
||||||
|
controllerAs: 'vm',
|
||||||
|
bindings: {
|
||||||
|
onClose: '=',
|
||||||
|
onSubmit: '=',
|
||||||
|
form: '=',
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<at-easy-modal title="vm.title" on-close="vm.onClose">
|
||||||
|
<at-form state="vm.form" autocomplete="off" id="external_test_form">
|
||||||
|
<at-input-group col="12" tab="20" state="vm.form.inputs" form-id="external_test"></at-input-group>
|
||||||
|
<at-action-group col="12" pos="right">
|
||||||
|
<at-action-button variant="tertiary" ng-click="vm.onClose()">
|
||||||
|
{{::vm.strings.get('CLOSE')}}
|
||||||
|
</at-action-button>
|
||||||
|
<at-action-button variant="primary" ng-click="vm.onSubmit()">
|
||||||
|
{{::vm.strings.get('RUN')}}
|
||||||
|
</at-action-button>
|
||||||
|
</at-action-group>
|
||||||
|
</at-form>
|
||||||
|
</at-easy-modal>
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
const templateUrl = require('~features/credentials/external-test.partial.html');
|
|
||||||
|
|
||||||
function ExternalTestModalController ($scope, $element, strings) {
|
|
||||||
const vm = this || {};
|
|
||||||
let overlay;
|
|
||||||
|
|
||||||
vm.strings = strings;
|
|
||||||
vm.title = 'Test External Credential';
|
|
||||||
|
|
||||||
vm.$onInit = () => {
|
|
||||||
const [el] = $element;
|
|
||||||
overlay = el.querySelector('#external-test-modal');
|
|
||||||
vm.show();
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.show = () => {
|
|
||||||
overlay.style.display = 'block';
|
|
||||||
overlay.style.opacity = 1;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ExternalTestModalController.$inject = [
|
|
||||||
'$scope',
|
|
||||||
'$element',
|
|
||||||
'CredentialsStrings',
|
|
||||||
];
|
|
||||||
|
|
||||||
export default {
|
|
||||||
templateUrl,
|
|
||||||
controller: ExternalTestModalController,
|
|
||||||
controllerAs: 'vm',
|
|
||||||
bindings: {
|
|
||||||
onClose: '=',
|
|
||||||
onSubmit: '=',
|
|
||||||
form: '=',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<div id="external-test-modal" class="modal at-Modal" tabindex="-1" role="dialog">
|
|
||||||
<div class="modal-dialog" role="document">
|
|
||||||
<div class="modal-content at-Modal-window">
|
|
||||||
<div class="Modal-header">
|
|
||||||
<div class="Modal-title">
|
|
||||||
<div class="at-Modal-heading">
|
|
||||||
<h4 class="modal-title at-Modal-title">{{ vm.title }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="Modal-exitHolder">
|
|
||||||
<div class="at-Modal-dismiss">
|
|
||||||
<i class="fa fa-lg fa-times-circle" ng-click="vm.onClose()"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<at-form state="vm.form" autocomplete="off" id="external_test_form">
|
|
||||||
<at-input-group col="12" tab="20" state="vm.form.inputs" form-id="external_test">
|
|
||||||
TITLE
|
|
||||||
</at-input-group>
|
|
||||||
</at-form>
|
|
||||||
<div >
|
|
||||||
<div class="Prompt-footer">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="Prompt-defaultButton"
|
|
||||||
ng-click="vm.onClose()"
|
|
||||||
>
|
|
||||||
{{:: vm.strings.get('CLOSE') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="Prompt-actionButton"
|
|
||||||
ng-click="vm.onSubmit()"
|
|
||||||
>
|
|
||||||
{{:: vm.strings.get('RUN') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -3,7 +3,7 @@ import AddController from './add-credentials.controller';
|
|||||||
import EditController from './edit-credentials.controller';
|
import EditController from './edit-credentials.controller';
|
||||||
import CredentialsStrings from './credentials.strings';
|
import CredentialsStrings from './credentials.strings';
|
||||||
import InputSourceLookupComponent from './input-source-lookup.component';
|
import InputSourceLookupComponent from './input-source-lookup.component';
|
||||||
import ExternalTestComponent from './external-test.component';
|
import ExternalTestModalComponent from './external-test-modal.component';
|
||||||
|
|
||||||
const MODULE_NAME = 'at.features.credentials';
|
const MODULE_NAME = 'at.features.credentials';
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ angular
|
|||||||
.service('LegacyCredentialsService', LegacyCredentials)
|
.service('LegacyCredentialsService', LegacyCredentials)
|
||||||
.service('CredentialsStrings', CredentialsStrings)
|
.service('CredentialsStrings', CredentialsStrings)
|
||||||
.component('atInputSourceLookup', InputSourceLookupComponent)
|
.component('atInputSourceLookup', InputSourceLookupComponent)
|
||||||
.component('atExternalCredentialTest', ExternalTestComponent)
|
.component('atExternalCredentialTest', ExternalTestModalComponent)
|
||||||
.run(CredentialsRun);
|
.run(CredentialsRun);
|
||||||
|
|
||||||
export default MODULE_NAME;
|
export default MODULE_NAME;
|
||||||
|
|||||||
@@ -1,76 +1,13 @@
|
|||||||
const templateUrl = require('~features/credentials/input-source-lookup.partial.html');
|
const templateUrl = require('~features/credentials/input-source-lookup.partial.html');
|
||||||
|
|
||||||
function InputSourceLookupController ($scope, $element, $http, GetBasePath, qs, strings) {
|
function InputSourceLookupController (strings) {
|
||||||
const vm = this || {};
|
const vm = this || {};
|
||||||
let overlay;
|
|
||||||
|
|
||||||
vm.strings = strings;
|
vm.strings = strings;
|
||||||
vm.name = 'credential';
|
vm.title = strings.get('inputSources.TITLE');
|
||||||
vm.title = 'Set Input Source';
|
|
||||||
|
|
||||||
vm.$onInit = () => {
|
|
||||||
const [el] = $element;
|
|
||||||
overlay = el.querySelector('#input-source-lookup');
|
|
||||||
|
|
||||||
const defaultParams = {
|
|
||||||
order_by: 'name',
|
|
||||||
credential_type__kind: 'external',
|
|
||||||
page_size: 5
|
|
||||||
};
|
|
||||||
vm.setDefaultParams(defaultParams);
|
|
||||||
vm.setData({ results: [], count: 0 });
|
|
||||||
$http({ method: 'GET', url: GetBasePath(`${vm.name}s`), params: defaultParams })
|
|
||||||
.then(({ data }) => {
|
|
||||||
vm.setData(data);
|
|
||||||
vm.show();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.show = () => {
|
|
||||||
overlay.style.display = 'block';
|
|
||||||
overlay.style.opacity = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.close = () => {
|
|
||||||
vm.onClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.next = () => {
|
|
||||||
vm.onNext();
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.select = () => {
|
|
||||||
vm.onSelect();
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.test = () => {
|
|
||||||
vm.onTest();
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.setData = ({ results, count }) => {
|
|
||||||
vm.dataset = { results, count };
|
|
||||||
vm.collection = vm.dataset.results;
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.setDefaultParams = (params) => {
|
|
||||||
vm.list = { name: vm.name, iterator: vm.name };
|
|
||||||
vm.defaultParams = params;
|
|
||||||
vm.queryset = params;
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.toggle_row = (obj) => {
|
|
||||||
vm.onRowClick(obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.onCredentialTabClick = () => vm.onTabSelect('credential');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InputSourceLookupController.$inject = [
|
InputSourceLookupController.$inject = [
|
||||||
'$scope',
|
|
||||||
'$element',
|
|
||||||
'$http',
|
|
||||||
'GetBasePath',
|
|
||||||
'QuerySet',
|
|
||||||
'CredentialsStrings',
|
'CredentialsStrings',
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -88,5 +25,6 @@ export default {
|
|||||||
onTest: '=',
|
onTest: '=',
|
||||||
selectedId: '=',
|
selectedId: '=',
|
||||||
form: '=',
|
form: '=',
|
||||||
|
resultsFilter: '=',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,174 +1,66 @@
|
|||||||
<div id="input-source-lookup" class="modal at-Modal" tabindex="-1" role="dialog">
|
<at-easy-modal title="vm.title" on-close="vm.onClose">
|
||||||
<div class="modal-dialog" role="document">
|
<at-tab-group>
|
||||||
<div class="modal-content at-Modal-window">
|
<at-tab
|
||||||
<div class="Modal-header">
|
state="vm.tabs.credential"
|
||||||
<div class="Modal-title">
|
ng-click="vm.onTabSelect('credential');"
|
||||||
<div class="at-Modal-heading">
|
>
|
||||||
<h4 class="modal-title at-Modal-title">{{ vm.title }}</h4>
|
{{::vm.strings.get('inputSources.CREDENTIAL')}}
|
||||||
</div>
|
</at-tab>
|
||||||
</div>
|
<at-tab
|
||||||
<div class="Modal-exitHolder">
|
state="vm.tabs.metadata"
|
||||||
<div class="at-Modal-dismiss">
|
ng-click="vm.onTabSelect('metadata');"
|
||||||
<i class="fa fa-lg fa-times-circle" ng-click="vm.close()"></i>
|
>
|
||||||
</div>
|
{{::vm.strings.get('inputSources.METADATA')}}
|
||||||
</div>
|
</at-tab>
|
||||||
</div>
|
</at-tab-group>
|
||||||
<!-- =================================================================================================== -->
|
<at-lookup-list
|
||||||
<at-tab-group>
|
ng-show="vm.tabs.credential._active"
|
||||||
<at-tab
|
resource-name="credential"
|
||||||
state="vm.tabs.credential"
|
base-params="{
|
||||||
ng-click="vm.onTabSelect('credential');"
|
order_by: 'name',
|
||||||
>
|
credential_type__kind: 'external',
|
||||||
CREDENTIAL
|
page_size: 5
|
||||||
</at-tab>
|
}"
|
||||||
<at-tab
|
results-filter="vm.resultsFilter"
|
||||||
state="vm.tabs.metadata"
|
selected-id="vm.selectedId"
|
||||||
ng-click="vm.onTabSelect('metadata');"
|
on-select="vm.onSelect"
|
||||||
>
|
on-row-click="vm.onRowClick"
|
||||||
METADATA
|
/>
|
||||||
</at-tab>
|
<at-form state="vm.form" autocomplete="off" id="input_source_form">
|
||||||
</at-tab-group>
|
<at-input-group
|
||||||
<div></div>
|
ng-if="vm.tabs.metadata._active"
|
||||||
<!-- =================================================================================================== -->
|
col="12" tab="20"
|
||||||
|
state="vm.form.inputs"
|
||||||
<div ng-if="vm.tabs.metadata._active">
|
form-id="input_source">
|
||||||
<at-form state="vm.form" autocomplete="off" id="input_source_form">
|
</at-input-group>
|
||||||
<at-input-group col="12" tab="20" state="vm.form.inputs" form-id="input_source">
|
<at-action-group col="12" pos="right">
|
||||||
TITLE
|
<at-action-button
|
||||||
</at-input-group>
|
variant="secondary"
|
||||||
</at-form>
|
ng-click="vm.onTest()"
|
||||||
</div>
|
ng-show="vm.tabs.metadata._active"
|
||||||
|
>
|
||||||
<div ng-show="vm.tabs.credential._active">
|
{{::vm.strings.get('TEST')}}
|
||||||
<div ng-hide="vm.collection.length === 0 && (searchTags | isEmpty)">
|
</at-action-button>
|
||||||
<div style="min-height: 20px"></div>
|
<at-action-button
|
||||||
<smart-search
|
variant="tertiary"
|
||||||
django-model="{{ vm.name + 's' }}"
|
ng-click="vm.onClose()"
|
||||||
base-path="{{ vm.name + 's' }}"
|
>
|
||||||
iterator="{{ vm.list.iterator }}"
|
{{::vm.strings.get('CANCEL')}}
|
||||||
dataset="vm.dataset"
|
</at-action-button>
|
||||||
list="vm.list"
|
<at-action-button
|
||||||
collection="vm.collection"
|
variant="primary"
|
||||||
default-params="vm.defaultParams"
|
ng-click="vm.onNext()"
|
||||||
query-set="vm.queryset"
|
ng-show="vm.tabs.credential._active"
|
||||||
search-tags="searchTags">
|
>
|
||||||
</smart-search>
|
{{::vm.strings.get('NEXT')}}
|
||||||
</div>
|
</at-action-button>
|
||||||
<div
|
<at-action-button
|
||||||
class="row"
|
variant="primary"
|
||||||
ng-show="vm.collection.length === 0 && !(searchTags | isEmpty)"
|
ng-click="vm.onSelect()"
|
||||||
>
|
ng-show="vm.tabs.metadata._active"
|
||||||
<div
|
>
|
||||||
class="col-lg-12 List-searchNoResults"
|
{{::vm.strings.get('OK')}}
|
||||||
translate
|
</at-action-button>
|
||||||
>
|
</at-action-group>
|
||||||
No records matched your search.
|
</at-form>
|
||||||
</div>
|
</at-easy-modal>
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="List-noItems"
|
|
||||||
ng-show="vm.collection.length === 0 && (searchTags | isEmpty)"
|
|
||||||
>
|
|
||||||
PLEASE ADD ITEMS TO THIS LIST
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="list-table-container"
|
|
||||||
ng-show="vm.collection.length > 0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
id="credential_input_source_table"
|
|
||||||
class="List-table"
|
|
||||||
is-extended="false"
|
|
||||||
>
|
|
||||||
<div class="List-lookupLayout List-tableHeaderRow">
|
|
||||||
<div></div>
|
|
||||||
<div class="d-flex h-100">
|
|
||||||
<div
|
|
||||||
base-path="{{ vm.name + 's' }}"
|
|
||||||
collection="vm.collection"
|
|
||||||
dataset="vm.dataset"
|
|
||||||
column-sort=""
|
|
||||||
column-field="name"
|
|
||||||
column-iterator="{{ vm.list.iterator }}"
|
|
||||||
column-label="Name"
|
|
||||||
column-custom-class="List-tableCell col-md-4 col-sm-9 col-xs-9"
|
|
||||||
query-set="vm.queryset"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
id="{{ obj.id }}"
|
|
||||||
class="List-lookupLayout List-tableRow"
|
|
||||||
ng-repeat="obj in vm.collection"
|
|
||||||
>
|
|
||||||
<div class="List-centerEnd select-column">
|
|
||||||
<input type="radio"
|
|
||||||
name="check_{{ vm.name }}_{{ obj.id }}"
|
|
||||||
ng-checked="obj.id === vm.selectedId"
|
|
||||||
ng-click="vm.toggle_row(obj)"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="d-flex h-100">
|
|
||||||
<div
|
|
||||||
class="List-tableCell name-column col-sm-12"
|
|
||||||
ng-click="vm.toggle_row(obj)"
|
|
||||||
>
|
|
||||||
{{ obj.name }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<paginate
|
|
||||||
base-path="{{ vm.name + 's' }}"
|
|
||||||
collection="vm.collection"
|
|
||||||
dataset="vm.dataset"
|
|
||||||
iterator="{{ vm.list.iterator }}"
|
|
||||||
query-set="vm.queryset"
|
|
||||||
hide-view-per-page="true"
|
|
||||||
>
|
|
||||||
</paginate>
|
|
||||||
</div>
|
|
||||||
<!-- ============================================================================================ -->
|
|
||||||
<div >
|
|
||||||
<div class="Prompt-footer">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="Prompt-infoButton"
|
|
||||||
ng-show="vm.tabs.metadata._active"
|
|
||||||
ng-click="vm.test()"
|
|
||||||
>
|
|
||||||
{{:: vm.strings.get('TEST') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="Prompt-defaultButton"
|
|
||||||
ng-click="vm.close()"
|
|
||||||
>
|
|
||||||
{{:: vm.strings.get('CANCEL') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="Prompt-actionButton"
|
|
||||||
ng-show="vm.tabs.credential._active"
|
|
||||||
ng-click="vm.next()"
|
|
||||||
>
|
|
||||||
{{:: vm.strings.get('NEXT') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
ng-disabled="true"
|
|
||||||
type="button"
|
|
||||||
class="Prompt-actionButton"
|
|
||||||
ng-show="vm.tabs.metadata._active"
|
|
||||||
ng-click="vm.select()"
|
|
||||||
>
|
|
||||||
{{:: vm.strings.get('OK') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
@import 'layout/_index';
|
@import 'layout/_index';
|
||||||
@import 'list/_index';
|
@import 'list/_index';
|
||||||
@import 'modal/_index';
|
@import 'modal/_index';
|
||||||
|
@import 'easy-modal/_index';
|
||||||
@import 'panel/_index';
|
@import 'panel/_index';
|
||||||
@import 'popover/_index';
|
@import 'popover/_index';
|
||||||
@import 'relaunchButton/_index';
|
@import 'relaunchButton/_index';
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
const templateUrl = require('~components/action/action-button.partial.html');
|
||||||
|
|
||||||
|
function ActionButtonController () {
|
||||||
|
const vm = this || {};
|
||||||
|
vm.$onInit = () => {
|
||||||
|
const { variant } = vm;
|
||||||
|
|
||||||
|
if (variant === 'primary') {
|
||||||
|
vm.color = 'success';
|
||||||
|
vm.fill = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant === 'secondary') {
|
||||||
|
vm.color = 'info';
|
||||||
|
vm.fill = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant === 'tertiary') {
|
||||||
|
vm.color = 'default';
|
||||||
|
vm.fill = 'Hollow';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
templateUrl,
|
||||||
|
controller: ActionButtonController,
|
||||||
|
controllerAs: 'vm',
|
||||||
|
transclude: true,
|
||||||
|
bindings: {
|
||||||
|
variant: '@',
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<button class="btn at-Button{{ vm.fill }}--{{ vm.color }}">
|
||||||
|
<ng-transclude></ng-transclude>
|
||||||
|
</button>
|
||||||
27
awx/ui/client/lib/components/easy-modal/_index.less
Normal file
27
awx/ui/client/lib/components/easy-modal/_index.less
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
.at-EasyModal-body {
|
||||||
|
font-size: @at-font-size;
|
||||||
|
padding: @at-padding-panel 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-EasyModal-dismiss {
|
||||||
|
.at-mixin-ButtonIcon();
|
||||||
|
font-size: @at-font-size-modal-dismiss;
|
||||||
|
color: @at-color-icon-dismiss;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-EasyModal-heading {
|
||||||
|
margin: 0;
|
||||||
|
overflow: visible;
|
||||||
|
|
||||||
|
& > .at-EasyModal-dismiss {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-EasyModal-title {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.at-mixin-Heading(@at-font-size-modal-heading);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
const templateUrl = require('~components/easy-modal/easy-modal.partial.html');
|
||||||
|
|
||||||
|
const overlaySelector = '.at-EasyModal';
|
||||||
|
|
||||||
|
function EasyModalController ($element) {
|
||||||
|
const vm = this || {};
|
||||||
|
|
||||||
|
vm.$onInit = () => {
|
||||||
|
const [el] = $element;
|
||||||
|
const overlay = el.querySelector(overlaySelector);
|
||||||
|
overlay.style.display = 'block';
|
||||||
|
overlay.style.opacity = 1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
EasyModalController.$inject = [
|
||||||
|
'$element',
|
||||||
|
];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
templateUrl,
|
||||||
|
controller: EasyModalController,
|
||||||
|
controllerAs: 'vm',
|
||||||
|
transclude: true,
|
||||||
|
bindings: {
|
||||||
|
title: '=',
|
||||||
|
onClose: '=',
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<div class="modal at-EasyModal fade" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content at-EasyModal-window">
|
||||||
|
<div class="Modal-header">
|
||||||
|
<div class="Modal-title">
|
||||||
|
<div class="at-EasyModal-heading">
|
||||||
|
<h4 class="modal-title at-EasyModal-title">{{ vm.title }}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Modal-exitHolder">
|
||||||
|
<div class="at-EasyModal-dismiss">
|
||||||
|
<i class="fa fa-lg fa-times-circle" ng-click="vm.onClose()"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ng-transclude></ng-transclude>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -30,7 +30,6 @@ function AtFormController (eventService, strings) {
|
|||||||
({ modal } = scope[scope.ns]);
|
({ modal } = scope[scope.ns]);
|
||||||
|
|
||||||
vm.state.disabled = scope.state.disabled;
|
vm.state.disabled = scope.state.disabled;
|
||||||
|
|
||||||
vm.setListeners();
|
vm.setListeners();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -203,6 +202,7 @@ function AtFormController (eventService, strings) {
|
|||||||
|
|
||||||
if (isValid !== vm.state.isValid) {
|
if (isValid !== vm.state.isValid) {
|
||||||
vm.state.isValid = isValid;
|
vm.state.isValid = isValid;
|
||||||
|
scope.state.isValid = vm.state.isValid;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import atLibServices from '~services';
|
import atLibServices from '~services';
|
||||||
|
|
||||||
import actionGroup from '~components/action/action-group.directive';
|
import actionGroup from '~components/action/action-group.directive';
|
||||||
|
import actionButton from '~components/action/action-button.component';
|
||||||
import divider from '~components/utility/divider.directive';
|
import divider from '~components/utility/divider.directive';
|
||||||
import dynamicSelect from '~components/input/dynamic-select.directive';
|
import dynamicSelect from '~components/input/dynamic-select.directive';
|
||||||
|
import easyModal from '~components/easy-modal/easy-modal.component';
|
||||||
import form from '~components/form/form.directive';
|
import form from '~components/form/form.directive';
|
||||||
import formAction from '~components/form/action.directive';
|
import formAction from '~components/form/action.directive';
|
||||||
import inputCheckbox from '~components/input/checkbox.directive';
|
import inputCheckbox from '~components/input/checkbox.directive';
|
||||||
@@ -20,6 +22,7 @@ import inputTextareaSecret from '~components/input/textarea-secret.directive';
|
|||||||
import launchTemplate from '~components/launchTemplateButton/launchTemplateButton.component';
|
import launchTemplate from '~components/launchTemplateButton/launchTemplateButton.component';
|
||||||
import layout from '~components/layout/layout.directive';
|
import layout from '~components/layout/layout.directive';
|
||||||
import list from '~components/list/list.directive';
|
import list from '~components/list/list.directive';
|
||||||
|
import lookupList from '~components/lookup-list/lookup-list.component';
|
||||||
import modal from '~components/modal/modal.directive';
|
import modal from '~components/modal/modal.directive';
|
||||||
import panel from '~components/panel/panel.directive';
|
import panel from '~components/panel/panel.directive';
|
||||||
import panelBody from '~components/panel/body.directive';
|
import panelBody from '~components/panel/body.directive';
|
||||||
@@ -53,8 +56,10 @@ angular
|
|||||||
atCodeMirror
|
atCodeMirror
|
||||||
])
|
])
|
||||||
.directive('atActionGroup', actionGroup)
|
.directive('atActionGroup', actionGroup)
|
||||||
|
.component('atActionButton', actionButton)
|
||||||
.directive('atDivider', divider)
|
.directive('atDivider', divider)
|
||||||
.directive('atDynamicSelect', dynamicSelect)
|
.directive('atDynamicSelect', dynamicSelect)
|
||||||
|
.component('atEasyModal', easyModal)
|
||||||
.directive('atForm', form)
|
.directive('atForm', form)
|
||||||
.directive('atFormAction', formAction)
|
.directive('atFormAction', formAction)
|
||||||
.directive('atInputCheckbox', inputCheckbox)
|
.directive('atInputCheckbox', inputCheckbox)
|
||||||
@@ -72,6 +77,7 @@ angular
|
|||||||
.component('atLaunchTemplate', launchTemplate)
|
.component('atLaunchTemplate', launchTemplate)
|
||||||
.directive('atLayout', layout)
|
.directive('atLayout', layout)
|
||||||
.directive('atList', list)
|
.directive('atList', list)
|
||||||
|
.component('atLookupList', lookupList)
|
||||||
.directive('atListToolbar', toolbar)
|
.directive('atListToolbar', toolbar)
|
||||||
.component('atRelaunch', relaunch)
|
.component('atRelaunch', relaunch)
|
||||||
.directive('atRow', row)
|
.directive('atRow', row)
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
const templateUrl = require('~components/lookup-list/lookup-list.partial.html');
|
||||||
|
|
||||||
|
function LookupListController (GetBasePath, Rest, strings) {
|
||||||
|
const vm = this || {};
|
||||||
|
|
||||||
|
vm.strings = strings;
|
||||||
|
|
||||||
|
vm.$onInit = () => {
|
||||||
|
const params = vm.baseParams;
|
||||||
|
setBaseParams(params);
|
||||||
|
setData({ results: [], count: 0 });
|
||||||
|
|
||||||
|
const resultsFilter = vm.resultsFilter || (data => data);
|
||||||
|
Rest.setUrl(GetBasePath(`${vm.resourceName}s`));
|
||||||
|
Rest.get({ params })
|
||||||
|
.then(({ data }) => {
|
||||||
|
setData(resultsFilter(data));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function setData ({ results, count }) {
|
||||||
|
vm.dataset = { results, count };
|
||||||
|
vm.collection = vm.dataset.results;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBaseParams (params) {
|
||||||
|
vm.list = { name: vm.resourceName, iterator: vm.resourceName };
|
||||||
|
vm.defaultParams = params;
|
||||||
|
vm.queryset = params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LookupListController.$inject = [
|
||||||
|
'GetBasePath',
|
||||||
|
'Rest',
|
||||||
|
'ComponentsStrings',
|
||||||
|
];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
templateUrl,
|
||||||
|
controller: LookupListController,
|
||||||
|
controllerAs: 'vm',
|
||||||
|
bindings: {
|
||||||
|
onSelect: '=',
|
||||||
|
onRowClick: '=',
|
||||||
|
selectedId: '=',
|
||||||
|
resourceName: '@',
|
||||||
|
baseParams: '=',
|
||||||
|
resultsFilter: '=',
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
<div>
|
||||||
|
<div ng-hide="vm.collection.length === 0 && (searchTags | isEmpty)">
|
||||||
|
<div style="min-height: 20px"></div>
|
||||||
|
<smart-search
|
||||||
|
django-model="{{ vm.resourceName + 's' }}"
|
||||||
|
base-path="{{ vm.resourceName + 's' }}"
|
||||||
|
iterator="{{ vm.list.iterator }}"
|
||||||
|
dataset="vm.dataset"
|
||||||
|
list="vm.list"
|
||||||
|
collection="vm.collection"
|
||||||
|
default-params="vm.defaultParams"
|
||||||
|
query-set="vm.queryset"
|
||||||
|
search-tags="searchTags">
|
||||||
|
</smart-search>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="row"
|
||||||
|
ng-show="vm.collection.length === 0 && !(searchTags | isEmpty)"
|
||||||
|
>
|
||||||
|
<div class="col-lg-12 List-searchNoResults">
|
||||||
|
{{::vm.strings.get('NO_MATCH')}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="List-noItems"
|
||||||
|
ng-show="vm.collection.length === 0 && (searchTags | isEmpty)"
|
||||||
|
>
|
||||||
|
{{::vm.strings.get('NO_ITEMS')}}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="list-table-container"
|
||||||
|
ng-show="vm.collection.length > 0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
id="credential_input_source_table"
|
||||||
|
class="List-table"
|
||||||
|
is-extended="false"
|
||||||
|
>
|
||||||
|
<div class="List-lookupLayout List-tableHeaderRow">
|
||||||
|
<div></div>
|
||||||
|
<div class="d-flex h-100">
|
||||||
|
<div
|
||||||
|
base-path="{{ vm.resourceName + 's' }}"
|
||||||
|
collection="vm.collection"
|
||||||
|
dataset="vm.dataset"
|
||||||
|
column-sort=""
|
||||||
|
column-field="name"
|
||||||
|
column-iterator="{{ vm.list.iterator }}"
|
||||||
|
column-label="Name"
|
||||||
|
column-custom-class="List-tableCell col-md-4 col-sm-9 col-xs-9"
|
||||||
|
query-set="vm.queryset"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
id="{{ obj.id }}"
|
||||||
|
class="List-lookupLayout List-tableRow"
|
||||||
|
ng-repeat="obj in vm.collection"
|
||||||
|
>
|
||||||
|
<div class="List-centerEnd select-column">
|
||||||
|
<input type="radio"
|
||||||
|
name="check_{{ vm.resourceName }}_{{ obj.id }}"
|
||||||
|
ng-checked="obj.id === vm.selectedId"
|
||||||
|
ng-click="vm.onRowClick(obj)"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex h-100">
|
||||||
|
<div
|
||||||
|
class="List-tableCell name-column col-sm-12"
|
||||||
|
ng-click="vm.onRowClick(obj)"
|
||||||
|
>
|
||||||
|
{{ obj.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<paginate
|
||||||
|
base-path="{{ vm.resourceName + 's' }}"
|
||||||
|
iterator="{{ vm.list.iterator }}"
|
||||||
|
collection="vm.collection"
|
||||||
|
dataset="vm.dataset"
|
||||||
|
query-set="vm.queryset"
|
||||||
|
hide-view-per-page="true"
|
||||||
|
>
|
||||||
|
</paginate>
|
||||||
|
</div>
|
||||||
@@ -44,17 +44,6 @@
|
|||||||
height: 30px;
|
height: 30px;
|
||||||
min-width: 85px;
|
min-width: 85px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Prompt-infoButton{
|
|
||||||
.at-mixin-ButtonColor('at-color-info', 'at-color-default');
|
|
||||||
text-transform: uppercase;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding-left:15px;
|
|
||||||
padding-right: 15px;
|
|
||||||
height: 30px;
|
|
||||||
min-width: 85px;
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
.Prompt-defaultButton:hover{
|
.Prompt-defaultButton:hover{
|
||||||
background-color: @btn-bg-hov;
|
background-color: @btn-bg-hov;
|
||||||
color: @btn-txt;
|
color: @btn-txt;
|
||||||
|
|||||||
Reference in New Issue
Block a user