From 1700526125b71a4d1cb3fb02504bd0fc9c90e593 Mon Sep 17 00:00:00 2001 From: Phil Renaud Date: Mon, 15 Aug 2022 17:24:34 -0400 Subject: [PATCH] Handle conflict swith a cas qp on save and create (#14100) * Handle conflict swith a cas qp on save and create * Notify error and give them refresh or overwrite options * Merge conflict missed, resolved * Mirage fixture * Integration test * Bracket closed (thx jai) * Adjust tests to account for number of variables with auto-conflicter --- ui/app/adapters/variable.js | 27 ++++++++++- ui/app/components/secure-variable-form.hbs | 21 ++++++++ ui/app/components/secure-variable-form.js | 48 +++++++++++++++---- .../styles/components/secure-variables.scss | 22 +++++++++ ui/app/utils/notify-conflict.js | 14 ++++++ ui/mirage/config.js | 29 ++++++++--- ui/mirage/scenarios/default.js | 10 ++++ ui/tests/acceptance/secure-variables-test.js | 45 +++++++++++++++-- ui/tests/pages/variables.js | 3 ++ 9 files changed, 198 insertions(+), 21 deletions(-) create mode 100644 ui/app/utils/notify-conflict.js diff --git a/ui/app/adapters/variable.js b/ui/app/adapters/variable.js index b6db2ea37..f6b417555 100644 --- a/ui/app/adapters/variable.js +++ b/ui/app/adapters/variable.js @@ -1,6 +1,7 @@ import ApplicationAdapter from './application'; import { pluralize } from 'ember-inflector'; import classic from 'ember-classic-decorator'; +import { ConflictError } from '@ember-data/adapter/error'; @classic export default class VariableAdapter extends ApplicationAdapter { @@ -11,7 +12,8 @@ export default class VariableAdapter extends ApplicationAdapter { createRecord(_store, type, snapshot) { let data = this.serialize(snapshot); let baseUrl = this.buildURL(type.modelName, data.ID); - return this.ajax(baseUrl, 'PUT', { data }); + const checkAndSetValue = snapshot?.attr('modifyIndex') || 0; + return this.ajax(`${baseUrl}&cas=${checkAndSetValue}`, 'PUT', { data }); } urlForFindAll(modelName) { @@ -33,7 +35,12 @@ export default class VariableAdapter extends ApplicationAdapter { urlForUpdateRecord(identifier, modelName, snapshot) { const { id } = _extractIDAndNamespace(identifier, snapshot); let baseUrl = this.buildURL(modelName, id); - return `${baseUrl}`; + if (snapshot?.adapterOptions?.overwrite) { + return `${baseUrl}`; + } else { + const checkAndSetValue = snapshot?.attr('modifyIndex') || 0; + return `${baseUrl}?cas=${checkAndSetValue}`; + } } urlForDeleteRecord(identifier, modelName, snapshot) { @@ -41,6 +48,15 @@ export default class VariableAdapter extends ApplicationAdapter { const baseUrl = this.buildURL(modelName, id); return `${baseUrl}?namespace=${namespace}`; } + + handleResponse(status, _, payload) { + if (status === 409) { + return new ConflictError([ + { detail: _normalizeConflictErrorObject(payload), status: 409 }, + ]); + } + return super.handleResponse(...arguments); + } } function _extractIDAndNamespace(identifier, snapshot) { @@ -51,3 +67,10 @@ function _extractIDAndNamespace(identifier, snapshot) { id, }; } + +function _normalizeConflictErrorObject(conflictingVariable) { + return { + modifyTime: Math.floor(conflictingVariable.ModifyTime / 1000000), + items: conflictingVariable.Items, + }; +} diff --git a/ui/app/components/secure-variable-form.hbs b/ui/app/components/secure-variable-form.hbs index 8c4449957..e50e9fe73 100644 --- a/ui/app/components/secure-variable-form.hbs +++ b/ui/app/components/secure-variable-form.hbs @@ -9,6 +9,27 @@ {{/if}} + {{#if this.hasConflict}} +
+

Heads up! Your Secure Variable has a conflict.

+

This might be because someone else tried saving in the time since you've had it open.

+ {{#if this.conflictingVariable.modifyTime}} + + {{moment-from-now this.conflictingVariable.modifyTime}} + + {{/if}} + {{#if this.conflictingVariable.items}} +
{{stringify-object this.conflictingVariable.items whitespace=2}}
+ {{else}} +

Your ACL token limits your ability to see further details about the conflicting variable.

+ {{/if}} +
+ + +
+
+ {{/if}} +