Add new OCS endpoint to get file info from file ID (#67)
Signed-off-by: Julien Veyssier <eneiluj@posteo.net> Signed-off-by: Artur Neumann <artur@jankaritech.com>
This commit is contained in:
Родитель
264bb3ee10
Коммит
a1aa2a3b5a
|
@ -80,7 +80,21 @@ jobs:
|
|||
run: npm run stylelint
|
||||
|
||||
- name: PHP & Vue Unit Tests
|
||||
run: make test
|
||||
run: |
|
||||
make phpunit
|
||||
make jsunit
|
||||
|
||||
- name: API Tests
|
||||
env:
|
||||
NEXTCLOUD_BASE_URL: http://localhost:8080
|
||||
run: |
|
||||
mkdir -p server/apps/integration_openproject
|
||||
cp -r `ls -A | grep -v 'server'` server/apps/integration_openproject/
|
||||
cd server
|
||||
./occ a:e integration_openproject
|
||||
php -S localhost:8080 2> /dev/null &
|
||||
cd apps/integration_openproject
|
||||
make api-test
|
||||
|
||||
- name: JS Code Coverage Summary Report
|
||||
if: ${{ github.event_name == 'pull_request' && matrix.nextcloudVersion == 'master' && matrix.phpVersion == '7.4' }}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<screenshot>https://github.com/eneiluj/integration_openproject/raw/master/img/screenshot1.jpg</screenshot>
|
||||
<screenshot>https://github.com/eneiluj/integration_openproject/raw/master/img/screenshot2.jpg</screenshot>
|
||||
<dependencies>
|
||||
<nextcloud min-version="22" max-version="24"/>
|
||||
<nextcloud min-version="22" max-version="25"/>
|
||||
</dependencies>
|
||||
<background-jobs>
|
||||
<job>OCA\OpenProject\BackgroundJob\CheckNotifications</job>
|
||||
|
|
|
@ -22,6 +22,10 @@ return [
|
|||
['name' => 'openProjectAPI#getWorkPackageFileLinks', 'url' => '/work-packages/{id}/file-links', 'verb' => 'GET'],
|
||||
['name' => 'openProjectAPI#getOpenProjectWorkPackageStatus', 'url' => '/statuses/{id}', 'verb' => 'GET'],
|
||||
['name' => 'openProjectAPI#getOpenProjectWorkPackageType', 'url' => '/types/{id}', 'verb' => 'GET'],
|
||||
],
|
||||
'ocs' => [
|
||||
['name' => 'files#getFileInfo', 'url' => '/fileinfo/{fileId}', 'verb' => 'GET'],
|
||||
['name' => 'files#getFilesInfo', 'url' => '/filesinfo', 'verb' => 'POST'],
|
||||
['name' => 'openProjectAPI#deleteFileLink', 'url' => '/file-links/{id}', 'verb' => 'DELETE'],
|
||||
]
|
||||
];
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
"nextcloud/coding-standard": "^1.0",
|
||||
"phpstan/phpstan": "^1.4",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"phpstan/extension-installer": "^1.1"
|
||||
"phpstan/extension-installer": "^1.1",
|
||||
"behat/behat": "^3.10",
|
||||
"helmich/phpunit-json-assert": "^3.4"
|
||||
},
|
||||
"scripts": {
|
||||
"cs:fix": "php-cs-fixer fix",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "357ea72dc7b1aec7aa1760af659e34a5",
|
||||
"content-hash": "cc2e607146701bef546d5dc2a149b9bd",
|
||||
"packages": [],
|
||||
"packages-dev": [
|
||||
{
|
||||
|
@ -996,6 +996,205 @@
|
|||
],
|
||||
"time": "2020-07-10T16:13:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "behat/behat",
|
||||
"version": "v3.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Behat/Behat.git",
|
||||
"reference": "a55661154079cf881ef643b303bfaf67bae3a09f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Behat/Behat/zipball/a55661154079cf881ef643b303bfaf67bae3a09f",
|
||||
"reference": "a55661154079cf881ef643b303bfaf67bae3a09f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"behat/gherkin": "^4.9.0",
|
||||
"behat/transliterator": "^1.2",
|
||||
"ext-mbstring": "*",
|
||||
"php": "^7.2 || ^8.0",
|
||||
"psr/container": "^1.0",
|
||||
"symfony/config": "^4.4 || ^5.0 || ^6.0",
|
||||
"symfony/console": "^4.4 || ^5.0 || ^6.0",
|
||||
"symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0",
|
||||
"symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0",
|
||||
"symfony/translation": "^4.4 || ^5.0 || ^6.0",
|
||||
"symfony/yaml": "^4.4 || ^5.0 || ^6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"container-interop/container-interop": "^1.2",
|
||||
"herrera-io/box": "~1.6.1",
|
||||
"phpunit/phpunit": "^8.5 || ^9.0",
|
||||
"symfony/process": "^4.4 || ^5.0 || ^6.0",
|
||||
"vimeo/psalm": "^4.8"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-dom": "Needed to output test results in JUnit format."
|
||||
},
|
||||
"bin": [
|
||||
"bin/behat"
|
||||
],
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Behat\\Hook\\": "src/Behat/Hook/",
|
||||
"Behat\\Step\\": "src/Behat/Step/",
|
||||
"Behat\\Behat\\": "src/Behat/Behat/",
|
||||
"Behat\\Testwork\\": "src/Behat/Testwork/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Konstantin Kudryashov",
|
||||
"email": "ever.zet@gmail.com",
|
||||
"homepage": "http://everzet.com"
|
||||
}
|
||||
],
|
||||
"description": "Scenario-oriented BDD framework for PHP",
|
||||
"homepage": "http://behat.org/",
|
||||
"keywords": [
|
||||
"Agile",
|
||||
"BDD",
|
||||
"ScenarioBDD",
|
||||
"Scrum",
|
||||
"StoryBDD",
|
||||
"User story",
|
||||
"business",
|
||||
"development",
|
||||
"documentation",
|
||||
"examples",
|
||||
"symfony",
|
||||
"testing"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Behat/Behat/issues",
|
||||
"source": "https://github.com/Behat/Behat/tree/v3.10.0"
|
||||
},
|
||||
"time": "2021-11-02T20:09:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "behat/gherkin",
|
||||
"version": "v4.9.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Behat/Gherkin.git",
|
||||
"reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Behat/Gherkin/zipball/0bc8d1e30e96183e4f36db9dc79caead300beff4",
|
||||
"reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "~7.2|~8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cucumber/cucumber": "dev-gherkin-22.0.0",
|
||||
"phpunit/phpunit": "~8|~9",
|
||||
"symfony/yaml": "~3|~4|~5"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/yaml": "If you want to parse features, represented in YAML files"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Behat\\Gherkin": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Konstantin Kudryashov",
|
||||
"email": "ever.zet@gmail.com",
|
||||
"homepage": "http://everzet.com"
|
||||
}
|
||||
],
|
||||
"description": "Gherkin DSL parser for PHP",
|
||||
"homepage": "http://behat.org/",
|
||||
"keywords": [
|
||||
"BDD",
|
||||
"Behat",
|
||||
"Cucumber",
|
||||
"DSL",
|
||||
"gherkin",
|
||||
"parser"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Behat/Gherkin/issues",
|
||||
"source": "https://github.com/Behat/Gherkin/tree/v4.9.0"
|
||||
},
|
||||
"time": "2021-10-12T13:05:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "behat/transliterator",
|
||||
"version": "v1.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Behat/Transliterator.git",
|
||||
"reference": "baac5873bac3749887d28ab68e2f74db3a4408af"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Behat/Transliterator/zipball/baac5873bac3749887d28ab68e2f74db3a4408af",
|
||||
"reference": "baac5873bac3749887d28ab68e2f74db3a4408af",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"chuyskywalker/rolling-curl": "^3.1",
|
||||
"php-yaoi/php-yaoi": "^1.0",
|
||||
"phpunit/phpunit": "^8.5.25 || ^9.5.19"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Behat\\Transliterator\\": "src/Behat/Transliterator"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Artistic-1.0"
|
||||
],
|
||||
"description": "String transliterator",
|
||||
"keywords": [
|
||||
"i18n",
|
||||
"slug",
|
||||
"transliterator"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Behat/Transliterator/issues",
|
||||
"source": "https://github.com/Behat/Transliterator/tree/v1.5.0"
|
||||
},
|
||||
"time": "2022-03-30T09:27:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "cash/lrucache",
|
||||
"version": "1.0.0",
|
||||
|
@ -1932,6 +2131,134 @@
|
|||
],
|
||||
"time": "2021-10-06T17:43:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "helmich/phpunit-json-assert",
|
||||
"version": "v3.4.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/martin-helmich/phpunit-json-assert.git",
|
||||
"reference": "e2ff61f042c6c86566db297f7da13538bfb80140"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/martin-helmich/phpunit-json-assert/zipball/e2ff61f042c6c86566db297f7da13538bfb80140",
|
||||
"reference": "e2ff61f042c6c86566db297f7da13538bfb80140",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"justinrainbow/json-schema": "^5.0",
|
||||
"php": "^7.2 || ^8.0",
|
||||
"softcreatr/jsonpath": "^0.7.2"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit": "<8.0 || >= 10.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.0 || ^9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Helmich\\JsonAssert\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Martin Helmich",
|
||||
"email": "m.helmich@mittwald.de"
|
||||
}
|
||||
],
|
||||
"description": "PHPUnit assertions for JSON documents",
|
||||
"support": {
|
||||
"issues": "https://github.com/martin-helmich/phpunit-json-assert/issues",
|
||||
"source": "https://github.com/martin-helmich/phpunit-json-assert/tree/v3.4.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://donate.helmich.me",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/martin-helmich",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-03-29T06:47:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "justinrainbow/json-schema",
|
||||
"version": "5.2.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/justinrainbow/json-schema.git",
|
||||
"reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/ad87d5a5ca981228e0e205c2bc7dfb8e24559b60",
|
||||
"reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1",
|
||||
"json-schema/json-schema-test-suite": "1.2.0",
|
||||
"phpunit/phpunit": "^4.8.35"
|
||||
},
|
||||
"bin": [
|
||||
"bin/validate-json"
|
||||
],
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"JsonSchema\\": "src/JsonSchema/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Bruno Prieto Reis",
|
||||
"email": "bruno.p.reis@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Justin Rainbow",
|
||||
"email": "justin.rainbow@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Igor Wiedler",
|
||||
"email": "igor@wiedler.ch"
|
||||
},
|
||||
{
|
||||
"name": "Robert Schönthal",
|
||||
"email": "seroscho@googlemail.com"
|
||||
}
|
||||
],
|
||||
"description": "A library to validate a json schema.",
|
||||
"homepage": "https://github.com/justinrainbow/json-schema",
|
||||
"keywords": [
|
||||
"json",
|
||||
"schema"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/justinrainbow/json-schema/issues",
|
||||
"source": "https://github.com/justinrainbow/json-schema/tree/5.2.12"
|
||||
},
|
||||
"time": "2022-04-13T08:02:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "kelunik/certificate",
|
||||
"version": "v1.1.2",
|
||||
|
@ -4887,6 +5214,150 @@
|
|||
],
|
||||
"time": "2020-09-28T06:39:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "softcreatr/jsonpath",
|
||||
"version": "0.7.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/SoftCreatR/JSONPath.git",
|
||||
"reference": "008569bf80aa3584834f7890781576bc7b65afa7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/SoftCreatR/JSONPath/zipball/008569bf80aa3584834f7890781576bc7b65afa7",
|
||||
"reference": "008569bf80aa3584834f7890781576bc7b65afa7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"replace": {
|
||||
"flow/jsonpath": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": ">=7.0",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Flow\\JSONPath\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Stephen Frank",
|
||||
"email": "stephen@flowsa.com",
|
||||
"homepage": "https://prismaticbytes.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Sascha Greuel",
|
||||
"email": "hello@1-2.dev",
|
||||
"homepage": "http://1-2.dev",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "JSONPath implementation for parsing, searching and flattening arrays",
|
||||
"support": {
|
||||
"email": "hello@1-2.dev",
|
||||
"forum": "https://github.com/SoftCreatR/JSONPath/discussions",
|
||||
"issues": "https://github.com/SoftCreatR/JSONPath/issues",
|
||||
"source": "https://github.com/SoftCreatR/JSONPath"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/softcreatr",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-06-02T22:15:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/config",
|
||||
"version": "v5.4.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/config.git",
|
||||
"reference": "05624c386afa1b4ccc1357463d830fade8d9d404"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/config/zipball/05624c386afa1b4ccc1357463d830fade8d9d404",
|
||||
"reference": "05624c386afa1b4ccc1357463d830fade8d9d404",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/deprecation-contracts": "^2.1|^3",
|
||||
"symfony/filesystem": "^4.4|^5.0|^6.0",
|
||||
"symfony/polyfill-ctype": "~1.8",
|
||||
"symfony/polyfill-php80": "^1.16",
|
||||
"symfony/polyfill-php81": "^1.22"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/finder": "<4.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/event-dispatcher": "^4.4|^5.0|^6.0",
|
||||
"symfony/finder": "^4.4|^5.0|^6.0",
|
||||
"symfony/messenger": "^4.4|^5.0|^6.0",
|
||||
"symfony/service-contracts": "^1.1|^2|^3",
|
||||
"symfony/yaml": "^4.4|^5.0|^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/yaml": "To use the yaml reference dumper"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Config\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/config/tree/v5.4.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-21T13:42:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v5.4.3",
|
||||
|
@ -4986,6 +5457,95 @@
|
|||
],
|
||||
"time": "2022-01-26T16:28:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/dependency-injection",
|
||||
"version": "v5.4.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/dependency-injection.git",
|
||||
"reference": "35588b2afb08ea3a142d62fefdcad4cb09be06ed"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/35588b2afb08ea3a142d62fefdcad4cb09be06ed",
|
||||
"reference": "35588b2afb08ea3a142d62fefdcad4cb09be06ed",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"psr/container": "^1.1.1",
|
||||
"symfony/deprecation-contracts": "^2.1|^3",
|
||||
"symfony/polyfill-php80": "^1.16",
|
||||
"symfony/polyfill-php81": "^1.22",
|
||||
"symfony/service-contracts": "^1.1.6|^2"
|
||||
},
|
||||
"conflict": {
|
||||
"ext-psr": "<1.1|>=2",
|
||||
"symfony/config": "<5.3",
|
||||
"symfony/finder": "<4.4",
|
||||
"symfony/proxy-manager-bridge": "<4.4",
|
||||
"symfony/yaml": "<4.4.26"
|
||||
},
|
||||
"provide": {
|
||||
"psr/container-implementation": "1.0",
|
||||
"symfony/service-implementation": "1.0|2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/config": "^5.3|^6.0",
|
||||
"symfony/expression-language": "^4.4|^5.0|^6.0",
|
||||
"symfony/yaml": "^4.4.26|^5.0|^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/config": "",
|
||||
"symfony/expression-language": "For using expressions in service container configuration",
|
||||
"symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required",
|
||||
"symfony/proxy-manager-bridge": "Generate service proxies to lazy load them",
|
||||
"symfony/yaml": ""
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\DependencyInjection\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Allows you to standardize and centralize the way objects are constructed in your application",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/dependency-injection/tree/v5.4.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-08T15:43:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v2.5.0",
|
||||
|
@ -6277,6 +6837,256 @@
|
|||
],
|
||||
"time": "2022-01-02T09:53:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
"version": "v5.4.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation.git",
|
||||
"reference": "e1eb790575202ee3ac2659f55b93b05853726f8e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/e1eb790575202ee3ac2659f55b93b05853726f8e",
|
||||
"reference": "e1eb790575202ee3ac2659f55b93b05853726f8e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/deprecation-contracts": "^2.1|^3",
|
||||
"symfony/polyfill-mbstring": "~1.0",
|
||||
"symfony/polyfill-php80": "^1.16",
|
||||
"symfony/translation-contracts": "^2.3"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/config": "<4.4",
|
||||
"symfony/console": "<5.3",
|
||||
"symfony/dependency-injection": "<5.0",
|
||||
"symfony/http-kernel": "<5.0",
|
||||
"symfony/twig-bundle": "<5.0",
|
||||
"symfony/yaml": "<4.4"
|
||||
},
|
||||
"provide": {
|
||||
"symfony/translation-implementation": "2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/log": "^1|^2|^3",
|
||||
"symfony/config": "^4.4|^5.0|^6.0",
|
||||
"symfony/console": "^5.4|^6.0",
|
||||
"symfony/dependency-injection": "^5.0|^6.0",
|
||||
"symfony/finder": "^4.4|^5.0|^6.0",
|
||||
"symfony/http-client-contracts": "^1.1|^2.0|^3.0",
|
||||
"symfony/http-kernel": "^5.0|^6.0",
|
||||
"symfony/intl": "^4.4|^5.0|^6.0",
|
||||
"symfony/polyfill-intl-icu": "^1.21",
|
||||
"symfony/service-contracts": "^1.1.2|^2|^3",
|
||||
"symfony/yaml": "^4.4|^5.0|^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log-implementation": "To use logging capability in translator",
|
||||
"symfony/config": "",
|
||||
"symfony/yaml": ""
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"Resources/functions.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Translation\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Provides tools to internationalize your application",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/translation/tree/v5.4.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-24T17:09:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation-contracts",
|
||||
"version": "v2.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation-contracts.git",
|
||||
"reference": "1211df0afa701e45a04253110e959d4af4ef0f07"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation-contracts/zipball/1211df0afa701e45a04253110e959d4af4ef0f07",
|
||||
"reference": "1211df0afa701e45a04253110e959d4af4ef0f07",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/translation-implementation": ""
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "2.5-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
"url": "https://github.com/symfony/contracts"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Contracts\\Translation\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Generic abstractions related to translation",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"abstractions",
|
||||
"contracts",
|
||||
"decoupling",
|
||||
"interfaces",
|
||||
"interoperability",
|
||||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/translation-contracts/tree/v2.5.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-02T09:53:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v5.4.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "e80f87d2c9495966768310fc531b487ce64237a2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/e80f87d2c9495966768310fc531b487ce64237a2",
|
||||
"reference": "e80f87d2c9495966768310fc531b487ce64237a2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/deprecation-contracts": "^2.1|^3",
|
||||
"symfony/polyfill-ctype": "^1.8"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/console": "<5.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/console": "^5.3|^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/console": "For validating YAML files using the lint command"
|
||||
},
|
||||
"bin": [
|
||||
"Resources/bin/yaml-lint"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Yaml\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Loads and dumps YAML files",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/yaml/tree/v5.4.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-26T16:32:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
"version": "1.2.1",
|
||||
|
@ -6393,5 +7203,5 @@
|
|||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.2.0"
|
||||
"plugin-api-version": "2.0.0"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
/**
|
||||
* Nextcloud - openproject
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later. See the COPYING file.
|
||||
*
|
||||
* @author Julien Veyssier <eneiluj@posteo.net>
|
||||
* @copyright Julien Veyssier 2022
|
||||
*/
|
||||
|
||||
namespace OCA\OpenProject\Controller;
|
||||
|
||||
use OC\User\User;
|
||||
use OCA\Files_Trashbin\Trash\ITrashManager;
|
||||
use OCP\Files\Config\IMountProviderCollection;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\IRequest;
|
||||
use OCP\AppFramework\OCSController;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserSession;
|
||||
|
||||
class FilesController extends OCSController {
|
||||
|
||||
/**
|
||||
* @var IUser|null
|
||||
*/
|
||||
private $user;
|
||||
/**
|
||||
* @var IRootFolder
|
||||
*/
|
||||
private $rootFolder;
|
||||
|
||||
/**
|
||||
* @var ITrashManager
|
||||
*/
|
||||
private $trashManager;
|
||||
|
||||
/**
|
||||
* @var IMountProviderCollection
|
||||
*/
|
||||
private $mountCollection;
|
||||
|
||||
public function __construct(string $appName,
|
||||
IRequest $request,
|
||||
IRootFolder $rootFolder,
|
||||
ITrashManager $trashManager,
|
||||
IUserSession $userSession,
|
||||
IMountProviderCollection $mountCollection) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->user = $userSession->getUser();
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->trashManager = $trashManager;
|
||||
$this->mountCollection = $mountCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* get file info from file ID
|
||||
*
|
||||
* This can be tested with
|
||||
* curl -H "Accept: application/json" -H "OCS-APIRequest: true" -u USER:PASSWD
|
||||
* http://my.nc.org/ocs/v1.php/apps/integration_openproject/fileinfo/FILE_ID
|
||||
* @NoAdminRequired
|
||||
*
|
||||
*/
|
||||
public function getFileInfo(int $fileId): DataResponse {
|
||||
$fileInfo = $this->compileFileInfo($fileId);
|
||||
return new DataResponse($fileInfo, $fileInfo['statuscode']);
|
||||
}
|
||||
|
||||
/**
|
||||
* get file info from file IDs
|
||||
*
|
||||
* This can be tested with:
|
||||
* curl -H "Accept: application/json" -H "Content-Type:application/json" -H "OCS-APIRequest: true"
|
||||
* -u USER:PASSWD http://my.nc.org/ocs/v1.php/apps/integration_openproject/filesinfo
|
||||
* -X POST -d '{"fileIds":[FILE_ID_1,FILE_ID_2,...]}'
|
||||
*
|
||||
* @param array<int>|null $fileIds
|
||||
* @NoAdminRequired
|
||||
*
|
||||
*/
|
||||
public function getFilesInfo(?array $fileIds): DataResponse {
|
||||
if (!is_array($fileIds)) {
|
||||
return new DataResponse('invalid request', Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
$result = [];
|
||||
foreach ($fileIds as $fileId) {
|
||||
$result[$fileId] = $this->compileFileInfo($fileId);
|
||||
}
|
||||
return new DataResponse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $fileId
|
||||
* @return array{'status': string, 'statuscode': int, 'id'?: int, 'name'?:string,
|
||||
* 'mtime'?: int, 'ctime'?: int, 'mimetype'?: string, 'path'?: string,
|
||||
* 'size'?: int, 'owner_name'?: string, 'owner_id'?: string}
|
||||
*/
|
||||
private function compileFileInfo($fileId) {
|
||||
$userFolder = $this->rootFolder->getUserFolder($this->user->getUID());
|
||||
$files = $userFolder->getById($fileId);
|
||||
if (is_array($files) && count($files) > 0) {
|
||||
$file = $files[0];
|
||||
$trashed = false;
|
||||
} else {
|
||||
$file = $this->trashManager->getTrashNodeById(
|
||||
$this->user, $fileId
|
||||
);
|
||||
$trashed = true;
|
||||
}
|
||||
|
||||
$mount = $this->mountCollection->getMountCache()->getMountsForFileId($fileId);
|
||||
|
||||
if ($file !== null && is_array($mount) && count($mount) > 0) {
|
||||
$owner = $file->getOwner();
|
||||
$internalPath = $mount[0]->getInternalPath();
|
||||
|
||||
return [
|
||||
'status' => 'OK',
|
||||
'statuscode' => 200,
|
||||
'id' => $file->getId(),
|
||||
'name' => basename($internalPath),
|
||||
'mtime' => $file->getMTime(),
|
||||
'ctime' => $file->getCreationTime(),
|
||||
'mimetype' => $file->getMimetype(),
|
||||
'size' => $file->getSize(),
|
||||
'owner_name' => $owner->getDisplayName(),
|
||||
'owner_id' => $owner->getUID(),
|
||||
'trashed' => $trashed
|
||||
];
|
||||
}
|
||||
|
||||
if (is_array($mount) && count($mount) > 0) {
|
||||
return [
|
||||
'status' => 'Forbidden',
|
||||
'statuscode' => 403,
|
||||
];
|
||||
}
|
||||
return [
|
||||
'status' => 'Not Found',
|
||||
'statuscode' => 404,
|
||||
];
|
||||
}
|
||||
}
|
14
makefile
14
makefile
|
@ -59,14 +59,20 @@ npm-dev:
|
|||
phpstan:
|
||||
composer run phpstan
|
||||
|
||||
.PHONY: test
|
||||
.PHONY: phpunit
|
||||
phpunit:
|
||||
vendor/phpunit/phpunit/phpunit
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
.PHONY: jsunit
|
||||
jsunit:
|
||||
npm run test:unit
|
||||
vendor/phpunit/phpunit/phpunit
|
||||
|
||||
.PHONY: api-test
|
||||
api-test:
|
||||
vendor/bin/behat -c tests/acceptance/config/behat.yml
|
||||
|
||||
.PHONY: test
|
||||
test: phpunit jsunit api-test
|
||||
|
||||
clean:
|
||||
sudo rm -rf $(build_dir)
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
default:
|
||||
autoload:
|
||||
'': '%paths.base%/../features/bootstrap'
|
||||
suites:
|
||||
api:
|
||||
paths:
|
||||
- '%paths.base%/../features/api'
|
||||
contexts:
|
||||
- FeatureContext:
|
||||
baseUrl: http://localhost
|
||||
adminUsername: admin
|
||||
adminPassword: admin
|
||||
regularUserPassword: 123456
|
|
@ -0,0 +1,438 @@
|
|||
Feature: retrieve file information of a single file, using the file ID
|
||||
|
||||
Scenario: get information of an existing file
|
||||
Given user "Alice" has been created
|
||||
And user "Alice" has uploaded file with content "some data" to "file.txt"
|
||||
When user "Alice" gets the information of last created file
|
||||
Then the HTTP status code should be "200"
|
||||
And the data of the response should match
|
||||
""""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode",
|
||||
"id",
|
||||
"size",
|
||||
"name",
|
||||
"mtime",
|
||||
"ctime",
|
||||
"mimetype",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
],
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^OK$"},
|
||||
"statuscode" : {"type" : "number", "enum": [200]},
|
||||
"id" : {"type" : "integer", "minimum": 1, "maximum": 99999},
|
||||
"size" : {"type" : "integer", "enum": [9] },
|
||||
"mtime" : {"type" : "integer"},
|
||||
"ctime" : {"type" : "integer", "enum": [0]},
|
||||
"name": {"type": "string", "pattern": "^file.txt$"},
|
||||
"mimetype": {"type": "string", "pattern": "^text\/plain$"},
|
||||
"owner_id": {"type": "string", "pattern": "^Alice$"},
|
||||
"owner_name": {"type": "string", "pattern": "^Alice$"},
|
||||
"trashed": {"type": "boolean", "enum": [false]}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: get information of an existing file in a subfolder
|
||||
Given user "Alice" has been created
|
||||
And user "Alice" has created folder "/subfolder"
|
||||
And user "Alice" has uploaded file with content "some data" to "/subfolder/file.txt"
|
||||
When user "Alice" gets the information of last created file
|
||||
Then the HTTP status code should be "200"
|
||||
And the data of the response should match
|
||||
""""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode",
|
||||
"id",
|
||||
"size",
|
||||
"name",
|
||||
"mtime",
|
||||
"ctime",
|
||||
"mimetype",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
],
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^OK$"},
|
||||
"statuscode" : {"type" : "number", "enum": [200] },
|
||||
"id" : {"type" : "integer", "minimum": 1, "maximum": 99999},
|
||||
"size" : {"type" : "integer", "enum": [9] },
|
||||
"mtime" : {"type" : "integer"},
|
||||
"ctime" : {"type" : "integer", "enum": [0]},
|
||||
"name": {"type": "string", "pattern": "^file.txt$"},
|
||||
"mimetype": {"type": "string", "pattern": "^text\/plain$"},
|
||||
"owner_id": {"type": "string", "pattern": "^Alice$"},
|
||||
"owner_name": {"type": "string", "pattern": "^Alice$"},
|
||||
"trashed": {"type": "boolean", "enum": [false]}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: get information of a trashed file
|
||||
Given user "Alice" has been created
|
||||
And user "Alice" has uploaded file with content "some data" to "file.txt"
|
||||
And user "Alice" has deleted file "file.txt"
|
||||
When user "Alice" gets the information of last created file
|
||||
Then the HTTP status code should be "200"
|
||||
And the data of the response should match
|
||||
""""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode",
|
||||
"id",
|
||||
"size",
|
||||
"name",
|
||||
"mtime",
|
||||
"ctime",
|
||||
"mimetype",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
],
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^OK$"},
|
||||
"statuscode" : {"type" : "number", "enum": [200]},
|
||||
"id" : {"type" : "integer", "minimum": 1, "maximum": 99999},
|
||||
"size" : {"type" : "integer", "enum": [9] },
|
||||
"mtime" : {"type" : "integer"},
|
||||
"ctime" : {"type" : "integer", "enum": [0]},
|
||||
"name": {"type": "string", "pattern": "^file.txt.d\\d{10}$"},
|
||||
"mimetype": {"type": "string", "pattern": "^text\/plain$"},
|
||||
"owner_id": {"type": "string", "pattern": "^Alice$"},
|
||||
"owner_name": {"type": "string", "pattern": "^Alice$"},
|
||||
"trashed": {"type": "boolean", "enum": [true]}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: get information of a file that is inside of a trashed folder
|
||||
Given user "Alice" has been created
|
||||
And user "Alice" has created folder "/subfolder"
|
||||
And user "Alice" has uploaded file with content "some data" to "/subfolder/file.txt"
|
||||
And user "Alice" has deleted folder "subfolder"
|
||||
When user "Alice" gets the information of last created file
|
||||
Then the HTTP status code should be "200"
|
||||
And the data of the response should match
|
||||
""""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode",
|
||||
"id",
|
||||
"size",
|
||||
"name",
|
||||
"mtime",
|
||||
"ctime",
|
||||
"mimetype",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
],
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^OK$"},
|
||||
"statuscode" : {"type" : "number", "enum": [200]},
|
||||
"id" : {"type" : "integer", "minimum": 1, "maximum": 99999},
|
||||
"size" : {"type" : "integer", "enum": [9] },
|
||||
"mtime" : {"type" : "integer"},
|
||||
"ctime" : {"type" : "integer", "enum": [0]},
|
||||
"name": {"type": "string", "pattern": "^file.txt$"},
|
||||
"mimetype": {"type": "string", "pattern": "^text\/plain$"},
|
||||
"owner_id": {"type": "string", "pattern": "^Alice$"},
|
||||
"owner_name": {"type": "string", "pattern": "^Alice$"},
|
||||
"trashed": {"type": "boolean", "enum": [true]}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: get information of a file owned by an different user
|
||||
Given user "Alice" has been created
|
||||
And user "Brian" has been created
|
||||
And user "Alice" has uploaded file with content "some data" to "file.txt"
|
||||
When user "Brian" gets the information of last created file
|
||||
Then the HTTP status code should be "403"
|
||||
And the data of the response should match
|
||||
""""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode"
|
||||
],
|
||||
"not": {
|
||||
"required": [
|
||||
"id",
|
||||
"size",
|
||||
"name",
|
||||
"mtime",
|
||||
"ctime",
|
||||
"mimetype",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^Forbidden$"},
|
||||
"statuscode" : {"type" : "number", "enum": [403]}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: get information of a non-existing file
|
||||
Given user "Alice" has been created
|
||||
When user "Brian" gets the information of the file with the id "9999999999999"
|
||||
Then the HTTP status code should be "404"
|
||||
And the data of the response should match
|
||||
""""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode"
|
||||
],
|
||||
"not": {
|
||||
"required": [
|
||||
"id",
|
||||
"size",
|
||||
"name",
|
||||
"mtime",
|
||||
"ctime",
|
||||
"mimetype",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^Not Found$"},
|
||||
"statuscode" : {"type" : "number", "enum": [404]}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: get information of a file received as a share
|
||||
Given user "Alice" has been created
|
||||
And user "Brian" has been created
|
||||
And user "Alice" has uploaded file with content "some data" to "/file.txt"
|
||||
And user "Alice" has shared file "/file.txt" with user "Brian"
|
||||
When user "Brian" gets the information of last created file
|
||||
Then the HTTP status code should be "200"
|
||||
And the data of the response should match
|
||||
""""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode",
|
||||
"name",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
],
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^OK$"},
|
||||
"statuscode" : {"type" : "number", "enum": [200] },
|
||||
"name": {"type": "string", "pattern": "^file.txt$"},
|
||||
"owner_id": {"type": "string", "pattern": "^Alice$"},
|
||||
"owner_name": {"type": "string", "pattern": "^Alice$"},
|
||||
"trashed": {"type": "boolean", "enum": [false]}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: get information of a file that is in a folder received as a share
|
||||
Given user "Alice" has been created
|
||||
And user "Brian" has been created
|
||||
And user "Alice" has created folder "/to-share"
|
||||
And user "Alice" has uploaded file with content "some data" to "/to-share/file.txt"
|
||||
And user "Alice" has shared folder "/to-share" with user "Brian"
|
||||
When user "Brian" gets the information of last created file
|
||||
Then the HTTP status code should be "200"
|
||||
And the data of the response should match
|
||||
""""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode",
|
||||
"name",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
],
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^OK$"},
|
||||
"statuscode" : {"type" : "number", "enum": [200] },
|
||||
"name": {"type": "string", "pattern": "^file.txt$"},
|
||||
"owner_id": {"type": "string", "pattern": "^Alice$"},
|
||||
"owner_name": {"type": "string", "pattern": "^Alice$"},
|
||||
"trashed": {"type": "boolean", "enum": [false]}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: get information of a file that is received through a folder and a file share
|
||||
Given user "Alice" has been created
|
||||
And user "Brian" has been created
|
||||
And user "Alice" has created folder "/to-share"
|
||||
And user "Alice" has uploaded file with content "some data" to "/to-share/file.txt"
|
||||
And user "Alice" has shared folder "/to-share" with user "Brian"
|
||||
And user "Alice" has shared file "/to-share/file.txt" with user "Brian"
|
||||
When user "Brian" gets the information of last created file
|
||||
Then the HTTP status code should be "200"
|
||||
And the data of the response should match
|
||||
""""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode",
|
||||
"name",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
],
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^OK$"},
|
||||
"statuscode" : {"type" : "number", "enum": [200] },
|
||||
"name": {"type": "string", "pattern": "^file.txt$"},
|
||||
"owner_id": {"type": "string", "pattern": "^Alice$"},
|
||||
"owner_name": {"type": "string", "pattern": "^Alice$"},
|
||||
"trashed": {"type": "boolean", "enum": [false]}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: get information of a file received as a share and renamed
|
||||
Given user "Alice" has been created
|
||||
And user "Brian" has been created
|
||||
And user "Alice" has uploaded file with content "some data" to "/file.txt"
|
||||
And user "Alice" has shared file "/file.txt" with user "Brian"
|
||||
And user "Brian" has renamed file "/file.txt" to "/renamed.txt"
|
||||
When user "Brian" gets the information of last created file
|
||||
Then the HTTP status code should be "200"
|
||||
And the data of the response should match
|
||||
""""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode",
|
||||
"name",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
],
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^OK$"},
|
||||
"statuscode" : {"type" : "number", "enum": [200] },
|
||||
"name": {"type": "string", "pattern": "^file.txt$"},
|
||||
"owner_id": {"type": "string", "pattern": "^Alice$"},
|
||||
"owner_name": {"type": "string", "pattern": "^Alice$"},
|
||||
"trashed": {"type": "boolean", "enum": [false]}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: get information of a file received in a folder share and renamed
|
||||
Given user "Alice" has been created
|
||||
And user "Brian" has been created
|
||||
And user "Alice" has created folder "/to-share"
|
||||
And user "Alice" has uploaded file with content "some data" to "/to-share/file.txt"
|
||||
And user "Alice" has shared folder "/to-share" with user "Brian"
|
||||
And user "Brian" has renamed file "/to-share/file.txt" to "/to-share/renamed.txt"
|
||||
When user "Brian" gets the information of last created file
|
||||
Then the HTTP status code should be "200"
|
||||
And the data of the response should match
|
||||
""""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode",
|
||||
"name",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
],
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^OK$"},
|
||||
"statuscode" : {"type" : "number", "enum": [200] },
|
||||
"name": {"type": "string", "pattern": "^renamed.txt$"},
|
||||
"owner_id": {"type": "string", "pattern": "^Alice$"},
|
||||
"owner_name": {"type": "string", "pattern": "^Alice$"},
|
||||
"trashed": {"type": "boolean", "enum": [false]}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: get information of a file received in a folder share and moved out of that share
|
||||
Given user "Alice" has been created
|
||||
And user "Brian" has been created
|
||||
And user "Alice" has created folder "/to-share"
|
||||
And user "Alice" has uploaded file with content "some data" to "/to-share/file.txt"
|
||||
And user "Alice" has shared folder "/to-share" with user "Brian"
|
||||
And user "Brian" has renamed file "/to-share/file.txt" to "/moved-out.txt"
|
||||
When user "Brian" gets the information of last created file
|
||||
Then the HTTP status code should be "200"
|
||||
And the data of the response should match
|
||||
""""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode",
|
||||
"name",
|
||||
"owner_id",
|
||||
"owner_name"
|
||||
],
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^OK$"},
|
||||
"statuscode" : {"type" : "number", "enum": [200] },
|
||||
"name": {"type": "string", "pattern": "^moved-out.txt$"},
|
||||
"owner_id": {"type": "string", "pattern": "^Brian$"},
|
||||
"owner_name": {"type": "string", "pattern": "^Brian$"}
|
||||
}
|
||||
}
|
||||
"""
|
||||
When user "Alice" gets the information of last created file
|
||||
Then the HTTP status code should be "403"
|
||||
And the data of the response should match
|
||||
""""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode"
|
||||
],
|
||||
"not": {
|
||||
"required": [
|
||||
"id",
|
||||
"size",
|
||||
"name",
|
||||
"mtime",
|
||||
"ctime",
|
||||
"mimetype",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^Forbidden$"},
|
||||
"statuscode" : {"type" : "number", "enum": [403]}
|
||||
}
|
||||
}
|
||||
"""
|
|
@ -0,0 +1,167 @@
|
|||
Feature: retrieve information of multiple files using the file IDs
|
||||
|
||||
Scenario: get information of four files, one own, one received as share, one trashed, one not accessible
|
||||
Given user "Alice" has been created
|
||||
And user "Brian" has been created
|
||||
And user "Alice" has uploaded file with content "some data" to "file.txt"
|
||||
And user "Brian" has uploaded file with content "some data" to "fromBrian.txt"
|
||||
And user "Alice" has uploaded file with content "more data" to "trashed.txt"
|
||||
And user "Brian" has uploaded file with content "some data" to "private.txt"
|
||||
And user "Alice" has uploaded file with content "some data" to "fully-deleted.txt"
|
||||
And user "Brian" has shared file "/fromBrian.txt" with user "Alice"
|
||||
And user "Alice" has deleted file "fully-deleted.txt"
|
||||
And user "Alice" has emptied the trash-bin
|
||||
And user "Alice" has deleted file "trashed.txt"
|
||||
And user "Alice" has renamed file "/fromBrian.txt" to "/renamedByAlice.txt"
|
||||
When user "Alice" gets the information of all files created in this scenario
|
||||
Then the HTTP status code should be "200"
|
||||
And the data of the response should match
|
||||
""""
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"%ids[0]%",
|
||||
"%ids[1]%",
|
||||
"%ids[2]%",
|
||||
"%ids[3]%",
|
||||
"%ids[4]%"
|
||||
],
|
||||
"properties": {
|
||||
"%ids[0]%": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode",
|
||||
"id",
|
||||
"size",
|
||||
"name",
|
||||
"mtime",
|
||||
"ctime",
|
||||
"mimetype",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
],
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^OK$"},
|
||||
"statuscode" : {"type" : "number", "enum": [200]},
|
||||
"id" : {"type" : "integer", "minimum": 1, "maximum": 99999},
|
||||
"size" : {"type" : "integer", "enum": [9] },
|
||||
"mtime" : {"type" : "integer"},
|
||||
"ctime" : {"type" : "integer", "enum": [0]},
|
||||
"name": {"type": "string", "pattern": "^file.txt$"},
|
||||
"mimetype": {"type": "string", "pattern": "^text\/plain$"},
|
||||
"owner_id": {"type": "string", "pattern": "^Alice$"},
|
||||
"owner_name": {"type": "string", "pattern": "^Alice$"},
|
||||
"trashed": {"type": "boolean", "enum": [false]}
|
||||
}
|
||||
},
|
||||
"%ids[1]%": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode",
|
||||
"id",
|
||||
"size",
|
||||
"name",
|
||||
"mtime",
|
||||
"ctime",
|
||||
"mimetype",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
],
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^OK$"},
|
||||
"statuscode" : {"type" : "number", "enum": [200]},
|
||||
"id" : {"type" : "integer", "minimum": 1, "maximum": 99999},
|
||||
"size" : {"type" : "integer", "enum": [9] },
|
||||
"mtime" : {"type" : "integer"},
|
||||
"ctime" : {"type" : "integer", "enum": [0]},
|
||||
"name": {"type": "string", "pattern": "^fromBrian.txt$"},
|
||||
"mimetype": {"type": "string", "pattern": "^text\/plain$"},
|
||||
"owner_id": {"type": "string", "pattern": "^Brian$"},
|
||||
"owner_name": {"type": "string", "pattern": "^Brian$"},
|
||||
"trashed": {"type": "boolean", "enum": [false]}
|
||||
}
|
||||
},
|
||||
"%ids[2]%": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode",
|
||||
"id",
|
||||
"size",
|
||||
"name",
|
||||
"mtime",
|
||||
"ctime",
|
||||
"mimetype",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
],
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^OK$"},
|
||||
"statuscode" : {"type" : "number", "enum": [200]},
|
||||
"id" : {"type" : "integer", "minimum": 1, "maximum": 99999},
|
||||
"size" : {"type" : "integer", "enum": [9] },
|
||||
"mtime" : {"type" : "integer"},
|
||||
"ctime" : {"type" : "integer", "enum": [0]},
|
||||
"name": {"type": "string", "pattern": "^trashed.txt.d\\d{10}$"},
|
||||
"mimetype": {"type": "string", "pattern": "^text\/plain$"},
|
||||
"owner_id": {"type": "string", "pattern": "^Alice$"},
|
||||
"owner_name": {"type": "string", "pattern": "^Alice$"},
|
||||
"trashed": {"type": "boolean", "enum": [true]}
|
||||
}
|
||||
},
|
||||
"%ids[3]%": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode"
|
||||
],
|
||||
"not": {
|
||||
"required": [
|
||||
"id",
|
||||
"size",
|
||||
"name",
|
||||
"mtime",
|
||||
"ctime",
|
||||
"mimetype",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^Forbidden$"},
|
||||
"statuscode" : {"type" : "number", "enum": [403]}
|
||||
}
|
||||
},
|
||||
"%ids[4]%": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"statuscode"
|
||||
],
|
||||
"not": {
|
||||
"required": [
|
||||
"id",
|
||||
"size",
|
||||
"name",
|
||||
"mtime",
|
||||
"ctime",
|
||||
"mimetype",
|
||||
"owner_id",
|
||||
"owner_name",
|
||||
"trashed"
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"status": {"type": "string", "pattern": "^Not Found$"},
|
||||
"statuscode" : {"type" : "number", "enum": [404]}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
|
@ -0,0 +1,464 @@
|
|||
<?php
|
||||
|
||||
use Behat\Behat\Context\Context;
|
||||
use Behat\Gherkin\Node\PyStringNode;
|
||||
use Helmich\JsonAssert\JsonAssertions;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Defines application features from the specific context.
|
||||
*/
|
||||
class FeatureContext implements Context {
|
||||
private string $regularUserPassword = '';
|
||||
private string $adminUsername = '';
|
||||
private string $adminPassword = '';
|
||||
private string $baseUrl = '';
|
||||
private const SHARE_TYPES = [
|
||||
'user' => 0,
|
||||
'group' => 1,
|
||||
];
|
||||
/**
|
||||
* @var array<int>
|
||||
*/
|
||||
private array $createdFiles = [];
|
||||
private ?ResponseInterface $response = null;
|
||||
|
||||
public function getAdminUsername(): string {
|
||||
return $this->adminUsername;
|
||||
}
|
||||
|
||||
public function getAdminPassword(): string {
|
||||
return $this->adminPassword;
|
||||
}
|
||||
|
||||
public function getRegularUserPassword(): string {
|
||||
return $this->regularUserPassword;
|
||||
}
|
||||
|
||||
public function getBaseUrl(): string {
|
||||
return $this->baseUrl;
|
||||
}
|
||||
|
||||
public function __construct(
|
||||
string $baseUrl,
|
||||
string $adminUsername,
|
||||
string $adminPassword,
|
||||
string $regularUserPassword
|
||||
) {
|
||||
$this->baseUrl = getenv('NEXTCLOUD_BASE_URL');
|
||||
if ($this->baseUrl === false) {
|
||||
$this->baseUrl = $baseUrl;
|
||||
}
|
||||
$this->baseUrl = self::sanitizeUrl($this->baseUrl, true);
|
||||
$this->adminUsername = $adminUsername;
|
||||
$this->adminPassword = $adminPassword;
|
||||
$this->regularUserPassword = $regularUserPassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given user :user has been created
|
||||
*/
|
||||
public function userHasBeenCreated(string $user):void {
|
||||
// delete the user if it exists
|
||||
$this->sendOCSRequest(
|
||||
'/cloud/users/' . $user, 'DELETE', $this->getAdminUsername()
|
||||
);
|
||||
$userAttributes['userid'] = $user;
|
||||
$userAttributes['password'] = $this->getRegularUserPassword();
|
||||
|
||||
$this->response = $this->sendOCSRequest(
|
||||
'/cloud/users', 'POST', $this->getAdminUsername(), $userAttributes
|
||||
);
|
||||
$this->theHttpStatusCodeShouldBe(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given user :user has uploaded file with content :content to :destination
|
||||
*/
|
||||
public function userHasUploadedFileWithContentTo(
|
||||
string $user, string $content, string $destination
|
||||
):void {
|
||||
$fileId = $this->uploadFileWithContent($user, $content, $destination);
|
||||
$this->theHTTPStatusCodeShouldBe(
|
||||
["201", "204"],
|
||||
"HTTP status code was not 201 or 204 while trying to upload file '$destination' for user '$user'"
|
||||
);
|
||||
|
||||
$this->createdFiles[] = $fileId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given user :user has created folder :folder
|
||||
*/
|
||||
public function userHasCreatedFolder(
|
||||
string $user, string $folder
|
||||
):void {
|
||||
$this->response = $this->makeDavRequest(
|
||||
$user,
|
||||
$this->regularUserPassword,
|
||||
"MKCOL",
|
||||
$folder
|
||||
);
|
||||
$this->theHTTPStatusCodeShouldBe(
|
||||
"201",
|
||||
"HTTP status code was not 201 while trying to create folder '$folder' for user '$user'"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^user "([^"]*)" has shared (?:file|folder) "([^"]*)" with (user|group) "([^"]*)"$/
|
||||
*/
|
||||
public function userHasSharedFileWithUser(
|
||||
string $sharer, string $path, string $userOrGroup, string $shareWith): void {
|
||||
$body['path'] = $path;
|
||||
$body['shareType'] = self::SHARE_TYPES[$userOrGroup];
|
||||
$body['shareWith'] = $shareWith;
|
||||
$body['permissions'] = 31;
|
||||
|
||||
$this->response = $this->sendOCSRequest(
|
||||
'/apps/files_sharing/api/v1/shares',
|
||||
'POST',
|
||||
$sharer,
|
||||
$body
|
||||
);
|
||||
$this->theHTTPStatusCodeShouldBe(
|
||||
"200",
|
||||
"HTTP status code was not 200 while sharing '$path' with '$shareWith'"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^user "([^"]*)" has deleted (?:file|folder) "([^"]*)"$/
|
||||
*/
|
||||
public function userHasDeletedFile(string $user, string $path):void {
|
||||
$this->response = $this->makeDavRequest(
|
||||
$user,
|
||||
$this->regularUserPassword,
|
||||
"DELETE",
|
||||
$path
|
||||
);
|
||||
$this->theHTTPStatusCodeShouldBe(
|
||||
"204",
|
||||
"HTTP status code was not 204 while deleting '$path' as '$user'"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^user "([^"]*)" has renamed (?:file|folder) "([^"]*)" to "([^"]*)"$/
|
||||
*/
|
||||
public function userHasRenamedFile(string $user, string $src, string $dst):void {
|
||||
$davPath = self::getDavPath($user);
|
||||
$fullDstUrl = self::sanitizeUrl($this->getBaseUrl() . $davPath . $dst);
|
||||
$this->response = $this->makeDavRequest(
|
||||
$user,
|
||||
$this->regularUserPassword,
|
||||
"MOVE",
|
||||
$src,
|
||||
["Destination" => $fullDstUrl]
|
||||
);
|
||||
$this->theHTTPStatusCodeShouldBe(
|
||||
"201",
|
||||
"HTTP status code was not 201 while moving '$src' to '$dst'"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^user "([^"]*)" has emptied the trash-bin$/
|
||||
*/
|
||||
public function userEmptiedTrashbin(string $user):void {
|
||||
$this->response = $this->makeDavRequest(
|
||||
$user,
|
||||
$this->regularUserPassword,
|
||||
"DELETE",
|
||||
"$user",
|
||||
null,
|
||||
null,
|
||||
'trash-bin'
|
||||
);
|
||||
$this->theHTTPStatusCodeShouldBe(
|
||||
"204",
|
||||
"HTTP status code was not 204 when emptying the trash-bin"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When user :user gets the information of last created file
|
||||
*/
|
||||
public function userGetsTheInformationOfLastCreatedFile(string $user): void {
|
||||
$fileId = array_slice($this->createdFiles, -1);
|
||||
$this->userGetsTheInformationOfFile($user, "$fileId[0]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @When user :user gets the information of the file with the id :id
|
||||
*/
|
||||
public function userGetsTheInformationOfFile(string $user, string $fileId): void {
|
||||
$this->response = $this->sendOCSRequest(
|
||||
'/apps/integration_openproject/fileinfo/' . $fileId,
|
||||
'GET',
|
||||
$user
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When user :user gets the information of all files created in this scenario
|
||||
*/
|
||||
public function userGetsTheInformationOfAllCreatedFiles(string $user): void {
|
||||
$body = json_encode(["fileIds" => $this->createdFiles]);
|
||||
Assert::assertNotFalse(
|
||||
$body,
|
||||
"could not encode to JSON"
|
||||
);
|
||||
$this->response = $this->sendOCSRequest(
|
||||
'/apps/integration_openproject/filesinfo',
|
||||
'POST',
|
||||
$user,
|
||||
$body
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the HTTP status code should be :expectedStatusCode
|
||||
*
|
||||
* Check that the status code in the saved response is the expected status
|
||||
* code, or one of the expected status codes.
|
||||
*
|
||||
* @param int|int[]|string|string[] $expectedStatusCode
|
||||
* @param string|null $message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function theHTTPStatusCodeShouldBe($expectedStatusCode, ?string $message = ""): void {
|
||||
$actualStatusCode = $this->response->getStatusCode();
|
||||
if (\is_array($expectedStatusCode)) {
|
||||
if ($message === "") {
|
||||
$message = "HTTP status code $actualStatusCode is not one of the expected values " .
|
||||
\implode(" or ", $expectedStatusCode);
|
||||
}
|
||||
|
||||
Assert::assertContainsEquals(
|
||||
$actualStatusCode,
|
||||
$expectedStatusCode,
|
||||
$message
|
||||
);
|
||||
} else {
|
||||
if ($message === "") {
|
||||
$message = "HTTP status code $actualStatusCode is not the expected value $expectedStatusCode";
|
||||
}
|
||||
|
||||
Assert::assertEquals(
|
||||
$expectedStatusCode,
|
||||
$actualStatusCode,
|
||||
$message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the data of the response should match
|
||||
*/
|
||||
public function theDataOfTheResponseShouldMatch(PyStringNode $schemaString): void {
|
||||
$schemaRawString = $schemaString->getRaw();
|
||||
for ($i = 0; $i < count($this->createdFiles); $i++) {
|
||||
$schemaRawString = str_replace(
|
||||
"%ids[$i]%", (string)$this->createdFiles[$i], $schemaRawString
|
||||
);
|
||||
}
|
||||
$schema = json_decode($schemaRawString);
|
||||
Assert::assertNotNull($schema, 'schema is not valid JSON');
|
||||
$responseAsJson = json_decode($this->response->getBody()->getContents());
|
||||
JsonAssertions::assertJsonDocumentMatchesSchema(
|
||||
$responseAsJson->ocs->data,
|
||||
$schema
|
||||
);
|
||||
}
|
||||
|
||||
public function uploadFileWithContent(
|
||||
string $user,
|
||||
?string $content,
|
||||
string $destination
|
||||
): int {
|
||||
$this->response = $this->makeDavRequest(
|
||||
$user,
|
||||
$this->regularUserPassword,
|
||||
"PUT",
|
||||
$destination,
|
||||
[],
|
||||
$content
|
||||
);
|
||||
$propfindResponse = $this->makeDavRequest(
|
||||
$user,
|
||||
$this->regularUserPassword,
|
||||
"PROPFIND",
|
||||
$destination,
|
||||
null,
|
||||
'<?xml version="1.0"?>
|
||||
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns" xmlns:ocs="http://open-collaboration-services.org/ns">
|
||||
<d:prop>
|
||||
<oc:fileid />
|
||||
</d:prop>
|
||||
</d:propfind>'
|
||||
);
|
||||
$xmlBody = $propfindResponse->getBody()->getContents();
|
||||
$responseXmlObject = new SimpleXMLElement($xmlBody);
|
||||
$responseXmlObject->registerXPathNamespace(
|
||||
'oc',
|
||||
'http://owncloud.org/ns'
|
||||
);
|
||||
return (int)(string)$responseXmlObject->xpath('//oc:fileid')[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param string $method
|
||||
* @param string $user
|
||||
* @param array<mixed>|string $body
|
||||
* @param int $ocsApiVersion
|
||||
* @return ResponseInterface
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function sendOCSRequest(
|
||||
string $path, string $method, string $user, $body = [], int $ocsApiVersion = 2
|
||||
): ResponseInterface {
|
||||
if ($user === $this->getAdminUsername()) {
|
||||
$password = $this->getAdminPassword();
|
||||
} else {
|
||||
$password = $this->getRegularUserPassword();
|
||||
}
|
||||
$fullUrl = $this->getBaseUrl();
|
||||
$fullUrl .= "ocs/v{$ocsApiVersion}.php" . $path;
|
||||
$headers['OCS-APIRequest'] = 'true';
|
||||
$headers['Accept'] = 'application/json';
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
return $this->sendHttpRequest(
|
||||
$fullUrl, $user, $password, $method, $headers, $body
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $method
|
||||
* @param array<mixed>|null $headers
|
||||
* @param array<mixed>|string|null $body
|
||||
* @return ResponseInterface
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function sendHttpRequest(
|
||||
string $url,
|
||||
string $user,
|
||||
string $password,
|
||||
string $method = 'GET',
|
||||
array $headers = null,
|
||||
$body = null
|
||||
): ResponseInterface {
|
||||
$options['auth'] = [$user, $password];
|
||||
$client = new Client($options);
|
||||
if ($headers === null) {
|
||||
$headers = [];
|
||||
}
|
||||
|
||||
if (\is_array($body)) {
|
||||
// when creating the client, it is possible to set 'form_params' and
|
||||
// the Client constructor sorts out doing this http_build_query stuff.
|
||||
// But 'new Request' does not have the flexibility to do that.
|
||||
// So we need to do it here.
|
||||
$body = \http_build_query($body, '', '&');
|
||||
$headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
}
|
||||
|
||||
$request = new Request(
|
||||
$method,
|
||||
$url,
|
||||
$headers,
|
||||
$body
|
||||
);
|
||||
|
||||
try {
|
||||
$response = $client->send($request);
|
||||
} catch (RequestException $ex) {
|
||||
$response = $ex->getResponse();
|
||||
|
||||
//if the response was null for some reason do not return it but re-throw
|
||||
if ($response === null) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string|null $method
|
||||
* @param string|null $path
|
||||
* @param array<mixed>|null $headers
|
||||
* @param array<mixed>|string|null $body
|
||||
* @return ResponseInterface
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function makeDavRequest(
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $method,
|
||||
?string $path,
|
||||
?array $headers = null,
|
||||
$body = null,
|
||||
string $type = 'files'
|
||||
): ResponseInterface {
|
||||
$davPath = self::getDavPath($user);
|
||||
|
||||
//replace %, # and ? and in the path, Guzzle will not encode them
|
||||
$urlSpecialChar = [['%', '#', '?'], ['%25', '%23', '%3F']];
|
||||
$path = \str_replace($urlSpecialChar[0], $urlSpecialChar[1], $path);
|
||||
|
||||
if ($type === "trash-bin") {
|
||||
$fullUrl = self::sanitizeUrl(
|
||||
$this->getBaseUrl() . '/remote.php/dav/trashbin/' . strtolower($user) . '/trash'
|
||||
);
|
||||
} else {
|
||||
$fullUrl = self::sanitizeUrl($this->getBaseUrl() . $davPath . $path);
|
||||
}
|
||||
|
||||
if ($headers !== null) {
|
||||
foreach ($headers as $key => $value) {
|
||||
//? and # need to be encoded in the Destination URL
|
||||
if ($key === "Destination") {
|
||||
$headers[$key] = \str_replace(
|
||||
$urlSpecialChar[0],
|
||||
$urlSpecialChar[1],
|
||||
$value
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->sendHttpRequest(
|
||||
$fullUrl,
|
||||
$user,
|
||||
$password,
|
||||
$method,
|
||||
$headers,
|
||||
$body
|
||||
);
|
||||
}
|
||||
|
||||
private static function getDavPath(string $user): string {
|
||||
return 'remote.php/dav/files/' . strtolower($user) . '/';
|
||||
}
|
||||
|
||||
public static function sanitizeUrl(?string $url, ?bool $trailingSlash = false): string {
|
||||
if ($trailingSlash === true) {
|
||||
$url = $url . "/";
|
||||
} else {
|
||||
$url = \rtrim($url, "/");
|
||||
}
|
||||
$url = \preg_replace("/([^:]\/)\/+/", '$1', $url);
|
||||
return $url;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,629 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\OpenProject\Controller;
|
||||
|
||||
use OCA\Files_Trashbin\Trash\ITrashManager;
|
||||
use OCP\Files\Config\ICachedMountFileInfo;
|
||||
use OCP\Files\Node;
|
||||
use OCP\IRequest;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function PHPUnit\Framework\assertSame;
|
||||
|
||||
class FilesControllerTest extends TestCase {
|
||||
|
||||
/**
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function getFileInfoDataProvider() {
|
||||
return [
|
||||
// getById returns only one result
|
||||
[
|
||||
[
|
||||
$this->getNodeMock('image/png')
|
||||
],
|
||||
'files/logo.png',
|
||||
'logo.png',
|
||||
'image/png'
|
||||
],
|
||||
// getById returns multiple results e.g. if the file was received through multiple path
|
||||
[
|
||||
[
|
||||
$this->getNodeMock('image/png'),
|
||||
$this->getNodeMock('image/png')
|
||||
|
||||
],
|
||||
'files/receivedAsFolderShare/logo.png',
|
||||
'logo.png',
|
||||
'image/png',
|
||||
],
|
||||
// getById returns a folder
|
||||
[
|
||||
[
|
||||
$this->getNodeMock('httpd/unix-directory')
|
||||
],
|
||||
'files/myFolder',
|
||||
'myFolder',
|
||||
'httpd/unix-directory'
|
||||
],
|
||||
// getById returns a sub folder
|
||||
[
|
||||
[
|
||||
$this->getNodeMock('httpd/unix-directory')
|
||||
],
|
||||
'files/myFolder/a-sub-folder',
|
||||
'a-sub-folder',
|
||||
'httpd/unix-directory'
|
||||
],
|
||||
// getById returns the root folder
|
||||
[
|
||||
[
|
||||
$this->getNodeMock('httpd/unix-directory')
|
||||
],
|
||||
'files',
|
||||
'files',
|
||||
'httpd/unix-directory'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getFileInfoDataProvider
|
||||
* @param array<mixed> $nodeMocks
|
||||
* @param string $internalPath
|
||||
* @param string $expectedName
|
||||
* @param string $expectedMimeType
|
||||
* @return void
|
||||
*/
|
||||
public function testGetFileInfo(
|
||||
$nodeMocks,
|
||||
$internalPath,
|
||||
$expectedName,
|
||||
$expectedMimeType
|
||||
) {
|
||||
$folderMock = $this->getMockBuilder('\OCP\Files\Folder')->getMock();
|
||||
$folderMock->method('getById')->willReturn($nodeMocks);
|
||||
|
||||
$mountCacheMock = $this->getSimpleMountCacheMock($internalPath);
|
||||
|
||||
$filesController = $this->createFilesController(
|
||||
$folderMock, null, $mountCacheMock
|
||||
);
|
||||
|
||||
$result = $filesController->getFileInfo(123);
|
||||
assertSame(
|
||||
[
|
||||
'status' => 'OK',
|
||||
'statuscode' => 200,
|
||||
"id" => 123,
|
||||
"name" => $expectedName,
|
||||
"mtime" => 1640008813,
|
||||
"ctime" => 1639906930,
|
||||
"mimetype" => $expectedMimeType,
|
||||
"size" => 200245,
|
||||
"owner_name" => "Test User",
|
||||
"owner_id" => "3df8ff78-49cb-4d60-8d8b-171b29591fd3",
|
||||
'trashed' => false
|
||||
],
|
||||
$result->getData()
|
||||
);
|
||||
assertSame(200, $result->getStatus());
|
||||
}
|
||||
|
||||
public function testGetFileInfoFileNotFound(): void {
|
||||
$folderMock = $this->getMockBuilder('\OCP\Files\Folder')->getMock();
|
||||
$folderMock->method('getById')->willReturn([]);
|
||||
|
||||
$filesController = $this->createFilesController($folderMock);
|
||||
|
||||
$result = $filesController->getFileInfo(123);
|
||||
assertSame($this->notFoundResponse, $result->getData());
|
||||
assertSame(404, $result->getStatus());
|
||||
}
|
||||
|
||||
public function testGetFileInfoFileInTrash(): void {
|
||||
$folderMock = $this->getMockBuilder('\OCP\Files\Folder')->getMock();
|
||||
$folderMock->method('getById')->willReturn([]);
|
||||
|
||||
$trashManagerMock = $this->getMockBuilder('\OCA\Files_Trashbin\Trash\ITrashManager')->getMock();
|
||||
$trashManagerMock->method('getTrashNodeById')->willReturn(
|
||||
$this->getNodeMock('text/plain', 759)
|
||||
);
|
||||
|
||||
$mountCacheMock = $this->getSimpleMountCacheMock(
|
||||
'files_trashbin/files/welcome.txt.d1648724302'
|
||||
);
|
||||
|
||||
$filesController = $this->createFilesController(
|
||||
$folderMock, $trashManagerMock, $mountCacheMock
|
||||
);
|
||||
|
||||
$result = $filesController->getFileInfo(759);
|
||||
assertSame($this->trashedWelcomeTxtResult, $result->getData());
|
||||
assertSame(200, $result->getStatus());
|
||||
}
|
||||
|
||||
public function testGetFileInfoFileExistingButNotReadable(): void {
|
||||
$folderMock = $this->getMockBuilder('\OCP\Files\Folder')->getMock();
|
||||
$folderMock->method('getById')->willReturn([]);
|
||||
|
||||
$trashManagerMock = $this->getMockBuilder('\OCA\Files_Trashbin\Trash\ITrashManager')->getMock();
|
||||
$trashManagerMock->method('getTrashNodeById')->willReturn(null);
|
||||
|
||||
$mountCacheMock = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache')->getMock();
|
||||
$mountCacheMock->method('getMountsForFileId')
|
||||
->willReturn(
|
||||
[$this->createMock(ICachedMountFileInfo::class)]
|
||||
);
|
||||
|
||||
$filesController = $this->createFilesController(
|
||||
$folderMock, $trashManagerMock, $mountCacheMock
|
||||
);
|
||||
|
||||
$result = $filesController->getFileInfo(759);
|
||||
assertSame($this->forbiddenResponse, $result->getData());
|
||||
assertSame(403, $result->getStatus());
|
||||
}
|
||||
|
||||
public function testGetFilesInfoOneIdRequestedFileInTrash(): void {
|
||||
$folderMock = $this->getMockBuilder('\OCP\Files\Folder')->getMock();
|
||||
$folderMock->method('getById')->willReturn([]);
|
||||
|
||||
$trashManagerMock = $this->getMockBuilder('\OCA\Files_Trashbin\Trash\ITrashManager')->getMock();
|
||||
$trashManagerMock->method('getTrashNodeById')->willReturn(
|
||||
$this->getNodeMock('text/plain', 759)
|
||||
);
|
||||
|
||||
$mountCacheMock = $this->getSimpleMountCacheMock(
|
||||
'files_trashbin/files/welcome.txt.d1648724302'
|
||||
);
|
||||
|
||||
$filesController = $this->createFilesController(
|
||||
$folderMock, $trashManagerMock, $mountCacheMock
|
||||
);
|
||||
|
||||
$result = $filesController->getFilesInfo([759]);
|
||||
assertSame(
|
||||
[
|
||||
759 => $this->trashedWelcomeTxtResult,
|
||||
],
|
||||
$result->getData()
|
||||
);
|
||||
assertSame(200, $result->getStatus());
|
||||
}
|
||||
|
||||
public function testGetFilesInfoFourIdsRequestedOneExistsOneInTrashOneNotExisitingOneForbidden(): void {
|
||||
$folderMock = $this->getMockBuilder('\OCP\Files\Folder')->getMock();
|
||||
$folderMock->method('getById')
|
||||
->withConsecutive([123], [759], [365], [956])
|
||||
->willReturnOnConsecutiveCalls(
|
||||
[
|
||||
$this->getNodeMock('image/png')
|
||||
],
|
||||
[],
|
||||
[]
|
||||
);
|
||||
|
||||
$trashManagerMock = $this->getMockBuilder('\OCA\Files_Trashbin\Trash\ITrashManager')->getMock();
|
||||
$trashManagerMock->method('getTrashNodeById')
|
||||
->withConsecutive([$this->anything(), 759], [$this->anything(), 365], [$this->anything(), 956])
|
||||
->willReturnOnConsecutiveCalls(
|
||||
$this->getNodeMock('text/plain', 759),
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
|
||||
$cachedMountFileInfoMock = $this->getMockBuilder(
|
||||
'\OCP\Files\Config\ICachedMountFileInfo'
|
||||
)->getMock();
|
||||
$cachedMountFileInfoMock->method('getInternalPath')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
'files/logo.png',
|
||||
'files_trashbin/files/welcome.txt.d1648724302',
|
||||
[],
|
||||
[]
|
||||
);
|
||||
|
||||
|
||||
$mountCacheMock = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache')->getMock();
|
||||
$mountCacheMock->method('getMountsForFileId')
|
||||
->withConsecutive([123], [759], [365], [956])
|
||||
->willReturnOnConsecutiveCalls(
|
||||
[$cachedMountFileInfoMock],
|
||||
[$cachedMountFileInfoMock],
|
||||
[], // not found
|
||||
[$cachedMountFileInfoMock],
|
||||
);
|
||||
|
||||
$filesController = $this->createFilesController(
|
||||
$folderMock,
|
||||
$trashManagerMock,
|
||||
$mountCacheMock
|
||||
);
|
||||
|
||||
$result = $filesController->getFilesInfo([123, 759, 365, 956]);
|
||||
assertSame(
|
||||
[
|
||||
123 => $this->logoPngResult,
|
||||
759 => $this->trashedWelcomeTxtResult,
|
||||
365 => $this->notFoundResponse,
|
||||
956 => $this->forbiddenResponse
|
||||
],
|
||||
$result->getData()
|
||||
);
|
||||
assertSame(200, $result->getStatus());
|
||||
}
|
||||
|
||||
public function testGetFilesInfoOneIdRequestedFileExistsReturnsOneResult(): void {
|
||||
$folderMock = $this->getMockBuilder('\OCP\Files\Folder')->getMock();
|
||||
$folderMock->method('getById')
|
||||
->willReturn(
|
||||
[
|
||||
$this->getNodeMock('image/png')
|
||||
]
|
||||
);
|
||||
|
||||
$mountCacheMock = $this->getSimpleMountCacheMock('files/logo.png');
|
||||
$filesController = $this->createFilesController(
|
||||
$folderMock, null, $mountCacheMock
|
||||
);
|
||||
|
||||
$result = $filesController->getFilesInfo([123]);
|
||||
assertSame(
|
||||
[
|
||||
123 => $this->logoPngResult,
|
||||
],
|
||||
$result->getData()
|
||||
);
|
||||
assertSame(200, $result->getStatus());
|
||||
}
|
||||
|
||||
public function testGetFilesInfoThreeIdsRequestedOneFileExistsReturnsOneResult(): void {
|
||||
$folderMock = $this->getMockBuilder('\OCP\Files\Folder')->getMock();
|
||||
$folderMock->method('getById')
|
||||
->withConsecutive([123], [256], [365])
|
||||
->willReturnOnConsecutiveCalls(
|
||||
[
|
||||
$this->getNodeMock('image/png')
|
||||
],
|
||||
[],
|
||||
[]
|
||||
);
|
||||
|
||||
$cachedMountFileInfoMock = $this->getMockBuilder(
|
||||
'\OCP\Files\Config\ICachedMountFileInfo'
|
||||
)->getMock();
|
||||
$cachedMountFileInfoMock->method('getInternalPath')
|
||||
->willReturn('files/logo.png');
|
||||
$mountCacheMock = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache')
|
||||
->getMock();
|
||||
$mountCacheMock->method('getMountsForFileId')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
[$cachedMountFileInfoMock], [], []
|
||||
);
|
||||
|
||||
$filesController = $this->createFilesController(
|
||||
$folderMock, null, $mountCacheMock
|
||||
);
|
||||
|
||||
$result = $filesController->getFilesInfo([123,256,365]);
|
||||
assertSame(
|
||||
[
|
||||
123 => $this->logoPngResult,
|
||||
256 => $this->notFoundResponse,
|
||||
365 => $this->notFoundResponse
|
||||
],
|
||||
$result->getData()
|
||||
);
|
||||
assertSame(200, $result->getStatus());
|
||||
}
|
||||
|
||||
public function testGetFilesInfoTwoIdsRequestedAllFilesExistsEachReturnsOneResult(): void {
|
||||
$folderMock = $this->getMockBuilder('\OCP\Files\Folder')->getMock();
|
||||
$folderMock->method('getById')
|
||||
->withConsecutive([123], [365])
|
||||
->willReturnOnConsecutiveCalls(
|
||||
[
|
||||
$this->getNodeMock('image/png', 123)
|
||||
],
|
||||
[
|
||||
$this->getNodeMock('image/png', 365),
|
||||
]
|
||||
);
|
||||
|
||||
$cachedMountFileInfoMock = $this->getMockBuilder(
|
||||
'\OCP\Files\Config\ICachedMountFileInfo'
|
||||
)->getMock();
|
||||
$cachedMountFileInfoMock->method('getInternalPath')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
'files/logo.png',
|
||||
'files/inFolder/image.png',
|
||||
);
|
||||
$mountCacheMock = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache')
|
||||
->getMock();
|
||||
$mountCacheMock->method('getMountsForFileId')
|
||||
->willReturn(
|
||||
[$cachedMountFileInfoMock]
|
||||
);
|
||||
$filesController = $this->createFilesController(
|
||||
$folderMock, null, $mountCacheMock
|
||||
);
|
||||
|
||||
$result = $filesController->getFilesInfo([123,365]);
|
||||
assertSame(
|
||||
[
|
||||
123 => $this->logoPngResult,
|
||||
365 => $this->imagePngResult,
|
||||
],
|
||||
$result->getData()
|
||||
);
|
||||
assertSame(200, $result->getStatus());
|
||||
}
|
||||
|
||||
public function testGetFilesInfoTwoIdsRequestedAllFilesExistsEachReturnsMultipleResults(): void {
|
||||
$folderMock = $this->getMockBuilder('\OCP\Files\Folder')->getMock();
|
||||
$folderMock->method('getById')
|
||||
->withConsecutive([123], [365])
|
||||
->willReturnOnConsecutiveCalls(
|
||||
[
|
||||
$this->getNodeMock('image/png'),
|
||||
$this->getNodeMock('image/png')
|
||||
],
|
||||
[
|
||||
$this->getNodeMock('image/png', 365),
|
||||
$this->getNodeMock('image/png', 365)
|
||||
]
|
||||
);
|
||||
|
||||
$cachedMountFileInfoMock = $this->getMockBuilder(
|
||||
'\OCP\Files\Config\ICachedMountFileInfo'
|
||||
)->getMock();
|
||||
$cachedMountFileInfoMock->method('getInternalPath')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
'files/logo.png',
|
||||
'files/inFolder/image.png',
|
||||
);
|
||||
$mountCacheMock = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache')
|
||||
->getMock();
|
||||
$mountCacheMock->method('getMountsForFileId')
|
||||
->willReturn(
|
||||
[$cachedMountFileInfoMock]
|
||||
);
|
||||
|
||||
$filesController = $this->createFilesController(
|
||||
$folderMock, null, $mountCacheMock
|
||||
);
|
||||
|
||||
$result = $filesController->getFilesInfo([123,365]);
|
||||
assertSame(
|
||||
[
|
||||
123 => $this->logoPngResult,
|
||||
365 => $this->imagePngResult
|
||||
],
|
||||
$result->getData()
|
||||
);
|
||||
assertSame(200, $result->getStatus());
|
||||
}
|
||||
|
||||
public function testGetFilesInfoTwoIdsRequestedEachReturnsOneFolder(): void {
|
||||
$folderMock = $this->getMockBuilder('\OCP\Files\Folder')->getMock();
|
||||
$folderMock->method('getById')
|
||||
->withConsecutive([2], [3])
|
||||
->willReturnOnConsecutiveCalls(
|
||||
[
|
||||
$this->getNodeMock(
|
||||
'httpd/unix-directory',
|
||||
2
|
||||
)
|
||||
],
|
||||
[
|
||||
$this->getNodeMock(
|
||||
'httpd/unix-directory',
|
||||
3
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
$cachedMountFileInfoMock = $this->getMockBuilder(
|
||||
'\OCP\Files\Config\ICachedMountFileInfo'
|
||||
)->getMock();
|
||||
$cachedMountFileInfoMock->method('getInternalPath')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
'files/myFolder/a-sub-folder',
|
||||
'files'
|
||||
);
|
||||
$mountCacheMock = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache')->getMock();
|
||||
$mountCacheMock->method('getMountsForFileId')
|
||||
->willReturn(
|
||||
[$cachedMountFileInfoMock]
|
||||
);
|
||||
|
||||
$filesController = $this->createFilesController($folderMock, null, $mountCacheMock);
|
||||
|
||||
$result = $filesController->getFilesInfo([2,3]);
|
||||
assertSame(
|
||||
[
|
||||
2 => [
|
||||
'status' => 'OK',
|
||||
'statuscode' => 200,
|
||||
'id' => 2,
|
||||
'name' => 'a-sub-folder',
|
||||
'mtime' => 1640008813,
|
||||
'ctime' => 1639906930,
|
||||
'mimetype' => 'httpd/unix-directory',
|
||||
'size' => 200245,
|
||||
'owner_name' => 'Test User',
|
||||
'owner_id' => '3df8ff78-49cb-4d60-8d8b-171b29591fd3',
|
||||
'trashed' => false
|
||||
],
|
||||
3 => [
|
||||
'status' => 'OK',
|
||||
'statuscode' => 200,
|
||||
'id' => 3,
|
||||
'name' => 'files',
|
||||
'mtime' => 1640008813,
|
||||
'ctime' => 1639906930,
|
||||
'mimetype' => 'httpd/unix-directory',
|
||||
'size' => 200245,
|
||||
'owner_name' => 'Test User',
|
||||
'owner_id' => '3df8ff78-49cb-4d60-8d8b-171b29591fd3',
|
||||
'trashed' => false
|
||||
]
|
||||
],
|
||||
$result->getData()
|
||||
);
|
||||
assertSame(200, $result->getStatus());
|
||||
}
|
||||
|
||||
public function testGetFilesInfoInvalidRequest(): void {
|
||||
$folderMock = $this->getMockBuilder('\OCP\Files\Folder')->getMock();
|
||||
$filesController = $this->createFilesController($folderMock);
|
||||
|
||||
$result = $filesController->getFilesInfo(null);
|
||||
assertSame(
|
||||
'invalid request',
|
||||
$result->getData()
|
||||
);
|
||||
assertSame(400, $result->getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array<mixed>
|
||||
*/
|
||||
private array $notFoundResponse = [
|
||||
'status' => 'Not Found',
|
||||
'statuscode' => 404,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<mixed>
|
||||
*/
|
||||
private array $forbiddenResponse = [
|
||||
'status' => 'Forbidden',
|
||||
'statuscode' => 403
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<mixed>
|
||||
*/
|
||||
private array $trashedWelcomeTxtResult = [
|
||||
'status' => 'OK',
|
||||
'statuscode' => 200,
|
||||
"id" => 759,
|
||||
"name" => 'welcome.txt.d1648724302',
|
||||
"mtime" => 1640008813,
|
||||
"ctime" => 1639906930,
|
||||
"mimetype" => 'text/plain',
|
||||
"size" => 200245,
|
||||
"owner_name" => "Test User",
|
||||
"owner_id" => "3df8ff78-49cb-4d60-8d8b-171b29591fd3",
|
||||
"trashed" => true
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<mixed>
|
||||
*/
|
||||
private array $logoPngResult = [
|
||||
'status' => 'OK',
|
||||
'statuscode' => 200,
|
||||
'id' => 123,
|
||||
'name' => 'logo.png',
|
||||
'mtime' => 1640008813,
|
||||
'ctime' => 1639906930,
|
||||
'mimetype' => 'image/png',
|
||||
'size' => 200245,
|
||||
'owner_name' => 'Test User',
|
||||
'owner_id' => '3df8ff78-49cb-4d60-8d8b-171b29591fd3',
|
||||
'trashed' => false
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<mixed>
|
||||
*/
|
||||
private array $imagePngResult = [
|
||||
'status' => 'OK',
|
||||
'statuscode' => 200,
|
||||
'id' => 365,
|
||||
'name' => 'image.png',
|
||||
'mtime' => 1640008813,
|
||||
'ctime' => 1639906930,
|
||||
'mimetype' => 'image/png',
|
||||
'size' => 200245,
|
||||
'owner_name' => 'Test User',
|
||||
'owner_id' => '3df8ff78-49cb-4d60-8d8b-171b29591fd3',
|
||||
'trashed' => false
|
||||
];
|
||||
|
||||
/**
|
||||
* @param MockObject $folderMock
|
||||
* @param MockObject|ITrashManager|null $trashManagerMock
|
||||
* @param MockObject|null $mountCacheMock mock for Files that exist but cannot be accessed by this user
|
||||
* @return FilesController
|
||||
*/
|
||||
private function createFilesController(
|
||||
MockObject $folderMock, $trashManagerMock = null, MockObject $mountCacheMock = null
|
||||
): FilesController {
|
||||
$storageMock = $this->getMockBuilder('\OCP\Files\IRootFolder')->getMock();
|
||||
$storageMock->method('getUserFolder')->willReturn($folderMock);
|
||||
|
||||
$userMock = $this->getMockBuilder('\OCP\IUser')->getMock();
|
||||
$userMock->method('getUID')->willReturn('testUser');
|
||||
|
||||
$userSessionMock = $this->getMockBuilder('\OCP\IUserSession')->getMock();
|
||||
$userSessionMock->method('getUser')->willReturn($userMock);
|
||||
if ($trashManagerMock === null) {
|
||||
$trashManagerMock = $this->createMock(ITrashManager::class);
|
||||
}
|
||||
|
||||
if ($mountCacheMock === null) {
|
||||
$mountCacheMock = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache')->getMock();
|
||||
$mountCacheMock->method('getMountsForFileId')->willReturn([]);
|
||||
}
|
||||
|
||||
$mountProviderCollectionMock = $this->getMockBuilder(
|
||||
'OCP\Files\Config\IMountProviderCollection'
|
||||
)->getMock();
|
||||
$mountProviderCollectionMock->method('getMountCache')->willReturn($mountCacheMock);
|
||||
|
||||
return new FilesController(
|
||||
'integration_openproject',
|
||||
$this->createMock(IRequest::class),
|
||||
$storageMock,
|
||||
$trashManagerMock,
|
||||
$userSessionMock,
|
||||
$mountProviderCollectionMock
|
||||
);
|
||||
}
|
||||
|
||||
private function getNodeMock(string $mimeType, int $id = 123
|
||||
): Node {
|
||||
$ownerMock = $this->getMockBuilder('\OCP\IUser')->getMock();
|
||||
$ownerMock->method('getDisplayName')->willReturn('Test User');
|
||||
$ownerMock->method('getUID')->willReturn('3df8ff78-49cb-4d60-8d8b-171b29591fd3');
|
||||
|
||||
$fileMock = $this->createMock('\OCP\Files\Node');
|
||||
$fileMock->method('getId')->willReturn($id);
|
||||
$fileMock->method('getOwner')->willReturn($ownerMock);
|
||||
$fileMock->method('getSize')->willReturn(200245);
|
||||
$fileMock->method('getMimeType')->willReturn($mimeType);
|
||||
$fileMock->method('getCreationTime')->willReturn(1639906930);
|
||||
$fileMock->method('getMTime')->willReturn(1640008813);
|
||||
return $fileMock;
|
||||
}
|
||||
|
||||
private function getSimpleMountCacheMock(string $internalPath): MockObject {
|
||||
$cachedMountFileInfoMock = $this->getMockBuilder(
|
||||
'\OCP\Files\Config\ICachedMountFileInfo'
|
||||
)->getMock();
|
||||
$cachedMountFileInfoMock->method('getInternalPath')
|
||||
->willReturn($internalPath);
|
||||
$mountCacheMock = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache')
|
||||
->getMock();
|
||||
$mountCacheMock->method('getMountsForFileId')
|
||||
->willReturn(
|
||||
[$cachedMountFileInfoMock]
|
||||
);
|
||||
return $mountCacheMock;
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче