Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updated to support json input; aws ca bundle; hiding columns: #203

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 42 additions & 9 deletions app/clients/aws.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,57 @@
import AWS from 'aws-sdk';
import localStore from '../store/localStore';
import { Agent as httpsAgent } from 'https';
import { readFileSync as fsReadFileSync } from 'fs';
import localStore, { availableSettings } from '../store/localStore';

process.env.AWS_SDK_LOAD_CONFIG = true;

const credentialProvider = new AWS.CredentialProviderChain([
() => new AWS.EnvironmentCredentials('AWS'),
() => new AWS.EnvironmentCredentials('AMAZON'),
() =>
new AWS.SharedIniFileCredentials({ profile: localStore.get('profile') }),
() => new AWS.ProcessCredentials({ profile: localStore.get('profile') })
new AWS.SharedIniFileCredentials({
profile: localStore.get(availableSettings.profile)
}),
() =>
new AWS.ProcessCredentials({
profile: localStore.get(availableSettings.profile)
})
// TODO: Add more credential providers as needed. https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CredentialProviderChain.html#providers-property
]);

const ssm = region =>
new AWS.SSM({ region, credentials: null, credentialProvider });
const kms = region =>
new AWS.KMS({ region, credentials: null, credentialProvider });
const caBundlePath = localStore.get(availableSettings.caBundlePath);

const ssm = region => {
const awsConfig: AWS.SSM.ClientConfiguration = {
region,
credentials: null,
credentialProvider
};
if (caBundlePath) {
awsConfig.httpOptions = {
// eslint-disable-next-line new-cap
agent: new httpsAgent({ ca: fsReadFileSync(caBundlePath) })
};
}
return new AWS.SSM(awsConfig);
};
const kms = region => {
const awsConfig: AWS.KMS.ClientConfiguration = {
region,
credentials: null,
credentialProvider
};
if (caBundlePath) {
awsConfig.httpOptions = {
// eslint-disable-next-line new-cap
agent: new httpsAgent({ ca: fsReadFileSync(caBundlePath) })
};
}
return new AWS.KMS(awsConfig);
};

const getSSM = () => ssm(localStore.get('ssmRegion'));
const getKMS = () => kms(localStore.get('kmsRegion'));
const getSSM = () => ssm(localStore.get(availableSettings.ssmRegion));
const getKMS = () => kms(localStore.get(availableSettings.kmsRegion));

