From 180f7d3358462941ed8705c359623d8a7d807c6e Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 6 Nov 2023 23:31:53 +0100 Subject: [PATCH] enh(api): js wrapper for collectives api Wrap all usage of the collectives api in a simple functional interface. This will allow us to change the layout of the api and only update it in a single place. Separate the API handling from the store. The api is concerned with axios and generateUrl while the store is only concerned with the current state of the app. Accept multiple arguments to `collectivesUrl` and `pagesUrl` and join them with `/` to build the url: Example: ```js api.pagesUrl({collectiveId: 10}, 'trash', 12) ``` results in `/apps/collectives/_api/10/_pages/trash/12` Use the api directly in the cypress commands. That way we do not require the page to be loaded. Also extract `collective-edit-mode.spec.js` from `pages.spec.js`. It requires a different beforeEach function to seed the edit mode before visiting the collective. Signed-off-by: Max --- cypress/e2e/circle-with-group.spec.js | 1 - cypress/e2e/collective-members.spec.js | 1 - cypress/e2e/collective-page-mode.spec.js | 61 +++++++ cypress/e2e/collective-readonly.spec.js | 5 +- cypress/e2e/collective-settings.spec.js | 1 - cypress/e2e/collective-share.spec.js | 1 - cypress/e2e/collective.spec.js | 2 - cypress/e2e/collectives-trash.spec.js | 1 - cypress/e2e/dashboard-widget.spec.js | 3 +- cypress/e2e/page-details.spec.js | 5 +- cypress/e2e/page-landingpage.spec.js | 14 +- cypress/e2e/page-list.spec.js | 33 ++-- cypress/e2e/page-share.spec.js | 5 +- cypress/e2e/pages-links.spec.js | 19 +- cypress/e2e/pages.spec.js | 35 +--- cypress/e2e/settings.spec.js | 1 - cypress/support/commands.js | 221 +++++++++++++---------- cypress/support/navigation.js | 3 +- src/apis/collectives/collectives.js | 119 ++++++++++++ src/apis/collectives/index.js | 6 + src/apis/collectives/pages.js | 207 +++++++++++++++++++++ src/apis/collectives/settings.js | 20 ++ src/apis/collectives/shares.js | 76 ++++++++ src/apis/collectives/urls.js | 36 ++++ src/apis/collectives/userSettings.js | 28 +++ src/store/collectives.js | 90 ++++----- src/store/pages.js | 98 +++------- src/store/settings.js | 10 +- 28 files changed, 793 insertions(+), 309 deletions(-) create mode 100644 cypress/e2e/collective-page-mode.spec.js create mode 100644 src/apis/collectives/collectives.js create mode 100644 src/apis/collectives/index.js create mode 100644 src/apis/collectives/pages.js create mode 100644 src/apis/collectives/settings.js create mode 100644 src/apis/collectives/shares.js create mode 100644 src/apis/collectives/urls.js create mode 100644 src/apis/collectives/userSettings.js diff --git a/cypress/e2e/circle-with-group.spec.js b/cypress/e2e/circle-with-group.spec.js index 9fc448a37..e45bcc8ad 100644 --- a/cypress/e2e/circle-with-group.spec.js +++ b/cypress/e2e/circle-with-group.spec.js @@ -32,7 +32,6 @@ describe('Pages are accessible via group membership to circle', function() { before(function() { cy.loginAs('jane') - cy.visit('apps/collectives') cy.deleteAndSeedCollective('Group Collective') cy.circleFind('Group Collective') .circleAddMember('Bobs Group', 2) diff --git a/cypress/e2e/collective-members.spec.js b/cypress/e2e/collective-members.spec.js index b764eae5d..b87b5f5b7 100644 --- a/cypress/e2e/collective-members.spec.js +++ b/cypress/e2e/collective-members.spec.js @@ -27,7 +27,6 @@ describe('Collective members', function() { before(function() { cy.loginAs('bob') - cy.visit('apps/collectives') cy.deleteAndSeedCollective('Members Collective') }) diff --git a/cypress/e2e/collective-page-mode.spec.js b/cypress/e2e/collective-page-mode.spec.js new file mode 100644 index 000000000..afa9ae30a --- /dev/null +++ b/cypress/e2e/collective-page-mode.spec.js @@ -0,0 +1,61 @@ +/** + * @copyright Copyright (c) 2021 Azul + * + * @author Azul + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +describe('Collective page mode', function() { + + before(function() { + cy.loginAs('bob') + cy.deleteAndSeedCollective('Our Garden') + .seedPage('Day 1', '', 'Readme.md') + .seedPage('Day 2', '', 'Readme.md') + }) + + beforeEach(function() { + cy.loginAs('bob') + }) + + describe('Changing page mode', function() { + it('Opens edit mode per default', function() { + cy.seedCollectivePageMode('Our Garden', 1) + cy.visit('/apps/collectives/Our Garden') + // make sure the page list loaded properly + cy.contains('.app-content-list-item a', 'Day 1') + cy.openPage('Day 2') + cy.getEditor() + .should('be.visible') + cy.getReadOnlyEditor() + .should('not.be.visible') + }) + + it('Opens view mode per default', function() { + cy.seedCollectivePageMode('Our Garden', 0) + cy.visit('/apps/collectives/Our Garden') + // make sure the page list loaded properly + cy.contains('.app-content-list-item a', 'Day 1') + cy.openPage('Day 2') + cy.getReadOnlyEditor() + .should('be.visible') + cy.getEditor() + .should('not.be.visible') + }) + }) +}) diff --git a/cypress/e2e/collective-readonly.spec.js b/cypress/e2e/collective-readonly.spec.js index afc92f4ea..27458fa27 100644 --- a/cypress/e2e/collective-readonly.spec.js +++ b/cypress/e2e/collective-readonly.spec.js @@ -24,12 +24,11 @@ describe('Read-only collective', function() { before(function() { cy.loginAs('alice') - cy.visit('apps/collectives') cy.deleteAndSeedCollective('PermissionCollective') - cy.seedPage('SecondPage', '', 'Readme.md') - cy.seedCollectivePermissions('PermissionCollective', 'edit', 4) + .seedPage('SecondPage') cy.circleFind('PermissionCollective') .circleAddMember('bob') + cy.seedCollectivePermissions('PermissionCollective', 'edit', 4) }) describe('in read-only collective', function() { diff --git a/cypress/e2e/collective-settings.spec.js b/cypress/e2e/collective-settings.spec.js index 144b80b30..4f837a30c 100644 --- a/cypress/e2e/collective-settings.spec.js +++ b/cypress/e2e/collective-settings.spec.js @@ -27,7 +27,6 @@ describe('Collective settings', function() { before(function() { cy.loginAs('bob') - cy.visit('apps/collectives') cy.deleteCollective('Change me now') cy.deleteAndSeedCollective('Change me') }) diff --git a/cypress/e2e/collective-share.spec.js b/cypress/e2e/collective-share.spec.js index c3b8d8b6b..abc275881 100644 --- a/cypress/e2e/collective-share.spec.js +++ b/cypress/e2e/collective-share.spec.js @@ -29,7 +29,6 @@ describe('Collective Share', function() { before(function() { cy.loginAs('bob') - cy.visit('/apps/collectives') cy.deleteAndSeedCollective('Share me') }) diff --git a/cypress/e2e/collective.spec.js b/cypress/e2e/collective.spec.js index a1e2b65c2..1f00817bf 100644 --- a/cypress/e2e/collective.spec.js +++ b/cypress/e2e/collective.spec.js @@ -29,7 +29,6 @@ describe('Collective', function() { before(function() { cy.loginAs('bob') - cy.visit('apps/collectives') cy.deleteCollective('Preexisting Circle') cy.deleteCollective('History Club') cy.deleteCollective(specialCollective) @@ -39,7 +38,6 @@ describe('Collective', function() { cy.seedCircle('Preexisting Circle') cy.seedCircle('History Club', { visible: true, open: true }) cy.loginAs('jane') - cy.visit('apps/collectives') cy.deleteCollective('Foreign Circle') cy.seedCircle('Foreign Circle', { visible: true, open: true }) }) diff --git a/cypress/e2e/collectives-trash.spec.js b/cypress/e2e/collectives-trash.spec.js index cc4f0efe7..df25d62e0 100644 --- a/cypress/e2e/collectives-trash.spec.js +++ b/cypress/e2e/collectives-trash.spec.js @@ -28,7 +28,6 @@ describe('Collective', function() { describe('move collective to trash and restore', function() { before(function() { cy.loginAs('bob') - cy.visit('apps/collectives') cy.deleteAndSeedCollective('Delete me') }) it('Allows moving the collective to trash', function() { diff --git a/cypress/e2e/dashboard-widget.spec.js b/cypress/e2e/dashboard-widget.spec.js index c9535e531..4671f4aad 100644 --- a/cypress/e2e/dashboard-widget.spec.js +++ b/cypress/e2e/dashboard-widget.spec.js @@ -30,9 +30,8 @@ describe('Collectives dashboard widget', function() { before(function() { cy.loginAs('bob') cy.enableDashboardWidget('collectives-recent-pages') - cy.visit('apps/collectives') cy.deleteAndSeedCollective('Dashboard Collective1') - cy.seedPage('Page 1', '', 'Readme.md') + .seedPage('Page 1', '', 'Readme.md') }) it('Lists pages in the dashboard widget', function() { cy.visit('/apps/dashboard/') diff --git a/cypress/e2e/page-details.spec.js b/cypress/e2e/page-details.spec.js index 78dea1859..ac668ede7 100644 --- a/cypress/e2e/page-details.spec.js +++ b/cypress/e2e/page-details.spec.js @@ -27,11 +27,10 @@ describe('Page details', function() { before(function() { cy.loginAs('bob') - cy.visit('/apps/collectives') cy.deleteAndSeedCollective('Our Garden') - cy.seedPage('Day 1', '', 'Readme.md') + .seedPage('Day 1', '', 'Readme.md') + .seedPage('TableOfContents', '', 'Readme.md') cy.seedPageContent('Our Garden/Day 2.md', 'A test string with Day 2 in the middle and a [link to Day 1](/index.php/apps/collectives/Our%20Garden/Day%201).') - cy.seedPage('TableOfContents', '', 'Readme.md') cy.seedPageContent('Our Garden/TableOfContents.md', '## Second-Level Heading') }) diff --git a/cypress/e2e/page-landingpage.spec.js b/cypress/e2e/page-landingpage.spec.js index 308ebc2df..a2a08d545 100644 --- a/cypress/e2e/page-landingpage.spec.js +++ b/cypress/e2e/page-landingpage.spec.js @@ -29,16 +29,18 @@ const collective = 'Landingpage Collective' describe('Page landing page', function() { before(function() { cy.loginAs('bob') - cy.visit('/apps/collectives') cy.deleteAndSeedCollective(collective) + .seedPage('Page 1', '', 'Readme.md') + .seedPage('Page 2', '', 'Readme.md') + .then(collective => { + // Wait 1 to make sure that page order by time is right + cy.wait(1000) // eslint-disable-line cypress/no-unnecessary-waiting + cy.wrap(collective) + .seedPage('Page 3', '', 'Readme.md') + }) cy.circleFind(collective).circleAddMember('alice') cy.circleFind(collective).circleAddMember('jane') cy.circleFind(collective).circleAddMember('john') - cy.seedPage('Page 1', '', 'Readme.md') - cy.seedPage('Page 2', '', 'Readme.md') - // Wait 1 second to make sure that page order by time is right - cy.wait(1000) // eslint-disable-line cypress/no-unnecessary-waiting - cy.seedPage('Page 3', '', 'Readme.md') }) beforeEach(function() { diff --git a/cypress/e2e/page-list.spec.js b/cypress/e2e/page-list.spec.js index b77fabaca..0d612ab9e 100644 --- a/cypress/e2e/page-list.spec.js +++ b/cypress/e2e/page-list.spec.js @@ -27,24 +27,25 @@ describe('Page list', function() { before(function() { cy.loginAs('bob') - cy.visit('apps/collectives') cy.deleteAndSeedCollective('Our Garden') - cy.seedPage('Target', '', 'Readme.md') - cy.seedPage('Target Subpage', '', 'Target.md') + .as('garden') + .seedPage('Target', '', 'Readme.md') + .seedPage('Target Subpage', '', 'Target.md') // Wait 1 second to make sure that page order by time is right cy.wait(1000) // eslint-disable-line cypress/no-unnecessary-waiting - cy.seedPage('Day 1', '', 'Readme.md') - cy.seedPage('Subpage Title', '', 'Day 1.md') - cy.seedPage('Day 2', '', 'Readme.md') - cy.seedPage('Page Title', '', 'Readme.md') - cy.seedPage('Move me internal', '', 'Readme.md') - cy.seedPage('Copy me internal', '', 'Readme.md') - cy.seedPage('Move me external', '', 'Readme.md') - cy.seedPage('Copy me external', '', 'Readme.md') - cy.seedPage('#% special chars', '', 'Readme.md') + cy.then(() => this.garden) + .seedPage('Day 1', '', 'Readme.md') + .seedPage('Subpage Title', '', 'Day 1.md') + .seedPage('Day 2', '', 'Readme.md') + .seedPage('Page Title', '', 'Readme.md') + .seedPage('Move me internal', '', 'Readme.md') + .seedPage('Copy me internal', '', 'Readme.md') + .seedPage('Move me external', '', 'Readme.md') + .seedPage('Copy me external', '', 'Readme.md') + .seedPage('#% special chars', '', 'Readme.md') cy.deleteAndSeedCollective('MoveCopyTargetCollective') - cy.seedPage('Target external', '', 'Readme.md') - cy.seedPage('Target Subpage external', '', 'Target external.md') + .seedPage('Target external', '', 'Readme.md') + .seedPage('Target Subpage external', '', 'Target external.md') }) beforeEach(function() { @@ -94,7 +95,6 @@ describe('Page list', function() { describe('Move and copy a page using the modal', function() { it('Moves page to a subpage', function() { - cy.visit('apps/collectives/Our%20Garden') cy.openPageMenu('Move me internal') cy.clickMenuButton('Move or copy') cy.get('.picker-list li') @@ -118,7 +118,6 @@ describe('Page list', function() { }) it('Copies page to a subpage', function() { - cy.visit('/apps/collectives/Our%20Garden') cy.openPageMenu('Copy me internal') cy.clickMenuButton('Move or copy') cy.get('.picker-list li') @@ -146,7 +145,6 @@ describe('Page list', function() { }) it('Moves page to a subpage in another collective', function() { - cy.visit('/apps/collectives/Our%20Garden') cy.openPageMenu('Move me external') cy.clickMenuButton('Move or copy') cy.get('.crumbs-home') @@ -176,7 +174,6 @@ describe('Page list', function() { }) it('Copies page to a subpage in another collective', function() { - cy.visit('/apps/collectives/Our%20Garden') cy.openPageMenu('Copy me external') cy.clickMenuButton('Move or copy') cy.get('.crumbs-home') diff --git a/cypress/e2e/page-share.spec.js b/cypress/e2e/page-share.spec.js index c33a8722d..55e21e372 100644 --- a/cypress/e2e/page-share.spec.js +++ b/cypress/e2e/page-share.spec.js @@ -29,10 +29,9 @@ describe('Collective Share', function() { before(function() { cy.loginAs('bob') - cy.visit('/apps/collectives') cy.deleteAndSeedCollective('Share me') - cy.seedPage('Sharepage', '', 'Readme.md') - cy.seedPage('Sharesubpage', '', 'Sharepage.md') + .seedPage('Sharepage', '', 'Readme.md') + .seedPage('Sharesubpage', '', 'Sharepage.md') cy.seedPageContent('Share%20me/Sharepage/Readme.md', '## Shared page') }) diff --git a/cypress/e2e/pages-links.spec.js b/cypress/e2e/pages-links.spec.js index f092b5951..f4447e4a7 100644 --- a/cypress/e2e/pages-links.spec.js +++ b/cypress/e2e/pages-links.spec.js @@ -32,19 +32,18 @@ let anotherCollectiveFirstPageId, linkTargetPageId describe('Page Link Handling', function() { before(function() { cy.loginAs('bob') - cy.visit('/apps/collectives') cy.deleteAndSeedCollective('Another Collective') - cy.seedPage('First Page', '', 'Readme.md').then((id) => { - anotherCollectiveFirstPageId = id - }) + .seedPage('First Page', '', 'Readme.md').then(({ pageId }) => { + anotherCollectiveFirstPageId = pageId + }) cy.deleteAndSeedCollective('Link Testing') - cy.seedPage('Parent', '', 'Readme.md') - cy.seedPage('Child', '', 'Parent.md') - cy.seedPage('Link Target', '', 'Readme.md').then((id) => { - linkTargetPageId = id - }) + .seedPage('Parent', '', 'Readme.md') + .seedPage('Child', '', 'Parent.md') + .seedPage('Link Target', '', 'Readme.md').then(({ pageId }) => { + linkTargetPageId = pageId + }) + .seedPage('Link Source', '', 'Readme.md') cy.seedPageContent('Link%20Testing/Link%20Target.md', 'Some content') - cy.seedPage('Link Source', '', 'Readme.md') cy.uploadFile('test.md', 'text/markdown').then((id) => { textId = id }).then(() => { diff --git a/cypress/e2e/pages.spec.js b/cypress/e2e/pages.spec.js index f8e6d2348..46cce9c80 100644 --- a/cypress/e2e/pages.spec.js +++ b/cypress/e2e/pages.spec.js @@ -25,18 +25,19 @@ */ describe('Page', function() { + before(function() { cy.loginAs('bob') - cy.visit('/apps/collectives') - cy.deleteAndSeedCollective('Our Garden') - cy.seedPage('Day 1', '', 'Readme.md') + cy.deleteAndSeedCollective('Our Garden').as('garden') + .seedPage('Day 1', '', 'Readme.md') // Wait 1 second to make sure that page order by time is right cy.wait(1000) // eslint-disable-line cypress/no-unnecessary-waiting - cy.seedPage('Day 2', '', 'Readme.md') - cy.seedPage('Page Title', '', 'Readme.md') - cy.seedPage('#% special chars', '', 'Readme.md') + cy.then(() => this.garden) + .seedPage('Day 2', '', 'Readme.md') + .seedPage('Page Title', '', 'Readme.md') + .seedPage('#% special chars', '', 'Readme.md') + .seedPage('Template', '', 'Readme.md') cy.seedPageContent('Our Garden/Day 2.md', 'A test string with Day 2 in the middle and a [link to Day 1](/index.php/apps/collectives/Our%20Garden/Day%201).') - cy.seedPage('Template', '', 'Readme.md') cy.seedPageContent('Our Garden/Template.md', 'This is going to be our template.') }) @@ -272,26 +273,6 @@ describe('Page', function() { }) } - describe('Changing page mode', function() { - it('Opens edit mode per default', function() { - cy.seedCollectivePageMode('Our Garden', 1) - cy.openPage('Day 2') - cy.getEditor() - .should('be.visible') - cy.getReadOnlyEditor() - .should('not.be.visible') - }) - - it('Opens view mode per default', function() { - cy.seedCollectivePageMode('Our Garden', 0) - cy.openPage('Day 2') - cy.getReadOnlyEditor() - .should('be.visible') - cy.getEditor() - .should('not.be.visible') - }) - }) - describe('Full width view', function() { it('Allows to toggle persistent full-width view', function() { cy.openPage('Day 2') diff --git a/cypress/e2e/settings.spec.js b/cypress/e2e/settings.spec.js index caa4630ea..341a2d6f2 100644 --- a/cypress/e2e/settings.spec.js +++ b/cypress/e2e/settings.spec.js @@ -27,7 +27,6 @@ describe('Settings', function() { before(function() { cy.loginAs('bob') - cy.visit('apps/collectives') cy.deleteAndSeedCollective('A Collective') }) diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 128e8a118..d95c651cf 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -1,20 +1,9 @@ import { login, logout } from '@nextcloud/cypress/commands' import { User } from '@nextcloud/cypress' -import { - GET_COLLECTIVES, - GET_TRASH_COLLECTIVES, - NEW_COLLECTIVE, - TRASH_COLLECTIVE, - DELETE_COLLECTIVE, - UPDATE_COLLECTIVE_EDIT_PERMISSIONS, - UPDATE_COLLECTIVE_SHARE_PERMISSIONS, - UPDATE_COLLECTIVE_PAGE_MODE, - GET_PAGES, - NEW_PAGE, - GET_CIRCLES, -} from '../../src/store/actions.js' +import * as api from '../../src/apis/collectives/index.js' import axios from '@nextcloud/axios' +import { generateOcsUrl } from '@nextcloud/router' const url = Cypress.config('baseUrl').replace(/\/index.php\/?$/g, '') Cypress.env('baseUrl', url) @@ -96,8 +85,8 @@ Cypress.Commands.add('disableApp', appName => { Cypress.Commands.add('setAppEnabled', (appName, value = true) => { const verb = value ? 'enable' : 'disable' - const api = `${Cypress.env('baseUrl')}/index.php/settings/apps/${verb}` - return axios.post(api, + const url = `${Cypress.env('baseUrl')}/index.php/settings/apps/${verb}` + return axios.post(url, { appIds: [appName] }, ) }) @@ -107,8 +96,8 @@ Cypress.Commands.add('setAppEnabled', (appName, value = true) => { */ Cypress.Commands.add('enableDashboardWidget', (widgetName) => { Cypress.log() - const api = `${Cypress.env('baseUrl')}/index.php/apps/dashboard/layout` - return axios.post(api, + const url = `${Cypress.env('baseUrl')}/index.php/apps/dashboard/layout` + return axios.post(url, { layout: widgetName }, ) }) @@ -176,11 +165,20 @@ Cypress.Commands.add('findBy', Cypress.Commands.add('deleteAndSeedCollective', (name) => { Cypress.log() cy.deleteCollective(name) - cy.dispatch(NEW_COLLECTIVE, { name }) - cy.store('getters.updatedCollectivePath') - .then(path => cy.routeTo(path)) - // Make sure new collective is loaded - cy.get('#titleform input').should('have.value', name) + cy.seedCollective(name) + cy.getCollectives() + .findBy({ name }) +}) + +Cypress.Commands.add('seedCollective', (name) => { + return api.newCollective({ name }) + .catch(e => { + if (e.request && e.request.status === 422) { + // The collective already existed... carry on. + } else { + throw e + } + }) }) /** @@ -202,18 +200,45 @@ Cypress.Commands.add('createCollective', (name, members = []) => { }) /** - * Delete a collective if exists and clean it from the trash. + * Delete a collective - no matter if it is in use or trashed. + * + * This command will succeed if the collective does not exist at all. */ Cypress.Commands.add('deleteCollective', (name) => { - cy.dispatch(GET_COLLECTIVES) - cy.store('state.collectives.collectives') + cy.trashCollective(name) + cy.deleteCollectiveFromTrash(name) +}) + +Cypress.Commands.add('getCollectives', () => { + return api.getCollectives() + .then(response => response.data.data) +}) + +/** + * Move a collective into the trash if it exists. + * + * This command will succeed if the collective does not exist at all. + */ +Cypress.Commands.add('trashCollective', (name) => { + cy.getCollectives() .findBy({ name }) - .dispatch(TRASH_COLLECTIVE) - // Try to find and delete collective from trash - cy.dispatch(GET_TRASH_COLLECTIVES) - cy.store('state.collectives.trashCollectives') + .then((found) => found && api.trashCollective(found.id)) +}) + +Cypress.Commands.add('getTrashCollectives', () => { + return api.getTrashCollectives() + .then(response => response.data.data) +}) + +/** + * Clear a collective from the trash if it is in there. + * + * This command will succeed if the collective does not exist at all. + */ +Cypress.Commands.add('deleteCollectiveFromTrash', (name) => { + cy.getTrashCollectives() .findBy({ name }) - .dispatch(DELETE_COLLECTIVE, { circle: true }) + .then((found) => found && api.deleteCollective(found.id, true)) }) /** @@ -222,11 +247,11 @@ Cypress.Commands.add('deleteCollective', (name) => { Cypress.Commands.add('seedCollectivePermissions', (name, type, level) => { Cypress.log() const action = (type === 'edit') - ? UPDATE_COLLECTIVE_EDIT_PERMISSIONS - : UPDATE_COLLECTIVE_SHARE_PERMISSIONS - cy.store('state.collectives.collectives') + ? api.updateCollectiveEditPermissions + : api.updateCollectiveSharePermissions + cy.getCollectives() .findBy({ name }) - .dispatch(action, { level }) + .then((found) => action(found.id, level)) }) /** @@ -234,54 +259,47 @@ Cypress.Commands.add('seedCollectivePermissions', (name, type, level) => { */ Cypress.Commands.add('seedCollectivePageMode', (name, mode) => { Cypress.log() - cy.store('state.collectives.collectives') + cy.getCollectives() .findBy({ name }) - .dispatch(UPDATE_COLLECTIVE_PAGE_MODE, { mode }) + .then((found) => api.updateCollectivePageMode(found.id, mode)) }) /** - * Add a page to a collective + * Context for the given collective for a logged in user. + * + * @param {object} collective - Collective to provide the context for. */ -Cypress.Commands.add('seedPage', (name, parentFilePath, parentFileName) => { - Cypress.log() - cy.dispatch(GET_PAGES) - cy.store('state.pages.pages') - .findBy({ filePath: parentFilePath, fileName: parentFileName }) - .its('id') - .as('parentId') - .then(id => ({ parentId: id })) - .dispatch(NEW_PAGE, { title: name, pagePath: name }) - // Return pageId of created page - cy.get('@parentId').then(parentId => { - return cy.store('state.pages.pages') - .findBy({ parentId, title: name }) - .its('id') - }) +function collectiveContext(collective) { + return { + isPublic: false, + collectiveId: collective.id, + shareTokenParam: null, + } +} + +Cypress.Commands.add('getPages', collective => { + return api.getPages(collectiveContext(collective)) + .then(response => response.data.data) }) /** - * Upload a file + * Add a page to a collective */ -Cypress.Commands.add('uploadFile', (path, mimeType, remotePath = '') => { - Cypress.log() - // Get fixture - return cy.fixture(path, 'base64').then(file => { - // convert the base64 string to a blob - const blob = Cypress.Blob.base64StringToBlob(file, mimeType) - try { - const file = new File([blob], path, { type: mimeType }) - return cy.uploadContent(remotePath + path, file, mimeType) - .then(response => { - const ocFileId = response.headers['oc-fileid'] - const fileId = parseInt(ocFileId.substring(0, ocFileId.indexOf('oc'))) - return fileId - }) - } catch (error) { - cy.log(error) - throw new Error(`Unable to process file ${path}`) - } +Cypress.Commands.add('seedPage', + { prevSubject: true }, + (subject, name, parentFilePath = '', parentFileName = 'Readme.md') => { + Cypress.log() + cy.getPages(subject) + .findBy({ filePath: parentFilePath, fileName: parentFileName }) + .then(({ id: parentId }) => { + return api.createPage( + collectiveContext(subject), + { parentId, title: name, pagePath: name }, + ) + }) + .its('data.data.id') + .then(pageId => ({ ...subject, pageId })) }) -}) /** * Upload content of a page @@ -294,22 +312,37 @@ Cypress.Commands.add('seedPageContent', (pagePath, content) => { cy.uploadContent(`Collectives/${pagePath}`, content) }) +Cypress.Commands.add('uploadFile', (path, mimeType, remotePath = '') => { + Cypress.log() + // Get fixture + return cy.fixture(path, 'base64').then(data => { + // convert the base64 string to a blob + const blob = Cypress.Blob.base64StringToBlob(data, mimeType) + const file = new File([blob], path, { type: mimeType }) + return cy.uploadContent(remotePath + path, file, mimeType) + .then(response => { + const ocFileId = response.headers['oc-fileid'] + const fileId = parseInt(ocFileId.substring(0, ocFileId.indexOf('oc'))) + return fileId + }) + }) +}) + /** * Generic upload of content - used by seedPageContent and uploadPage */ Cypress.Commands.add('uploadContent', (path, content, mimetype = 'text/markdown') => { // @nextcloud/axios automatic handling for request tokens does not work for webdav - cy.window() - .its('app.OC.requestToken') - .then(requesttoken => { - const url = `${Cypress.env('baseUrl')}/remote.php/webdav/${path}` - return axios.put(url, content, { - headers: { - requesttoken, - 'Content-Type': mimetype, - }, - }) + cy.request('/csrftoken').then(({ body }) => { + const requesttoken = body.token + const url = `${Cypress.env('baseUrl')}/remote.php/webdav/${path}` + return axios.put(url, content, { + headers: { + requesttoken, + 'Content-Type': mimetype, + }, }) + }) }) /** @@ -317,14 +350,12 @@ Cypress.Commands.add('uploadContent', (path, content, mimetype = 'text/markdown' */ Cypress.Commands.add('seedCircle', (name, config = null) => { Cypress.log() - cy.dispatch(GET_CIRCLES) - cy.store('state.circles.circles') - .findBy({ sanitizedName: name }) + cy.circleFind(name) .then(async circle => { - const api = `${Cypress.env('baseUrl')}/ocs/v2.php/apps/circles/circles` + const url = `${Cypress.env('baseUrl')}/ocs/v2.php/apps/circles/circles` let circleId if (!circle) { - const response = await axios.post(api, + const response = await axios.post(url, { name, personal: false, local: true }, ) circleId = response.data.ocs.data.id @@ -340,23 +371,27 @@ Cypress.Commands.add('seedCircle', (name, config = null) => { const value = bits .filter(([k, v]) => config[k]) .reduce((sum, [k, v]) => sum + v, 0) - await axios.put(`${api}/${circleId}/config`, + await axios.put(`${url}/${circleId}/config`, { value }, ) } }) }) -/** - * Add someone to a circle - */ +Cypress.Commands.add('getCircles', () => { + return axios.get(generateOcsUrl('apps/circles/circles')) + .then(response => response.data.ocs.data) +}) + Cypress.Commands.add('circleFind', (name) => { Cypress.log() - cy.dispatch(GET_CIRCLES) - return cy.store('state.circles.circles') + cy.getCircles() .findBy({ sanitizedName: name }) }) +/** + * Add someone to a circle + */ Cypress.Commands.add('circleAddMember', { prevSubject: true }, async ({ id }, userId, type = 1) => { diff --git a/cypress/support/navigation.js b/cypress/support/navigation.js index a02469cba..566d02612 100644 --- a/cypress/support/navigation.js +++ b/cypress/support/navigation.js @@ -17,8 +17,7 @@ Cypress.Commands.add('openPageMenu', (pageName) => { Cypress.Commands.add('openCollective', (collectiveName) => { Cypress.log() - cy.get(`.collectives_list_item a[title="${collectiveName}"]`) - .click() + cy.routeTo(collectiveName) }) Cypress.Commands.add('openCollectiveMenu', (collectiveName) => { diff --git a/src/apis/collectives/collectives.js b/src/apis/collectives/collectives.js new file mode 100644 index 000000000..c710bfe2e --- /dev/null +++ b/src/apis/collectives/collectives.js @@ -0,0 +1,119 @@ +import axios from '@nextcloud/axios' +import { collectivesUrl } from './urls.js' + +/** + * Get all active (i.e. not trashed) collectives for the current user + */ +export function getCollectives() { + return axios.get(collectivesUrl()) +} + +/** + * Get the shared collective for a given share token. + * + * @param {string} shareToken authentication token from the share + */ +export function getSharedCollective(shareToken) { + return axios.get(collectivesUrl('p', shareToken)) +} + +/** + * Get all trashed collectives for the current user + */ +export function getTrashCollectives() { + return axios.get(collectivesUrl('trash')) +} + +/** + * Create a new collective with the given properties. + * + * @param {object} collective - properties for the new collective + */ +export function newCollective(collective) { + return axios.post( + collectivesUrl(), + collective, + ) +} + +/** + * Trash the collective with the given id + * + * @param {number} collectiveId - Id of the collective to trash. + */ +export function trashCollective(collectiveId) { + return axios.delete(collectivesUrl(collectiveId)) +} + +/** + * Delete the collective with the given id. + * + * @param {number} collectiveId - id of the collective to delete + * @param {boolean} removeCircle - also remove the circle if true + */ +export function deleteCollective(collectiveId, removeCircle) { + const query = removeCircle ? '?circle=1' : '' + return axios.delete(collectivesUrl('trash', collectiveId + query)) +} + +/** + * Restore a collective with the given id from trash + * + * @param {number} collectiveId Id of the colletive to be restored + */ +export function restoreCollective(collectiveId) { + return axios.patch(collectivesUrl('trash', collectiveId)) +} + +/** + * Update a collective with the given properties + * + * @param {object} collective Properties for the collective + */ +export function updateCollective(collective) { + return axios.put( + collectivesUrl(collective.id), + collective, + ) +} + +/** + * Set the permission level required for editing. + * + * @param {number} collectiveId - id of the collective to update + * @param {number} level - required level for editing + */ +export function updateCollectiveEditPermissions(collectiveId, level) { + return axios.put( + collectivesUrl(collectiveId, 'editLevel'), + { level }, + ) +} + +/** + * Set the permission level required for sharing. + * + * @param {number} collectiveId - id of the collective to update + * @param {number} level - required level for sharing + */ +export function updateCollectiveSharePermissions(collectiveId, level) { + return axios.put( + collectivesUrl(collectiveId, 'shareLevel'), + { level }, + ) +} + +/** + * Set the edit mode for the given collective + * + * @param {number} collectiveId - id of the collective to update + * @param {number} mode - pageMode to use. + * + * Possible modes: pageModes.MODE_VIEW or pageModes.MODE_EDIT + */ +export function updateCollectivePageMode(collectiveId, mode) { + return axios.put( + collectivesUrl(collectiveId, 'pageMode'), + { mode }, + ) +} diff --git a/src/apis/collectives/index.js b/src/apis/collectives/index.js new file mode 100644 index 000000000..50cc7c857 --- /dev/null +++ b/src/apis/collectives/index.js @@ -0,0 +1,6 @@ +export * from './urls.js' +export * from './collectives.js' +export * from './pages.js' +export * from './userSettings.js' +export * from './settings.js' +export * from './shares.js' diff --git a/src/apis/collectives/pages.js b/src/apis/collectives/pages.js new file mode 100644 index 000000000..e0164f7bf --- /dev/null +++ b/src/apis/collectives/pages.js @@ -0,0 +1,207 @@ +import axios from '@nextcloud/axios' +import { pagesUrl } from './urls.js' + +/** + * Get all pages in the given context (collective or public share) + * + * @param {object} context - either the current collective or a share context + */ +export function getPages(context) { + return axios.get(pagesUrl(context)) +} + +/** + * Get all trashed pages in the given context. + * + * @param {object} context - either the current collective or a share context + */ +export function getTrashPages(context) { + return axios.get(pagesUrl(context, 'trash')) +} + +/** + * Create a new page in the given context (collective or public share) + * + * @param {object} context - either the current collective or a share context + * @param {object} page - properties of the new page + */ +export function createPage(context, page) { + return axios.post( + pagesUrl(context, page.parentId), + page, + ) +} + +/** + * Get a page in the given context (collective or public share) + * + * @param {object} context - either the current collective or a share context + * @param {number} pageId - Id of the page to retrieve + */ +export function getPage(context, pageId) { + return axios.get(pagesUrl(context, pageId)) +} + +/** + * Touch a page in the given context (collective or public share) + * + * @param {object} context - either the current collective or a share context + * @param {number} pageId - Id of the page to touch + */ +export function touchPage(context, pageId) { + return axios.get(pagesUrl(context, pageId, '/touch')) +} + +/** + * Rename a page in the given context (collective or public share) + * + * @param {object} context - either the current collective or a share context + * @param {number} pageId - Id of the page to rename + * @param {string} title - New title for the page + */ +export function renamePage(context, pageId, title) { + return axios.put( + pagesUrl(context, pageId), + { title }, + ) +} + +/** + * Copy a page inside the given context (collective or public share) + * + * @param {object} context - either the current collective or a share context + * @param {number} pageId - Id of the page to copy + * @param {number} parentId - Id of the page to copy to + * @param {number} index - Index for subpage order of parent page + */ +export function copyPage(context, pageId, parentId, index) { + return axios.put( + pagesUrl(context, pageId), + { parentId, index, copy: true }, + ) +} + +/** + * Move a page inside the given context (collective or public share) + * + * @param {object} context - either the current collective or a share context + * @param {number} pageId - Id of the page to move + * @param {number} parentId - Id of the page to move to + * @param {number} index - Index for subpage order of parent page + */ +export function movePage(context, pageId, parentId, index) { + return axios.put( + pagesUrl(context, pageId), + { parentId, index }, + ) +} + +/** + * Copy page to another collective + * + * @param {object} context - either the current collective or a share context + * @param {number} pageId - Id of the page to move + * @param {number} collectiveId - Id of the new collective + * @param {number} parentId - Id of the page to move to + * @param {number} index - Index for subpage order of parent page + */ +export function copyPageToCollective(context, pageId, collectiveId, parentId, index) { + return axios.put( + pagesUrl(context, pageId, 'to', collectiveId), + { parentId, index, copy: true }, + ) +} + +/** + * Move page to another collective + * + * @param {object} context - either the current collective or a share context + * @param {number} pageId - Id of the page to move + * @param {number} collectiveId - Id of the new collective + * @param {number} parentId - Id of the page to move to + * @param {number} index - Index for subpage order of parent page + */ +export function movePageToCollective(context, pageId, collectiveId, parentId, index) { + return axios.put( + pagesUrl(context, pageId, 'to', collectiveId), + { parentId, index }, + ) +} + +/** + * Set emoji for a page + * + * @param {object} context - either the current collective or a share context + * @param {number} pageId - Id of the page to update + * @param {string} emoji - New emojie for the page + */ +export function setPageEmoji(context, pageId, emoji) { + return axios.put( + pagesUrl(context, pageId, 'emoji'), + { emoji }, + ) +} + +/** + * Set subpageOrder for a page + * + * @param {object} context - either the current collective or a share context + * @param {number} pageId - Id of the page to update + * @param {string} subpageOrder - New subpageOrdere for the page + */ +export function setPageSubpageOrder(context, pageId, subpageOrder) { + return axios.put( + pagesUrl(context, pageId, 'subpageOrder'), + { subpageOrder }, + ) +} + +/** + * Trash a page in the given context (collective or public share) + * + * @param {object} context - either the current collective or a share context + * @param {number} pageId - Id of the page to trash + */ +export function trashPage(context, pageId) { + return axios.delete(pagesUrl(context, pageId)) +} + +/** + * Restore the page with the given id from trash + * + * @param {object} context - either the current collective or a share context + * @param {number} pageId - Id of the page to restore + */ +export function restorePage(context, pageId) { + return axios.patch(pagesUrl(context, '/trash', pageId)) +} + +/** + * Delete the page with the given id from trash + * + * @param {object} context - either the current collective or a share context + * @param {number} pageId - Id of the page to trash + */ +export function deletePage(context, pageId) { + return axios.delete(pagesUrl(context, '/trash', pageId)) +} + +/** + * Get list of attachments for a page + * + * @param {object} context - either the current collective or a share context + * @param {number} pageId - Id of the page to list attachments for + */ +export function getPageAttachments(context, pageId) { + return axios.get(pagesUrl(context, pageId, 'attachments')) +} + +/** + * Get list of backlinks for a page + * + * @param {object} context - either the current collective or a share context + * @param {number} pageId - Id of the page to list backlinks for + */ +export function getPageBacklinks(context, pageId) { + return axios.get(pagesUrl(context, pageId, 'backlinks')) +} diff --git a/src/apis/collectives/settings.js b/src/apis/collectives/settings.js new file mode 100644 index 000000000..6f4a1e256 --- /dev/null +++ b/src/apis/collectives/settings.js @@ -0,0 +1,20 @@ +import axios from '@nextcloud/axios' +import { apiUrl } from './urls.js' + +/** + * Get collectives folder setting for the current user + */ +export function getCollectivesFolder() { + return axios.get(apiUrl('v1.0', 'settings/user/user_folder')) +} + +/** + * Set collectives folder setting for the current user + * @param {string} value Name of the collective folder to use + */ +export function setCollectivesFolder(value) { + return axios.post( + apiUrl('v1.0', 'settings/user'), + { key: 'user_folder', value }, + ) +} diff --git a/src/apis/collectives/shares.js b/src/apis/collectives/shares.js new file mode 100644 index 000000000..9550ff298 --- /dev/null +++ b/src/apis/collectives/shares.js @@ -0,0 +1,76 @@ +import axios from '@nextcloud/axios' +import { collectivesUrl } from './urls.js' + +/** + * Get shares of a collective and its pages + * + * @param {number} collectiveId Id of the colletive + */ +export function getShares(collectiveId) { + return axios.get(collectivesUrl(collectiveId, 'shares')) +} + +/** + * Create a public collective share + * + * @param {number} collectiveId Id of the colletive to be shared + */ +export function createCollectiveShare(collectiveId) { + return axios.post(collectivesUrl(collectiveId, 'share')) +} + +/** + * Create a public page share + * + * @param {number} collectiveId Id of the colletive the page belongs to + * @param {number} pageId Id of the page to be shared + */ +export function createPageShare(collectiveId, pageId) { + return axios.post( + collectivesUrl(collectiveId, '_pages', pageId, 'share'), + ) +} + +/** + * Update a public collective share + * + * @param {object} share Share to update + * @param {number} share.collectiveId Id of the colletive + * @param {number} share.pageId Id of the colletive + * @param {string} share.token Token of the share to be updated + * @param {boolean} share.editable editable state to set + */ +export function updateShare(share) { + return axios.put( + shareUrl(share), + { editable: share.editable }, + ) +} + +/** + * Delete a public collective share + * + * @param {object} share Share to update + * @param {number} share.collectiveId Id of the colletive + * @param {number} share.pageId Id of the colletive + * @param {string} share.token Token of the share to be updated + */ +export function deleteShare(share) { + return axios.delete( + shareUrl(share), + ) +} + +/** + * Url of a share + * + * @param {object} share Share to update + * @param {number} share.collectiveId Id of the colletive + * @param {number} share.pageId Id of the colletive + * @param {string} share.token Token of the share to be updated + */ +function shareUrl({ collectiveId, pageId, token }) { + return pageId + ? collectivesUrl(collectiveId, '_pages', pageId, 'share', token) + : collectivesUrl(collectiveId, 'share', token) +} diff --git a/src/apis/collectives/urls.js b/src/apis/collectives/urls.js new file mode 100644 index 000000000..9f652c0e7 --- /dev/null +++ b/src/apis/collectives/urls.js @@ -0,0 +1,36 @@ +import { generateUrl, generateOcsUrl } from '@nextcloud/router' + +/** + * Url for the versioned collectives api + * + * @param {string} version - Version of the api - currently `v1.0` + * @param {...any} parts - url parts to append - will be joined with `/` + */ +export function apiUrl(version, ...parts) { + const path = ['apps/collectives/api', version, ...parts] + .join('/') + return generateOcsUrl(path) +} + +/** + * Url for the collectives app endpoints + * + * @param {...any} parts - url parts to append - will be joined with `/` + */ +export function collectivesUrl(...parts) { + const path = ['apps/collectives/_api', ...parts] + .join('/') + return generateUrl(path) +} + +/** + * Url for pages paths inside the given context. + * + * @param {object} context - either the current collective or a share context + * @param {...any} parts - url parts to append. + */ +export function pagesUrl(context, ...parts) { + return context.isPublic + ? collectivesUrl('p', context.shareTokenParam, '_pages', ...parts) + : collectivesUrl(context.collectiveId, '_pages', ...parts) +} diff --git a/src/apis/collectives/userSettings.js b/src/apis/collectives/userSettings.js new file mode 100644 index 000000000..d2bbd9486 --- /dev/null +++ b/src/apis/collectives/userSettings.js @@ -0,0 +1,28 @@ +import axios from '@nextcloud/axios' +import { collectivesUrl } from './urls.js' + +/** + * Set the page order for the current user + * + * @param {number} collectiveId ID of the colletive to be updated + * @param {number} pageOrder the desired page order for the current user + */ +export function setCollectiveUserSettingPageOrder(collectiveId, pageOrder) { + return axios.put( + collectivesUrl(collectiveId, '_userSettings', 'pageOrder'), + { pageOrder }, + ) +} + +/** + * Set the the `show recent pages` toggle for the current user + * + * @param {number} collectiveId ID of the colletive to be updated + * @param {boolean} showRecentPages the desired value + */ +export function setCollectiveUserSettingShowRecentPages(collectiveId, showRecentPages) { + return axios.put( + collectivesUrl(collectiveId, '_userSettings', 'showRecentPages'), + { showRecentPages }, + ) +} diff --git a/src/store/collectives.js b/src/store/collectives.js index fea78d4c7..6b21673a6 100644 --- a/src/store/collectives.js +++ b/src/store/collectives.js @@ -1,8 +1,7 @@ -import axios from '@nextcloud/axios' -import { generateUrl } from '@nextcloud/router' import { byName } from '../util/sortOrders.js' import randomEmoji from '../util/randomEmoji.js' import { memberLevels } from '../constants.js' +import * as api from '../apis/collectives/index.js' import { SET_COLLECTIVES, @@ -257,8 +256,8 @@ export default { commit('load', 'collectives') try { const response = getters.isPublic - ? await axios.get(generateUrl(`/apps/collectives/_api/p/${getters.shareTokenParam}`)) - : await axios.get(generateUrl('/apps/collectives/_api')) + ? await api.getSharedCollective(getters.shareTokenParam) + : await api.getCollectives() commit(SET_COLLECTIVES, response.data.data) } finally { commit('done', 'collectives') @@ -273,7 +272,7 @@ export default { */ async [GET_TRASH_COLLECTIVES]({ commit }) { commit('load', 'collectiveTrash') - const response = await axios.get(generateUrl('/apps/collectives/_api/trash')) + const response = await api.getTrashCollectives() commit(SET_TRASH_COLLECTIVES, response.data.data) commit('done', 'collectiveTrash') }, @@ -288,10 +287,7 @@ export default { * @param {object} collective Properties for the new collective */ async [NEW_COLLECTIVE]({ commit, rootState, dispatch }, collective) { - const response = await axios.post( - generateUrl('/apps/collectives/_api'), - collective, - ) + const response = await api.newCollective(collective) commit('info', response.data.message) commit(ADD_OR_UPDATE_COLLECTIVE, response.data.data) // If collectives folder wasn't initialized already, now it should be there @@ -308,10 +304,7 @@ export default { * @param {object} collective Properties for the collective */ async [UPDATE_COLLECTIVE]({ commit }, collective) { - const response = await axios.put( - generateUrl('/apps/collectives/_api/' + collective.id), - collective, - ) + const response = await api.updateCollective(collective) commit(ADD_OR_UPDATE_COLLECTIVE, response.data.data) }, @@ -324,7 +317,7 @@ export default { * @param {number} collective.id ID of the collective to be trashed */ async [TRASH_COLLECTIVE]({ commit }, { id }) { - const response = await axios.delete(generateUrl('/apps/collectives/_api/' + id)) + const response = await api.trashCollective(id) commit(MOVE_COLLECTIVE_INTO_TRASH, response.data.data) }, @@ -337,7 +330,7 @@ export default { * @param {number} collective.id ID of the collective to be restored */ async [RESTORE_COLLECTIVE]({ commit }, { id }) { - const response = await axios.patch(generateUrl('/apps/collectives/_api/trash/' + id)) + const response = await api.restoreCollective(id) commit(RESTORE_COLLECTIVE_FROM_TRASH, response.data.data) }, @@ -351,11 +344,7 @@ export default { * @param {boolean} collective.circle Whether to delete the circle as well */ async [DELETE_COLLECTIVE]({ commit }, { id, circle }) { - let doCircle = '' - if (circle) { - doCircle = '?circle=1' - } - const response = await axios.delete(generateUrl('/apps/collectives/_api/trash/' + id + doCircle)) + const response = await api.deleteCollective(id, circle) commit(DELETE_COLLECTIVE_FROM_TRASH, response.data.data) if (circle) { commit(DELETE_CIRCLE_FOR, response.data.data) @@ -371,7 +360,7 @@ export default { */ async [GET_SHARES]({ commit, getters }) { commit('load', 'shares') - const response = await axios.get(generateUrl('/apps/collectives/_api/' + getters.currentCollective.id + '/shares')) + const response = await api.getShares(getters.currentCollective.id) commit(SET_SHARES, response.data.data) commit('done', 'shares') }, @@ -387,31 +376,23 @@ export default { */ async [CREATE_SHARE]({ commit }, { collectiveId, pageId = 0 }) { commit('load', 'share') - const url = pageId - ? '/apps/collectives/_api/' + collectiveId + '/_pages/' + pageId + '/share' - : '/apps/collectives/_api/' + collectiveId + '/share' - const response = await axios.post(generateUrl(url)) + const response = pageId + ? await api.createPageShare(collectiveId, pageId) + : await api.createCollectiveShare(collectiveId) commit(ADD_OR_UPDATE_SHARE, response.data.data) commit('done', 'share') }, /** - * Create a public collective/page share + * Update a public collective/page share * * @param {object} store the vuex store * @param {Function} store.commit commit changes - * @param {object} share the share to be deleted + * @param {object} share the share to be updated */ async [UPDATE_SHARE]({ commit }, share) { commit('load', 'share') - const url = share.pageId - ? '/apps/collectives/_api/' + share.collectiveId + '/_pages/' + share.pageId + '/share/' + share.token - : '/apps/collectives/_api/' + share.collectiveId + '/share/' + share.token - const response = await axios.put( - generateUrl(url), - { editable: share.editable }, - - ) + const response = await api.updateShare(share) commit(ADD_OR_UPDATE_SHARE, response.data.data) commit('done', 'share') }, @@ -425,10 +406,7 @@ export default { */ async [DELETE_SHARE]({ commit }, share) { commit('load', 'unshare') - const url = share.pageId - ? '/apps/collectives/_api/' + share.collectiveId + '/_pages/' + share.pageId + '/share/' + share.token - : '/apps/collectives/_api/' + share.collectiveId + '/share/' + share.token - await axios.delete(generateUrl(url)) + await api.deleteShare(share) commit(REMOVE_SHARE, share) commit('done', 'unshare') }, @@ -441,10 +419,7 @@ export default { * @param {number} data.level new minimum level for sharing */ async [UPDATE_COLLECTIVE_EDIT_PERMISSIONS]({ commit }, { id, level }) { - const response = await axios.put( - generateUrl('/apps/collectives/_api/' + id + '/editLevel'), - { level }, - ) + const response = await api.updateCollectiveEditPermissions(id, level) commit(ADD_OR_UPDATE_COLLECTIVE, response.data.data) }, @@ -456,10 +431,7 @@ export default { * @param {number} data.level new minimum level for sharing */ async [UPDATE_COLLECTIVE_SHARE_PERMISSIONS]({ commit }, { id, level }) { - const response = await axios.put( - generateUrl('/apps/collectives/_api/' + id + '/shareLevel'), - { level }, - ) + const response = await api.updateCollectiveSharePermissions(id, level) commit(ADD_OR_UPDATE_COLLECTIVE, response.data.data) }, @@ -471,27 +443,27 @@ export default { * @param {number} data.mode page mode */ async [UPDATE_COLLECTIVE_PAGE_MODE]({ commit }, { id, mode }) { - const response = await axios.put( - generateUrl('/apps/collectives/_api/' + id + '/pageMode'), - { mode }, - ) + const response = await api.updateCollectivePageMode(id, mode) commit(ADD_OR_UPDATE_COLLECTIVE, response.data.data) }, + /** + * Set the page order for the current user + * + * @param {object} store the vuex store + * @param {Function} store.commit commit changes + * @param {object} data the data object + * @param {number} data.id ID of the colletive to be updated + * @param {number} data.pageOrder the desired page order for the current user + */ async [SET_COLLECTIVE_USER_SETTING_PAGE_ORDER]({ commit }, { id, pageOrder }) { - await axios.put( - generateUrl('/apps/collectives/_api/' + id + '/_userSettings/pageOrder'), - { pageOrder }, - ) + await api.setCollectiveUserSettingPageOrder(id, pageOrder) commit(PATCH_COLLECTIVE_WITH_PROPERTY, { id, property: 'userPageOrder', value: pageOrder }) }, async [SET_COLLECTIVE_USER_SETTING_SHOW_RECENT_PAGES]({ commit }, { id, showRecentPages }) { commit(PATCH_COLLECTIVE_WITH_PROPERTY, { id, property: 'userShowRecentPages', value: showRecentPages }) - await axios.put( - generateUrl('/apps/collectives/_api/' + id + '/_userSettings/showRecentPages'), - { showRecentPages }, - ) + await api.setCollectiveUserSettingShowRecentPages(id, showRecentPages) }, [MARK_COLLECTIVE_DELETED]({ commit }, collective) { diff --git a/src/store/pages.js b/src/store/pages.js index 7ded7bac0..6afe08513 100644 --- a/src/store/pages.js +++ b/src/store/pages.js @@ -1,11 +1,11 @@ import { set } from 'vue' import { getCurrentUser } from '@nextcloud/auth' import { getBuilder } from '@nextcloud/browser-storage' -import axios from '@nextcloud/axios' -import { generateRemoteUrl, generateUrl } from '@nextcloud/router' +import { generateRemoteUrl } from '@nextcloud/router' /* eslint import/namespace: ['error', { allowComputed: true }] */ import * as sortOrders from '../util/sortOrders.js' import { sortedSubpages, pageParents } from './pageExtracts.js' +import * as api from '../apis/collectives/index.js' import { SET_PAGES, @@ -244,46 +244,12 @@ export default { return state.newPage && getters.pagePath(state.newPage) }, - pagesUrl(_state, getters) { - return getters.isPublic - ? generateUrl(`/apps/collectives/_api/p/${getters.shareTokenParam}/_pages`) - : generateUrl(`/apps/collectives/_api/${getters.currentCollective.id}/_pages`) - }, - - pageCreateUrl(_state, getters) { - return parentId => `${getters.pagesUrl}/${parentId}` - }, - - pageUrl(_state, getters) { - return (pageId) => `${getters.pagesUrl}/${pageId}` - }, - - emojiUrl(_state, getters) { - return (pageId) => `${getters.pageUrl(pageId)}/emoji` - }, - - subpageOrderUrl(_state, getters) { - return (pageId) => `${getters.pageUrl(pageId)}/subpageOrder` - }, - - touchUrl(_state, getters) { - return `${getters.pageUrl(getters.currentPage.id)}/touch` - }, - - attachmentsUrl(_state, getters) { - return (pageId) => `${getters.pageUrl(pageId)}/attachments` - }, - - backlinksUrl(_state, getters) { - return (pageId) => `${getters.pageUrl(pageId)}/backlinks` - }, - - trashIndexUrl(_state, getters) { - return `${getters.pagesUrl}/trash` - }, - - trashActionUrl(_state, getters) { - return (pageId) => `${getters.pagesUrl}/trash/${pageId}` + context(_state, getters) { + return { + isPublic: getters.isPublic, + collectiveId: getters.currentCollective.id, + shareTokenParam: getters.shareTokenParam, + } }, pageTitle(state, getters) { @@ -509,7 +475,7 @@ export default { if (setLoading) { commit('load', 'collective') } - const response = await axios.get(getters.pagesUrl) + const response = await api.getPages(getters.context) commit(SET_PAGES, { pages: response.data.data, current: getters.currentPage, @@ -526,7 +492,7 @@ export default { */ async [GET_TRASH_PAGES]({ commit, getters }) { commit('load', 'pageTrash') - const response = await axios.get(getters.trashIndexUrl) + const response = await api.getTrashPages(getters.context) commit(SET_TRASH_PAGES, response.data.data) commit('done', 'pageTrash') }, @@ -541,7 +507,7 @@ export default { * @param {number} pageId Page ID */ async [GET_PAGE]({ commit, getters, state }, pageId) { - const response = await axios.get(getters.pageUrl(pageId)) + const response = await api.getPage(getters.context, pageId) commit(UPDATE_PAGE, response.data.data) }, @@ -559,13 +525,13 @@ export default { // We'll be done when the editor is loaded. commit('load', 'newPageContent') - const response = await axios.post(getters.pageCreateUrl(page.parentId), page) + const response = await api.createPage(getters.context, page) // Add new page to the beginning of pages array commit(ADD_PAGE, response.data.data) }, /** - * Create a new page + * Create a new template page * * @param {object} store the vuex store * @param {Function} store.commit commit changes @@ -581,7 +547,7 @@ export default { // We'll be done when the editor is loaded. commit('load', 'newPageContent') - const response = await axios.post(getters.pageCreateUrl(page.parentId), page) + const response = await api.createPage(getters.context, page) // Add new page to the beginning of pages array commit(ADD_PAGE, response.data.data) }, @@ -594,7 +560,7 @@ export default { * @param {object} store.getters getters of the store */ async [TOUCH_PAGE]({ commit, getters }) { - const response = await axios.get(getters.touchUrl) + const response = await api.touchPage(getters.context, getters.currentPage.id) commit(UPDATE_PAGE, response.data.data) }, @@ -607,8 +573,7 @@ export default { * @param {string} newTitle new title for the page */ async [RENAME_PAGE]({ commit, getters }, newTitle) { - const url = getters.pageUrl(getters.currentPage.id) - const response = await axios.put(url, { title: newTitle }) + const response = await api.renamePage(getters.context, getters.currentPage.id, newTitle) await commit(UPDATE_PAGE, response.data.data) }, @@ -637,9 +602,8 @@ export default { index += 1 } - const url = getters.pageUrl(pageId) try { - await axios.put(url, { index, parentId: newParentId, copy: true }) + await api.copyPage(getters.context, pageId, newParentId, index) // Reload the page list to make new page appear await dispatch(GET_PAGES, false) } finally { @@ -676,9 +640,8 @@ export default { page.parentId = newParentId commit(UPDATE_PAGE, page) - const url = getters.pageUrl(pageId) try { - const response = await axios.put(url, { index, parentId: newParentId }) + const response = await api.movePage(getters.context, pageId, newParentId, index) commit(UPDATE_PAGE, response.data.data) } catch (e) { commit(UPDATE_PAGE, pageClone) @@ -711,8 +674,7 @@ export default { async [COPY_PAGE_TO_COLLECTIVE]({ commit, getters, state, dispatch }, { collectiveId, newParentId, pageId, index }) { commit('load', 'pagelist') - const url = `${getters.pageUrl(pageId)}/to/${collectiveId}` - await axios.put(url, { index, parentId: newParentId, copy: true }) + await api.copyPageToCollective(getters.context, pageId, collectiveId, newParentId, index) commit('done', 'pagelist') }, @@ -735,8 +697,7 @@ export default { const page = { ...state.pages.find(p => p.id === pageId) } const hasSubpages = getters.visibleSubpages(pageId).length > 0 - const url = `${getters.pageUrl(pageId)}/to/${collectiveId}` - await axios.put(url, { index, parentId: newParentId }) + await api.movePageToCollective(getters.context, pageId, collectiveId, newParentId, index) commit(REMOVE_PAGE, page) commit('done', 'pagelist') @@ -759,7 +720,7 @@ export default { */ async [SET_PAGE_EMOJI]({ commit, getters }, { pageId, emoji }) { commit('load', `pageEmoji-${pageId}`) - const response = await axios.put(getters.emojiUrl(pageId), { emoji }) + const response = await api.setPageEmoji(getters.context, pageId, emoji) commit(UPDATE_PAGE, response.data.data) commit('done', `pageEmoji-${pageId}`) }, @@ -788,9 +749,10 @@ export default { commit(UPDATE_PAGE, page) try { - const response = await axios.put( - getters.subpageOrderUrl(pageId), - { subpageOrder: JSON.stringify(subpageOrder) }, + const response = await api.setPageSubpageOrder( + getters.context, + pageId, + JSON.stringify(subpageOrder), ) commit(UPDATE_PAGE, response.data.data) } catch (e) { @@ -811,7 +773,7 @@ export default { * @param {number} page.pageId ID of the page */ async [TRASH_PAGE]({ commit, getters }, { pageId }) { - const response = await axios.delete(getters.pageUrl(pageId)) + const response = await api.trashPage(getters.context, pageId) commit(MOVE_PAGE_INTO_TRASH, response.data.data) }, @@ -825,7 +787,7 @@ export default { * @param {number} page.pageId ID of the page to restore */ async [RESTORE_PAGE]({ commit, getters }, { pageId }) { - const response = await axios.patch(getters.trashActionUrl(pageId)) + const response = await api.restorePage(getters.context, pageId) commit(RESTORE_PAGE_FROM_TRASH, response.data.data) }, @@ -839,7 +801,7 @@ export default { * @param {number} page.pageId ID of the page to delete */ async [DELETE_PAGE]({ commit, getters }, { pageId }) { - axios.delete(getters.trashActionUrl(pageId)) + await api.deletePage(getters.context, pageId) commit(DELETE_PAGE_FROM_TRASH_BY_ID, pageId) }, @@ -852,7 +814,7 @@ export default { * @param {object} page Page to get attachments for */ async [GET_ATTACHMENTS]({ commit, getters }, page) { - const response = await axios.get(getters.attachmentsUrl(page.id)) + const response = await api.getPageAttachments(getters.context, page.id) commit(SET_ATTACHMENTS, { attachments: response.data.data }) }, @@ -865,7 +827,7 @@ export default { * @param {object} page Page to get backlinks for */ async [GET_BACKLINKS]({ commit, getters }, page) { - const response = await axios.get(getters.backlinksUrl(page.id)) + const response = await api.getPageBacklinks(getters.context, page.id) commit(SET_BACKLINKS, { pages: response.data.data }) }, diff --git a/src/store/settings.js b/src/store/settings.js index f7961ada6..9ff6bb75f 100644 --- a/src/store/settings.js +++ b/src/store/settings.js @@ -1,10 +1,9 @@ -import axios from '@nextcloud/axios' -import { generateOcsUrl } from '@nextcloud/router' import { GET_COLLECTIVES_FOLDER, UPDATE_COLLECTIVES_FOLDER, } from './actions.js' import { SET_COLLECTIVES_FOLDER } from './mutations.js' +import * as settings from '../apis/collectives/settings.js' export default { state: { @@ -25,7 +24,7 @@ export default { * @param {Function} store.commit commit changes */ async [GET_COLLECTIVES_FOLDER]({ commit }) { - const response = await axios.get(generateOcsUrl('apps/collectives/api/v1.0/settings/user/user_folder')) + const response = await settings.getCollectivesFolder() commit(SET_COLLECTIVES_FOLDER, response.data.ocs.data) }, @@ -37,10 +36,7 @@ export default { * @param {string} collectivesFolder path to collectives folder */ async [UPDATE_COLLECTIVES_FOLDER]({ commit }, collectivesFolder) { - const response = await axios.post(generateOcsUrl('apps/collectives/api/v1.0/settings/user'), { - key: 'user_folder', - value: collectivesFolder, - }) + const response = await settings.setCollectivesFolder(collectivesFolder) commit(SET_COLLECTIVES_FOLDER, response.data.ocs.data) }, },