diff --git a/.github/workflows/lint-info-xml.yml b/.github/workflows/lint-info-xml.yml
new file mode 100644
index 0000000..bf6f178
--- /dev/null
+++ b/.github/workflows/lint-info-xml.yml
@@ -0,0 +1,33 @@
+# This workflow is provided via the organization template repository
+#
+# https://github.com/nextcloud/.github
+# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
+
+name: Lint info.xml
+
+on: pull_request
+
+permissions:
+ contents: read
+
+concurrency:
+ group: lint-info-xml-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
+jobs:
+ xml-linters:
+ runs-on: ubuntu-latest
+
+ name: info.xml lint
+ steps:
+ - name: Checkout
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+
+ - name: Download schema
+ run: wget https://raw.githubusercontent.com/nextcloud/appstore/master/nextcloudappstore/api/v1/release/info.xsd
+
+ - name: Lint info.xml
+ uses: ChristophWurst/xmllint-action@36f2a302f84f8c83fceea0b9c59e1eb4a616d3c1 # v1.2
+ with:
+ xml-file: ./appinfo/info.xml
+ xml-schema-file: ./info.xsd
diff --git a/.github/workflows/lint-php-cs.yml b/.github/workflows/lint-php-cs.yml
new file mode 100644
index 0000000..c23fc7c
--- /dev/null
+++ b/.github/workflows/lint-php-cs.yml
@@ -0,0 +1,40 @@
+# This workflow is provided via the organization template repository
+#
+# https://github.com/nextcloud/.github
+# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
+
+name: Lint php-cs
+
+on: pull_request
+
+permissions:
+ contents: read
+
+concurrency:
+ group: lint-php-cs-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+
+ name: php-cs
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+
+ - name: Set up php8.2
+ uses: shivammathur/setup-php@81cd5ae0920b34eef300e1775313071038a53429 # v2
+ with:
+ php-version: 8.2
+ coverage: none
+ ini-file: development
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Install dependencies
+ run: composer i
+
+ - name: Lint
+ run: composer run cs:check || ( echo 'Please run `composer run cs:fix` to format your code' && exit 1 )
diff --git a/.github/workflows/lint-php.yml b/.github/workflows/lint-php.yml
new file mode 100644
index 0000000..92769c4
--- /dev/null
+++ b/.github/workflows/lint-php.yml
@@ -0,0 +1,54 @@
+# This workflow is provided via the organization template repository
+#
+# https://github.com/nextcloud/.github
+# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
+
+name: Lint php
+
+on: pull_request
+
+permissions:
+ contents: read
+
+concurrency:
+ group: lint-php-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
+jobs:
+ php-lint:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ php-versions: [ '8.0', '8.1', '8.2', '8.3' ]
+
+ name: php-lint
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+
+ - name: Set up php ${{ matrix.php-versions }}
+ uses: shivammathur/setup-php@81cd5ae0920b34eef300e1775313071038a53429 # v2
+ with:
+ php-version: ${{ matrix.php-versions }}
+ coverage: none
+ ini-file: development
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Lint
+ run: composer run lint
+
+ summary:
+ permissions:
+ contents: none
+ runs-on: ubuntu-latest
+ needs: php-lint
+
+ if: always()
+
+ name: php-lint-summary
+
+ steps:
+ - name: Summary status
+ run: if ${{ needs.php-lint.result != 'success' && needs.php-lint.result != 'skipped' }}; then exit 1; fi
diff --git a/.github/workflows/node-build.yml b/.github/workflows/node-build.yml
new file mode 100644
index 0000000..46be448
--- /dev/null
+++ b/.github/workflows/node-build.yml
@@ -0,0 +1,53 @@
+name: Node Build
+
+on:
+ pull_request:
+ paths:
+ - src/**
+ - .eslintrc.js
+ - stylelint.config.js
+ - webpack.js
+ push:
+ branches:
+ - main
+ paths:
+ - src/**
+ - .eslintrc.js
+ - stylelint.config.js
+ - webpack.js
+
+env:
+ APP_NAME: integration_jira
+
+jobs:
+ build:
+ name: node-build
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ with:
+ path: ${{ env.APP_NAME }}
+
+ - name: Read package.json node and npm engines version
+ uses: skjnldsv/read-package-engines-version-actions@0ce2ed60f6df073a62a77c0a4958dd0fc68e32e7 # v2.1
+ id: versions
+ with:
+ path: ${{ env.APP_NAME }}
+ fallbackNode: "^20"
+ fallbackNpm: "^9"
+
+ - name: Set up node ${{ steps.versions.outputs.nodeVersion }}
+ uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3
+ with:
+ node-version: ${{ steps.versions.outputs.nodeVersion }}
+
+ - name: Set up npm ${{ steps.versions.outputs.npmVersion }}
+ run: npm i -g npm@"${{ steps.versions.outputs.npmVersion }}"
+
+ - name: Build ${{ env.APP_NAME }}
+ run: |
+ cd ${{ env.APP_NAME }}
+ npm ci
+ npm run build
diff --git a/.github/workflows/phpunit-sqlite.yml b/.github/workflows/phpunit-sqlite.yml
new file mode 100644
index 0000000..605addd
--- /dev/null
+++ b/.github/workflows/phpunit-sqlite.yml
@@ -0,0 +1,157 @@
+# This workflow is provided via the organization template repository
+#
+# https://github.com/nextcloud/.github
+# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
+
+name: PHPUnit sqlite
+
+on: pull_request
+
+permissions:
+ contents: read
+
+concurrency:
+ group: phpunit-sqlite-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
+jobs:
+ changes:
+ runs-on: ubuntu-latest
+
+ outputs:
+ src: ${{ steps.changes.outputs.src}}
+
+ steps:
+ - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1
+ id: changes
+ continue-on-error: true
+ with:
+ filters: |
+ src:
+ - '.github/workflows/**'
+ - 'appinfo/**'
+ - 'lib/**'
+ - 'templates/**'
+ - 'tests/**'
+ - 'vendor/**'
+ - 'vendor-bin/**'
+ - '.php-cs-fixer.dist.php'
+ - 'composer.json'
+ - 'composer.lock'
+
+ phpunit-sqlite:
+ runs-on: ubuntu-latest
+
+ needs: changes
+ if: needs.changes.outputs.src != 'false'
+
+ strategy:
+ matrix:
+ php-versions: ['8.1', '8.2']
+ server-versions: ['stable26', 'stable27', 'master']
+
+ steps:
+ - name: Set app env
+ run: |
+ # Split and keep last
+ echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
+
+ - name: Checkout server
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ with:
+ submodules: true
+ repository: nextcloud/server
+ ref: ${{ matrix.server-versions }}
+
+ - name: Checkout app
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ with:
+ path: apps/${{ env.APP_NAME }}
+
+ - name: Set up php ${{ matrix.php-versions }}
+ uses: shivammathur/setup-php@81cd5ae0920b34eef300e1775313071038a53429 # v2
+ with:
+ php-version: ${{ matrix.php-versions }}
+ # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
+ extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite
+ coverage: none
+ ini-file: development
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Check composer file existence
+ id: check_composer
+ uses: andstor/file-existence-action@20b4d2e596410855db8f9ca21e96fbe18e12930b # v2
+ with:
+ files: apps/${{ env.APP_NAME }}/composer.json
+
+ - name: Set up dependencies
+ # Only run if phpunit config file exists
+ if: steps.check_composer.outputs.files_exists == 'true'
+ working-directory: apps/${{ env.APP_NAME }}
+ run: composer i
+
+ - name: Set up Nextcloud
+ env:
+ DB_PORT: 4444
+ run: |
+ mkdir data
+ ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin
+ ./occ app:enable --force ${{ env.APP_NAME }}
+
+ - name: Check PHPUnit script is defined
+ id: check_phpunit
+ continue-on-error: true
+ working-directory: apps/${{ env.APP_NAME }}
+ run: |
+ composer run --list | grep "^ test:unit " | wc -l | grep 1
+
+ - name: PHPUnit
+ # Only run if phpunit config file exists
+ if: steps.check_phpunit.outcome == 'success'
+ working-directory: apps/${{ env.APP_NAME }}
+ run: composer run test:unit
+
+ - name: Check PHPUnit integration script is defined
+ id: check_integration
+ continue-on-error: true
+ working-directory: apps/${{ env.APP_NAME }}
+ run: |
+ composer run --list | grep "^ test:integration " | wc -l | grep 1
+
+ - name: Run Nextcloud
+ # Only run if phpunit integration config file exists
+ if: steps.check_integration.outcome == 'success'
+ run: php -S localhost:8080 &
+
+ - name: PHPUnit integration
+ # Only run if phpunit integration config file exists
+ if: steps.check_integration.outcome == 'success'
+ working-directory: apps/${{ env.APP_NAME }}
+ run: composer run test:integration
+
+ - name: Print logs
+ if: always()
+ run: |
+ cat data/nextcloud.log
+
+ - name: Skipped
+ # Fail the action when neither unit nor integration tests ran
+ if: steps.check_phpunit.outcome == 'failure' && steps.check_integration.outcome == 'failure'
+ run: |
+ echo 'Neither PHPUnit nor PHPUnit integration tests are specified in composer.json scripts'
+ exit 1
+
+ summary:
+ permissions:
+ contents: none
+ runs-on: ubuntu-latest
+ needs: [changes, phpunit-sqlite]
+
+ if: always()
+
+ name: phpunit-sqlite-summary
+
+ steps:
+ - name: Summary status
+ run: if ${{ needs.changes.outputs.src != 'false' && needs.phpunit-sqlite.result != 'success' }}; then exit 1; fi
diff --git a/.gitignore b/.gitignore
index b4fccbf..70c55d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,6 @@ js/
.vscode-upload.json
.*.sw*
node_modules
+/vendor/
+.php-cs-fixer.cache
+tests/.phpunit.cache/
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
new file mode 100644
index 0000000..f7bbdd8
--- /dev/null
+++ b/.php-cs-fixer.dist.php
@@ -0,0 +1,18 @@
+getFinder()
+ ->ignoreVCSIgnored(true)
+ ->notPath('build')
+ ->notPath('l10n')
+ ->notPath('src')
+ ->notPath('vendor')
+ ->in(__DIR__);
+return $config;
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc1300b..3729d56 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
+## 1.0.7 – 202x-xx-xx
+### Changed
+- minimum required NC raised from v25 to v26.
+- bump js libs
+
## 1.0.6 – 2023-10-24
### Changed
- added support of NC28
diff --git a/appinfo/info.xml b/appinfo/info.xml
index df9c13e..117cb5f 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -6,7 +6,7 @@
- 1.0.6
+ 1.0.7
agpl
Julien Veyssier
Jira
@@ -19,7 +19,7 @@ and notifications about recent activity related to your assigned issues.]]>https://github.com/nextcloud/integration_jira/issues
https://github.com/nextcloud/integration_jira/raw/master/img/screenshot1.jpg
-
+
OCA\Jira\BackgroundJob\CheckOpenTickets
diff --git a/appinfo/routes.php b/appinfo/routes.php
index e102dab..9c8aad4 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -10,13 +10,13 @@
*/
return [
- 'routes' => [
- ['name' => 'config#oauthRedirect', 'url' => '/oauth-redirect', 'verb' => 'GET'],
- ['name' => 'config#connectToSoftware', 'url' => '/soft-connect', 'verb' => 'PUT'],
- ['name' => 'config#setConfig', 'url' => '/config', 'verb' => 'PUT'],
- ['name' => 'config#setAdminConfig', 'url' => '/admin-config', 'verb' => 'PUT'],
- ['name' => 'jiraAPI#getNotifications', 'url' => '/notifications', 'verb' => 'GET'],
- ['name' => 'jiraAPI#getJiraUrl', 'url' => '/url', 'verb' => 'GET'],
- ['name' => 'jiraAPI#getJiraAvatar', 'url' => '/avatar', 'verb' => 'GET'],
- ]
+ 'routes' => [
+ ['name' => 'config#oauthRedirect', 'url' => '/oauth-redirect', 'verb' => 'GET'],
+ ['name' => 'config#connectToSoftware', 'url' => '/soft-connect', 'verb' => 'PUT'],
+ ['name' => 'config#setConfig', 'url' => '/config', 'verb' => 'PUT'],
+ ['name' => 'config#setAdminConfig', 'url' => '/admin-config', 'verb' => 'PUT'],
+ ['name' => 'jiraAPI#getNotifications', 'url' => '/notifications', 'verb' => 'GET'],
+ ['name' => 'jiraAPI#getJiraUrl', 'url' => '/url', 'verb' => 'GET'],
+ ['name' => 'jiraAPI#getJiraAvatar', 'url' => '/avatar', 'verb' => 'GET'],
+ ]
];
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..2586878
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,22 @@
+{
+ "name": "nextcloud/integration_jira",
+ "type": "project",
+ "license": "APL-3.0-or-later",
+ "autoload": {
+ "psr-4": {
+ "OCA\\Jira\\": "lib/"
+ }
+ },
+ "minimum-stability": "stable",
+ "require-dev": {
+ "nextcloud/ocp": "^26.0",
+ "nextcloud/coding-standard": "^1.1",
+ "phpunit/phpunit": "^10"
+ },
+ "scripts": {
+ "lint": "find . -name \\*.php -not -path './vendor/*' -not -path './build/*' -print0 | xargs -0 -n1 php -l",
+ "cs:check": "php-cs-fixer fix --dry-run --diff",
+ "cs:fix": "php-cs-fixer fix",
+ "test:unit": "vendor/bin/phpunit -c tests/phpunit.xml --colors=always --fail-on-warning --fail-on-risky"
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..54b3a42
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,1916 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "0e34d4e4dfb82de6e27049972faf5242",
+ "packages": [],
+ "packages-dev": [
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.11.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
+ "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/collections": "<1.6.8",
+ "doctrine/common": "<2.13.3 || >=3,<3.2.2"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.6.8",
+ "doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ],
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-03-08T13:26:56+00:00"
+ },
+ {
+ "name": "nextcloud/coding-standard",
+ "version": "v1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nextcloud/coding-standard.git",
+ "reference": "55def702fb9a37a219511e1d8c6fe8e37164c1fb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/55def702fb9a37a219511e1d8c6fe8e37164c1fb",
+ "reference": "55def702fb9a37a219511e1d8c6fe8e37164c1fb",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.3|^8.0",
+ "php-cs-fixer/shim": "^3.17"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Nextcloud\\CodingStandard\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christoph Wurst",
+ "email": "christoph@winzerhof-wurst.at"
+ }
+ ],
+ "description": "Nextcloud coding standards for the php cs fixer",
+ "support": {
+ "issues": "https://github.com/nextcloud/coding-standard/issues",
+ "source": "https://github.com/nextcloud/coding-standard/tree/v1.1.1"
+ },
+ "time": "2023-06-01T12:05:01+00:00"
+ },
+ {
+ "name": "nextcloud/ocp",
+ "version": "v26.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nextcloud-deps/ocp.git",
+ "reference": "2b24e15929d27f4bd800e38fd3a3cf3dcefd00a4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/2b24e15929d27f4bd800e38fd3a3cf3dcefd00a4",
+ "reference": "2b24e15929d27f4bd800e38fd3a3cf3dcefd00a4",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ~8.0 || ~8.1",
+ "psr/container": "^1.1.1",
+ "psr/event-dispatcher": "^1.0",
+ "psr/log": "^1.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "26.0.0-dev"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "AGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Christoph Wurst",
+ "email": "christoph@winzerhof-wurst.at"
+ }
+ ],
+ "description": "Composer package containing Nextcloud's public API (classes, interfaces)",
+ "support": {
+ "issues": "https://github.com/nextcloud-deps/ocp/issues",
+ "source": "https://github.com/nextcloud-deps/ocp/tree/v26.0.8"
+ },
+ "time": "2023-10-11T12:57:20+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v4.17.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
+ "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1"
+ },
+ "time": "2023-08-13T19:53:39+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "97803eca37d319dfa7826cc2437fc020857acb53"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
+ "reference": "97803eca37d319dfa7826cc2437fc020857acb53",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.3"
+ },
+ "time": "2021-07-20T11:28:43+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
+ },
+ "time": "2022-02-21T01:04:05+00:00"
+ },
+ {
+ "name": "php-cs-fixer/shim",
+ "version": "v3.38.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHP-CS-Fixer/shim.git",
+ "reference": "b00e057155e00bbfaf3e753590dec01e549770f7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/b00e057155e00bbfaf3e753590dec01e549770f7",
+ "reference": "b00e057155e00bbfaf3e753590dec01e549770f7",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "php": "^7.4 || ^8.0"
+ },
+ "replace": {
+ "friendsofphp/php-cs-fixer": "self.version"
+ },
+ "suggest": {
+ "ext-dom": "For handling output formats in XML",
+ "ext-mbstring": "For handling non-UTF8 characters."
+ },
+ "bin": [
+ "php-cs-fixer",
+ "php-cs-fixer.phar"
+ ],
+ "type": "application",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Dariusz Rumiński",
+ "email": "dariusz.ruminski@gmail.com"
+ }
+ ],
+ "description": "A tool to automatically fix PHP code style",
+ "support": {
+ "issues": "https://github.com/PHP-CS-Fixer/shim/issues",
+ "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.38.2"
+ },
+ "time": "2023-11-14T00:19:50+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "10.1.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "355324ca4980b8916c18b9db29f3ef484078f26e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/355324ca4980b8916c18b9db29f3ef484078f26e",
+ "reference": "355324ca4980b8916c18b9db29f3ef484078f26e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^4.15",
+ "php": ">=8.1",
+ "phpunit/php-file-iterator": "^4.0",
+ "phpunit/php-text-template": "^3.0",
+ "sebastian/code-unit-reverse-lookup": "^3.0",
+ "sebastian/complexity": "^3.0",
+ "sebastian/environment": "^6.0",
+ "sebastian/lines-of-code": "^2.0",
+ "sebastian/version": "^4.0",
+ "theseer/tokenizer": "^1.2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.1"
+ },
+ "suggest": {
+ "ext-pcov": "PHP extension that provides line coverage",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "10.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.7"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-10-04T15:34:17+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "4.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c",
+ "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-08-31T06:24:48+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
+ "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^10.0"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:56:09+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748",
+ "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-08-31T14:07:24+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d",
+ "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:57:52+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "10.4.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/cacd8b9dd224efa8eb28beb69004126c7ca1a1a1",
+ "reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.10.1",
+ "phar-io/manifest": "^2.0.3",
+ "phar-io/version": "^3.0.2",
+ "php": ">=8.1",
+ "phpunit/php-code-coverage": "^10.1.5",
+ "phpunit/php-file-iterator": "^4.0",
+ "phpunit/php-invoker": "^4.0",
+ "phpunit/php-text-template": "^3.0",
+ "phpunit/php-timer": "^6.0",
+ "sebastian/cli-parser": "^2.0",
+ "sebastian/code-unit": "^2.0",
+ "sebastian/comparator": "^5.0",
+ "sebastian/diff": "^5.0",
+ "sebastian/environment": "^6.0",
+ "sebastian/exporter": "^5.1",
+ "sebastian/global-state": "^6.0.1",
+ "sebastian/object-enumerator": "^5.0",
+ "sebastian/recursion-context": "^5.0",
+ "sebastian/type": "^4.0",
+ "sebastian/version": "^4.0"
+ },
+ "suggest": {
+ "ext-soap": "To be able to generate mocks based on WSDL files"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "10.4-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ],
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/10.4.2"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsors.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-10-26T07:21:45+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "1.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/1.1.2"
+ },
+ "time": "2021-11-05T16:50:12+00:00"
+ },
+ {
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Standard interfaces for event handling.",
+ "keywords": [
+ "events",
+ "psr",
+ "psr-14"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+ },
+ "time": "2019-01-08T18:20:26+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "1.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "Psr/Log/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/1.1.4"
+ },
+ "time": "2021-05-03T11:20:27+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/efdc130dbbbb8ef0b545a994fd811725c5282cae",
+ "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:58:15+00:00"
+ },
+ {
+ "name": "sebastian/code-unit",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "a81fee9eef0b7a76af11d121767abc44c104e503"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503",
+ "reference": "a81fee9eef0b7a76af11d121767abc44c104e503",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:58:43+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
+ "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:59:15+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "5.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "2db5010a484d53ebf536087a70b4a5423c102372"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372",
+ "reference": "2db5010a484d53ebf536087a70b4a5423c102372",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "php": ">=8.1",
+ "sebastian/diff": "^5.0",
+ "sebastian/exporter": "^5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "security": "https://github.com/sebastianbergmann/comparator/security/policy",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-08-14T13:18:12+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "3.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "68cfb347a44871f01e33ab0ef8215966432f6957"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957",
+ "reference": "68cfb347a44871f01e33ab0ef8215966432f6957",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.10",
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "security": "https://github.com/sebastianbergmann/complexity/security/policy",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-09-28T11:50:59+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "5.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b",
+ "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0",
+ "symfony/process": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "security": "https://github.com/sebastianbergmann/diff/security/policy",
+ "source": "https://github.com/sebastianbergmann/diff/tree/5.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-05-01T07:48:21+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "6.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/43c751b41d74f96cbbd4e07b7aec9675651e2951",
+ "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "https://github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "security": "https://github.com/sebastianbergmann/environment/security/policy",
+ "source": "https://github.com/sebastianbergmann/environment/tree/6.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-04-11T05:39:26+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "5.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc",
+ "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=8.1",
+ "sebastian/recursion-context": "^5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "security": "https://github.com/sebastianbergmann/exporter/security/policy",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-09-24T13:22:09+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "6.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/7ea9ead78f6d380d2a667864c132c2f7b83055e4",
+ "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "sebastian/object-reflector": "^3.0",
+ "sebastian/recursion-context": "^5.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "http://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "security": "https://github.com/sebastianbergmann/global-state/security/policy",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-07-19T07:19:23+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d",
+ "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.10",
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-08-31T09:25:50+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906",
+ "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "sebastian/object-reflector": "^3.0",
+ "sebastian/recursion-context": "^5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T07:08:32+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "24ed13d98130f0e7122df55d06c5c4942a577957"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957",
+ "reference": "24ed13d98130f0e7122df55d06c5c4942a577957",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T07:06:18+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "05909fb5bc7df4c52992396d0116aed689f93712"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712",
+ "reference": "05909fb5bc7df4c52992396d0116aed689f93712",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T07:05:40+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "462699a16464c3944eefc02ebdd77882bd3925bf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf",
+ "reference": "462699a16464c3944eefc02ebdd77882bd3925bf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "source": "https://github.com/sebastianbergmann/type/tree/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T07:10:45+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17",
+ "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "source": "https://github.com/sebastianbergmann/version/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-07T11:34:05+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/1.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2021-07-28T10:34:58+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": [],
+ "plugin-api-version": "2.3.0"
+}
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index c7fe0cf..6d6c77c 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -9,16 +9,16 @@
namespace OCA\Jira\AppInfo;
+use OCA\Jira\Dashboard\JiraWidget;
+use OCA\Jira\Notification\Notifier;
+use OCA\Jira\Search\JiraSearchProvider;
use OCP\AppFramework\App;
-use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\AppFramework\Bootstrap\IBootContext;
+
use OCP\AppFramework\Bootstrap\IBootstrap;
+use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\Notification\IManager as INotificationManager;
-use OCA\Jira\Dashboard\JiraWidget;
-use OCA\Jira\Search\JiraSearchProvider;
-use OCA\Jira\Notification\Notifier;
-
/**
* Class Application
*
@@ -27,8 +27,9 @@
class Application extends App implements IBootstrap {
public const APP_ID = 'integration_jira';
- public const JIRA_API_URL = 'https://api.atlassian.com';
- public const JIRA_AUTH_URL = 'https://auth.atlassian.com';
+ public const INTEGRATION_USER_AGENT = 'Nextcloud Jira Integration';
+ public const INTEGRATION_API_URL = 'https://api.atlassian.com/';
+ public const JIRA_AUTH_URL = 'https://auth.atlassian.com/';
/**
* Constructor
@@ -51,4 +52,3 @@ public function register(IRegistrationContext $context): void {
public function boot(IBootContext $context): void {
}
}
-
diff --git a/lib/BackgroundJob/CheckOpenTickets.php b/lib/BackgroundJob/CheckOpenTickets.php
index bbc7a35..1e9a7cc 100644
--- a/lib/BackgroundJob/CheckOpenTickets.php
+++ b/lib/BackgroundJob/CheckOpenTickets.php
@@ -23,11 +23,11 @@
namespace OCA\Jira\BackgroundJob;
-use OCP\BackgroundJob\TimedJob;
+use OCA\Jira\Service\JiraAPIService;
use OCP\AppFramework\Utility\ITimeFactory;
-use Psr\Log\LoggerInterface;
+use OCP\BackgroundJob\TimedJob;
-use OCA\Jira\Service\JiraAPIService;
+use Psr\Log\LoggerInterface;
/**
* Class CheckOpenTickets
@@ -36,15 +36,13 @@
*/
class CheckOpenTickets extends TimedJob {
- /** @var JiraAPIService */
- protected $jiraAPIService;
+ protected JiraAPIService $jiraAPIService;
- /** @var LoggerInterface */
- protected $logger;
+ protected LoggerInterface $logger;
public function __construct(ITimeFactory $time,
- JiraAPIService $jiraAPIService,
- LoggerInterface $logger) {
+ JiraAPIService $jiraAPIService,
+ LoggerInterface $logger) {
parent::__construct($time);
// Every 15 minutes
$this->setInterval(60 * 15);
diff --git a/lib/Controller/ConfigController.php b/lib/Controller/ConfigController.php
index fcc88e4..7ee2d3a 100644
--- a/lib/Controller/ConfigController.php
+++ b/lib/Controller/ConfigController.php
@@ -12,53 +12,38 @@
namespace OCA\Jira\Controller;
use DateTime;
-use OCP\IURLGenerator;
+use OCA\Jira\AppInfo\Application;
+use OCA\Jira\Service\NetworkService;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\Http\RedirectResponse;
use OCP\IConfig;
use OCP\IL10N;
-use OCP\AppFramework\Http\RedirectResponse;
-use OCP\IRequest;
-use OCP\AppFramework\Http\DataResponse;
-use OCP\AppFramework\Controller;
-use OCA\Jira\Service\JiraAPIService;
-use OCA\Jira\AppInfo\Application;
+use OCP\IRequest;
+use OCP\IURLGenerator;
class ConfigController extends Controller {
- /**
- * @var IConfig
- */
- private $config;
- /**
- * @var IURLGenerator
- */
- private $urlGenerator;
- /**
- * @var IL10N
- */
- private $l;
- /**
- * @var JiraAPIService
- */
- private $jiraAPIService;
- /**
- * @var string|null
- */
- private $userId;
+ private IConfig $config;
+ private IURLGenerator $urlGenerator;
+ private IL10N $l;
+ private ?string $userId;
+ private NetworkService $networkService;
public function __construct(string $appName,
- IRequest $request,
- IConfig $config,
- IURLGenerator $urlGenerator,
- IL10N $l,
- JiraAPIService $jiraAPIService,
- ?string $userId) {
+ IRequest $request,
+ IConfig $config,
+ IURLGenerator $urlGenerator,
+ IL10N $l,
+ NetworkService $networkService,
+ ?string $userId) {
parent::__construct($appName, $request);
$this->config = $config;
$this->urlGenerator = $urlGenerator;
$this->l = $l;
- $this->jiraAPIService = $jiraAPIService;
$this->userId = $userId;
+ $this->networkService = $networkService;
}
/**
@@ -121,7 +106,7 @@ public function connectToSoftware(string $url, string $login, string $password):
$basicAuthHeader = base64_encode($login . ':' . $password);
- $info = $this->jiraAPIService->basicRequest($targetInstanceUrl, $basicAuthHeader, 'rest/api/2/myself');
+ $info = $this->networkService->basicRequest($targetInstanceUrl, $basicAuthHeader, 'rest/api/2/myself');
if (isset($info['displayName'])) {
$this->config->setUserValue($this->userId, Application::APP_ID, 'user_name', $info['displayName']);
// in self hosted version, key is the only account identifier
@@ -153,13 +138,13 @@ public function oauthRedirect(string $code = '', string $state = ''): RedirectRe
if ($clientID && $clientSecret && $configState !== '' && $configState === $state) {
$redirect_uri = $this->config->getUserValue($this->userId, Application::APP_ID, 'redirect_uri');
- $result = $this->jiraAPIService->requestOAuthAccessToken([
+ $result = $this->networkService->requestOAuthAccessToken([
'client_id' => $clientID,
'client_secret' => $clientSecret,
'code' => $code,
'redirect_uri' => $redirect_uri,
'grant_type' => 'authorization_code'
- ], 'POST');
+ ]);
if (isset($result['access_token'])) {
$accessToken = $result['access_token'];
$this->config->setUserValue($this->userId, Application::APP_ID, 'token', $accessToken);
@@ -171,13 +156,13 @@ public function oauthRedirect(string $code = '', string $state = ''): RedirectRe
$this->config->setUserValue($this->userId, Application::APP_ID, 'token_expires_at', $expiresAt);
}
// get accessible resources
- $resources = $this->jiraAPIService->oauthRequest($this->userId, 'oauth/token/accessible-resources');
+ $resources = $this->networkService->oauthRequest($this->userId, 'oauth/token/accessible-resources');
if (!isset($resources['error']) && count($resources) > 0) {
$encodedResources = json_encode($resources);
$this->config->setUserValue($this->userId, Application::APP_ID, 'resources', $encodedResources);
// get user info
$cloudId = $resources[0]['id'];
- $info = $this->jiraAPIService->oauthRequest($this->userId, 'ex/jira/' . $cloudId . '/rest/api/2/myself');
+ $info = $this->networkService->oauthRequest($this->userId, 'ex/jira/' . $cloudId . '/rest/api/2/myself');
if (isset($info['accountId'], $info['displayName'])) {
$this->config->setUserValue($this->userId, Application::APP_ID, 'user_name', $info['displayName']);
// in cloud version, accountId is there and key is not
diff --git a/lib/Controller/JiraAPIController.php b/lib/Controller/JiraAPIController.php
index 2135739..94d5daa 100644
--- a/lib/Controller/JiraAPIController.php
+++ b/lib/Controller/JiraAPIController.php
@@ -11,28 +11,23 @@
namespace OCA\Jira\Controller;
+use OCA\Jira\Service\JiraAPIService;
+use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\DataDisplayResponse;
-use OCP\IRequest;
use OCP\AppFramework\Http\DataResponse;
-use OCP\AppFramework\Controller;
-use OCA\Jira\Service\JiraAPIService;
+use OCP\IRequest;
class JiraAPIController extends Controller {
- /**
- * @var JiraAPIService
- */
- private $jiraAPIService;
- /**
- * @var string|null
- */
- private $userId;
+ private JiraAPIService $jiraAPIService;
+
+ private ?string $userId;
public function __construct(string $appName,
- IRequest $request,
- JiraAPIService $jiraAPIService,
- ?string $userId) {
+ IRequest $request,
+ JiraAPIService $jiraAPIService,
+ ?string $userId) {
parent::__construct($appName, $request);
$this->jiraAPIService = $jiraAPIService;
$this->userId = $userId;
@@ -53,7 +48,7 @@ public function getJiraAvatar(string $accountId = '', string $accountKey = ''):
return new DataDisplayResponse('', 401);
} else {
$response = new DataDisplayResponse($avatarContent);
- $response->cacheFor(60*60*24);
+ $response->cacheFor(60 * 60 * 24);
return $response;
}
}
diff --git a/lib/Dashboard/JiraWidget.php b/lib/Dashboard/JiraWidget.php
index 76f137d..50a2ee6 100644
--- a/lib/Dashboard/JiraWidget.php
+++ b/lib/Dashboard/JiraWidget.php
@@ -23,24 +23,20 @@
namespace OCA\Jira\Dashboard;
+use OCA\Jira\AppInfo\Application;
use OCP\Dashboard\IWidget;
use OCP\IL10N;
-use OCP\Util;
use OCP\IURLGenerator;
-use OCA\Jira\AppInfo\Application;
+use OCP\Util;
class JiraWidget implements IWidget {
- /** @var IL10N */
- private $l10n;
- /**
- * @var IURLGenerator
- */
- private $url;
+ private IL10N $l10n;
+ private IURLGenerator $url;
public function __construct(IL10N $l10n,
- IURLGenerator $url) {
+ IURLGenerator $url) {
$this->l10n = $l10n;
$this->url = $url;
}
@@ -57,7 +53,7 @@ public function getId(): string {
*/
public function getTitle(): string {
return $this->l10n->t('Jira notifications');
- }
+ }
/**
* @inheritDoc
diff --git a/lib/Notification/Notifier.php b/lib/Notification/Notifier.php
index b76b488..02433e8 100644
--- a/lib/Notification/Notifier.php
+++ b/lib/Notification/Notifier.php
@@ -12,27 +12,23 @@
namespace OCA\Jira\Notification;
use InvalidArgumentException;
+use OCA\Jira\AppInfo\Application;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\L10N\IFactory;
use OCP\Notification\IManager as INotificationManager;
use OCP\Notification\INotification;
use OCP\Notification\INotifier;
-use OCA\Jira\AppInfo\Application;
class Notifier implements INotifier {
- /** @var IFactory */
- protected $factory;
+ protected IFactory $factory;
- /** @var IUserManager */
- protected $userManager;
+ protected IUserManager $userManager;
- /** @var INotificationManager */
- protected $notificationManager;
+ protected INotificationManager $notificationManager;
- /** @var IURLGenerator */
- protected $url;
+ protected IURLGenerator $url;
/**
* @param IFactory $factory
@@ -41,9 +37,9 @@ class Notifier implements INotifier {
* @param IURLGenerator $urlGenerator
*/
public function __construct(IFactory $factory,
- IUserManager $userManager,
- INotificationManager $notificationManager,
- IURLGenerator $urlGenerator) {
+ IUserManager $userManager,
+ INotificationManager $notificationManager,
+ IURLGenerator $urlGenerator) {
$this->factory = $factory;
$this->userManager = $userManager;
$this->notificationManager = $notificationManager;
@@ -85,25 +81,25 @@ public function prepare(INotification $notification, string $languageCode): INot
$l = $this->factory->get('integration_jira', $languageCode);
switch ($notification->getSubject()) {
- case 'new_open_tickets':
- $p = $notification->getSubjectParameters();
- $nbOpen = (int) ($p['nbOpen'] ?? 0);
- $content = $l->n('You have %s open issue with recent activity in Jira.', 'You have %s open issues with recent activity in Jira.', $nbOpen, [$nbOpen]);
+ case 'new_open_tickets':
+ $p = $notification->getSubjectParameters();
+ $nbOpen = (int) ($p['nbOpen'] ?? 0);
+ $content = $l->n('You have %s open issue with recent activity in Jira.', 'You have %s open issues with recent activity in Jira.', $nbOpen, [$nbOpen]);
- //$theme = $this->config->getUserValue($userId, 'accessibility', 'theme', '');
- //$iconUrl = ($theme === 'dark')
- // ? $this->url->imagePath(Application::APP_ID, 'app.svg')
- // : $this->url->imagePath(Application::APP_ID, 'app-dark.svg');
+ //$theme = $this->config->getUserValue($userId, 'accessibility', 'theme', '');
+ //$iconUrl = ($theme === 'dark')
+ // ? $this->url->imagePath(Application::APP_ID, 'app.svg')
+ // : $this->url->imagePath(Application::APP_ID, 'app-dark.svg');
- $notification->setParsedSubject($content)
- ->setLink($p['link'] ?? '')
- ->setIcon($this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.svg')));
+ $notification->setParsedSubject($content)
+ ->setLink($p['link'] ?? '')
+ ->setIcon($this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.svg')));
//->setIcon($this->url->getAbsoluteURL($iconUrl));
- return $notification;
+ return $notification;
- default:
- // Unknown subject => Unknown notification => throw
- throw new InvalidArgumentException();
+ default:
+ // Unknown subject => Unknown notification => throw
+ throw new InvalidArgumentException();
}
}
}
diff --git a/lib/Search/JiraSearchProvider.php b/lib/Search/JiraSearchProvider.php
index 888c6cf..064c85a 100644
--- a/lib/Search/JiraSearchProvider.php
+++ b/lib/Search/JiraSearchProvider.php
@@ -24,11 +24,11 @@
*/
namespace OCA\Jira\Search;
-use OCA\Jira\Service\JiraAPIService;
use OCA\Jira\AppInfo\Application;
+use OCA\Jira\Service\JiraAPIService;
use OCP\App\IAppManager;
-use OCP\IL10N;
use OCP\IConfig;
+use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\Search\IProvider;
@@ -37,22 +37,11 @@
class JiraSearchProvider implements IProvider {
- /** @var IAppManager */
- private $appManager;
-
- /** @var IL10N */
- private $l10n;
-
- /** @var IURLGenerator */
- private $urlGenerator;
- /**
- * @var IConfig
- */
- private $config;
- /**
- * @var JiraAPIService
- */
- private $service;
+ private IAppManager $appManager;
+ private IL10N $l10n;
+ private IURLGenerator $urlGenerator;
+ private IConfig $config;
+ private JiraAPIService $service;
/**
* CospendSearchProvider constructor.
@@ -64,10 +53,10 @@ class JiraSearchProvider implements IProvider {
* @param JiraAPIService $service
*/
public function __construct(IAppManager $appManager,
- IL10N $l10n,
- IConfig $config,
- IURLGenerator $urlGenerator,
- JiraAPIService $service) {
+ IL10N $l10n,
+ IConfig $config,
+ IURLGenerator $urlGenerator,
+ JiraAPIService $service) {
$this->appManager = $appManager;
$this->l10n = $l10n;
$this->config = $config;
diff --git a/lib/Search/JiraSearchResultEntry.php b/lib/Search/JiraSearchResultEntry.php
index d01817d..5632c9b 100644
--- a/lib/Search/JiraSearchResultEntry.php
+++ b/lib/Search/JiraSearchResultEntry.php
@@ -27,4 +27,4 @@
use OCP\Search\SearchResultEntry;
class JiraSearchResultEntry extends SearchResultEntry {
-}
\ No newline at end of file
+}
diff --git a/lib/Service/JiraAPIService.php b/lib/Service/JiraAPIService.php
index 7753e6b..b22f07a 100644
--- a/lib/Service/JiraAPIService.php
+++ b/lib/Service/JiraAPIService.php
@@ -12,63 +12,45 @@
namespace OCA\Jira\Service;
use DateTime;
-use Exception;
-use OCP\IL10N;
-use OCP\PreConditionNotMetException;
-use Psr\Log\LoggerInterface;
+use OCA\Jira\AppInfo\Application;
+use OCP\Http\Client\IClient;
+use OCP\Http\Client\IClientService;
use OCP\IConfig;
-use OCP\IUserManager;
+use OCP\IL10N;
use OCP\IUser;
-use OCP\Http\Client\IClientService;
+use OCP\IUserManager;
use OCP\Notification\IManager as INotificationManager;
-use GuzzleHttp\Exception\ClientException;
-use GuzzleHttp\Exception\ServerException;
-use GuzzleHttp\Exception\ConnectException;
+use OCP\PreConditionNotMetException;
-use OCA\Jira\AppInfo\Application;
-use Throwable;
+use Psr\Log\LoggerInterface;
class JiraAPIService {
- /**
- * @var IUserManager
- */
- private $userManager;
- /**
- * @var LoggerInterface
- */
- private $logger;
- /**
- * @var IL10N
- */
- private $l10n;
- /**
- * @var IConfig
- */
- private $config;
- /**
- * @var INotificationManager
- */
- private $notificationManager;
- /**
- * @var \OCP\Http\Client\IClient
- */
- private $client;
+ private IUserManager $userManager;
+ private LoggerInterface $logger;
+ private IL10N $l10n;
+ private IConfig $config;
+ private INotificationManager $notificationManager;
+ private NetworkService $networkService;
+ private IClient $client;
/**
* Service to make requests to Jira v3 (JSON) API
*/
- public function __construct (string $appName,
- IUserManager $userManager,
- LoggerInterface $logger,
- IL10N $l10n,
- IConfig $config,
- INotificationManager $notificationManager,
- IClientService $clientService) {
+ public function __construct(
+ IUserManager $userManager,
+ LoggerInterface $logger,
+ IL10N $l10n,
+ IConfig $config,
+ INotificationManager $notificationManager,
+ NetworkService $networkService,
+ IClientService $clientService
+ ) {
$this->userManager = $userManager;
$this->logger = $logger;
$this->l10n = $l10n;
$this->config = $config;
$this->notificationManager = $notificationManager;
+ $this->networkService = $networkService;
$this->client = $clientService->newClient();
}
@@ -110,15 +92,15 @@ private function checkOpenTicketsForUser(string $userId): void {
$status_key = $n['fields']['status']['statusCategory']['key'] ?? '';
$assigneeKey = $n['fields']['assignee']['key'] ?? '';
$assigneeId = $n['fields']['assignee']['accountId'] ?? '';
- $embededAccountId = $n['my_account_id'] ?? '';
- // from what i saw, key is used in self hosted and accountId in cloud version
- // embededAccountId can be usefull when accessing multiple cloud resources, it being specific to the resource
- if ( (
- ($myAccountKey !== '' && $assigneeKey === $myAccountKey)
- || ($myAccountId !== '' && $myAccountId === $assigneeId)
- || ($embededAccountId !== '' && $embededAccountId === $assigneeId)
- )
- && $status_key !== 'done') {
+ $embeddedAccountId = $n['my_account_id'] ?? '';
+ // from what I saw, key is used in self-hosted and accountId in cloud version
+ // embeddedAccountId can be usefull when accessing multiple cloud resources, it being specific to the resource
+ if ((
+ ($myAccountKey !== '' && $assigneeKey === $myAccountKey)
+ || ($myAccountId !== '' && $myAccountId === $assigneeId)
+ || ($embeddedAccountId !== '' && $embeddedAccountId === $assigneeId)
+ )
+ && $status_key !== 'done') {
$nbOpen++;
}
}
@@ -173,7 +155,7 @@ public function getNotifications(string $userId, ?string $since = null, ?int $li
$endPoint = 'rest/api/2/search';
$basicAuthHeader = $this->config->getUserValue($userId, Application::APP_ID, 'basic_auth_header');
- // self hosted Jira
+ // self-hosted Jira
if ($basicAuthHeader !== '') {
$jiraUrl = $this->config->getUserValue($userId, Application::APP_ID, 'url');
@@ -185,7 +167,7 @@ public function getNotifications(string $userId, ?string $since = null, ?int $li
];
}
- $issuesResult = $this->basicRequest($jiraUrl, $basicAuthHeader, $endPoint);
+ $issuesResult = $this->networkService->basicRequest($jiraUrl, $basicAuthHeader, $endPoint);
if (isset($issuesResult['error'])) {
return $issuesResult;
}
@@ -201,7 +183,7 @@ public function getNotifications(string $userId, ?string $since = null, ?int $li
foreach ($resources as $resource) {
$cloudId = $resource['id'];
$jiraUrl = $resource['url'];
- $issuesResult = $this->oauthRequest($userId, 'ex/jira/' . $cloudId . '/' . $endPoint);
+ $issuesResult = $this->networkService->oauthRequest($userId, 'ex/jira/' . $cloudId . '/' . $endPoint);
if (!isset($issuesResult['error']) && isset($issuesResult['issues'])) {
foreach ($issuesResult['issues'] as $k => $issue) {
$issuesResult['issues'][$k]['jiraUrl'] = $jiraUrl;
@@ -217,7 +199,7 @@ public function getNotifications(string $userId, ?string $since = null, ?int $li
if (!is_null($since)) {
$sinceDate = new Datetime($since);
$sinceTimestamp = $sinceDate->getTimestamp();
- $myIssues = array_filter($myIssues, function($elem) use ($sinceTimestamp) {
+ $myIssues = array_filter($myIssues, function ($elem) use ($sinceTimestamp) {
$date = new Datetime($elem['fields']['updated']);
$elemTs = $date->getTimestamp();
return $elemTs > $sinceTimestamp;
@@ -225,7 +207,7 @@ public function getNotifications(string $userId, ?string $since = null, ?int $li
}
// sort by updated
- usort($myIssues, function($a, $b) {
+ usort($myIssues, function ($a, $b) {
$a = new Datetime($a['fields']['updated']);
$ta = $a->getTimestamp();
$b = new Datetime($b['fields']['updated']);
@@ -249,7 +231,7 @@ public function search(string $userId, string $query, int $offset = 0, int $limi
$endPoint = 'rest/api/2/search';
// jira cloud does not support "*TERM*" but just "TERM*"
- // self hosted jira is fine with "*TERM*"...
+ // self-hosted jira is fine with "*TERM*"...
// other problem, '*' does not work with japanese chars (for example)
$words = preg_split('/\s+/', $query);
$searchString = '';
@@ -270,7 +252,7 @@ public function search(string $userId, string $query, int $offset = 0, int $limi
];
$basicAuthHeader = $this->config->getUserValue($userId, Application::APP_ID, 'basic_auth_header');
- // self hosted Jira
+ // self-hosted Jira
if ($basicAuthHeader !== '') {
$jiraUrl = $this->config->getUserValue($userId, Application::APP_ID, 'url');
@@ -282,7 +264,7 @@ public function search(string $userId, string $query, int $offset = 0, int $limi
];
}
- $issuesResult = $this->basicRequest($jiraUrl, $basicAuthHeader, $endPoint, $params);
+ $issuesResult = $this->networkService->basicRequest($jiraUrl, $basicAuthHeader, $endPoint, $params);
if (isset($issuesResult['error'])) {
return $issuesResult;
}
@@ -293,12 +275,11 @@ public function search(string $userId, string $query, int $offset = 0, int $limi
} else {
// Jira cloud
$resources = $this->getJiraResources($userId);
- $myIssues = [];
foreach ($resources as $resource) {
$cloudId = $resource['id'];
$jiraUrl = $resource['url'];
- $issuesResult = $this->oauthRequest($userId, 'ex/jira/' . $cloudId . '/' . $endPoint, $params);
+ $issuesResult = $this->networkService->oauthRequest($userId, 'ex/jira/' . $cloudId . '/' . $endPoint, $params);
if (!isset($issuesResult['error']) && isset($issuesResult['issues'])) {
foreach ($issuesResult['issues'] as $k => $issue) {
$issuesResult['issues'][$k]['jiraUrl'] = $jiraUrl;
@@ -341,12 +322,12 @@ public function getAccountInfo(string $userId, string $accountId, string $accoun
];
}
- return $this->basicRequest($jiraUrl, $basicAuthHeader, $endPoint, $params);
+ return $this->networkService->basicRequest($jiraUrl, $basicAuthHeader, $endPoint, $params);
} else {
$accessToken = $this->config->getUserValue($userId, Application::APP_ID, 'token');
$refreshToken = $this->config->getUserValue($userId, Application::APP_ID, 'refresh_token');
- $clientID = $this->config->getAppValue(Application::APP_ID, 'client_id');
- $clientSecret = $this->config->getAppValue(Application::APP_ID, 'client_secret');
+ // $clientID = $this->config->getAppValue(Application::APP_ID, 'client_id');
+ // $clientSecret = $this->config->getAppValue(Application::APP_ID, 'client_secret');
if ($accessToken === '' || $refreshToken === '') {
return ['error' => 'no credentials'];
}
@@ -355,8 +336,8 @@ public function getAccountInfo(string $userId, string $accountId, string $accoun
foreach ($resources as $resource) {
$cloudId = $resource['id'];
-// $jiraUrl = $resource['url'];
- $result = $this->oauthRequest($userId, 'ex/jira/' . $cloudId . '/' . $endPoint, $params);
+ // $jiraUrl = $resource['url'];
+ $result = $this->networkService->oauthRequest($userId, 'ex/jira/' . $cloudId . '/' . $endPoint, $params);
if (!isset($result['error'])) {
return $result;
}
@@ -402,255 +383,4 @@ public function getJiraAvatar(string $userId, string $accountId, string $account
return $this->client->get($imageUrl, $options)->getBody();
}
-
- /**
- * @param string $url
- * @param string $authHeader
- * @param string $endPoint
- * @param array $params
- * @param string $method
- * @return array
- */
- public function basicRequest(string $url, string $authHeader,
- string $endPoint, array $params = [], string $method = 'GET'): array {
- try {
- $url = $url . '/' . $endPoint;
- $options = [
- 'headers' => [
- 'Authorization' => 'Basic ' . $authHeader,
- 'User-Agent' => 'Nextcloud Jira integration',
- ]
- ];
- if ($method === 'POST') {
- $options['headers']['Content-Type'] = 'application/json';
- }
-
- if (count($params) > 0) {
- if ($method === 'GET') {
- // manage array parameters
- $paramsContent = '';
- foreach ($params as $key => $value) {
- if (is_array($value)) {
- foreach ($value as $oneArrayValue) {
- $paramsContent .= $key . '[]=' . urlencode($oneArrayValue) . '&';
- }
- unset($params[$key]);
- }
- }
- $paramsContent .= http_build_query($params);
- $url .= '?' . $paramsContent;
- } else {
- $options['body'] = json_encode($params, JSON_UNESCAPED_UNICODE);
- }
- }
-
- if ($method === 'GET') {
- $response = $this->client->get($url, $options);
- } else if ($method === 'POST') {
- $response = $this->client->post($url, $options);
- } else if ($method === 'PUT') {
- $response = $this->client->put($url, $options);
- } else if ($method === 'DELETE') {
- $response = $this->client->delete($url, $options);
- } else {
- return ['error' => $this->l10n->t('Bad HTTP method')];
- }
- $body = $response->getBody();
- $respCode = $response->getStatusCode();
-// $headers = $response->getHeaders();
-
- if ($respCode >= 400) {
- return ['error' => $this->l10n->t('Bad credentials')];
- } else {
- return json_decode($body, true);
- }
- } catch (ServerException | ClientException $e) {
- $this->logger->warning('Jira API error : '.$e->getMessage(), ['app' => Application::APP_ID]);
- return ['error' => $e->getMessage()];
- } catch (ConnectException $e) {
- $this->logger->warning('Jira API connection error : '.$e->getMessage(), ['app' => Application::APP_ID]);
- return ['error' => $e->getMessage()];
- }
- }
-
- /**
- * @param string $userId
- * @param string $endPoint
- * @param array $params
- * @param string $method
- * @return array
- * @throws PreConditionNotMetException
- */
- public function oauthRequest(string $userId, string $endPoint, array $params = [], string $method = 'GET'): array {
- $this->checkTokenExpiration($userId);
- $accessToken = $this->config->getUserValue($userId, Application::APP_ID, 'token');
- try {
- $url = Application::JIRA_API_URL . '/' . $endPoint;
- $options = [
- 'headers' => [
- 'Authorization' => 'Bearer ' . $accessToken,
- 'User-Agent' => 'Nextcloud Jira integration',
- ]
- ];
-
- if (count($params) > 0) {
- if ($method === 'GET') {
- // manage array parameters
- $paramsContent = '';
- foreach ($params as $key => $value) {
- if (is_array($value)) {
- foreach ($value as $oneArrayValue) {
- $paramsContent .= $key . '[]=' . urlencode($oneArrayValue) . '&';
- }
- unset($params[$key]);
- }
- }
- $paramsContent .= http_build_query($params);
- $url .= '?' . $paramsContent;
- } else {
- $options['body'] = $params;
- }
- }
-
- if ($method === 'GET') {
- $response = $this->client->get($url, $options);
- } else if ($method === 'POST') {
- $response = $this->client->post($url, $options);
- } else if ($method === 'PUT') {
- $response = $this->client->put($url, $options);
- } else if ($method === 'DELETE') {
- $response = $this->client->delete($url, $options);
- } else {
- return ['error' => $this->l10n->t('Bad HTTP method')];
- }
- $body = $response->getBody();
- $respCode = $response->getStatusCode();
- $headers = $response->getHeaders();
-
- if ($respCode >= 400) {
- return ['error' => $this->l10n->t('Bad credentials')];
- } else {
- $decodedResult = json_decode($body, true);
- if (isset($headers['x-aaccountid']) && is_array($headers['x-aaccountid']) && count($headers['x-aaccountid']) > 0) {
- $decodedResult['my_account_id'] = $headers['x-aaccountid'][0];
- }
- return $decodedResult;
- }
- } catch (ServerException | ClientException $e) {
- $this->logger->warning('Jira API error : '.$e->getMessage(), ['app' => Application::APP_ID]);
- return ['error' => $e->getMessage()];
- } catch (ConnectException $e) {
- $this->logger->warning('Jira API connection error : '.$e->getMessage(), ['app' => Application::APP_ID]);
- return ['error' => $e->getMessage()];
- }
- }
-
- /**
- * @param string $userId
- * @return void
- * @throws PreConditionNotMetException
- */
- private function checkTokenExpiration(string $userId): void {
- $refreshToken = $this->config->getUserValue($userId, Application::APP_ID, 'refresh_token');
- $expireAt = $this->config->getUserValue($userId, Application::APP_ID, 'token_expires_at');
- if ($refreshToken !== '' && $expireAt !== '') {
- $nowTs = (new Datetime())->getTimestamp();
- $expireAt = (int) $expireAt;
- // if token expires in less than a minute or is already expired
- if ($nowTs > $expireAt - 60) {
- $this->refreshToken($userId);
- }
- }
- }
-
- /**
- * @param string $userId
- * @return bool
- * @throws PreConditionNotMetException
- */
- private function refreshToken(string $userId): bool {
- $clientID = $this->config->getAppValue(Application::APP_ID, 'client_id');
- $clientSecret = $this->config->getAppValue(Application::APP_ID, 'client_secret');
- $refreshToken = $this->config->getUserValue($userId, Application::APP_ID, 'refresh_token');
- if (!$refreshToken) {
- $this->logger->error('No Jira refresh token found', ['app' => Application::APP_ID]);
- return false;
- }
-
- $result = $this->requestOAuthAccessToken([
- 'client_id' => $clientID,
- 'client_secret' => $clientSecret,
- 'grant_type' => 'refresh_token',
- 'refresh_token' => $refreshToken,
- ], 'POST');
- if (isset($result['access_token'], $result['refresh_token'])) {
- $accessToken = $result['access_token'];
- $refreshToken = $result['refresh_token'];
- $this->config->setUserValue($userId, Application::APP_ID, 'token', $accessToken);
- $this->config->setUserValue($userId, Application::APP_ID, 'refresh_token', $refreshToken);
- if (isset($result['expires_in'])) {
- $nowTs = (new Datetime())->getTimestamp();
- $expiresAt = $nowTs + (int) $result['expires_in'];
- $this->config->setUserValue($userId, Application::APP_ID, 'token_expires_at', $expiresAt);
- }
- return true;
- } else {
- // impossible to refresh the token
- $this->logger->error(
- 'Token is not valid anymore. Impossible to refresh it. '
- . $result['error'] . ' '
- . $result['error_description'] ?? '[no error description]',
- ['app' => Application::APP_ID]
- );
- return false;
- }
- }
-
- /**
- * @param array $params
- * @param string $method
- * @return array
- */
- public function requestOAuthAccessToken(array $params = [], string $method = 'GET'): array {
- try {
- $url = Application::JIRA_AUTH_URL . '/oauth/token';
- $options = [
- 'headers' => [
- 'User-Agent' => 'Nextcloud Jira integration',
- ]
- ];
-
- if (count($params) > 0) {
- if ($method === 'GET') {
- $paramsContent = http_build_query($params);
- $url .= '?' . $paramsContent;
- } else {
- $options['body'] = $params;
- }
- }
-
- if ($method === 'GET') {
- $response = $this->client->get($url, $options);
- } else if ($method === 'POST') {
- $response = $this->client->post($url, $options);
- } else if ($method === 'PUT') {
- $response = $this->client->put($url, $options);
- } else if ($method === 'DELETE') {
- $response = $this->client->delete($url, $options);
- } else {
- return ['error' => $this->l10n->t('Bad HTTP method')];
- }
- $body = $response->getBody();
- $respCode = $response->getStatusCode();
-
- if ($respCode >= 400) {
- return ['error' => $this->l10n->t('OAuth access token refused')];
- } else {
- return json_decode($body, true);
- }
- } catch (Exception | Throwable $e) {
- $this->logger->warning('Jira OAuth error : '.$e->getMessage(), ['app' => Application::APP_ID]);
- return ['error' => $e->getMessage()];
- }
- }
}
diff --git a/lib/Service/NetworkService.php b/lib/Service/NetworkService.php
new file mode 100644
index 0000000..e54ea1b
--- /dev/null
+++ b/lib/Service/NetworkService.php
@@ -0,0 +1,263 @@
+client = $clientService->newClient();
+ }
+
+ /**
+ * @param string $authHeader
+ * @param string $endPoint
+ * @param array $params
+ * @param string $method
+ * @param string $contentType
+ * @param bool $jsonResponse
+ * @return array|mixed|resource|string|string[]|IResponse
+ * @throws PreConditionNotMetException
+ */
+ public function request_integration(string $authHeader, string $endPoint, array $params = [], string $method = 'GET',
+ string $contentType = '', bool $jsonResponse = true, bool $returnRaw = false) {
+ return $this->request(
+ $authHeader,
+ Application::INTEGRATION_API_URL . $endPoint,
+ $params,
+ $method,
+ $contentType,
+ $jsonResponse,
+ $returnRaw);
+ }
+
+ /**
+ * @param string $authHeader
+ * @param string $url
+ * @param array $params
+ * @param string $method
+ * @param string $contentType
+ * @param bool $jsonResponse
+ * @return array|mixed|resource|string|string[]|IResponse
+ * @throws PreConditionNotMetException
+ */
+ public function request(string $authHeader, string $url, array $params = [], string $method = 'GET',
+ string $contentType = '', bool $jsonResponse = true, bool $returnRaw = false) {
+ try {
+ $options = [
+ 'headers' => [
+ 'User-Agent' => Application::INTEGRATION_USER_AGENT,
+ ],
+ ];
+ if ($contentType !== '') {
+ $options['headers']['Content-Type'] = $contentType;
+ }
+ if ($authHeader !== '') {
+ $options['headers']['Authorization'] = $authHeader;
+ }
+
+ if (count($params) > 0) {
+ if ($method === 'GET') {
+ // manage array parameters
+ $paramsContent = '';
+ foreach ($params as $key => $value) {
+ if (is_array($value)) {
+ foreach ($value as $oneArrayValue) {
+ $paramsContent .= $key . '[]=' . urlencode($oneArrayValue) . '&';
+ }
+ unset($params[$key]);
+ }
+ }
+ $paramsContent .= http_build_query($params);
+
+ $url .= '?' . $paramsContent;
+ } else {
+ $options['body'] = $params;
+ }
+ }
+
+ if ($method === 'GET') {
+ $response = $this->client->get($url, $options);
+ } elseif ($method === 'POST') {
+ $response = $this->client->post($url, $options);
+ } elseif ($method === 'PUT') {
+ $response = $this->client->put($url, $options);
+ } elseif ($method === 'DELETE') {
+ $response = $this->client->delete($url, $options);
+ } else {
+ return ['error' => $this->l10n->t('Bad HTTP method')];
+ }
+ if ($returnRaw) {
+ return $response;
+ }
+ $body = $response->getBody();
+ $respCode = $response->getStatusCode();
+
+ if ($respCode >= 400) {
+ return ['error' => $this->l10n->t('Bad credentials')];
+ }
+ if ($jsonResponse) {
+ return json_decode($body, true, flags: JSON_UNESCAPED_UNICODE);
+ }
+ return $body;
+ } catch (ServerException | ClientException $e) {
+ $body = $e->getResponse()->getBody();
+ $this->logger->warning('Network API error : ' . $body, ['app' => Application::APP_ID]);
+ return ['error' => $e->getMessage()];
+ } catch (Exception | Throwable $e) {
+ $this->logger->warning('Network API error', ['exception' => $e, 'app' => Application::APP_ID]);
+ return ['error' => $e->getMessage()];
+ }
+ }
+
+ /**
+ * @param string $userId
+ * @param string $endPoint
+ * @param array $params
+ * @param string $method
+ * @return array
+ * @throws PreConditionNotMetException
+ */
+ public function oauthRequest(string $userId, string $endPoint, array $params = [], string $method = 'GET'): array {
+ $this->checkTokenExpiration($userId);
+ $accessToken = $this->config->getUserValue($userId, Application::APP_ID, 'token');
+
+ $response = $this->request_integration(
+ 'Bearer ' . $accessToken,
+ $endPoint,
+ $params,
+ $method,
+ returnRaw: true,
+ );
+ $body = $response->getBody();
+ $respCode = $response->getStatusCode();
+ $headers = $response->getHeaders();
+
+ if ($respCode >= 400) {
+ return ['error' => $this->l10n->t('Bad credentials')];
+ }
+ $decodedResult = json_decode($body, true);
+ if (isset($headers['x-aaccountid']) && is_array($headers['x-aaccountid']) && count($headers['x-aaccountid']) > 0) {
+ $decodedResult['my_account_id'] = $headers['x-aaccountid'][0];
+ }
+ return $decodedResult;
+ }
+
+ /**
+ * @param string $userId
+ * @return void
+ * @throws PreConditionNotMetException
+ */
+ private function checkTokenExpiration(string $userId): void {
+ $refreshToken = $this->config->getUserValue($userId, Application::APP_ID, 'refresh_token');
+ $expireAt = $this->config->getUserValue($userId, Application::APP_ID, 'token_expires_at');
+ if ($refreshToken !== '' && $expireAt !== '') {
+ $nowTs = (new Datetime())->getTimestamp();
+ $expireAt = (int) $expireAt;
+ // if token expires in less than a minute or is already expired
+ if ($nowTs > $expireAt - 60) {
+ $this->refreshToken($userId);
+ }
+ }
+ }
+
+ /**
+ * @param string $userId
+ * @return bool
+ * @throws PreConditionNotMetException
+ */
+ private function refreshToken(string $userId): bool {
+ $clientID = $this->config->getAppValue(Application::APP_ID, 'client_id');
+ $clientSecret = $this->config->getAppValue(Application::APP_ID, 'client_secret');
+ $refreshToken = $this->config->getUserValue($userId, Application::APP_ID, 'refresh_token');
+ if (!$refreshToken) {
+ $this->logger->error('No Jira refresh token found', ['app' => Application::APP_ID]);
+ return false;
+ }
+
+ $result = $this->requestOAuthAccessToken([
+ 'client_id' => $clientID,
+ 'client_secret' => $clientSecret,
+ 'grant_type' => 'refresh_token',
+ 'refresh_token' => $refreshToken,
+ ]);
+ if (isset($result['access_token'], $result['refresh_token'])) {
+ $accessToken = $result['access_token'];
+ $refreshToken = $result['refresh_token'];
+ $this->config->setUserValue($userId, Application::APP_ID, 'token', $accessToken);
+ $this->config->setUserValue($userId, Application::APP_ID, 'refresh_token', $refreshToken);
+ if (isset($result['expires_in'])) {
+ $nowTs = (new Datetime())->getTimestamp();
+ $expiresAt = $nowTs + (int) $result['expires_in'];
+ $this->config->setUserValue($userId, Application::APP_ID, 'token_expires_at', $expiresAt);
+ }
+ return true;
+ } else {
+ // impossible to refresh the token
+ $this->logger->error(
+ 'Token is not valid anymore. Impossible to refresh it. '
+ . $result['error'] . ' '
+ . $result['error_description'] ?? '[no error description]',
+ ['app' => Application::APP_ID]
+ );
+ return false;
+ }
+ }
+
+ /**
+ * @param array $params
+ * @return array
+ */
+ public function requestOAuthAccessToken(array $params = []): array {
+ return $this->request(
+ '',
+ Application::JIRA_AUTH_URL . 'oauth/token',
+ $params,
+ method: 'POST',
+ );
+ }
+
+ /**
+ * @param string $url
+ * @param string $authHeader
+ * @param string $endPoint
+ * @param array $params
+ * @param string $method
+ * @return array
+ */
+ public function basicRequest(string $url, string $authHeader,
+ string $endPoint, array $params = [], string $method = 'GET'): array {
+ return $this->request(
+ 'Basic ' . $authHeader,
+ $url . '/' . $endPoint,
+ $params,
+ $method,
+ contentType: $method === 'POST' ? 'application/json' : '',
+ );
+ }
+}
diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php
index e9243b4..30f4f99 100644
--- a/lib/Settings/Admin.php
+++ b/lib/Settings/Admin.php
@@ -1,26 +1,21 @@
config = $config;
$this->initialStateService = $initialStateService;
}
diff --git a/lib/Settings/AdminSection.php b/lib/Settings/AdminSection.php
index 1f628c3..68f6598 100644
--- a/lib/Settings/AdminSection.php
+++ b/lib/Settings/AdminSection.php
@@ -1,20 +1,18 @@
l = $l;
$this->urlGenerator = $urlGenerator;
}
diff --git a/lib/Settings/Personal.php b/lib/Settings/Personal.php
index ff4fa57..297b2b6 100644
--- a/lib/Settings/Personal.php
+++ b/lib/Settings/Personal.php
@@ -1,31 +1,23 @@
config = $config;
$this->initialStateService = $initialStateService;
$this->userId = $userId;
diff --git a/lib/Settings/PersonalSection.php b/lib/Settings/PersonalSection.php
index e3bda58..76cb1a9 100644
--- a/lib/Settings/PersonalSection.php
+++ b/lib/Settings/PersonalSection.php
@@ -1,20 +1,18 @@
l = $l;
$this->urlGenerator = $urlGenerator;
}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644
index 0000000..2aa34bf
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,9 @@
+
+
+
+ .
+
+
+
diff --git a/tests/unit/Service/JiraAPIServiceTest.php b/tests/unit/Service/JiraAPIServiceTest.php
new file mode 100644
index 0000000..2363611
--- /dev/null
+++ b/tests/unit/Service/JiraAPIServiceTest.php
@@ -0,0 +1,88 @@
+assertEquals('integration_jira', $app::APP_ID);
+ }
+
+ public function setUp(): void {
+ parent::setUp();
+
+ $this->setupDummies();
+ }
+
+ private function setupDummies(): void {
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+ $this->l10n = $this->createMock(L10N::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->notificationManager = $this->createMock(INotificationManager::class);
+ $this->networkService = $this->createMock(NetworkService::class);
+ $this->clientService = $this->createMock(ClientService::class);
+
+ $this->apiService = new JiraAPIService(
+ $this->userManager,
+ $this->logger,
+ $this->l10n,
+ $this->config,
+ $this->notificationManager,
+ $this->networkService,
+ $this->clientService
+ );
+ }
+
+ public function testSearch() {
+ $this->networkService->method('oauthRequest')->willReturnCallback(function (
+ string $userId, string $endPoint, array $params = [], string $method = 'GET'
+ ) {
+ if (str_contains($endPoint, 'rest/api/2/search')) {
+ return json_decode(file_get_contents('tests/data/search.json'), true);
+ }
+ return 'dummy';
+ });
+
+ $this->config->method('getUserValue')->willReturnCallback(function (
+ $userId, $appName, $key, $default = ''
+ ) {
+ if ($key === 'url') {
+ return 'jira_url';
+ }
+ if ($key == 'resources') {
+ return "[{\"id\":\"7dc26f20-c097-4ca6-8d41-d8617d9b258e\",\"url\":\"https:\\\/\\\/ncintegration.atlassian.net\",\"name\":\"ncintegration\",\"scopes\":[\"manage:jira-project\",\"manage:jira-configuration\",\"manage:jira-data-provider\",\"read:jira-work\",\"write:jira-work\",\"read:jira-user\"],\"avatarUrl\":\"https:\\\/\\\/site-admin-avatar-cdn.prod.public.atl-paas.net\\\/avatars\\\/240\\\/koala.png\"}]";
+ }
+ return '';
+ });
+
+ $expected = $this->apiService->search('admin', 'zop', 0, 5);
+ $this->assertEquals(1, sizeof($expected));
+ $this->assertEquals('FIRST-1', $expected[0]['key']);
+ }
+}