🐛 Disabled bulk deletion when multiple member filters are applied (#20681)

fixes https://linear.app/tryghost/issue/ONC-206
ref https://app.incident.io/ghost/incidents/90

- when multiple member filters are used in combination, NQL sometimes
hit a limitation that results in the wrong members being returned
- while we work on the NQL limitation, we are temporarily disabling bulk
member deletion when more than one member filter has been applied
This commit is contained in:
Sag 2024-07-29 17:33:23 +02:00 committed by GitHub
parent 7411724a03
commit f64820b1be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 66 additions and 8 deletions

View File

@ -209,6 +209,10 @@ export default class MembersController extends Controller {
return uniqueColumns.splice(0, 2); // Maximum 2 columns
}
get isMultiFiltered() {
return this.isFiltered && this.filters.length >= 2;
}
includeTierQuery() {
const availableFilters = this.filters.length ? this.filters : this.softFilters;
return availableFilters.some((f) => {

View File

@ -104,12 +104,14 @@
</button>
</li>
{{/if}}
<li class="divider"></li>
<li>
<button class="mr2" data-test-button="delete-selected" type="button" {{on "click" this.bulkDelete}}>
<span class="red">Delete selected members ({{this.members.length}})</span>
</button>
</li>
{{#unless this.isMultiFiltered}}
<li class="divider"></li>
<li>
<button class="mr2" data-test-button="delete-selected" type="button" {{on "click" this.bulkDelete}}>
<span class="red">Delete selected members ({{this.members.length}})</span>
</button>
</li>
{{/unless}}
{{/if}}
</GhDropdown>
</span>

View File

@ -143,6 +143,53 @@ describe('Acceptance: Members', function () {
.to.equal('example@domain.com');
});
/* NOTE: Bulk deletion is disabled temporarily when multiple filters are applied, due to a NQL limitation.
* Delete this test once we have fixed the root NQL limitation.
* See https://linear.app/tryghost/issue/ONC-203
*/
it('cannot bulk delete members if more than 1 filter is selected', async function () {
// Members with label
const labelOne = this.server.create('label');
const labelTwo = this.server.create('label');
this.server.createList('member', 2, {labels: [labelOne]});
this.server.createList('member', 2, {labels: [labelOne, labelTwo]});
await visit('/members');
expect(findAll('[data-test-member]').length).to.equal(4);
// The delete button should not be visible by default
await click('[data-test-button="members-actions"]');
expect(find('[data-test-button="delete-selected"]')).to.not.exist;
// Apply a single filter
await click('[data-test-button="members-filter-actions"]');
await fillIn('[data-test-members-filter="0"] [data-test-select="members-filter"]', 'label');
await click('.gh-member-label-input input');
await click(`[data-test-label-filter="${labelOne.name}"]`);
await click(`[data-test-button="members-apply-filter"]`);
expect(findAll('[data-test-member]').length).to.equal(4);
expect(currentURL()).to.equal(`/members?filter=label%3A%5B${labelOne.slug}%5D`);
await click('[data-test-button="members-actions"]');
expect(find('[data-test-button="delete-selected"]')).to.exist;
// Apply a second filter
await click('[data-test-button="members-filter-actions"]');
await click('[data-test-button="add-members-filter"]');
await fillIn('[data-test-members-filter="1"] [data-test-select="members-filter"]', 'label');
await click('[data-test-members-filter="1"] .gh-member-label-input input');
await click(`[data-test-members-filter="1"] [data-test-label-filter="${labelTwo.name}"]`);
await click(`[data-test-button="members-apply-filter"]`);
expect(findAll('[data-test-member]').length).to.equal(2);
expect(currentURL()).to.equal(`/members?filter=label%3A%5B${labelOne.slug}%5D%2Blabel%3A%5B${labelTwo.slug}%5D`);
await click('[data-test-button="members-actions"]');
expect(find('[data-test-button="delete-selected"]')).to.not.exist;
});
it('can bulk delete members', async function () {
// members to be kept
this.server.createList('member', 6);
@ -167,7 +214,7 @@ describe('Acceptance: Members', function () {
await click(`[data-test-button="members-apply-filter"]`);
expect(findAll('[data-test-member]').length).to.equal(5);
expect(currentURL()).to.equal('/members?filter=label%3A%5Blabel-0%5D');
expect(currentURL()).to.equal(`/members?filter=label%3A%5B${label.slug}%5D`);
await click('[data-test-button="members-actions"]');

View File

@ -1328,7 +1328,12 @@ describe('Acceptance: Members filtering', function () {
expect(find('[data-test-button="add-label-selected"]'), 'add label to selected button').to.exist;
expect(find('[data-test-button="remove-label-selected"]'), 'remove label from selected button').to.exist;
expect(find('[data-test-button="unsubscribe-selected"]'), 'unsubscribe selected button').to.exist;
expect(find('[data-test-button="delete-selected"]'), 'delete selected button').to.exist;
/* NOTE: Bulk deletion is disabled temporarily when multiple filters are applied, due to a NQL limitation.
* Re-enable following line once we have fixed the root NQL limitation.
* See https://linear.app/tryghost/issue/ONC-203
*/
// expect(find('[data-test-button="delete-selected"]'), 'delete selected button').to.exist;
// filter is active and has # of filters
expect(find('[data-test-button="members-filter-actions"] span'), 'filter button').to.have.class('gh-btn-label-green');