export default {
getSSM,
Expand Down
69 changes: 53 additions & 16 deletions app/components/CreationForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ import {

import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import JSONInput from 'react-json-editor-ajrm';
import locale from 'react-json-editor-ajrm/locale/en';
import { formShape, formDataShape } from './formDataShape.propType';
import {
actions as parameterActions,
selectors as parameterSelectors
} from '../ducks/parameters';
import { valueIsJson } from '../utils/valueIsJson';

const { Option } = Select;
const { TextArea } = Input;
Expand Down Expand Up @@ -79,14 +82,16 @@ class CreationForm extends React.Component {
} = this.props;
const { validateFields } = form;
validateFields((validationErr, values) => {
const params = values;
params.value = values.value.json || values.value; // handle json editor
if (!validationErr) {
const { creationType } = this.state;
const creationFn =
creationType === 'service'
? createServiceParameters
: createGenericParameter;
this.setState({ creationState: ENTITY_STATUS.loading });
creationFn(values, !!editFlow)
creationFn(params, !!editFlow)
.then(res => {
notification.success({
message: editFlow
Expand All @@ -113,6 +118,21 @@ class CreationForm extends React.Component {
this.setState({ creationType: e.target.value });
};

validateValue = async (rule, value, callback) => {
if (!value || !value.plainText || !value.plainText.length === 0) {
return callback('Value is required');
}
if (value.error && value.error.reason) {
return callback(value.error.reason);
}
if (value.plainText.length > 4096) {
return callback(
'The maximum allowed size of 4096 characters (assuming all chars are one byte).'
);
}
return callback();
};

render() {
const {
form,
Expand All @@ -130,6 +150,8 @@ class CreationForm extends React.Component {
wrapperCol: { span: 14 }
};

let useJsonInput = valueIsJson(initialFormData.value);

return (
<div>
{!editFlow && (
Expand Down Expand Up @@ -247,8 +269,8 @@ class CreationForm extends React.Component {
<Radio.Group>
<Radio value="String">String</Radio>
<Radio value="SecureString">SecureString</Radio>
<Radio value="StringList" disabled>
StringList (Not supported yet)
<Radio value="StringList">
StringList (comma separated string, no spaces)
</Radio>
</Radio.Group>
)}
Expand Down Expand Up @@ -285,19 +307,34 @@ class CreationForm extends React.Component {
)}
</Form.Item>
)}
<Form.Item label="Value">
{getFieldDecorator('value', {
initialValue: initialFormData.value,
rules: [
{ required: true, message: 'Please provide the value.' },
{
max: 4096,
message: 'The maximum allowed length is 4096 characters.'
}
]
})(<TextArea rows={4} autosize={{ minRows: 2, maxRows: 8 }} />)}
</Form.Item>

{useJsonInput && (
<Form.Item label="Value">
{getFieldDecorator('value', {
initialValue: initialFormData.value,
rules: [{ validator: this.validateValue }]
})(
<JSONInput
placeholder={JSON.parse(initialFormData.value)}
theme="light_mitsuketa_tribute"
locale={locale}
/>
)}
</Form.Item>
)}
{!useJsonInput && (
<Form.Item label="Value">
{getFieldDecorator('value', {
initialValue: initialFormData.value,
rules: [
{ required: true, message: 'Please provide the value.' },
{
max: 4096,
message: 'The maximum allowed length is 4096 characters.'
}
]
})(<TextArea rows={4} autosize={{ minRows: 2, maxRows: 8 }} />)}
</Form.Item>
)}
{!editFlow &&
creationType === 'service' &&
form.getFieldValue('serviceName') &&
Expand Down
141 changes: 90 additions & 51 deletions app/components/Home.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
Typography
} from 'antd';
import PropTypes from 'prop-types';

import JSONInput from 'react-json-editor-ajrm';
import locale from 'react-json-editor-ajrm/locale/en';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import ReactTimeAgo from 'react-time-ago';
Expand All @@ -25,6 +26,7 @@ import CreationFormButton from './CreationFormButton';
import DeleteButton from './DeleteButton';
import localStore, { availableSettings } from '../store/localStore';
import SettingsButton from './SettingsButton';
import { valueIsJson } from '../utils/valueIsJson';

const { TreeNode } = Tree;
const { Paragraph } = Typography;
Expand Down Expand Up @@ -121,6 +123,18 @@ class Home extends Component {
})
: parameters;

const descWidth = 250;
const typeWidth = 120;
const modifiedWidth = 175;
let valueWidth = 300;
valueWidth += localStore.get(availableSettings.hideDescription)
? descWidth
: 0;
valueWidth += localStore.get(availableSettings.hideLastModifiedDate)
? modifiedWidth
: 0;
valueWidth += localStore.get(availableSettings.hideType) ? typeWidth : 0;

const columns = [
{
title: 'Name',
Expand Down Expand Up @@ -164,24 +178,41 @@ class Home extends Component {
);
}
},

{
title: 'Value',
dataIndex: 'Value',
key: 'Value',
width: 300,
width: valueWidth,

render: value => (
<Paragraph style={{ wordBreak: 'break-word' }} copyable>
{value}
</Paragraph>
)
},
{
render: value => {
let useJsonInput = valueIsJson(value);
if (useJsonInput) {
return (
<JSONInput
placeholder={JSON.parse(value)}
viewOnly
theme="light_mitsuketa_tribute"
locale={locale}
height="150px"
width={`${valueWidth - 25}px`}
confirmGood={false}
/>
);
}
return (
<Paragraph style={{ wordBreak: 'break-word' }} copyable>
{value}
</Paragraph>
);
}
}
];
if (!localStore.get(availableSettings.hideDescription)) {
columns.push({
title: 'Description',
dataIndex: 'Description',
key: 'Description',
width: 250,
width: descWidth,
render: value => {
return value ? (
<Paragraph style={{ wordBreak: 'break-word' }} copyable>
Expand All @@ -191,60 +222,68 @@ class Home extends Component {
<i>No Description</i>
);
}
},
{
});
}

if (!localStore.get(availableSettings.hideType)) {
columns.push({
title: 'Type',
dataIndex: 'Type',
key: 'Type',
width: 120
},
{
width: typeWidth
});
}

if (!localStore.get(availableSettings.hideLastModifiedDate)) {
columns.push({
title: 'LastModifiedDate',
dataIndex: 'LastModifiedDate',
key: 'LastModifiedDate',
width: modifiedWidth,
sorter: (a, b) =>
new Date(a.LastModifiedDate) - new Date(b.LastModifiedDate),
render: date => (
<span>
{<ReactTimeAgo date={date} />} ({date.toLocaleString()})
</span>
)
},
{
title: 'Actions',
key: 'Actions',
width: 100,
fixed: 'right',
render: e => {
const currentData = {
name: e.Name,
description: e.Description,
type: e.Type,
value: e.Value,
kmsKey: e.KeyId
};
const { deleteParameter } = this.props;
return (
<Layout>
<CreationFormButton
buttonText="Edit"
modalText="Edit"
initialFormData={currentData}
resetOnClose
editFlow
/>
<CreationFormButton
buttonType="primary"
buttonText="Duplicate"
initialFormData={currentData}
resetOnClose
/>
<DeleteButton name={e.Name} onDelete={deleteParameter} />
</Layout>
);
}
});
}

columns.push({
title: 'Actions',
key: 'Actions',
width: 125,
fixed: 'right',
render: e => {
const currentData = {
name: e.Name,
description: e.Description,
type: e.Type,
value: e.Value,
kmsKey: e.KeyId
};
const { deleteParameter } = this.props;
return (
<Layout>
<CreationFormButton
buttonText="Edit"
modalText="Edit"
initialFormData={currentData}
resetOnClose
editFlow
/>
<CreationFormButton
buttonType="primary"
buttonText="Duplicate"
initialFormData={currentData}
resetOnClose
/>
<DeleteButton name={e.Name} onDelete={deleteParameter} />
</Layout>
);
}
];
});

return (
<Layout>
Expand Down
Loading