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:
Julien Veyssier 2022-05-03 10:07:58 +02:00 коммит произвёл GitHub
Родитель 264bb3ee10
Коммит a1aa2a3b5a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 2703 добавлений и 9 удалений

16
.github/workflows/ci.yml поставляемый
Просмотреть файл

@ -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",

814
composer.lock сгенерированный
Просмотреть файл

@ -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,
];
}
}

Просмотреть файл

@ -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;
}
}