diff --git a/Makefile.local b/Makefile.local --- a/Makefile.local +++ b/Makefile.local @@ -35,7 +35,7 @@ .PHONY: run-migrations-test run-migrations-test: - rm -f swh/web/settings/testdb.sqlite3 + rm -f swh-web-test.sqlite3 django-admin migrate --settings=$(SETTINGS_TEST) -v0 2>/dev/null add-users-test: run-migrations-test diff --git a/assets/src/bundles/admin/origin-save.js b/assets/src/bundles/admin/origin-save.js --- a/assets/src/bundles/admin/origin-save.js +++ b/assets/src/bundles/admin/origin-save.js @@ -307,20 +307,78 @@ } } +const rejectModalHtml = ` +
+
+ +
+ +
+
+
+ +
+ +
+`; + export function rejectOriginSaveRequest() { const selectedRow = pendingSaveRequestsTable.row('.selected'); + const rowData = selectedRow.data(); if (selectedRow.length) { const rejectOriginSaveRequestCallback = async() => { - const rowData = selectedRow.data(); - const rejectSaveRequestUrl = Urls.admin_origin_save_request_reject(rowData['visit_type'], rowData['origin_url']); - await csrfPost(rejectSaveRequestUrl); + $('#swh-web-modal-html').modal('hide'); + const rejectSaveRequestUrl = Urls.admin_origin_save_request_reject( + rowData['visit_type'], rowData['origin_url']); + await csrfPost(rejectSaveRequestUrl, {}, + JSON.stringify({note: $('#swh-rejection-text').val()})); pendingSaveRequestsTable.ajax.reload(null, false); }; - swh.webapp.showModalConfirm( - 'Reject origin save request ?', - 'Are you sure to reject this origin save request ?', - rejectOriginSaveRequestCallback); + let currentRejectionReason = 'custom'; + const rejectionTexts = {}; + swh.webapp.showModalHtml('Reject origin save request ?', rejectModalHtml); + $('#swh-rejection-reason').on('change', (event) => { + // backup current textarea value + rejectionTexts[currentRejectionReason] = $('#swh-rejection-text').val(); + currentRejectionReason = event.target.value; + let newRejectionText = ''; + if (rejectionTexts.hasOwnProperty(currentRejectionReason)) { + // restore previous textarea value + newRejectionText = rejectionTexts[currentRejectionReason]; + } else { + // fill textarea with default text according to rejection type + if (currentRejectionReason === 'invalid-origin') { + newRejectionText = `The origin with URL ${rowData['origin_url']} is not ` + + `a link to a ${rowData['visit_type']} repository.`; + } else if (currentRejectionReason === 'invalid-origin-type') { + newRejectionText = `The origin with URL ${rowData['origin_url']} is not ` + + `of type ${rowData['visit_type']}.`; + } else if (currentRejectionReason === 'origin-not-found') { + newRejectionText = `The origin with URL ${rowData['origin_url']} cannot be found.`; + } + } + $('#swh-rejection-text').val(newRejectionText); + }); + $('#swh-rejection-form').on('submit', (event) => { + event.preventDefault(); + event.stopPropagation(); + // ensure confirmation modal will be displayed above the html modal + $('#swh-web-modal-html').css('z-index', 4000); + swh.webapp.showModalConfirm( + 'Reject origin save request ?', + 'Are you sure to reject this origin save request ?', + rejectOriginSaveRequestCallback); + }); } } diff --git a/cypress/integration/admin.spec.js b/cypress/integration/admin.spec.js --- a/cypress/integration/admin.spec.js +++ b/cypress/integration/admin.spec.js @@ -219,3 +219,63 @@ }); }); + +describe('Test Admin Origin Save', function() { + + it(`should reject a save code now request with note`, function() { + const originUrl = `https://example.org/${Date.now()}`; + const rejectionNote = 'The provided URL does not target a git repository.'; + + // anonymous user create request put in pending state + cy.visit(this.Urls.origin_save()); + + cy.get('#swh-input-origin-url') + .type(originUrl); + + cy.get('#swh-input-origin-save-submit') + .click(); + + // admin user logs in and visit save code now admin page + cy.adminLogin(); + cy.visit(this.Urls.admin_origin_save()); + + // admin rejects the save request and adds a rejection note + cy.contains('#swh-origin-save-pending-requests', originUrl) + .click(); + + cy.get('#swh-reject-save-origin-request') + .click(); + + cy.get('#swh-rejection-text') + .then(textarea => { + textarea.val(rejectionNote); + }); + + cy.get('#swh-rejection-submit') + .click(); + + cy.get('#swh-web-modal-confirm-ok-btn') + .click(); + + // checks rejection note has been saved to swh-web database + cy.request(this.Urls.api_1_save_origin('git', originUrl)) + .then(response => { + expect(response.body[0]['note']).to.equal(rejectionNote); + }); + + // remove rejected request from swh-web database to avoid side effects + // in tests located in origin-save.spec.js + cy.visit(this.Urls.admin_origin_save()); + cy.get('#swh-save-requests-rejected-tab') + .click(); + + cy.contains('#swh-origin-save-rejected-requests', originUrl) + .click(); + + cy.get('#swh-remove-rejected-save-origin-request') + .click(); + + cy.get('#swh-web-modal-confirm-ok-btn') + .click(); + }); +}); diff --git a/swh/web/common/origin_save.py b/swh/web/common/origin_save.py --- a/swh/web/common/origin_save.py +++ b/swh/web/common/origin_save.py @@ -609,10 +609,16 @@ task_ids.append(sor.loading_task_id) save_requests = [] if task_ids: - tasks = scheduler().get_tasks(task_ids) - tasks = {task["id"]: task for task in tasks} - task_runs = scheduler().get_task_runs(tasks) - task_runs = {task_run["task"]: task_run for task_run in task_runs} + try: + tasks = scheduler().get_tasks(task_ids) + tasks = {task["id"]: task for task in tasks} + task_runs = scheduler().get_task_runs(tasks) + task_runs = {task_run["task"]: task_run for task_run in task_runs} + except Exception: + # allow to avoid mocking api GET responses for /origin/save endpoint when + # running cypress tests as scheduler is not available + tasks = {} + task_runs = {} for sor in requests_queryset: sr_dict = _update_save_request_info( sor, tasks.get(sor.loading_task_id), task_runs.get(sor.loading_task_id), diff --git a/swh/web/templates/admin/origin-save.html b/swh/web/templates/admin/origin-save.html --- a/swh/web/templates/admin/origin-save.html +++ b/swh/web/templates/admin/origin-save.html @@ -1,7 +1,7 @@ {% extends "layout.html" %} {% comment %} -Copyright (C) 2018-2019 The Software Heritage developers +Copyright (C) 2018-2021 The Software Heritage developers See the AUTHORS file at the top-level directory of this distribution License: GNU Affero General Public License version 3, or any later version See top-level LICENSE file for more information @@ -105,7 +105,7 @@
- +