Skip to content

Commit

Permalink
Template Instance update diffs and bug fixes
Browse files Browse the repository at this point in the history
- Add ability from the UI to see the diff of a template instance before performing update
- Fix bug with Template Item editors not saving their modified state in the UI
 - Re-add back session auth - it needs to be after Token auth otherwise it screws up the JS UI
- Add functionality to modal to load data when it opens
- Remove debug logging
  • Loading branch information
Kyle Rockman committed Sep 17, 2017
1 parent e7b68ae commit 1c87f60
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 13 deletions.
1 change: 0 additions & 1 deletion estate/assets/js/api/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export function error(message) {
export function handleResponseError(err) {
if (err.response || err.errors) {
var data = err.response.data
console.log(data)
var message = ""
if (isArray(data.errors)) {
each(data.errors, (item) => {
Expand Down
11 changes: 11 additions & 0 deletions estate/assets/js/api/terraform.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@ export function updateTemplateOfTemplateInstance(id) {
return req
}

export function diffTemplateInstance(id) {
const req = axios.get(`/api/v1/terraform/templateinstance/${id}/diff_latest/`)
req.then((res) => {
dispatch({
type: "LOAD_TEMPLATE_INSTANCE_DIFF",
payload: res.data
})
}, messages.handleResponseError)
return req
}

export function removeTemplateFromNamespace(slug, id) {
const req = axios.delete(`/api/v1/terraform/templateinstance/${id}/`)
req.then(() => {
Expand Down
27 changes: 27 additions & 0 deletions estate/assets/js/components/DiffModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from "react"
import Diff from "react-diff"
import Modal from "./Modal"


export default class ConfirmModal extends React.Component {
getResult() {
return {}
}
render() {
return(
<Modal
className={this.props.className}
buttonText={this.props.buttonText}
titleText={this.props.titleText}
tooltipText={this.props.tooltipText}
tooltipDelay={this.props.tooltipDelay}
disabled={this.props.disabled}
getResult={this.getResult.bind(this)}
performLoad={this.props.load}
performSubmit={this.props.callback}
>
<Diff inputA={this.props.diff_old || ""} inputB={this.props.diff_new || ""} type={this.props.diff_type || "chars"} />
</Modal>
)
}
}
2 changes: 2 additions & 0 deletions estate/assets/js/components/Modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class Modal extends React.Component {
this.closeModal()
}
openModal() {
if (this.props.performLoad)
this.props.performLoad()
if (!this.props.disabled)
this.setState({modalIsOpen: true})
}
Expand Down
22 changes: 17 additions & 5 deletions estate/assets/js/components/TerraformNamespaceItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ReactTooltip from "react-tooltip"
import Ansi from "ansi-to-react"
import Editor from "./Editor"
import ConfirmModal from "./ConfirmModal"
import DiffModal from "./DiffModal"
import TerraformNamespaceAddFileModal from "./TerraformNamespaceAddFileModal"
import TerraformTemplateRenderer from "./TerraformTemplateRenderer"
import * as terraform from "../api/terraform"
Expand Down Expand Up @@ -297,12 +298,21 @@ class TerraformNamespaceItem extends React.Component {
})
return elements
}
createTemplateUpdateButton(id) {
createTemplateUpdateButton(locked, templateInstance) {
if (locked)
return null
if (!templateInstance.is_outdated)
return null
var id = templateInstance.pk
return (
<ConfirmModal className="flashing text-danger glyphicon glyphicon-exclamation-sign"
<DiffModal className="flashing text-danger glyphicon glyphicon-exclamation-sign"
buttonText=" "
titleText="Update to latest template version?"
tooltipText="Click to update to latest version"
diff_old={JSON.stringify(this.props.diffTemplateOld, null, 2)}
diff_new={JSON.stringify(this.props.diffTemplateNew, null, 2)}
diff_type="json"
load={this.props.diffTemplateInstance.bind(null, id)}
callback={this.props.updateTemplateOfTemplateInstance.bind(null, id)} />
)
}
Expand All @@ -326,17 +336,16 @@ class TerraformNamespaceItem extends React.Component {
<div className="col-xs-12">
<h1 className="page-header">
{templateInstance.title}
{locked ? null :
<div className="pull-right">
{ templateInstance.nextDisable ?
<div className="btn btn-success btn-inline" onClick={this.onTemplateToggleDisable.bind(this, index, false)}>Enable</div>
:
<div className="btn btn-danger btn-inline" onClick={this.onTemplateToggleDisable.bind(this, index, true)}>Disable</div>
}
{/*<div className="btn btn-default btn-inline">Update</div>*/}
<span> [ {templateInstance.template.title} : {templateInstance.template.version} { templateInstance.is_outdated ? this.createTemplateUpdateButton.bind(this)(templateInstance.pk) : "" } ]</span>
<span> [ {templateInstance.template.title} : {templateInstance.template.version} { this.createTemplateUpdateButton.bind(this)(locked, templateInstance) } ]
</span>
</div>
}
</h1>
</div>
<div className="col-xs-12">
Expand Down Expand Up @@ -635,6 +644,9 @@ const mapStateToProps = (state, ownProps) => {
updateFile: terraform.updateFile,
updateTemplateInstance: terraform.updateTemplateInstance,
updateTemplateOfTemplateInstance: terraform.updateTemplateOfTemplateInstance,
diffTemplateInstance: terraform.diffTemplateInstance,
diffTemplateOld: state.terraform.diffTemplateOld,
diffTemplateNew: state.terraform.diffTemplateNew,
planOutput: state.terraform.planOutput,
applyOutput: state.terraform.applyOutput,
stateObject: state.terraform.stateObject,
Expand Down
8 changes: 4 additions & 4 deletions estate/assets/js/components/TerraformTemplateItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,20 +186,20 @@ class TerraformTemplateItem extends React.Component {
<h1 className="page-header">Edit <span className="pull-right">{template.version}</span></h1>
<div className="row">
<div className="col-xs-12">
<Editor title="Description" options={{ mode: "markdown" }} content={template.description} onUpdateContent={this.onDescriptionChange.bind(this)} />
<Editor title="Description" options={{ mode: "markdown" }} content={this.state.description} initialContent={template.description} onUpdateContent={this.onDescriptionChange.bind(this)} />
</div>
</div>
<div className="row">
<div className="col-xs-6">
<Editor title={this.createJsonSchemaHeader()} content={template.json_schema || "{}"} onUpdateContent={this.onJsonSchemaChange.bind(this)} />
<Editor title={this.createJsonSchemaHeader()} content={this.state.json_schema} initialContent={template.json_schema || "{}"} onUpdateContent={this.onJsonSchemaChange.bind(this)} />
</div>
<div className="col-xs-6">
<Editor title="UI Schema" content={template.ui_schema || "{}"} onUpdateContent={this.onUISchemaChange.bind(this)} />
<Editor title="UI Schema" content={this.state.ui_schema} initialContent={template.ui_schema || "{}"} onUpdateContent={this.onUISchemaChange.bind(this)} />
</div>
</div>
<div className="row">
<div className="col-xs-12">
<Editor title={this.createTemplateBodyHeader()} options={options} content={template.body} onUpdateContent={this.onBodyChange.bind(this)} />
<Editor title={this.createTemplateBodyHeader()} options={options} content={this.state.body} initialContent={template.body} onUpdateContent={this.onBodyChange.bind(this)} />
</div>
</div>
</WithLoading>
Expand Down
8 changes: 8 additions & 0 deletions estate/assets/js/reducers/terraform.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ var initialState = {
files: [],
templates: [],
renderedTemplate: "{}",
diffTemplateOld: "{}",
diffTemplateNew: "{}",
templatesPage: 0,
templatesPages: 0,
}
Expand Down Expand Up @@ -145,4 +147,10 @@ export default createReducer(initialState, {
state = set(["renderedTemplate"])(action.payload)(state)
return state
},

["LOAD_TEMPLATE_INSTANCE_DIFF"]: (state, action) => {
state = set(["diffTemplateOld"])(action.payload.old)(state)
state = set(["diffTemplateNew"])(action.payload.new)(state)
return state
}
})
3 changes: 2 additions & 1 deletion estate/settings/drf.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def api_exception_handler(exc, context):
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
Expand Down Expand Up @@ -61,7 +62,7 @@ def api_exception_handler(exc, context):
},
'LOGIN_URL': 'rest_framework:login',
'LOGOUT_URL': 'rest_framework:logout',
'USE_SESSION_AUTH': False,
'USE_SESSION_AUTH': True,
'APIS_SORTER': 'alpha',
'JSON_EDITOR': True,
'VALIDATOR_URL': None
Expand Down
42 changes: 41 additions & 1 deletion estate/terraform/views/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from estate.core.views import HistoricalSerializer, HistoryMixin, OwnsNamespace
from estate.core import renderer


Namespace = apps.get_model("terraform.Namespace")
Template = apps.get_model("terraform.Template")
TemplateInstance = apps.get_model("terraform.TemplateInstance")
Expand Down Expand Up @@ -127,6 +128,11 @@ def update(self, instance, validated_data):
return super(TemplateInstanceSerializer, self).update(instance, validated_data)


class TemplateDiffSerializer(serializers.Serializer):
old = serializers.JSONField(read_only=True)
new = serializers.JSONField(read_only=True)


class TemplateInstanceFilter(filters.FilterSet):

class Meta:
Expand All @@ -136,13 +142,47 @@ class Meta:

class TemplateInstanceApiView(HistoryMixin, viewsets.ModelViewSet):
queryset = TemplateInstance.objects.all()
serializer_class = TemplateInstanceSerializer
serializers = {
"default": TemplateInstanceSerializer,
"diff_latest": TemplateDiffSerializer,
}
filter_class = TemplateInstanceFilter
permission_classes = (OwnsNamespace, )
filter_fields = ("slug",)
search_fields = ("title",)
ordering_fields = ("title", "created", "modified")

def get_serializer_class(self):
return self.serializers.get(self.action, self.serializers["default"])

@decorators.detail_route(methods=["GET"])
def diff_latest(self, request, *args, **kwargs):
instance = self.get_object()
latest_template = Template.all_objects.get(pk=instance.template.id)
old_data = {
"template_str": instance.template.body,
"inputs": instance.inputs,
"overrides": instance.overrides,
"disable": instance.disable,
}
new_data = {
"template_str": latest_template.body,
"inputs": instance.inputs,
"overrides": instance.overrides,
"disable": instance.disable,
}
try:
data = {
"old": renderer.render_template(**old_data),
"new": renderer.render_template(**new_data),
}
except Exception as e:
raise exceptions.APIException(str(e))
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
headers = self.get_success_headers(serializer.data)
return response.Response(data, status=status.HTTP_200_OK, headers=headers)

@decorators.detail_route(methods=["POST"])
def update_template(self, request, *args, **kwargs):
instance = self.get_object()
Expand Down
1 change: 0 additions & 1 deletion estate/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
url(r'^api/$', RedirectView.as_view(url='/api/swagger/')),
url(r'^api/schema/$', base_schema_view),
url(r'^api/swagger/', swagger_view),
url(r'^api/docs/', include_docs_urls(title=title), name="api-docs"),
url(r'^api/v1/terraform/', include('estate.terraform.urls')),
# Acts as a catchall for everything else and react router will take over
url(r'^', TemplateView.as_view(template_name='index.html')),
Expand Down

0 comments on commit 1c87f60

Please sign in to comment.