Show and allow modification of exclusive bit in gr-permission
Bug: Issue 8034 Change-Id: Ia66c8308c242e7f5a09dc796cbd02944a3b02aa6
This commit is contained in:
		@@ -16,6 +16,7 @@ limitations under the License.
 | 
			
		||||
 | 
			
		||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
 | 
			
		||||
<link rel="import" href="../../../behaviors/gr-access-behavior/gr-access-behavior.html">
 | 
			
		||||
<link rel="import" href="../../../bower_components/paper-toggle-button/paper-toggle-button.html">
 | 
			
		||||
<link rel="import" href="../../../styles/gr-form-styles.html">
 | 
			
		||||
<link rel="import" href="../../../styles/gr-menu-page-styles.html">
 | 
			
		||||
<link rel="import" href="../../../styles/shared-styles.html">
 | 
			
		||||
@@ -52,8 +53,13 @@ limitations under the License.
 | 
			
		||||
      #removeBtn {
 | 
			
		||||
        display: none;
 | 
			
		||||
      }
 | 
			
		||||
      .right {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
      }
 | 
			
		||||
      .editing #removeBtn {
 | 
			
		||||
        display: block;
 | 
			
		||||
        margin-left: 1.5em;
 | 
			
		||||
      }
 | 
			
		||||
      .editing #addRule {
 | 
			
		||||
        display: block;
 | 
			
		||||
@@ -82,10 +88,17 @@ limitations under the License.
 | 
			
		||||
      <div id="mainContainer">
 | 
			
		||||
        <div class="header">
 | 
			
		||||
          <span class="title">[[name]]</span>
 | 
			
		||||
          <div class="right">
 | 
			
		||||
            <paper-toggle-button
 | 
			
		||||
                id="exclusiveToggle"
 | 
			
		||||
                checked="{{permission.value.exclusive}}"
 | 
			
		||||
                on-change="_handleValueChange"
 | 
			
		||||
                disabled$="[[!editing]]"></paper-toggle-button>Exclusive
 | 
			
		||||
            <gr-button
 | 
			
		||||
                link
 | 
			
		||||
                id="removeBtn"
 | 
			
		||||
                on-tap="_handleRemovePermission">Remove</gr-button>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div><!-- end header -->
 | 
			
		||||
        <div class="rules">
 | 
			
		||||
          <template
 | 
			
		||||
 
 | 
			
		||||
