2017-09-20 12:30:53 +03:00
|
|
|
import React from 'react';
|
|
|
|
import paramCase from 'param-case';
|
2017-11-09 13:27:32 +02:00
|
|
|
import Value, { set } from 'react-redux-values';
|
2017-09-20 12:30:53 +03:00
|
|
|
import { compose, graphql } from 'react-apollo';
|
2017-11-09 13:27:32 +02:00
|
|
|
import { connect } from 'react-redux';
|
|
|
|
import { SubmissionError, reset, startSubmit, stopSubmit } from 'redux-form';
|
|
|
|
import ReduxForm from 'declarative-redux-form';
|
2017-09-20 12:30:53 +03:00
|
|
|
import find from 'lodash.find';
|
2017-11-09 13:27:32 +02:00
|
|
|
import sortBy from 'lodash.sortby';
|
2017-09-20 12:30:53 +03:00
|
|
|
import get from 'lodash.get';
|
|
|
|
|
2017-10-09 16:43:51 +03:00
|
|
|
import {
|
|
|
|
ViewContainer,
|
|
|
|
Title,
|
|
|
|
StatusLoader,
|
|
|
|
Message,
|
|
|
|
MessageDescription,
|
2017-11-09 13:27:32 +02:00
|
|
|
MessageTitle,
|
|
|
|
Button
|
2017-10-09 16:43:51 +03:00
|
|
|
} from 'joyent-ui-toolkit';
|
2017-09-20 12:30:53 +03:00
|
|
|
|
|
|
|
import GetMetadata from '@graphql/list-metadata.gql';
|
2017-11-09 13:27:32 +02:00
|
|
|
import UpdateMetadata from '@graphql/update-metadata.gql';
|
|
|
|
import DeleteMetadata from '@graphql/delete-metadata.gql';
|
2017-09-20 12:30:53 +03:00
|
|
|
import { KeyValue } from '@components/instances';
|
|
|
|
|
2017-11-09 13:27:32 +02:00
|
|
|
const METADATA_FORM_KEY = (name, field) => `instance-metadata-${name}-${field}`;
|
|
|
|
const CREATE_METADATA_FORM_KEY = name => `instance-create-metadata-${name}`;
|
2017-09-27 17:24:40 +03:00
|
|
|
|
2017-11-09 13:27:32 +02:00
|
|
|
const Metadata = ({
|
|
|
|
instance,
|
|
|
|
values = [],
|
|
|
|
loading,
|
|
|
|
error,
|
|
|
|
handleRemove,
|
|
|
|
handleClear,
|
|
|
|
handleUpdate,
|
|
|
|
handleCreate
|
|
|
|
}) => {
|
2017-09-20 12:30:53 +03:00
|
|
|
const _title = <Title>Metadata</Title>;
|
2017-10-04 20:27:55 +03:00
|
|
|
const _loading = !(loading && !values.length) ? null : <StatusLoader />;
|
2017-09-20 12:30:53 +03:00
|
|
|
|
2017-11-09 13:27:32 +02:00
|
|
|
// metadata items forms
|
|
|
|
const _metadata =
|
|
|
|
!_loading &&
|
|
|
|
values.map(({ form, initialValues }, i) => (
|
|
|
|
<Value name={`${form}-expanded`} key={form}>
|
|
|
|
{({ value: expanded, onValueChange }) => (
|
|
|
|
<ReduxForm
|
|
|
|
form={form}
|
|
|
|
initialValues={initialValues}
|
|
|
|
onSubmit={newValues => handleUpdate(newValues, form)}
|
|
|
|
destroyOnUnmount
|
|
|
|
id={form}
|
|
|
|
onClear={() => handleClear(form)}
|
|
|
|
onToggleExpanded={() => onValueChange(!expanded)}
|
|
|
|
onRemove={() => handleRemove(form)}
|
|
|
|
label="metadata"
|
|
|
|
last={values.length - 1 === i}
|
|
|
|
first={i === 0}
|
|
|
|
expanded={expanded}
|
|
|
|
textarea
|
|
|
|
>
|
|
|
|
{KeyValue}
|
|
|
|
</ReduxForm>
|
|
|
|
)}
|
|
|
|
</Value>
|
|
|
|
));
|
|
|
|
|
|
|
|
// create metadata form
|
|
|
|
const _addKey = instance && CREATE_METADATA_FORM_KEY(instance.name);
|
|
|
|
const _add = _metadata &&
|
|
|
|
_addKey && (
|
|
|
|
<Value name={`${_addKey}-expanded`}>
|
|
|
|
{({ value: expanded, onValueChange }) =>
|
|
|
|
!expanded ? (
|
|
|
|
<Button
|
|
|
|
type="button"
|
|
|
|
onClick={() => onValueChange(!expanded)}
|
|
|
|
secondary
|
|
|
|
>
|
|
|
|
Add metadata
|
|
|
|
</Button>
|
|
|
|
) : (
|
|
|
|
<ReduxForm
|
|
|
|
form={_addKey}
|
|
|
|
onSubmit={handleCreate}
|
|
|
|
id={_addKey}
|
|
|
|
onClear={() => handleClear(_addKey)}
|
|
|
|
onToggleExpanded={() => onValueChange(!expanded)}
|
|
|
|
onRemove={() => handleRemove(_addKey)}
|
|
|
|
expanded={expanded}
|
|
|
|
label="metadata"
|
|
|
|
create
|
|
|
|
textarea
|
|
|
|
>
|
|
|
|
{KeyValue}
|
|
|
|
</ReduxForm>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
</Value>
|
|
|
|
);
|
2017-09-20 12:30:53 +03:00
|
|
|
|
2017-11-09 13:27:32 +02:00
|
|
|
// fetching error
|
2017-10-04 20:27:55 +03:00
|
|
|
const _error =
|
|
|
|
error && !values.length && !_loading ? (
|
2017-10-09 16:43:51 +03:00
|
|
|
<Message error>
|
|
|
|
<MessageTitle>Ooops!</MessageTitle>
|
|
|
|
<MessageDescription>
|
|
|
|
An error occurred while loading your instance metadata
|
|
|
|
</MessageDescription>
|
|
|
|
</Message>
|
2017-10-04 20:27:55 +03:00
|
|
|
) : null;
|
2017-09-20 12:30:53 +03:00
|
|
|
|
|
|
|
return (
|
|
|
|
<ViewContainer center={Boolean(_loading)} main>
|
|
|
|
{_title}
|
|
|
|
{_loading}
|
|
|
|
{_error}
|
2017-09-27 17:24:40 +03:00
|
|
|
{_metadata}
|
2017-11-09 13:27:32 +02:00
|
|
|
{_add}
|
2017-09-20 12:30:53 +03:00
|
|
|
</ViewContainer>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default compose(
|
2017-11-09 13:27:32 +02:00
|
|
|
graphql(UpdateMetadata, { name: 'updateMetadata' }),
|
|
|
|
graphql(DeleteMetadata, { name: 'deleteMetadata' }),
|
2017-09-20 12:30:53 +03:00
|
|
|
graphql(GetMetadata, {
|
|
|
|
options: ({ match }) => ({
|
|
|
|
pollInterval: 1000,
|
|
|
|
variables: {
|
|
|
|
name: get(match, 'params.instance')
|
|
|
|
}
|
|
|
|
}),
|
2017-11-09 13:27:32 +02:00
|
|
|
props: ({ data: { loading, error, variables, refetch, ...rest } }) => {
|
|
|
|
const { name } = variables;
|
|
|
|
|
|
|
|
const instance = find(get(rest, 'machines', []), ['name', name]);
|
|
|
|
const metadata = get(instance, 'metadata', []);
|
2017-09-27 17:24:40 +03:00
|
|
|
|
2017-11-09 13:27:32 +02:00
|
|
|
const values = sortBy(metadata, 'name').map(({ name, value }) => {
|
|
|
|
const field = paramCase(name);
|
|
|
|
const form = METADATA_FORM_KEY(name, field);
|
2017-09-27 17:24:40 +03:00
|
|
|
|
|
|
|
return {
|
2017-11-09 13:27:32 +02:00
|
|
|
form,
|
|
|
|
initialValues: {
|
|
|
|
name,
|
|
|
|
value
|
2017-09-27 17:24:40 +03:00
|
|
|
}
|
|
|
|
};
|
2017-11-09 13:27:32 +02:00
|
|
|
});
|
2017-09-27 17:24:40 +03:00
|
|
|
|
2017-11-09 13:27:32 +02:00
|
|
|
return {
|
|
|
|
values,
|
|
|
|
instance,
|
|
|
|
loading,
|
|
|
|
error,
|
|
|
|
refetch
|
|
|
|
};
|
2017-09-27 17:24:40 +03:00
|
|
|
}
|
2017-11-09 13:27:32 +02:00
|
|
|
}),
|
|
|
|
connect(null, (dispatch, ownProps) => {
|
|
|
|
const {
|
|
|
|
instance,
|
|
|
|
values,
|
|
|
|
refetch,
|
|
|
|
updateMetadata,
|
|
|
|
deleteMetadata
|
|
|
|
} = ownProps;
|
|
|
|
|
|
|
|
return {
|
|
|
|
// reset sets values to initialValues
|
|
|
|
handleClear: form => dispatch(reset(form)),
|
|
|
|
handleRemove: form =>
|
|
|
|
Promise.resolve(
|
|
|
|
// set removing=true (so that we can have a specific removing spinner)
|
|
|
|
// because remove button is not a submit button, we have to manually flip that flag
|
|
|
|
dispatch([
|
|
|
|
set({ name: `${form}-removing`, value: true }),
|
|
|
|
startSubmit(form)
|
|
|
|
])
|
|
|
|
)
|
|
|
|
.then(() =>
|
|
|
|
// call mutation. get key from values' initialValues
|
|
|
|
deleteMetadata({
|
|
|
|
variables: {
|
|
|
|
id: instance.id,
|
|
|
|
name: get(find(values, ['form', form]), 'initialValues.name')
|
|
|
|
}
|
|
|
|
})
|
|
|
|
)
|
|
|
|
// fetch metadata again
|
|
|
|
.then(() => refetch())
|
|
|
|
// we only flip removing and submitting when there is an error.
|
|
|
|
// the reason for that is that metadata is updated asyncronously and
|
|
|
|
// it takes longer to have an efect than the mutation
|
|
|
|
.catch(error =>
|
|
|
|
dispatch([
|
|
|
|
set({ name: `${form}-removing`, value: false }),
|
|
|
|
stopSubmit(form, {
|
|
|
|
_error: error.graphQLErrors
|
|
|
|
.map(({ message }) => message)
|
|
|
|
.join('\n')
|
|
|
|
})
|
|
|
|
])
|
|
|
|
),
|
|
|
|
handleUpdate: ({ name, value }, form) =>
|
|
|
|
// call mutation. delete existing metadata, add new
|
|
|
|
Promise.all([
|
|
|
|
deleteMetadata({
|
|
|
|
variables: {
|
|
|
|
id: instance.id,
|
|
|
|
name: get(find(values, ['form', form]), 'initialValues.name')
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
updateMetadata({
|
|
|
|
variables: {
|
|
|
|
id: instance.id,
|
|
|
|
metadata: [{ name, value }]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
])
|
|
|
|
// fetch metadata again
|
|
|
|
.then(() => refetch())
|
|
|
|
// submit is flipped once the promise is resolved
|
|
|
|
.catch(error => {
|
|
|
|
throw new SubmissionError({
|
|
|
|
_error: error.graphQLErrors
|
|
|
|
.map(({ message }) => message)
|
|
|
|
.join('\n')
|
|
|
|
});
|
|
|
|
}),
|
|
|
|
handleCreate: ({ name, value }) =>
|
|
|
|
// call mutation
|
|
|
|
updateMetadata({
|
|
|
|
variables: {
|
|
|
|
id: instance.id,
|
|
|
|
metadata: [{ name, value }]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
// fetch metadata again
|
|
|
|
.then(() => refetch())
|
|
|
|
// reset create new metadata form
|
|
|
|
.then(() => dispatch(reset(CREATE_METADATA_FORM_KEY(instance.name))))
|
|
|
|
// submit is flipped once the promise is resolved
|
|
|
|
.catch(error => {
|
|
|
|
throw new SubmissionError({
|
|
|
|
_error: error.graphQLErrors
|
|
|
|
.map(({ message }) => message)
|
|
|
|
.join('\n')
|
|
|
|
});
|
|
|
|
})
|
|
|
|
};
|
2017-09-20 12:30:53 +03:00
|
|
|
})
|
|
|
|
)(Metadata);
|