@@ -58,6 +58,7 @@
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _originalExclusiveValue: Boolean,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
@@ -68,6 +69,26 @@
 | 
			
		||||
      '_handleRulesChanged(_rules.splices)',
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      'access-saved': '_handleAccessSaved',
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    ready() {
 | 
			
		||||
      this._setupValues();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setupValues() {
 | 
			
		||||
      if (!this.permission) { return; }
 | 
			
		||||
      this._originalExclusiveValue = !!this.permission.value.exclusive;
 | 
			
		||||
      Polymer.dom.flush();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _handleAccessSaved() {
 | 
			
		||||
      // Set a new 'original' value to keep track of after the value has been
 | 
			
		||||
      // saved.
 | 
			
		||||
      this._setupValues();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _handleEditingChanged(editing, editingOld) {
 | 
			
		||||
      // Ignore when editing gets set initially.
 | 
			
		||||
      if (!editingOld) { return; }
 | 
			
		||||
@@ -76,9 +97,19 @@
 | 
			
		||||
        this._deleted = false;
 | 
			
		||||
        this._groupFilter = '';
 | 
			
		||||
        this._rules = this._rules.filter(rule => !rule.value.added);
 | 
			
		||||
 | 
			
		||||
        // Restore exclusive bit to original.
 | 
			
		||||
        this.set(['permission', 'value', 'exclusive'],
 | 
			
		||||
            this._originalExclusiveValue);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _handleValueChange() {
 | 
			
		||||
      this.permission.value.modified = true;
 | 
			
		||||
      // Allows overall access page to know a change has been made.
 | 
			
		||||
      this.dispatchEvent(new CustomEvent('access-modified', {bubbles: true}));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _handleRemovePermission() {
 | 
			
		||||
      this._deleted = true;
 | 
			
		||||
      this.permission.value.deleted = true;
 | 
			
		||||
 
 | 
			
		||||
@@ -288,6 +288,7 @@ limitations under the License.
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        };
 | 
			
		||||
        element._setupValues();
 | 
			
		||||
        flushAsynchronousOperations();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
@@ -326,6 +327,32 @@ limitations under the License.
 | 
			
		||||
        assert.isFalse(element.$.permission.classList.contains('deleted'));
 | 
			
		||||
        assert.isFalse(element._deleted);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('modify a permission', () => {
 | 
			
		||||
        element.editing = true;
 | 
			
		||||
        element.name = 'Priority';
 | 
			
		||||
        element.section = 'refs/*';
 | 
			
		||||
 | 
			
		||||
        assert.isFalse(element._originalExclusiveValue);
 | 
			
		||||
        assert.isNotOk(element.permission.value.modified);
 | 
			
		||||
        MockInteractions.tap(element.$.exclusiveToggle);
 | 
			
		||||
        flushAsynchronousOperations();
 | 
			
		||||
        assert.isTrue(element.permission.value.exclusive);
 | 
			
		||||
        assert.isTrue(element.permission.value.modified);
 | 
			
		||||
        assert.isFalse(element._originalExclusiveValue);
 | 
			
		||||
        element.editing = false;
 | 
			
		||||
        assert.isFalse(element.permission.value.exclusive);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('_handleValueChange', () => {
 | 
			
		||||
        const modifiedHandler = sandbox.stub();
 | 
			
		||||
        element.permission = {value: {rules: {}}};
 | 
			
		||||
        element.addEventListener('access-modified', modifiedHandler);
 | 
			
		||||
        assert.isNotOk(element.permission.value.modified);
 | 
			
		||||
        element._handleValueChange();
 | 
			
		||||
        assert.isTrue(element.permission.value.modified);
 | 
			
		||||
        assert.isTrue(modifiedHandler.called);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -76,30 +76,6 @@ limitations under the License.
 | 
			
		||||
        'Code-Review': {},
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
    const repoAccessInput = {
 | 
			
		||||
      add: {
 | 
			
		||||
        'refs/*': {
 | 
			
		||||
          permissions: {
 | 
			
		||||
            owner: {
 | 
			
		||||
              rules: {
 | 
			
		||||
                123: {action: 'DENY', modified: true},
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      remove: {
 | 
			
		||||
        'refs/*': {
 | 
			
		||||
          permissions: {
 | 
			
		||||
            owner: {
 | 
			
		||||
              rules: {
 | 
			
		||||
                123: null,
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
    setup(() => {
 | 
			
		||||
      sandbox = sinon.sandbox.create();
 | 
			
		||||
      element = fixture('basic');
 | 
			
		||||
@@ -126,14 +102,13 @@ limitations under the License.
 | 
			
		||||
          name: 'Create Account',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      const accessStub = sandbox.stub(element.$.restAPI,
 | 
			
		||||
          'getRepoAccessRights');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      accessStub.withArgs('New Repo').returns(Promise.resolve(accessRes));
 | 
			
		||||
      accessStub.withArgs('New Repo').returns(
 | 
			
		||||
          Promise.resolve(JSON.parse(JSON.stringify(accessRes))));
 | 
			
		||||
      accessStub.withArgs('Another New Repo')
 | 
			
		||||
          .returns(Promise.resolve(accessRes2));
 | 
			
		||||
          .returns(Promise.resolve(JSON.parse(JSON.stringify(accessRes2))));
 | 
			
		||||
      const capabilitiesStub = sandbox.stub(element.$.restAPI,
 | 
			
		||||
          'getCapabilities');
 | 
			
		||||
      capabilitiesStub.returns(Promise.resolve(capabilitiesRes));
 | 
			
		||||
@@ -168,26 +143,13 @@ limitations under the License.
 | 
			
		||||
          name: 'Access Database',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
      const accessRes = {
 | 
			
		||||
        local: {
 | 
			
		||||
          GLOBAL_CAPABILITIES: {
 | 
			
		||||
            permissions: {
 | 
			
		||||
              accessDatabase: {
 | 
			
		||||
                rules: {
 | 
			
		||||
                  123: {},
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
      const repoRes = {
 | 
			
		||||
        labels: {
 | 
			
		||||
          'Code-Review': {},
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
      const accessStub = sandbox.stub(element.$.restAPI,
 | 
			
		||||
          'getRepoAccessRights').returns(Promise.resolve(accessRes));
 | 
			
		||||
      const accessStub = sandbox.stub(element.$.restAPI, 'getRepoAccessRights')
 | 
			
		||||
          .returns(Promise.resolve(JSON.parse(JSON.stringify(accessRes2))));
 | 
			
		||||
      const capabilitiesStub = sandbox.stub(element.$.restAPI,
 | 
			
		||||
          'getCapabilities').returns(Promise.resolve(capabilitiesRes));
 | 
			
		||||
      const repoStub = sandbox.stub(element.$.restAPI, 'getRepo').returns(
 | 
			
		||||
@@ -228,7 +190,8 @@ limitations under the License.
 | 
			
		||||
 | 
			
		||||
    suite('with defined sections', () => {
 | 
			
		||||
      setup(() => {
 | 
			
		||||
        element._sections = element.toSortedArray(accessRes.local);
 | 
			
		||||
        element._sections =
 | 
			
		||||
            element.toSortedArray(JSON.parse(JSON.stringify(accessRes.local)));
 | 
			
		||||
        flushAsynchronousOperations();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
@@ -268,7 +231,7 @@ limitations under the License.
 | 
			
		||||
        element._local = JSON.parse(JSON.stringify(accessRes.local));
 | 
			
		||||
        assert.deepEqual(element._computeAddAndRemove(), {add: {}, remove: {}});
 | 
			
		||||
        element._local['refs/*'].permissions.owner.rules[123].deleted = true;
 | 
			
		||||
        const expectedInput = {
 | 
			
		||||
        let expectedInput = {
 | 
			
		||||
          add: {},
 | 
			
		||||
          remove: {
 | 
			
		||||
            'refs/*': {
 | 
			
		||||
@@ -285,19 +248,26 @@ limitations under the License.
 | 
			
		||||
        assert.deepEqual(element._computeAddAndRemove(), expectedInput);
 | 
			
		||||
        delete element._local['refs/*'].permissions.owner.rules[123].deleted;
 | 
			
		||||
        element._local['refs/*'].permissions.owner.rules[123].modified = true;
 | 
			
		||||
        assert.deepEqual(element._computeAddAndRemove(), repoAccessInput);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('_computeAddAndRemove permissions', () => {
 | 
			
		||||
        element._local = JSON.parse(JSON.stringify(accessRes.local));
 | 
			
		||||
        assert.deepEqual(element._computeAddAndRemove(), {add: {}, remove: {}});
 | 
			
		||||
        element._local['refs/*'].permissions.owner.deleted = true;
 | 
			
		||||
        const expectedInput = {
 | 
			
		||||
          add: {},
 | 
			
		||||
        expectedInput = {
 | 
			
		||||
          add: {
 | 
			
		||||
            'refs/*': {
 | 
			
		||||
              permissions: {
 | 
			
		||||
                owner: {
 | 
			
		||||
                  rules: {
 | 
			
		||||
                    123: {action: 'DENY', modified: true},
 | 
			
		||||
                  },
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          remove: {
 | 
			
		||||
            'refs/*': {
 | 
			
		||||
              permissions: {
 | 
			
		||||
                owner: {rules: {}},
 | 
			
		||||
                owner: {
 | 
			
		||||
                  rules: {
 | 
			
		||||
                    123: null,
 | 
			
		||||
                  },
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
@@ -309,7 +279,7 @@ limitations under the License.
 | 
			
		||||
        element._local = JSON.parse(JSON.stringify(accessRes.local));
 | 
			
		||||
        assert.deepEqual(element._computeAddAndRemove(), {add: {}, remove: {}});
 | 
			
		||||
        element._local['refs/*'].permissions.owner.deleted = true;
 | 
			
		||||
        const expectedInput = {
 | 
			
		||||
        let expectedInput = {
 | 
			
		||||
          add: {},
 | 
			
		||||
          remove: {
 | 
			
		||||
            'refs/*': {
 | 
			
		||||
@@ -320,6 +290,31 @@ limitations under the License.
 | 
			
		||||
          },
 | 
			
		||||
        };
 | 
			
		||||
        assert.deepEqual(element._computeAddAndRemove(), expectedInput);
 | 
			
		||||
        delete element._local['refs/*'].permissions.owner.deleted;
 | 
			
		||||
        element._local['refs/*'].permissions.owner.modified = true;
 | 
			
		||||
        expectedInput = {
 | 
			
		||||
          add: {
 | 
			
		||||
            'refs/*': {
 | 
			
		||||
              permissions: {
 | 
			
		||||
                owner: {
 | 
			
		||||
                  modified: true,
 | 
			
		||||
                  rules: {
 | 
			
		||||
                    234: {action: 'ALLOW'},
 | 
			
		||||
                    123: {action: 'DENY'},
 | 
			
		||||
                  },
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          remove: {
 | 
			
		||||
            'refs/*': {
 | 
			
		||||
              permissions: {
 | 
			
		||||
                owner: {rules: {}},
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        };
 | 
			
		||||
        assert.deepEqual(element._computeAddAndRemove(), expectedInput);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('_computeAddAndRemove combinations', () => {
 | 
			
		||||
@@ -368,11 +363,65 @@ limitations under the License.
 | 
			
		||||
          },
 | 
			
		||||
        };
 | 
			
		||||
        assert.deepEqual(element._computeAddAndRemove(), expectedInput);
 | 
			
		||||
        // Modify both permissions with an exclusive bit. Owner is still
 | 
			
		||||
        // deleted.
 | 
			
		||||
        element._local['refs/*'].permissions.owner.exclusive = true;
 | 
			
		||||
        element._local['refs/*'].permissions.owner.modified = true;
 | 
			
		||||
        element._local['refs/*'].permissions.read.exclusive = true;
 | 
			
		||||
        element._local['refs/*'].permissions.read.modified = true;
 | 
			
		||||
        expectedInput = {
 | 
			
		||||
          add: {
 | 
			
		||||
            'refs/*': {
 | 
			
		||||
              permissions: {
 | 
			
		||||
                read: {
 | 
			
		||||
                  exclusive: true,
 | 
			
		||||
                  modified: true,
 | 
			
		||||
                  rules: {
 | 
			
		||||
                    234: {action: 'ALLOW'},
 | 
			
		||||
                  },
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          remove: {
 | 
			
		||||
            'refs/*': {
 | 
			
		||||
              permissions: {
 | 
			
		||||
                owner: {rules: {}},
 | 
			
		||||
                read: {rules: {}},
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        };
 | 
			
		||||
        assert.deepEqual(element._computeAddAndRemove(), expectedInput);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('_handleSaveForReview', done => {
 | 
			
		||||
        sandbox.stub(element.$.restAPI, 'getRepoAccessRights')
 | 
			
		||||
            .returns(Promise.resolve(accessRes));
 | 
			
		||||
        const repoAccessInput = {
 | 
			
		||||
          add: {
 | 
			
		||||
            'refs/*': {
 | 
			
		||||
              permissions: {
 | 
			
		||||
                owner: {
 | 
			
		||||
                  rules: {
 | 
			
		||||
                    123: {action: 'DENY', modified: true},
 | 
			
		||||
                  },
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          remove: {
 | 
			
		||||
            'refs/*': {
 | 
			
		||||
              permissions: {
 | 
			
		||||
                owner: {
 | 
			
		||||
                  rules: {
 | 
			
		||||
                    123: null,
 | 
			
		||||
                  },
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        };
 | 
			
		||||
        sandbox.stub(element.$.restAPI, 'getRepoAccessRights').returns(
 | 
			
		||||
            Promise.resolve(JSON.parse(JSON.stringify(accessRes))));
 | 
			
		||||
        sandbox.stub(element.$.restAPI, 'getRepo')
 | 
			
		||||
            .returns(Promise.resolve({}));
 | 
			
		||||
        sandbox.stub(Gerrit.Nav, 'navigateToChange');
 | 
			
		||||
@@ -381,8 +430,7 @@ limitations under the License.
 | 
			
		||||
            .returns(Promise.resolve({_number: 1}));
 | 
			
		||||
 | 
			
		||||
        element.repo = 'test-repo';
 | 
			
		||||
        sandbox.stub(element, '_computeAddAndRemove')
 | 
			
		||||
            .returns(repoAccessInput);
 | 
			
		||||
        sandbox.stub(element, '_computeAddAndRemove').returns(repoAccessInput);
 | 
			
		||||
 | 
			
		||||
        element._handleSaveForReview().then(() => {
 | 
			
		||||
          assert.isTrue(saveForReviewStub.called);
 | 
			
		||||
 
 | 
			
		||||
@@ -62,10 +62,6 @@ limitations under the License.
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        display: flex;
 | 
			
		||||
      }
 | 
			
		||||
      paper-toggle-button {
 | 
			
		||||
        --paper-toggle-button-checked-bar-color: var(--color-link);
 | 
			
		||||
        --paper-toggle-button-checked-button-color: var(--color-link);
 | 
			
		||||
      }
 | 
			
		||||
    </style>
 | 
			
		||||
    <div class="header">
 | 
			
		||||
      <h3>Messages</h3>
 | 
			
		||||
 
 | 
			
		||||
@@ -96,6 +96,10 @@ limitations under the License.
 | 
			
		||||
      .separator.transparent {
 | 
			
		||||
        background-color: transparent;
 | 
			
		||||
      }
 | 
			
		||||
      paper-toggle-button {
 | 
			
		||||
        --paper-toggle-button-checked-bar-color: var(--color-link);
 | 
			
		||||
        --paper-toggle-button-checked-button-color: var(--color-link);
 | 
			
		||||
      }
 | 
			
		||||
    </style>
 | 
			
		||||
  </template>
 | 
			
		||||
</dom-module>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user