diff --git a/.github/workflows/build-images.yaml b/.github/workflows/build-images.yaml index 3b913d38e..1fed03e70 100644 --- a/.github/workflows/build-images.yaml +++ b/.github/workflows/build-images.yaml @@ -45,6 +45,8 @@ jobs: php_version: "8.0" - module: zmscalldisplay php_version: "8.0" + - module: zmscitizenapi + php_version: "8.0" - module: zmsmessaging php_version: "8.0" - module: zmsstatistic diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml index 57f376e2c..108cb342e 100644 --- a/.github/workflows/unit-tests.yaml +++ b/.github/workflows/unit-tests.yaml @@ -27,6 +27,8 @@ jobs: php_version: "8.0" - module: zmscalldisplay php_version: "8.0" + - module: zmscitizenapi + php_version: "8.0" - module: zmsdldb php_version: "8.0" - module: zmsentities diff --git a/.htaccess b/.htaccess index 8826a3601..8f2e4166c 100644 --- a/.htaccess +++ b/.htaccess @@ -39,6 +39,14 @@ RewriteRule ^terminvereinbarung/calldisplay(.*) /var/www/html/zmscalldisplay/pub RewriteRule ^terminvereinbarung/calldisplay/+_(.*) /var/www/html/zmscalldisplay/public/_$1 [QSA] +# zmscitizenapi +SetEnvIf Request_URI ^/zmscitizenapi ZMS_MODULE_BASEPATH=/terminvereinbarung/api/citizen +RewriteCond %{REQUEST_URI} !^/terminvereinbarung/api/citizen/+(_|doc) +RewriteCond %{REQUEST_FILENAME} !-f +RewriteRule ^terminvereinbarung/api/citizen(.*) /var/www/html/zmscitizenapi/public/index.php?$1 [QSA] +RewriteRule ^terminvereinbarung/api/citizen/+(doc|_)(.*) /var/www/html/zmscitizenapi/public/$1$2 [QSA] + + # zmsstatistic SetEnvIf Request_URI ^/zmsstatistic ZMS_MODULE_BASEPATH=/terminvereinbarung/statistic RewriteCond %{REQUEST_URI} !^/terminvereinbarung/[^/]+/+_ diff --git a/cli b/cli index b9c91682d..4f0f349c3 100755 --- a/cli +++ b/cli @@ -22,6 +22,7 @@ modules = [ "zmsadmin", "zmsapi", "zmscalldisplay", + "zmscitizenapi", "zmsclient", "zmsdb", "zmsdldb", diff --git a/mellon/phpunit.xml.dist b/mellon/phpunit.xml.dist index 73763f0ef..24f093133 100644 --- a/mellon/phpunit.xml.dist +++ b/mellon/phpunit.xml.dist @@ -10,7 +10,7 @@ convertWarningsToExceptions="true" processIsolation="false" forceCoversAnnotation="false" - stopOnFailure="true" + stopOnFailure="false" bootstrap="tests/bootstrap.php" > diff --git a/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_empty.json b/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_empty.json index 70a75536d..ebd499321 100644 --- a/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_empty.json +++ b/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_empty.json @@ -4,7 +4,7 @@ "$schema": "https://schema.berlin.de/queuemanagement/metaresult.json", "error": false, "generated": "2019-02-11T16:07:58+01:00", - "server": "Zmsapi-ENV (v2.19.02-38-g0ba2cba)" + "server": "Zmsapi" }, "data": [] } \ No newline at end of file diff --git a/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_fake_entry.json b/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_fake_entry.json index b77ee7d25..2917b18ea 100644 --- a/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_fake_entry.json +++ b/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_fake_entry.json @@ -4,7 +4,7 @@ "$schema": "https://schema.berlin.de/queuemanagement/metaresult.json", "error": false, "generated": "2019-02-11T16:07:58+01:00", - "server": "Zmsapi-ENV (v2.19.02-38-g0ba2cba)" + "server": "Zmsapi" }, "data": [ { diff --git a/zmsadmin/tests/Zmsadmin/fixtures/GET_source_unittest.json b/zmsadmin/tests/Zmsadmin/fixtures/GET_source_unittest.json index bee3c0341..bb0f11de7 100644 --- a/zmsadmin/tests/Zmsadmin/fixtures/GET_source_unittest.json +++ b/zmsadmin/tests/Zmsadmin/fixtures/GET_source_unittest.json @@ -4,7 +4,7 @@ "$schema": "https://schema.berlin.de/queuemanagement/metaresult.json", "error": false, "generated": "2019-02-08T14:45:15+01:00", - "server": "Zmsapi-ENV (v2.19.02-38-g0ba2cba)" + "server": "Zmsapi" }, "data": { "$schema": "https://schema.berlin.de/queuemanagement/source.json", diff --git a/zmsapi/public/doc/README.md b/zmsapi/public/doc/README.md index 69dbddfdb..5fba8c591 100644 --- a/zmsapi/public/doc/README.md +++ b/zmsapi/public/doc/README.md @@ -1,6 +1,22 @@ ## How does the Open Api definition work ## Version 2.0 +``` +bin/configure +npm i +npm run build +npm run doc +swagger-cli bundle -o public/doc/swagger.json public/doc/swagger.yaml +python3 -m http.server 8001 +``` + +Reachable at: +``` +http://[::]:8001/public/doc/ +https://zms.ddev.site/terminvereinbarung/api/2/doc/index.html +https://it-at-m.github.io/eappointment/zmsapi/public/doc/index.html +``` + * Under /public/doc are the schema from zmsentities. A symbolic link points to the corresponding folder under vendor/eappointment/zmsentities/schema. * Under /bin there is a build_swagger.js file. This is executed via ``npm run doc`` and validates the existing swagger.yaml file. If valid, the open api annotations are read from routing.php and the remaining information such as info, definitions, version and tags are compiled from the yaml files under ./partials into a complete swagger.yaml. @@ -9,8 +25,4 @@ * To access all paths resolved via redoc or the open api documentation, a resolved swagger.json must be created from the swagger.yaml. This is done via the swagger cli with a call to ``bin/doc``. This call executes the above npm command ``npm run doc`` and subsequently creates a full swagger.json. -To render the open-api doc by redoc and swagger, appropriate files such as swagger-ui files are fetched in the CI process and stored at https://eappointment.gitlab.io/zmsapi/. - -* If a new entity definition should be added, the reference must be set here under definitions. - -Translated with www.DeepL.com/Translator (free version) \ No newline at end of file +To render the open-api doc by redoc and swagger, appropriate files such as swagger-ui files are fetched in the CI process and stored at https://eappointment.gitlab.io/zmsapi/. \ No newline at end of file diff --git a/zmscalldisplay/package-lock.json b/zmscalldisplay/package-lock.json index d2e08bbd4..79ee0a2b1 100644 --- a/zmscalldisplay/package-lock.json +++ b/zmscalldisplay/package-lock.json @@ -2682,108 +2682,6 @@ "postcss": "^8.0.9" } }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-select/node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/css-select/node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/css-select/node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/css-select/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -2885,48 +2783,6 @@ "postcss": "^8.2.15" } }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true, - "license": "CC0-1.0", - "optional": true, - "peer": true - }, "node_modules/data-view-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", @@ -4786,15 +4642,6 @@ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "dev": true }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true, - "license": "CC0-1.0", - "optional": true, - "peer": true - }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -6137,21 +5984,6 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "node_modules/srcset": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-5.0.1.tgz", - "integrity": "sha512-/P1UYbGfJVlxZag7aABNRrulEXAwCSDo7fklafOQrantuPTDmYgijJMks2zusPCVzgW9+4P69mq7w6pYuZpgxw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -6296,34 +6128,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/svgo": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", - "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.3.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.0.0" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, "node_modules/table": { "version": "6.8.2", "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", diff --git a/zmscalldisplay/phpunit.xml b/zmscalldisplay/phpunit.xml index aae7f1a39..d4fd86860 100644 --- a/zmscalldisplay/phpunit.xml +++ b/zmscalldisplay/phpunit.xml @@ -10,7 +10,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" forceCoversAnnotation="false" - stopOnFailure="true" + stopOnFailure="false" verbose="true" processIsolation="false" > diff --git a/zmscitizenapi/.gitignore b/zmscitizenapi/.gitignore new file mode 100644 index 000000000..dbf690fac --- /dev/null +++ b/zmscitizenapi/.gitignore @@ -0,0 +1,17 @@ +node_modules +vendor +vendor.zmsbase +config.php +tags +.DS_* +public/_tests +public/doc/assets/redoc.min.js +public/doc/swagger.* +public/_test/assets/swagger-ui-bundle.js +public/_test/assets/swagger-ui.css +public/_test/assets/swagger-ui-standalone-preset.js +cache +vendor.old +vendor.update +VERSION +.phpunit.result.cache \ No newline at end of file diff --git a/zmscitizenapi/Makefile b/zmscitizenapi/Makefile new file mode 100644 index 000000000..896703b0a --- /dev/null +++ b/zmscitizenapi/Makefile @@ -0,0 +1,32 @@ +.PHONY: help now dev live watch fix openapi coverage paratest + +help: # This help + @echo "Possible Targets:" + @grep -P "^\w+:" Makefile|sort|perl -pe 's/^(\w+):([^\#]+)(\#\s*(.*))?/ \1\n\t\4\n/' + +now: # Dummy target + +dev: # init development system + COMPOSER=composer.json composer update + npm install + +live: # init live system, delete unnecessary libs + composer install --no-dev --prefer-dist + bin/configure + npm install + npm run build + npm run doc + npx swagger-cli bundle -o public/doc/swagger.json public/doc/swagger.yaml + +fix: # run code fixing + php vendor/bin/phpcbf --standard=psr2 src/ + php vendor/bin/phpcbf --standard=psr2 tests/ + +openapi: # Swagger docs on changes + ./bin/doc + +coverage: + php vendor/bin/phpunit --coverage-html public/_tests/coverage/ + +paratest: # init parallel unit testing with 5 processes + vendor/bin/paratest -p20 --coverage-html public/_tests/coverage/ diff --git a/zmscitizenapi/bin/build_swagger.js b/zmscitizenapi/bin/build_swagger.js new file mode 100755 index 000000000..4529bb3d3 --- /dev/null +++ b/zmscitizenapi/bin/build_swagger.js @@ -0,0 +1,89 @@ +var fs = require('fs'); +const swaggerParser = require('swagger-parser'); +const swaggerJsdoc = require('swagger-jsdoc'); +const yaml = require('js-yaml'); + +const options = { + definition: { + openapi: '2.0.0', + info: { + version: readApiVersion(), + title: "ZMS API" + }, + }, + apis: ['./routing.php'] + }; + +const openapiSpecification = swaggerJsdoc(options); + +buildSwagger(); +validateSwagger(); + +function validateSwagger() { + + fs.stat('public/doc/swagger.yaml', function(error, stats) { + var routessize = stats.size; + if (error) { + console.log(error); + } else { + console.log("Found public/doc/swagger.yaml with " + routessize + " bytes"); + } + + swaggerParser.validate('public/doc/swagger.yaml', (err, api) => { + if (err) { + console.error(err); + } + else { + console.log("Validated API %s, Version: %s", api.info.title, api.info.version); + } + }) + }); +} + +function buildSwagger() { + let version = readFileContent('public/doc/partials/version.yaml') + "\n"; + let info = readFileContent('public/doc/partials/info.yaml'); + //append current api version to info + info = info + "\n version: '" + readFileContent("./VERSION").trim() + "'\n"; + + let basics = readFileContent('public/doc/partials/basic.yaml') + "\n"; + let paths = { + paths: + openapiSpecification.paths, + } + let tags = readFileContent('public/doc/partials/tags.yaml'); + let definitions = readFileContent('public/doc/partials/definitions.yaml'); + writeSwaggerFile(version + info + basics + tags + yaml.dump(paths) + definitions) + +} + +function writeSwaggerFile(data) +{ + try { + fs.writeFileSync('public/doc/swagger.yaml', data, 'utf8'); + console.log("Build new swagger file successfully!"); + } catch (e) { + console.log(e); + } +} + +function readFileContent(file) { + try { + const data = fs.readFileSync(file, 'utf8'); + return data; + } catch (e) { + console.log(e); + } +} + +function readApiVersion() { + fs.readFile('./VERSION', 'utf8' , (err, data) => { + if (err) { + console.error(err) + return + } + return data; + }) +} + + diff --git a/zmscitizenapi/bin/configure b/zmscitizenapi/bin/configure new file mode 100755 index 000000000..9b59a7b79 --- /dev/null +++ b/zmscitizenapi/bin/configure @@ -0,0 +1,28 @@ +#!/bin/bash + +REALPATH=$(which realpath || which readlink) +REALPATH=$([[ "$REALPATH" =~ 'readlink' ]] && echo "$REALPATH -e" || echo "$REALPATH") +DIR=$(dirname $($REALPATH ${BASH_SOURCE[0]})) +ROOT=$($REALPATH $DIR/..) + +#echo "Get DLDB Exports" +#$ROOT/bin/getDldbExport + +echo -n "Configuring application" +mkdir -p $ROOT/.git/hooks +ln -sf $ROOT/bin/test $ROOT/.git/hooks/pre-commit +ln -sf $ROOT/bin/configure $ROOT/.git/hooks/post-checkout +ln -sf $ROOT/bin/configure $ROOT/.git/hooks/post-commit +ln -sf $ROOT/bin/configure $ROOT/.git/hooks/post-merge + +if [ ! -e $ROOT/config.php ] +then + cp $ROOT/config.example.php $ROOT/config.php +fi +test -d $ROOT/cache && chmod -fR a+rwX $ROOT/cache || echo "Could not chmod cache files" + + +#VERSION=`git symbolic-ref -q --short HEAD || git describe --tags --exact-match` +VERSION=`git describe --tags --always` +echo $VERSION > $ROOT/VERSION +echo " $VERSION" \ No newline at end of file diff --git a/zmscitizenapi/bootstrap.php b/zmscitizenapi/bootstrap.php new file mode 100644 index 000000000..5599065f2 --- /dev/null +++ b/zmscitizenapi/bootstrap.php @@ -0,0 +1,39 @@ +addBodyParsingMiddleware(); + +\App::$http = new \BO\Zmsclient\Http(\App::ZMS_API_URL); +//\BO\Zmsclient\Psr7\Client::$curlopt = \App::$http_curl_config; + +$errorMiddleware = \App::$slim->getContainer()->get('errorMiddleware'); +$errorMiddleware->setDefaultErrorHandler(new \BO\Zmscitizenapi\Helper\ErrorHandler()); + +// load routing +\BO\Slim\Bootstrap::loadRouting(\App::APP_PATH . '/routing.php'); diff --git a/zmscitizenapi/composer.json b/zmscitizenapi/composer.json new file mode 100644 index 000000000..542e3e336 --- /dev/null +++ b/zmscitizenapi/composer.json @@ -0,0 +1,53 @@ +{ + "name": "eappointment/zmscitizenapi", + "description": "This application offers a REST-like interface for citizens on the internet.", + "license": "EUPL-1.2", + "authors": [], + "repositories": [ + { + "type": "path", + "url": "../*", + "options": { + "symlink": true + } + } + ], + "config": { + "platform": { + "php": "8.0" + }, + "allow-plugins": { + "php-http/discovery": true + } + }, + "require-dev": { + "phpmd/phpmd": "@stable", + "squizlabs/php_codesniffer": "*", + "phpunit/phpunit": "^9.5.4", + "helmich/phpunit-psr7-assert": "^4.3.0", + "phpspec/prophecy-phpunit": "^2.0.0" + }, + "require": { + "eappointment/mellon": "@dev", + "eappointment/zmsslim": "@dev", + "eappointment/zmsclient": "@dev", + "eappointment/zmsentities": "@dev" + }, + "scripts": { + "clean": "rm -f public/doc/assets/*.* && rm -f public/_test/assets/*.*", + "command": "bin/configure", + "post-install-cmd": [ + "@wget-files" + ], + "post-update-cmd": [ + "@wget-files" + ] + }, + "bin": [], + "autoload": { + "psr-4": { + "BO\\Zmscitizenapi\\": "src/Zmscitizenapi/", + "BO\\Zmscitizenapi\\Tests\\": "tests/Zmscitizenapi/" + } + } +} diff --git a/zmscitizenapi/composer.lock b/zmscitizenapi/composer.lock new file mode 100644 index 000000000..76f43aad0 --- /dev/null +++ b/zmscitizenapi/composer.lock @@ -0,0 +1,6781 @@ +{ + "_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": "323ae4e4d3276cd30903570c163d1e9e", + "packages": [ + { + "name": "aronduby/dump", + "version": "0.9.1", + "source": { + "type": "git", + "url": "https://github.com/aronduby/dump.git", + "reference": "8b3eb9b54248d520e7ad2f9f1295e2862056fb28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aronduby/dump/zipball/8b3eb9b54248d520e7ad2f9f1295e2862056fb28", + "reference": "8b3eb9b54248d520e7ad2f9f1295e2862056fb28", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "": "src/", + "D\\": "src/D/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "authors": [ + { + "name": "Aron Duby", + "email": "aron.duby@gmail.com" + } + ], + "description": "D::ump - a PHP 5.4 print_r/var_dump replacement base on Krumo", + "keywords": [ + "debug", + "debugging", + "dump", + "krumo", + "pretty", + "print", + "print_r", + "var_dump" + ], + "support": { + "issues": "https://github.com/aronduby/dump/issues", + "source": "https://github.com/aronduby/dump/tree/0.9.1" + }, + "time": "2016-09-30T05:02:34+00:00" + }, + { + "name": "clue/stream-filter", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/clue/stream-filter.git", + "reference": "049509fef80032cb3f051595029ab75b49a3c2f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/stream-filter/zipball/049509fef80032cb3f051595029ab75b49a3c2f7", + "reference": "049509fef80032cb3f051595029ab75b49a3c2f7", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "Clue\\StreamFilter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "A simple and modern approach to stream filtering in PHP", + "homepage": "https://github.com/clue/stream-filter", + "keywords": [ + "bucket brigade", + "callback", + "filter", + "php_user_filter", + "stream", + "stream_filter_append", + "stream_filter_register" + ], + "support": { + "issues": "https://github.com/clue/stream-filter/issues", + "source": "https://github.com/clue/stream-filter/tree/v1.7.0" + }, + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2023-12-20T15:40:13+00:00" + }, + { + "name": "eappointment/mellon", + "version": "dev-feature-MPDZBS-877-zmscitizenapi", + "dist": { + "type": "path", + "url": "../mellon", + "reference": "0d261e9c33e48b356b4130cce2beb95c95b9d28a" + }, + "require": { + "ext-json": ">=0", + "ext-pcre": ">=0", + "php": ">=7.0.0" + }, + "require-dev": { + "phpmd/phpmd": "^2.8.0", + "phpunit/phpunit": "^9.5.4", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "BO\\Mellon\\": "src/Mellon/" + } + }, + "scripts": { + "test": [ + "php -dzend_extension=xdebug.so vendor/bin/phpunit --coverage-html coverage/" + ], + "command": [ + "bin/configure" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mathias Fischer", + "email": "mathias.fischer@berlinonline.de" + } + ], + "description": "Validator for parameters and validation helper", + "transport-options": { + "symlink": true, + "relative": true + } + }, + { + "name": "eappointment/zmsclient", + "version": "dev-feature-MPDZBS-877-zmscitizenapi", + "dist": { + "type": "path", + "url": "../zmsclient", + "reference": "249f1aa96e13e3c2379353bf33539f0174fced53" + }, + "require": { + "aronduby/dump": "^0.9", + "eappointment/mellon": "@dev", + "eappointment/zmsentities": "@dev", + "eappointment/zmsslim": "@dev", + "ext-curl": ">=0", + "ext-json": ">=1.0", + "ext-mbstring": ">=0", + "ext-pcre": ">=0", + "php": ">=7.3.0", + "php-http/curl-client": "^2.2", + "psr/http-message": "^1.0", + "slim/psr7": "^1.5", + "tracy/tracy": "^2.9", + "twig/twig": "3.*" + }, + "require-dev": { + "helmich/phpunit-psr7-assert": "^4.3.0", + "phpmd/phpmd": "@stable", + "phpspec/prophecy-phpunit": "^2.0.0", + "phpunit/phpunit": "^9.5.4", + "squizlabs/php_codesniffer": "^3.5.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "BO\\Zmsclient\\": "src/Zmsclient/", + "BO\\Zmsclient\\Tests\\": "tests/Zmsclient/" + } + }, + "scripts": { + "command": [ + "bin/configure" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mathias Fischer", + "email": "mathias.fischer@berlinonline.de" + } + ], + "description": "Use this library to fetch data from the eappointment API via HTTP.", + "transport-options": { + "symlink": true, + "relative": true + } + }, + { + "name": "eappointment/zmsentities", + "version": "dev-feature-MPDZBS-877-zmscitizenapi", + "dist": { + "type": "path", + "url": "../zmsentities", + "reference": "0d261e9c33e48b356b4130cce2beb95c95b9d28a" + }, + "require": { + "eappointment/mellon": "@dev", + "ext-curl": ">=0", + "ext-json": ">=1.0", + "ext-mbstring": ">=0", + "ext-pcre": ">=0", + "giggsey/libphonenumber-for-php": "^8.8.4", + "league/html-to-markdown": "^5.0", + "league/json-guard": "^1.0", + "php": ">=7.3.0", + "symfony/translation": "^5.4", + "symfony/twig-bridge": "^5.4", + "twig/intl-extra": "^3.4", + "twig/twig": "3.*" + }, + "require-dev": { + "helmich/phpunit-psr7-assert": "^4.3.0", + "league/json-reference": "^1.0", + "phpmd/phpmd": "@stable", + "phpunit/phpunit": "^9.6", + "squizlabs/php_codesniffer": "^3.5.8", + "yuloh/json-guard-cli": "^0.3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "BO\\Zmsentities\\": "src/Zmsentities/", + "BO\\Zmsentities\\Tests\\": "tests/Zmsentities/" + } + }, + "scripts": { + "command": [ + "bin/configure" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mathias Fischer", + "email": "mathias.fischer@berlinonline.de" + } + ], + "description": "Entity definitions for eappoinment", + "transport-options": { + "symlink": true, + "relative": true + } + }, + { + "name": "eappointment/zmsslim", + "version": "dev-feature-MPDZBS-877-zmscitizenapi", + "dist": { + "type": "path", + "url": "../zmsslim", + "reference": "0d261e9c33e48b356b4130cce2beb95c95b9d28a" + }, + "require": { + "eappointment/mellon": "@dev", + "ext-json": "*", + "ext-posix": "*", + "monolog/monolog": "1.*", + "php": ">=7.3.0", + "slim/http-cache": "1.*", + "slim/psr7": "^1.5", + "slim/slim": "4.*", + "slim/twig-view": "3.*", + "stevenmaguire/oauth2-keycloak": "^4.0", + "symfony/translation": "^5.2", + "symfony/twig-bridge": "^5.2", + "tracy/tracy": "^2.9", + "twig/twig": "3.*" + }, + "provide": { + "psr/container-implementation": "2.0" + }, + "require-dev": { + "helmich/phpunit-psr7-assert": "^4.3.0", + "phpmd/phpmd": "@stable", + "phpunit/phpunit": "^9.5.4", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "BO\\Slim\\": "src/Slim/", + "BO\\Slim\\Tests\\": "tests/Slim/" + } + }, + "scripts": { + "command": [ + "bin/configure" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mathias Fischer", + "email": "mathias.fischer@berlinonline.de" + } + ], + "description": "Basic configuration for a slim framework", + "transport-options": { + "symlink": true, + "relative": true + } + }, + { + "name": "fig/http-message-util", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message-util.git", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "suggest": { + "psr/http-message": "The package containing the PSR-7 interfaces" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Fig\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Utility classes and constants for use with PSR-7 (psr/http-message)", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-message-util/issues", + "source": "https://github.com/php-fig/http-message-util/tree/1.1.5" + }, + "time": "2020-11-24T22:02:12+00:00" + }, + { + "name": "firebase/php-jwt", + "version": "v6.10.0", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "a49db6f0a5033aef5143295342f1c95521b075ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/a49db6f0a5033aef5143295342f1c95521b075ff", + "reference": "a49db6f0a5033aef5143295342f1c95521b075ff", + "shasum": "" + }, + "require": { + "php": "^7.4||^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^6.5||^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^1.0||^2.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.10.0" + }, + "time": "2023-12-01T16:26:39+00:00" + }, + { + "name": "giggsey/libphonenumber-for-php", + "version": "8.13.36", + "source": { + "type": "git", + "url": "https://github.com/giggsey/libphonenumber-for-php.git", + "reference": "9ca4179e4332d21578cb29f0c0406f0a2b8803e3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/9ca4179e4332d21578cb29f0c0406f0a2b8803e3", + "reference": "9ca4179e4332d21578cb29f0c0406f0a2b8803e3", + "shasum": "" + }, + "require": { + "giggsey/locale": "^1.7|^2.0", + "php": ">=5.3.2", + "symfony/polyfill-mbstring": "^1.17" + }, + "replace": { + "giggsey/libphonenumber-for-php-lite": "self.version" + }, + "require-dev": { + "pear/pear-core-minimal": "^1.9", + "pear/pear_exception": "^1.0", + "pear/versioncontrol_git": "^0.5", + "phing/phing": "^2.7", + "php-coveralls/php-coveralls": "^1.0|^2.0", + "symfony/console": "^2.8|^3.0|^v4.4|^v5.2", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "libphonenumber\\": "src/" + }, + "exclude-from-classmap": [ + "/src/data/", + "/src/carrier/data/", + "/src/geocoding/data/", + "/src/timezone/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Joshua Gigg", + "email": "giggsey@gmail.com", + "homepage": "https://giggsey.com/" + } + ], + "description": "PHP Port of Google's libphonenumber", + "homepage": "https://github.com/giggsey/libphonenumber-for-php", + "keywords": [ + "geocoding", + "geolocation", + "libphonenumber", + "mobile", + "phonenumber", + "validation" + ], + "support": { + "issues": "https://github.com/giggsey/libphonenumber-for-php/issues", + "source": "https://github.com/giggsey/libphonenumber-for-php" + }, + "time": "2024-05-03T06:27:03+00:00" + }, + { + "name": "giggsey/locale", + "version": "2.6", + "source": { + "type": "git", + "url": "https://github.com/giggsey/Locale.git", + "reference": "37874fa473131247c348059fb7b8985efc18b5ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/giggsey/Locale/zipball/37874fa473131247c348059fb7b8985efc18b5ea", + "reference": "37874fa473131247c348059fb7b8985efc18b5ea", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "require-dev": { + "ext-json": "*", + "pear/pear-core-minimal": "^1.9", + "pear/pear_exception": "^1.0", + "pear/versioncontrol_git": "^0.5", + "phing/phing": "^2.7", + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.5|^9.5", + "symfony/console": "^5.0|^6.0", + "symfony/filesystem": "^5.0|^6.0", + "symfony/finder": "^5.0|^6.0", + "symfony/process": "^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Giggsey\\Locale\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joshua Gigg", + "email": "giggsey@gmail.com", + "homepage": "https://giggsey.com/" + } + ], + "description": "Locale functions required by libphonenumber-for-php", + "support": { + "issues": "https://github.com/giggsey/Locale/issues", + "source": "https://github.com/giggsey/Locale/tree/2.6" + }, + "time": "2024-04-18T19:31:19+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", + "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.8.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2023-12-03T20:35:24+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", + "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2023-12-03T20:19:20+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.6.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.36 || ^9.6.15" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.6.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2023-12-03T20:05:35+00:00" + }, + { + "name": "league/html-to-markdown", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/html-to-markdown.git", + "reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/0b4066eede55c48f38bcee4fb8f0aa85654390fd", + "reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xml": "*", + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "mikehaertl/php-shellcommand": "^1.1.0", + "phpstan/phpstan": "^1.8.8", + "phpunit/phpunit": "^8.5 || ^9.2", + "scrutinizer/ocular": "^1.6", + "unleashedtech/php-coding-standard": "^2.7 || ^3.0", + "vimeo/psalm": "^4.22 || ^5.0" + }, + "bin": [ + "bin/html-to-markdown" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\HTMLToMarkdown\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + }, + { + "name": "Nick Cernis", + "email": "nick@cern.is", + "homepage": "http://modernnerd.net", + "role": "Original Author" + } + ], + "description": "An HTML-to-markdown conversion helper for PHP", + "homepage": "https://github.com/thephpleague/html-to-markdown", + "keywords": [ + "html", + "markdown" + ], + "support": { + "issues": "https://github.com/thephpleague/html-to-markdown/issues", + "source": "https://github.com/thephpleague/html-to-markdown/tree/5.1.1" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/html-to-markdown", + "type": "tidelift" + } + ], + "time": "2023-07-12T21:21:09+00:00" + }, + { + "name": "league/json-guard", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/json-guard.git", + "reference": "596059d2c013bcea1a8a1386bd0e60d32ef39eb9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/json-guard/zipball/596059d2c013bcea1a8a1386bd0e60d32ef39eb9", + "reference": "596059d2c013bcea1a8a1386bd0e60d32ef39eb9", + "shasum": "" + }, + "require": { + "ext-bcmath": "*", + "php": ">=5.6.0", + "psr/container": "^1.0" + }, + "require-dev": { + "ext-curl": "*", + "json-schema/json-schema-test-suite": "1.2.0", + "league/json-reference": "1.0.0", + "phpunit/phpunit": "4.*", + "scrutinizer/ocular": "~1.1", + "squizlabs/php_codesniffer": "~2.3" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "League\\JsonGuard\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Allan", + "email": "matthew.james.allan@gmail.com", + "homepage": "https://mattallan.org", + "role": "Developer" + } + ], + "description": "A validator for JSON using json-schema.", + "homepage": "https://github.com/thephpleague/json-guard", + "keywords": [ + "json", + "json-schema", + "json-schema.org", + "schema", + "validation" + ], + "support": { + "issues": "https://github.com/thephpleague/json-guard/issues", + "source": "https://github.com/thephpleague/json-guard/tree/master" + }, + "abandoned": true, + "time": "2017-05-03T21:12:30+00:00" + }, + { + "name": "league/oauth2-client", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-client.git", + "reference": "160d6274b03562ebeb55ed18399281d8118b76c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/160d6274b03562ebeb55ed18399281d8118b76c8", + "reference": "160d6274b03562ebeb55ed18399281d8118b76c8", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0 || ^7.0", + "paragonie/random_compat": "^1 || ^2 || ^9.99", + "php": "^5.6 || ^7.0 || ^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.5", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpunit/phpunit": "^5.7 || ^6.0 || ^9.5", + "squizlabs/php_codesniffer": "^2.3 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Bilbie", + "email": "hello@alexbilbie.com", + "homepage": "http://www.alexbilbie.com", + "role": "Developer" + }, + { + "name": "Woody Gilk", + "homepage": "https://github.com/shadowhand", + "role": "Contributor" + } + ], + "description": "OAuth 2.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "identity", + "idp", + "oauth", + "oauth2", + "single sign on" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth2-client/issues", + "source": "https://github.com/thephpleague/oauth2-client/tree/2.7.0" + }, + "time": "2023-04-16T18:19:15+00:00" + }, + { + "name": "monolog/monolog", + "version": "1.27.1", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "904713c5929655dc9b97288b69cfeedad610c9a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/904713c5929655dc9b97288b69cfeedad610c9a1", + "reference": "904713c5929655dc9b97288b69cfeedad610c9a1", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpstan/phpstan": "^0.12.59", + "phpunit/phpunit": "~4.5", + "ruflin/elastica": ">=0.90 <3.0", + "sentry/sentry": "^0.13", + "swiftmailer/swiftmailer": "^5.3|^6.0" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" + }, + "type": "library", + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/1.27.1" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2022-06-09T08:53:42+00:00" + }, + { + "name": "nikic/fast-route", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/FastRoute.git", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|~5.7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "FastRoute\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Fast request router for PHP", + "keywords": [ + "router", + "routing" + ], + "support": { + "issues": "https://github.com/nikic/FastRoute/issues", + "source": "https://github.com/nikic/FastRoute/tree/master" + }, + "time": "2018-02-13T20:26:39+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "php-http/curl-client", + "version": "2.3.2", + "source": { + "type": "git", + "url": "https://github.com/php-http/curl-client.git", + "reference": "0b869922458b1cde9137374545ed4fff7ac83623" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/curl-client/zipball/0b869922458b1cde9137374545ed4fff7ac83623", + "reference": "0b869922458b1cde9137374545ed4fff7ac83623", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": "^7.4 || ^8.0", + "php-http/discovery": "^1.6", + "php-http/httplug": "^2.0", + "php-http/message": "^1.2", + "psr/http-client": "^1.0", + "psr/http-factory-implementation": "^1.0", + "symfony/options-resolver": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "provide": { + "php-http/async-client-implementation": "1.0", + "php-http/client-implementation": "1.0", + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "guzzlehttp/psr7": "^2.0", + "laminas/laminas-diactoros": "^2.0", + "php-http/client-integration-tests": "^3.0", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^7.5 || ^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Client\\Curl\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Михаил Красильников", + "email": "m.krasilnikov@yandex.ru" + } + ], + "description": "PSR-18 and HTTPlug Async client with cURL", + "homepage": "http://php-http.org", + "keywords": [ + "curl", + "http", + "psr-18" + ], + "support": { + "issues": "https://github.com/php-http/curl-client/issues", + "source": "https://github.com/php-http/curl-client/tree/2.3.2" + }, + "time": "2024-03-03T08:21:07+00:00" + }, + { + "name": "php-http/discovery", + "version": "1.19.4", + "source": { + "type": "git", + "url": "https://github.com/php-http/discovery.git", + "reference": "0700efda8d7526335132360167315fdab3aeb599" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/discovery/zipball/0700efda8d7526335132360167315fdab3aeb599", + "reference": "0700efda8d7526335132360167315fdab3aeb599", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0|^2.0", + "php": "^7.1 || ^8.0" + }, + "conflict": { + "nyholm/psr7": "<1.0", + "zendframework/zend-diactoros": "*" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "*", + "psr/http-factory-implementation": "*", + "psr/http-message-implementation": "*" + }, + "require-dev": { + "composer/composer": "^1.0.2|^2.0", + "graham-campbell/phpspec-skip-example-extension": "^5.0", + "php-http/httplug": "^1.0 || ^2.0", + "php-http/message-factory": "^1.0", + "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3", + "sebastian/comparator": "^3.0.5 || ^4.0.8", + "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1" + }, + "type": "composer-plugin", + "extra": { + "class": "Http\\Discovery\\Composer\\Plugin", + "plugin-optional": true + }, + "autoload": { + "psr-4": { + "Http\\Discovery\\": "src/" + }, + "exclude-from-classmap": [ + "src/Composer/Plugin.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations", + "homepage": "http://php-http.org", + "keywords": [ + "adapter", + "client", + "discovery", + "factory", + "http", + "message", + "psr17", + "psr7" + ], + "support": { + "issues": "https://github.com/php-http/discovery/issues", + "source": "https://github.com/php-http/discovery/tree/1.19.4" + }, + "time": "2024-03-29T13:00:05+00:00" + }, + { + "name": "php-http/httplug", + "version": "2.4.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/httplug.git", + "reference": "625ad742c360c8ac580fcc647a1541d29e257f67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/httplug/zipball/625ad742c360c8ac580fcc647a1541d29e257f67", + "reference": "625ad742c360c8ac580fcc647a1541d29e257f67", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "php-http/promise": "^1.1", + "psr/http-client": "^1.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "require-dev": { + "friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0", + "phpspec/phpspec": "^5.1 || ^6.0 || ^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eric GELOEN", + "email": "geloen.eric@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "HTTPlug, the HTTP client abstraction for PHP", + "homepage": "http://httplug.io", + "keywords": [ + "client", + "http" + ], + "support": { + "issues": "https://github.com/php-http/httplug/issues", + "source": "https://github.com/php-http/httplug/tree/2.4.0" + }, + "time": "2023-04-14T15:10:03+00:00" + }, + { + "name": "php-http/message", + "version": "1.16.1", + "source": { + "type": "git", + "url": "https://github.com/php-http/message.git", + "reference": "5997f3289332c699fa2545c427826272498a2088" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/message/zipball/5997f3289332c699fa2545c427826272498a2088", + "reference": "5997f3289332c699fa2545c427826272498a2088", + "shasum": "" + }, + "require": { + "clue/stream-filter": "^1.5", + "php": "^7.2 || ^8.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "provide": { + "php-http/message-factory-implementation": "1.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.6", + "ext-zlib": "*", + "guzzlehttp/psr7": "^1.0 || ^2.0", + "laminas/laminas-diactoros": "^2.0 || ^3.0", + "php-http/message-factory": "^1.0.2", + "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1", + "slim/slim": "^3.0" + }, + "suggest": { + "ext-zlib": "Used with compressor/decompressor streams", + "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories", + "laminas/laminas-diactoros": "Used with Diactoros Factories", + "slim/slim": "Used with Slim Framework PSR-7 implementation" + }, + "type": "library", + "autoload": { + "files": [ + "src/filters.php" + ], + "psr-4": { + "Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "HTTP Message related tools", + "homepage": "http://php-http.org", + "keywords": [ + "http", + "message", + "psr-7" + ], + "support": { + "issues": "https://github.com/php-http/message/issues", + "source": "https://github.com/php-http/message/tree/1.16.1" + }, + "time": "2024-03-07T13:22:09+00:00" + }, + { + "name": "php-http/promise", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/php-http/promise.git", + "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/promise/zipball/fc85b1fba37c169a69a07ef0d5a8075770cc1f83", + "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "friends-of-phpspec/phpspec-code-coverage": "^4.3.2 || ^6.3", + "phpspec/phpspec": "^5.1.2 || ^6.2 || ^7.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joel Wurtz", + "email": "joel.wurtz@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Promise used for asynchronous HTTP requests", + "homepage": "http://httplug.io", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/php-http/promise/issues", + "source": "https://github.com/php-http/promise/tree/1.3.1" + }, + "time": "2024-03-15T13:55:21+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/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/1.1" + }, + "time": "2023-04-04T09:50:52+00:00" + }, + { + "name": "psr/http-server-handler", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-handler.git", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side request handler", + "keywords": [ + "handler", + "http", + "http-interop", + "psr", + "psr-15", + "psr-7", + "request", + "response", + "server" + ], + "support": { + "source": "https://github.com/php-fig/http-server-handler/tree/1.0.2" + }, + "time": "2023-04-10T20:06:20+00:00" + }, + { + "name": "psr/http-server-middleware", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-middleware.git", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/http-server-handler": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side middleware", + "keywords": [ + "http", + "http-interop", + "middleware", + "psr", + "psr-15", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-server-middleware/issues", + "source": "https://github.com/php-fig/http-server-middleware/tree/1.0.2" + }, + "time": "2023-04-11T06:14:47+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": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "slim/http-cache", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Slim-HttpCache.git", + "reference": "d1a091aca45695a2159194132872f4a544416bc9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Slim-HttpCache/zipball/d1a091aca45695a2159194132872f4a544416bc9", + "reference": "d1a091aca45695a2159194132872f4a544416bc9", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "psr/http-message": "^1.0", + "psr/http-server-middleware": "^1.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.28", + "phpunit/phpunit": "^8.5.13 || ^9.3.8", + "slim/psr7": "^1.1", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\HttpCache\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "http://joshlockhart.com" + } + ], + "description": "Slim Framework HTTP cache middleware and service provider", + "homepage": "https://www.slimframework.com", + "keywords": [ + "cache", + "framework", + "middleware", + "slim" + ], + "support": { + "issues": "https://github.com/slimphp/Slim-HttpCache/issues", + "source": "https://github.com/slimphp/Slim-HttpCache/tree/1.1.0" + }, + "time": "2020-12-08T17:32:05+00:00" + }, + { + "name": "slim/psr7", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Slim-Psr7.git", + "reference": "72d2b2bac94ab4575d369f605dbfafbe168d3163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/72d2b2bac94ab4575d369f605dbfafbe168d3163", + "reference": "72d2b2bac94ab4575d369f605dbfafbe168d3163", + "shasum": "" + }, + "require": { + "fig/http-message-util": "^1.1.5", + "php": "^7.4 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3.0", + "symfony/polyfill-php80": "^1.26" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "adriansuter/php-autoload-override": "^1.3", + "ext-json": "*", + "http-interop/http-factory-tests": "^0.9.0", + "php-http/psr7-integration-tests": "1.1", + "phpspec/prophecy": "^1.15", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\Psr7\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "http://joshlockhart.com" + }, + { + "name": "Andrew Smith", + "email": "a.smith@silentworks.co.uk", + "homepage": "http://silentworks.co.uk" + }, + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + } + ], + "description": "Strict PSR-7 implementation", + "homepage": "https://www.slimframework.com", + "keywords": [ + "http", + "psr-7", + "psr7" + ], + "support": { + "issues": "https://github.com/slimphp/Slim-Psr7/issues", + "source": "https://github.com/slimphp/Slim-Psr7/tree/1.6.1" + }, + "time": "2023-04-17T16:02:20+00:00" + }, + { + "name": "slim/slim", + "version": "4.13.0", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Slim.git", + "reference": "038fd5713d5a41636fdff0e8dcceedecdd17fc17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/038fd5713d5a41636fdff0e8dcceedecdd17fc17", + "reference": "038fd5713d5a41636fdff0e8dcceedecdd17fc17", + "shasum": "" + }, + "require": { + "ext-json": "*", + "nikic/fast-route": "^1.3", + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "psr/http-server-handler": "^1.0", + "psr/http-server-middleware": "^1.0", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "adriansuter/php-autoload-override": "^1.4", + "ext-simplexml": "*", + "guzzlehttp/psr7": "^2.6", + "httpsoft/http-message": "^1.1", + "httpsoft/http-server-request": "^1.1", + "laminas/laminas-diactoros": "^2.17 || ^3", + "nyholm/psr7": "^1.8", + "nyholm/psr7-server": "^1.1", + "phpspec/prophecy": "^1.19", + "phpspec/prophecy-phpunit": "^2.1", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.6", + "slim/http": "^1.3", + "slim/psr7": "^1.6", + "squizlabs/php_codesniffer": "^3.9" + }, + "suggest": { + "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", + "ext-xml": "Needed to support XML format in BodyParsingMiddleware", + "php-di/php-di": "PHP-DI is the recommended container library to be used with Slim", + "slim/psr7": "Slim PSR-7 implementation. See https://www.slimframework.com/docs/v4/start/installation.html for more information." + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\": "Slim" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "https://joshlockhart.com" + }, + { + "name": "Andrew Smith", + "email": "a.smith@silentworks.co.uk", + "homepage": "http://silentworks.co.uk" + }, + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + }, + { + "name": "Gabriel Manricks", + "email": "gmanricks@me.com", + "homepage": "http://gabrielmanricks.com" + } + ], + "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs", + "homepage": "https://www.slimframework.com", + "keywords": [ + "api", + "framework", + "micro", + "router" + ], + "support": { + "docs": "https://www.slimframework.com/docs/v4/", + "forum": "https://discourse.slimframework.com/", + "irc": "irc://irc.freenode.net:6667/slimphp", + "issues": "https://github.com/slimphp/Slim/issues", + "rss": "https://www.slimframework.com/blog/feed.rss", + "slack": "https://slimphp.slack.com/", + "source": "https://github.com/slimphp/Slim", + "wiki": "https://github.com/slimphp/Slim/wiki" + }, + "funding": [ + { + "url": "https://opencollective.com/slimphp", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/slim/slim", + "type": "tidelift" + } + ], + "time": "2024-03-03T21:25:30+00:00" + }, + { + "name": "slim/twig-view", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Twig-View.git", + "reference": "1b351536b9a07ed90a3563ee9d71a987c5d74610" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Twig-View/zipball/1b351536b9a07ed90a3563ee9d71a987c5d74610", + "reference": "1b351536b9a07ed90a3563ee9d71a987c5d74610", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/http-message": "^1.1 || ^2.0", + "slim/slim": "^4.12", + "symfony/polyfill-php81": "^1.29", + "twig/twig": "^3.8" + }, + "require-dev": { + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/phpstan": "^1.10.59", + "phpunit/phpunit": "^9.6", + "psr/http-factory": "^1.0", + "squizlabs/php_codesniffer": "^3.9" + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\Views\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "http://joshlockhart.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + } + ], + "description": "Slim Framework 4 view helper built on top of the Twig 3 templating component", + "homepage": "https://www.slimframework.com", + "keywords": [ + "framework", + "slim", + "template", + "twig", + "view" + ], + "support": { + "issues": "https://github.com/slimphp/Twig-View/issues", + "source": "https://github.com/slimphp/Twig-View/tree/3.4.0" + }, + "time": "2024-04-28T20:36:39+00:00" + }, + { + "name": "stevenmaguire/oauth2-keycloak", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/stevenmaguire/oauth2-keycloak.git", + "reference": "05ead6bb6bcd2b6f96dfae87c769dcd3e5f6129d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stevenmaguire/oauth2-keycloak/zipball/05ead6bb6bcd2b6f96dfae87c769dcd3e5f6129d", + "reference": "05ead6bb6bcd2b6f96dfae87c769dcd3e5f6129d", + "shasum": "" + }, + "require": { + "firebase/php-jwt": "^4.0 || ^5.0 || ^6.0", + "league/oauth2-client": "^2.0", + "php": "~7.2 || ~8.0" + }, + "require-dev": { + "mockery/mockery": "~1.5.0", + "phpunit/phpunit": "~9.6.4", + "squizlabs/php_codesniffer": "~3.7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Stevenmaguire\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Steven Maguire", + "email": "stevenmaguire@gmail.com", + "homepage": "https://github.com/stevenmaguire" + } + ], + "description": "Keycloak OAuth 2.0 Client Provider for The PHP League OAuth2-Client", + "keywords": [ + "authorisation", + "authorization", + "client", + "keycloak", + "oauth", + "oauth2" + ], + "support": { + "issues": "https://github.com/stevenmaguire/oauth2-keycloak/issues", + "source": "https://github.com/stevenmaguire/oauth2-keycloak/tree/4.0.0" + }, + "time": "2023-03-14T09:43:47+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "80d075412b557d41002320b96a096ca65aa2c98d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/80d075412b557d41002320b96a096ca65aa2c98d", + "reference": "80d075412b557d41002320b96a096ca65aa2c98d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-24T14:02:46+00:00" + }, + { + "name": "symfony/intl", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/intl.git", + "reference": "0ae24e7ead0761e3e29e89c3336353b991c90f96" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/intl/zipball/0ae24e7ead0761e3e29e89c3336353b991c90f96", + "reference": "0ae24e7ead0761e3e29e89c3336353b991c90f96", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "symfony/filesystem": "^4.4|^5.0|^6.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Intl\\": "" + }, + "classmap": [ + "Resources/stubs" + ], + "exclude-from-classmap": [ + "/Tests/", + "/Resources/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Eriksen Costa", + "email": "eriksen.costa@infranology.com.br" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a PHP replacement layer for the C intl extension that includes additional data from the ICU library", + "homepage": "https://symfony.com", + "keywords": [ + "i18n", + "icu", + "internationalization", + "intl", + "l10n", + "localization" + ], + "support": { + "source": "https://github.com/symfony/intl/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "1303bb73d6c3882f07c618129295503085dfddb9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1303bb73d6c3882f07c618129295503085dfddb9", + "reference": "1303bb73d6c3882f07c618129295503085dfddb9", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php73": "~1.0", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "21bd091060673a1177ae842c0ef8fe30893114d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/21bd091060673a1177ae842c0ef8fe30893114d2", + "reference": "21bd091060673a1177ae842c0ef8fe30893114d2", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/c565ad1e63f30e7477fc40738343c62b40bc672d", + "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/translation", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "0fabede35e3985c4f96089edeeefe8313e15ca3a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/0fabede35e3985c4f96089edeeefe8313e15ca3a", + "reference": "0fabede35e3985c4f96089edeeefe8313e15ca3a", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation-contracts": "^2.3" + }, + "conflict": { + "symfony/config": "<4.4", + "symfony/console": "<5.3", + "symfony/dependency-injection": "<5.0", + "symfony/http-kernel": "<5.0", + "symfony/twig-bundle": "<5.0", + "symfony/yaml": "<4.4" + }, + "provide": { + "symfony/translation-implementation": "2.3" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-kernel": "^5.0|^6.0", + "symfony/intl": "^4.4|^5.0|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/yaml": "^4.4|^5.0|^6.0" + }, + "suggest": { + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v2.5.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "b0073a77ac0b7ea55131020e87b1e3af540f4664" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b0073a77ac0b7ea55131020e87b1e3af540f4664", + "reference": "b0073a77ac0b7ea55131020e87b1e3af540f4664", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v2.5.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T13:51:25+00:00" + }, + { + "name": "symfony/twig-bridge", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/twig-bridge.git", + "reference": "1384448132165f95f76ca67cd722c560e29b8245" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/1384448132165f95f76ca67cd722c560e29b8245", + "reference": "1384448132165f95f76ca67cd722c560e29b8245", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16", + "symfony/translation-contracts": "^1.1|^2|^3", + "twig/twig": "^2.13|^3.0.4" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/console": "<5.3", + "symfony/form": "<5.4.21|>=6,<6.2.7", + "symfony/http-foundation": "<5.3", + "symfony/http-kernel": "<4.4", + "symfony/translation": "<5.2", + "symfony/workflow": "<5.2" + }, + "require-dev": { + "doctrine/annotations": "^1.12|^2", + "egulias/email-validator": "^2.1.10|^3|^4", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/asset": "^4.4|^5.0|^6.0", + "symfony/console": "^5.3|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/form": "^5.4.21|^6.2.7", + "symfony/http-foundation": "^5.3|^6.0", + "symfony/http-kernel": "^4.4|^5.0|^6.0", + "symfony/intl": "^4.4|^5.0|^6.0", + "symfony/mime": "^5.2|^6.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/property-info": "^4.4|^5.1|^6.0", + "symfony/routing": "^4.4|^5.0|^6.0", + "symfony/security-acl": "^2.8|^3.0", + "symfony/security-core": "^4.4|^5.0|^6.0", + "symfony/security-csrf": "^4.4|^5.0|^6.0", + "symfony/security-http": "^4.4|^5.0|^6.0", + "symfony/serializer": "^5.4.35|~6.3.12|^6.4.3", + "symfony/stopwatch": "^4.4|^5.0|^6.0", + "symfony/translation": "^5.2|^6.0", + "symfony/web-link": "^4.4|^5.0|^6.0", + "symfony/workflow": "^5.2|^6.0", + "symfony/yaml": "^4.4|^5.0|^6.0", + "twig/cssinliner-extra": "^2.12|^3", + "twig/inky-extra": "^2.12|^3", + "twig/markdown-extra": "^2.12|^3" + }, + "suggest": { + "symfony/asset": "For using the AssetExtension", + "symfony/expression-language": "For using the ExpressionExtension", + "symfony/finder": "", + "symfony/form": "For using the FormExtension", + "symfony/http-kernel": "For using the HttpKernelExtension", + "symfony/routing": "For using the RoutingExtension", + "symfony/security-core": "For using the SecurityExtension", + "symfony/security-csrf": "For using the CsrfExtension", + "symfony/security-http": "For using the LogoutUrlExtension", + "symfony/stopwatch": "For using the StopwatchExtension", + "symfony/translation": "For using the TranslationExtension", + "symfony/var-dumper": "For using the DumpExtension", + "symfony/web-link": "For using the WebLinkExtension", + "symfony/yaml": "For using the YamlExtension" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Twig\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides integration for Twig with various Symfony components", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/twig-bridge/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "tracy/tracy", + "version": "v2.10.7", + "source": { + "type": "git", + "url": "https://github.com/nette/tracy.git", + "reference": "7e7b25ba103968d5318d37db330b2e9c755dc765" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/tracy/zipball/7e7b25ba103968d5318d37db330b2e9c755dc765", + "reference": "7e7b25ba103968d5318d37db330b2e9c755dc765", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-session": "*", + "php": ">=8.0 <8.4" + }, + "conflict": { + "nette/di": "<3.0" + }, + "require-dev": { + "latte/latte": "^2.5", + "nette/di": "^3.0", + "nette/http": "^3.0", + "nette/mail": "^3.0", + "nette/tester": "^2.2", + "nette/utils": "^3.0", + "phpstan/phpstan": "^1.0", + "psr/log": "^1.0 || ^2.0 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.10-dev" + } + }, + "autoload": { + "files": [ + "src/Tracy/functions.php" + ], + "classmap": [ + "src" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "😎 Tracy: the addictive tool to ease debugging PHP code for cool developers. Friendly design, logging, profiler, advanced features like debugging AJAX calls or CLI support. You will love it.", + "homepage": "https://tracy.nette.org", + "keywords": [ + "Xdebug", + "debug", + "debugger", + "nette", + "profiler" + ], + "support": { + "issues": "https://github.com/nette/tracy/issues", + "source": "https://github.com/nette/tracy/tree/v2.10.7" + }, + "time": "2024-04-29T11:44:00+00:00" + }, + { + "name": "twig/intl-extra", + "version": "v3.10.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/intl-extra.git", + "reference": "693f6beb8ca91fc6323e01b3addf983812f65c93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/693f6beb8ca91fc6323e01b3addf983812f65c93", + "reference": "693f6beb8ca91fc6323e01b3addf983812f65c93", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/intl": "^5.4|^6.4|^7.0", + "twig/twig": "^3.10" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\Extra\\Intl\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Twig extension for Intl", + "homepage": "https://twig.symfony.com", + "keywords": [ + "intl", + "twig" + ], + "support": { + "source": "https://github.com/twigphp/intl-extra/tree/v3.10.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2024-05-11T07:35:57+00:00" + }, + { + "name": "twig/twig", + "version": "v3.10.2", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "7aaed0b8311a557cc8c4047a71fd03153a00e755" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/7aaed0b8311a557cc8c4047a71fd03153a00e755", + "reference": "7aaed0b8311a557cc8c4047a71fd03153a00e755", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php80": "^1.22" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.10.2" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2024-05-14T06:04:16+00:00" + } + ], + "packages-dev": [ + { + "name": "composer/pcre", + "version": "3.1.3", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", + "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.1.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-03-19T10:26:25+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + }, + "time": "2024-01-30T19:34:25+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:15:36+00:00" + }, + { + "name": "helmich/phpunit-json-assert", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/martin-helmich/phpunit-json-assert.git", + "reference": "5a3a53e46f4d1f1b324c5cc2e33d87ad2d37260c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/martin-helmich/phpunit-json-assert/zipball/5a3a53e46f4d1f1b324c5cc2e33d87ad2d37260c", + "reference": "5a3a53e46f4d1f1b324c5cc2e33d87ad2d37260c", + "shasum": "" + }, + "require": { + "justinrainbow/json-schema": "^5.0", + "php": "^8.0", + "softcreatr/jsonpath": "^0.8" + }, + "conflict": { + "phpunit/phpunit": "<8.0 || >= 11.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Helmich\\JsonAssert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Martin Helmich", + "email": "m.helmich@mittwald.de" + } + ], + "description": "PHPUnit assertions for JSON documents", + "support": { + "issues": "https://github.com/martin-helmich/phpunit-json-assert/issues", + "source": "https://github.com/martin-helmich/phpunit-json-assert/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://donate.helmich.me", + "type": "custom" + }, + { + "url": "https://github.com/martin-helmich", + "type": "github" + } + ], + "time": "2023-03-03T14:09:38+00:00" + }, + { + "name": "helmich/phpunit-psr7-assert", + "version": "v4.4.1", + "source": { + "type": "git", + "url": "https://github.com/martin-helmich/phpunit-psr7-assert.git", + "reference": "f35fa69e07cc16977b52805d3abd873cc16747fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/martin-helmich/phpunit-psr7-assert/zipball/f35fa69e07cc16977b52805d3abd873cc16747fd", + "reference": "f35fa69e07cc16977b52805d3abd873cc16747fd", + "shasum": "" + }, + "require": { + "helmich/phpunit-json-assert": "^3.4", + "php": "^8.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "conflict": { + "phpunit/phpunit": "<8.0 || >= 11.0" + }, + "require-dev": { + "guzzlehttp/psr7": "^2.4", + "mockery/mockery": "^1.4.1", + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Helmich\\Psr7Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Martin Helmich", + "email": "m.helmich@mittwald.de" + } + ], + "description": "PHPUnit assertions for testing PSR7-compliant applications", + "support": { + "issues": "https://github.com/martin-helmich/phpunit-psr7-assert/issues", + "source": "https://github.com/martin-helmich/phpunit-psr7-assert/tree/v4.4.1" + }, + "funding": [ + { + "url": "https://donate.helmich.me", + "type": "custom" + }, + { + "url": "https://github.com/martin-helmich", + "type": "github" + } + ], + "time": "2023-07-26T19:04:29+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "v5.2.13", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/fbbe7e5d79f618997bc3332a6f49246036c45793", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/v5.2.13" + }, + "time": "2023-09-26T02:20:38+00:00" + }, + { + "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": "nikic/php-parser", + "version": "v5.0.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-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/v5.0.2" + }, + "time": "2024-03-05T20:51:40+00:00" + }, + { + "name": "pdepend/pdepend", + "version": "2.16.2", + "source": { + "type": "git", + "url": "https://github.com/pdepend/pdepend.git", + "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/f942b208dc2a0868454d01b29f0c75bbcfc6ed58", + "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58", + "shasum": "" + }, + "require": { + "php": ">=5.3.7", + "symfony/config": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/dependency-injection": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/filesystem": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/polyfill-mbstring": "^1.19" + }, + "require-dev": { + "easy-doc/easy-doc": "0.0.0|^1.2.3", + "gregwar/rst": "^1.0", + "squizlabs/php_codesniffer": "^2.0.0" + }, + "bin": [ + "src/bin/pdepend" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "PDepend\\": "src/main/php/PDepend" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Official version of pdepend to be handled with Composer", + "keywords": [ + "PHP Depend", + "PHP_Depend", + "dev", + "pdepend" + ], + "support": { + "issues": "https://github.com/pdepend/pdepend/issues", + "source": "https://github.com/pdepend/pdepend/tree/2.16.2" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/pdepend/pdepend", + "type": "tidelift" + } + ], + "time": "2023-12-17T18:09:59+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "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.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+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": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.4.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.5", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + }, + "time": "2024-05-21T05:55:05+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "153ae662783729388a584b4361f2545e4d841e3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + }, + "time": "2024-02-23T11:10:43+00:00" + }, + { + "name": "phpmd/phpmd", + "version": "2.15.0", + "source": { + "type": "git", + "url": "https://github.com/phpmd/phpmd.git", + "reference": "74a1f56e33afad4128b886e334093e98e1b5e7c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/74a1f56e33afad4128b886e334093e98e1b5e7c0", + "reference": "74a1f56e33afad4128b886e334093e98e1b5e7c0", + "shasum": "" + }, + "require": { + "composer/xdebug-handler": "^1.0 || ^2.0 || ^3.0", + "ext-xml": "*", + "pdepend/pdepend": "^2.16.1", + "php": ">=5.3.9" + }, + "require-dev": { + "easy-doc/easy-doc": "0.0.0 || ^1.3.2", + "ext-json": "*", + "ext-simplexml": "*", + "gregwar/rst": "^1.0", + "mikey179/vfsstream": "^1.6.8", + "squizlabs/php_codesniffer": "^2.9.2 || ^3.7.2" + }, + "bin": [ + "src/bin/phpmd" + ], + "type": "library", + "autoload": { + "psr-0": { + "PHPMD\\": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Manuel Pichler", + "email": "github@manuel-pichler.de", + "homepage": "https://github.com/manuelpichler", + "role": "Project Founder" + }, + { + "name": "Marc Würth", + "email": "ravage@bluewin.ch", + "homepage": "https://github.com/ravage84", + "role": "Project Maintainer" + }, + { + "name": "Other contributors", + "homepage": "https://github.com/phpmd/phpmd/graphs/contributors", + "role": "Contributors" + } + ], + "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", + "homepage": "https://phpmd.org/", + "keywords": [ + "dev", + "mess detection", + "mess detector", + "pdepend", + "phpmd", + "pmd" + ], + "support": { + "irc": "irc://irc.freenode.org/phpmd", + "issues": "https://github.com/phpmd/phpmd/issues", + "source": "https://github.com/phpmd/phpmd/tree/2.15.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/phpmd/phpmd", + "type": "tidelift" + } + ], + "time": "2023-12-11T08:22:20+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.19.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/67a759e7d8746d501c41536ba40cd9c0a07d6a87", + "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2 || ^2.0", + "php": "^7.2 || 8.0.* || 8.1.* || 8.2.* || 8.3.*", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0", + "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "dev", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.19.0" + }, + "time": "2024-02-29T11:52:51+00:00" + }, + { + "name": "phpspec/prophecy-phpunit", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy-phpunit.git", + "reference": "16e1247e139434bce0bac09848bc5c8d882940fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy-phpunit/zipball/16e1247e139434bce0bac09848bc5c8d882940fc", + "reference": "16e1247e139434bce0bac09848bc5c8d882940fc", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8", + "phpspec/prophecy": "^1.18", + "phpunit/phpunit": "^9.1 || ^10.1 || ^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\PhpUnit\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christophe Coevoet", + "email": "stof@notk.org" + } + ], + "description": "Integrating the Prophecy mocking library in PHPUnit test cases", + "homepage": "http://phpspec.net", + "keywords": [ + "phpunit", + "prophecy" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy-phpunit/issues", + "source": "https://github.com/phpspec/prophecy-phpunit/tree/v2.2.0" + }, + "time": "2024-03-01T08:33:58+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.29.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/536889f2b340489d328f5ffb7b02bb6b183ddedc", + "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.0" + }, + "time": "2024-05-06T12:04:23+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.31", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "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-master": "9.2-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/9.2.31" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:37:42+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "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": "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", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "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": "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/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "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": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.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/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.6.19", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1a54a473501ef4cdeaae4e06891674114d79db8", + "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1 || ^2", + "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": ">=7.3", + "phpunit/php-code-coverage": "^9.2.28", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.5", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.2", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.6-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/9.6.19" + }, + "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": "2024-04-05T04:35:58+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.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/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:27:43+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.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/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.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/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.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", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "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 calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:19:30+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.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", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:30:58+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-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": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.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": "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", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:33:00+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "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": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:35:11+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.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", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:20:34+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.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/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.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/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.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/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.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": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-14T16:00:52+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-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/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "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": "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/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "softcreatr/jsonpath", + "version": "0.8.3", + "source": { + "type": "git", + "url": "https://github.com/SoftCreatR/JSONPath.git", + "reference": "fc12dee0b46f3fa3a175c4051dbab60984acef4b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SoftCreatR/JSONPath/zipball/fc12dee0b46f3fa3a175c4051dbab60984acef4b", + "reference": "fc12dee0b46f3fa3a175c4051dbab60984acef4b", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=8.0" + }, + "replace": { + "flow/jsonpath": "*" + }, + "require-dev": { + "phpunit/phpunit": "^9.6", + "roave/security-advisories": "dev-latest" + }, + "type": "library", + "autoload": { + "psr-4": { + "Flow\\JSONPath\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Frank", + "email": "stephen@flowsa.com", + "homepage": "https://prismaticbytes.com", + "role": "Developer" + }, + { + "name": "Sascha Greuel", + "email": "hello@1-2.dev", + "homepage": "https://1-2.dev", + "role": "Developer" + } + ], + "description": "JSONPath implementation for parsing, searching and flattening arrays", + "support": { + "email": "hello@1-2.dev", + "forum": "https://github.com/SoftCreatR/JSONPath/discussions", + "issues": "https://github.com/SoftCreatR/JSONPath/issues", + "source": "https://github.com/SoftCreatR/JSONPath" + }, + "funding": [ + { + "url": "https://ecologi.com/softcreatr?r=61212ab3fc69b8eb8a2014f4", + "type": "custom" + }, + { + "url": "https://github.com/softcreatr", + "type": "github" + } + ], + "time": "2023-08-17T20:14:00+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.9.2", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "aac1f6f347a5c5ac6bc98ad395007df00990f480" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/aac1f6f347a5c5ac6bc98ad395007df00990f480", + "reference": "aac1f6f347a5c5ac6bc98ad395007df00990f480", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2024-04-23T20:25:34+00:00" + }, + { + "name": "symfony/config", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "62cec4a067931552624a9962002c210c502d42fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/62cec4a067931552624a9962002c210c502d42fd", + "reference": "62cec4a067931552624a9962002c210c502d42fd", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/filesystem": "^4.4|^5.0|^6.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php80": "^1.16", + "symfony/polyfill-php81": "^1.22" + }, + "conflict": { + "symfony/finder": "<4.4" + }, + "require-dev": { + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/messenger": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/yaml": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "5b4505f2afbe1d11d43a3917d0c1c178a38f6f19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/5b4505f2afbe1d11d43a3917d0c1c178a38f6f19", + "reference": "5b4505f2afbe1d11d43a3917d0c1c178a38f6f19", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1.1", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16", + "symfony/polyfill-php81": "^1.22", + "symfony/service-contracts": "^1.1.6|^2" + }, + "conflict": { + "ext-psr": "<1.1|>=2", + "symfony/config": "<5.3", + "symfony/finder": "<4.4", + "symfony/proxy-manager-bridge": "<4.4", + "symfony/yaml": "<4.4.26" + }, + "provide": { + "psr/container-implementation": "1.0", + "symfony/service-implementation": "1.0|2.0" + }, + "require-dev": { + "symfony/config": "^5.3|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/yaml": "^4.4.26|^5.0|^6.0" + }, + "suggest": { + "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows you to standardize and centralize the way objects are constructed in your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "e6edd875d5d39b03de51f3c3951148cfa79a4d12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/e6edd875d5d39b03de51f3c3951148cfa79a4d12", + "reference": "e6edd875d5d39b03de51f3c3951148cfa79a4d12", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16", + "symfony/process": "^5.4|^6.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "symfony/process", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "85a554acd7c28522241faf2e97b9541247a0d3d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/85a554acd7c28522241faf2e97b9541247a0d3d5", + "reference": "85a554acd7c28522241faf2e97b9541247a0d3d5", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.5.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a2329596ddc8fd568900e3fc76cba42489ecc7f3", + "reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.5.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-04-21T15:04:16+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "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.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "eappointment/mellon": 20, + "eappointment/zmsslim": 20, + "eappointment/zmsclient": 20, + "eappointment/zmsentities": 20, + "phpmd/phpmd": 0 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "platform-overrides": { + "php": "8.0" + }, + "plugin-api-version": "2.6.0" +} diff --git a/zmscitizenapi/config.example.php b/zmscitizenapi/config.example.php new file mode 100644 index 000000000..b20799d9b --- /dev/null +++ b/zmscitizenapi/config.example.php @@ -0,0 +1,16 @@ +=10" + } + }, + "node_modules/@apidevtools/swagger-cli": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-cli/-/swagger-cli-4.0.4.tgz", + "integrity": "sha512-hdDT3B6GLVovCsRZYDi3+wMcB1HfetTU20l2DC8zD3iFRNMC6QNAZG5fo/6PYeHWBEv7ri4MvnlKodhNB0nt7g==", + "deprecated": "This package has been abandoned. Please switch to using the actively maintained @redocly/cli", + "license": "MIT", + "dependencies": { + "@apidevtools/swagger-parser": "^10.0.1", + "chalk": "^4.1.0", + "js-yaml": "^3.14.0", + "yargs": "^15.4.1" + }, + "bin": { + "swagger-cli": "bin/swagger-cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-cli/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@apidevtools/swagger-cli/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", + "license": "MIT" + }, + "node_modules/@apidevtools/swagger-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.1.0.tgz", + "integrity": "sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==", + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "9.0.6", + "@apidevtools/openapi-schemas": "^2.1.0", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "ajv": "^8.6.3", + "ajv-draft-04": "^1.0.0", + "call-me-maybe": "^1.0.1" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "license": "MIT" + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "license": "MIT" + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "license": "BSD-3-Clause" + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/openapi-types": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-10.0.0.tgz", + "integrity": "sha512-Y8xOCT2eiKGYDzMW9R4x5cmfc3vGaaI4EL2pwhDmodWw1HlK18YcZ4uJxc7Rdp7/gGzAygzH9SXr6GKYIXbRcQ==", + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/swagger-jsdoc": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", + "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "6.2.0", + "doctrine": "3.0.0", + "glob": "7.1.6", + "lodash.mergewith": "^4.6.2", + "swagger-parser": "^10.0.3", + "yaml": "2.0.0-1" + }, + "bin": { + "swagger-jsdoc": "bin/swagger-jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apidevtools/swagger-parser": "10.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/swagger-parser/node_modules/@apidevtools/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^9.0.6", + "@apidevtools/openapi-schemas": "^2.0.4", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "z-schema": "^5.0.1" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.0.0-1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", + "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, + "node_modules/z-schema/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } + } + } +} diff --git a/zmscitizenapi/package.json b/zmscitizenapi/package.json new file mode 100644 index 000000000..94d497b07 --- /dev/null +++ b/zmscitizenapi/package.json @@ -0,0 +1,26 @@ +{ + "name": "zmscitizenapi", + "version": "1.0.0", + "description": "This application offers a REST-like interface for ZMS functions and database access.", + "main": "index.js", + "private": true, + "scripts": { + "preinstall": "", + "build": "node ./bin/build_swagger.js", + "doc": "npm run build && npx swagger-cli bundle -o public/doc/swagger.json public/doc/swagger.yaml" + }, + "keywords": [ + "eappointment" + ], + "author": "BerlinOnline GmbH & Co. KG", + "license": "SEE LICENSE IN LICENSE FILE", + "dependencies": { + "@apidevtools/swagger-cli": "^4.0.4" + }, + "devDependencies": { + "swagger-jsdoc": "^6.1.0", + "swagger-parser": "^10.0.0", + "openapi-types": "^10.0.0", + "js-yaml": "^4.1.0" + } +} diff --git a/zmscitizenapi/phpunit.xml b/zmscitizenapi/phpunit.xml new file mode 100644 index 000000000..00617d8f7 --- /dev/null +++ b/zmscitizenapi/phpunit.xml @@ -0,0 +1,27 @@ + + + + + + ./tests/Zmscitizenapi/ + + + + + ./src + + + diff --git a/zmscitizenapi/public/doc/README.md b/zmscitizenapi/public/doc/README.md new file mode 100644 index 000000000..b968509cb --- /dev/null +++ b/zmscitizenapi/public/doc/README.md @@ -0,0 +1,29 @@ +## How does the Open Api definition work +## Version 2.0 + +``` +bin/configure +npm i +npm run build +npm run doc +swagger-cli bundle -o public/doc/swagger.json public/doc/swagger.yaml +python3 -m http.server 8000 +``` + +Reachable at: +``` +http://[::]:8000/public/doc/ +https://zms.ddev.site/terminvereinbarung/api/citizen/doc/index.html +https://it-at-m.github.io/eappointment/zmscitizenapi/public/doc/index.html +``` + + +* Under /public/doc are the schema from zmsentities. A symbolic link points to the corresponding folder under vendor/eappointment/zmsentities/schema. + +* Under /bin there is a build_swagger.js file. This is executed via ``npm run doc`` and validates the existing swagger.yaml file. If valid, the open api annotations are read from routing.php and the remaining information such as info, definitions, version and tags are compiled from the yaml files under ./partials into a complete swagger.yaml. + +* a bin/configure must be executed before a bin/doc so that the latest API version is in the ./VERSION file. + +* To access all paths resolved via redoc or the open api documentation, a resolved swagger.json must be created from the swagger.yaml. This is done via the swagger cli with a call to ``bin/doc``. This call executes the above npm command ``npm run doc`` and subsequently creates a full swagger.json. + +To render the open-api doc by redoc and swagger, appropriate files such as swagger-ui files are fetched in the CI process and stored at https://eappointment.gitlab.io/zmsapi/. \ No newline at end of file diff --git a/zmscitizenapi/public/doc/assets/.gitkeep b/zmscitizenapi/public/doc/assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/zmscitizenapi/public/doc/index.html b/zmscitizenapi/public/doc/index.html new file mode 100644 index 000000000..5537aa266 --- /dev/null +++ b/zmscitizenapi/public/doc/index.html @@ -0,0 +1,23 @@ + + + + ReDoc + + + + + + + + + + + + diff --git a/zmscitizenapi/public/doc/logo.png b/zmscitizenapi/public/doc/logo.png new file mode 100644 index 000000000..1a1014b9e Binary files /dev/null and b/zmscitizenapi/public/doc/logo.png differ diff --git a/zmscitizenapi/public/doc/partials/basic.yaml b/zmscitizenapi/public/doc/partials/basic.yaml new file mode 100644 index 000000000..b92be7750 --- /dev/null +++ b/zmscitizenapi/public/doc/partials/basic.yaml @@ -0,0 +1,7 @@ +basePath: /terminvereinbarung/api/citizen +schemes: + - https +consumes: + - application/json +produces: + - application/json \ No newline at end of file diff --git a/zmscitizenapi/public/doc/partials/definitions.yaml b/zmscitizenapi/public/doc/partials/definitions.yaml new file mode 100644 index 000000000..170621445 --- /dev/null +++ b/zmscitizenapi/public/doc/partials/definitions.yaml @@ -0,0 +1,33 @@ +definitions: + appointment: + $ref: "schema/citizenapi/appointment.json" + appointmentCancel: + $ref: "schema/citizenapi/appointmentCancel.json" + appointmentConfirm: + $ref: "schema/citizenapi/appointmentConfirm.json" + appointmentPreconfirm: + $ref: "schema/citizenapi/appointmentPreconfirm.json" + appointmentReserve: + $ref: "schema/citizenapi/appointmentReserve.json" + appointmentUpdate: + $ref: "schema/citizenapi/appointmentUpdate.json" + availableAppointments: + $ref: "schema/citizenapi/availableAppointments.json" + availableDays: + $ref: "schema/citizenapi/availableDays.json" + captchaDetails: + $ref: "schema/citizenapi/captchaDetails.json" + offices: + $ref: "schema/citizenapi/offices.json" + officesAndServices: + $ref: "schema/citizenapi/officesAndServices.json" + officesByService: + $ref: "schema/citizenapi/officesByService.json" + scope: + $ref: "schema/citizenapi/scope.json" + scopes: + $ref: "schema/citizenapi/scopes.json" + services: + $ref: "schema/citizenapi/services.json" + servicesByOffice: + $ref: "schema/citizenapi/servicesByOffice.json" \ No newline at end of file diff --git a/zmscitizenapi/public/doc/partials/info.yaml b/zmscitizenapi/public/doc/partials/info.yaml new file mode 100644 index 000000000..1f96e255b --- /dev/null +++ b/zmscitizenapi/public/doc/partials/info.yaml @@ -0,0 +1,20 @@ +info: + title: ZMSCitizenAPI + x-logo: + url: "./logo.png" + description: | + The ZMSCitizenAPI builds upon the ZMSApi to provide an interface specifically designed for external citizens. Its key features include: + + * Citizens can select services and providers (locations) from ZMS sources. + * Citizens can book ZMS appointments via an integrated calendar and email system, enabling a seamless process for managing appointments. + * Citizens can modify or reschedule ZMS appointments through the same calendar and email functionality. + + This documentation provides detailed API-level guidance for accessing the Citizen Frontend (Bürgeransicht) features. + termsOfService: 'http://service.berlin.de/terminvereinbarung/' + contact: + name: '' + email: 'example@example.com' + url: 'https://opensource.muenchen.de/software/zeitmanagementsystem.html' + license: + name: 'MIT' + url: '' diff --git a/zmscitizenapi/public/doc/partials/tags.yaml b/zmscitizenapi/public/doc/partials/tags.yaml new file mode 100644 index 000000000..e2944a00f --- /dev/null +++ b/zmscitizenapi/public/doc/partials/tags.yaml @@ -0,0 +1,6 @@ +tags: + - name: zmscitizenapi + description: | + The zmscitizenapi is a streamlined API designed specifically for the citizen-facing frontend application "Bürgeransicht." It acts as an intermediary between the Bürgeransicht frontend and various backend services, including the broader zmsapi system. The zmscitizenapi consolidates and simplifies endpoints from zmsapi and other integrated services to provide a cohesive experience for users, enabling actions such as appointment booking, service requests, and other interactions with municipal services. Its purpose is to enhance the usability of the citizen interface by providing a lightweight and efficient layer on top of the complex zmsapi structure. + + diff --git a/zmscitizenapi/public/doc/partials/version.yaml b/zmscitizenapi/public/doc/partials/version.yaml new file mode 100644 index 000000000..5b308b04f --- /dev/null +++ b/zmscitizenapi/public/doc/partials/version.yaml @@ -0,0 +1 @@ +swagger: "2.0" \ No newline at end of file diff --git a/zmscitizenapi/public/doc/schema b/zmscitizenapi/public/doc/schema new file mode 120000 index 000000000..c83e4ce91 --- /dev/null +++ b/zmscitizenapi/public/doc/schema @@ -0,0 +1 @@ +../../vendor/eappointment/zmsentities/schema \ No newline at end of file diff --git a/zmscitizenapi/public/index.php b/zmscitizenapi/public/index.php new file mode 100644 index 000000000..1c83fad51 --- /dev/null +++ b/zmscitizenapi/public/index.php @@ -0,0 +1,4 @@ +run(); diff --git a/zmscitizenapi/routing.php b/zmscitizenapi/routing.php new file mode 100644 index 000000000..0c8d5fe44 --- /dev/null +++ b/zmscitizenapi/routing.php @@ -0,0 +1,602 @@ +get( + '/services/', + '\BO\Zmscitizenapi\ServicesList' +) + ->setName("ServicesList"); + +/** + * @swagger + * /scopes/: + * get: + * summary: Get the list of scopes + * tags: + * - scopes + * responses: + * 200: + * description: List of scopes + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/scopes.json" + */ +\App::$slim->get( + '/scopes/', + '\BO\Zmscitizenapi\ScopesList' +) + ->setName("ScopesList"); + +/** + * @swagger + * /offices/: + * get: + * summary: Get the list of offices + * tags: + * - offices + * responses: + * 200: + * description: List of offices + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/offices.json" + */ +\App::$slim->get( + '/offices/', + '\BO\Zmscitizenapi\OfficesList' +) + ->setName("OfficesList"); + +/** + * @swagger + * /offices-and-services/: + * get: + * summary: Get the relations between offices and services + * tags: + * - offices-services + * responses: + * 200: + * description: List of office-service relations + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/officesAndServices.json" + */ +\App::$slim->get( + '/offices-and-services/', + '\BO\Zmscitizenapi\OfficesServicesRelations' +) + ->setName("OfficesServicesRelations"); + +/** + * @swagger + * /scope-by-id/: + * get: + * summary: Get a scope by ID + * tags: + * - scopes + * parameters: + * - name: scopeId + * description: Scope ID + * in: query + * required: true + * type: integer + * responses: + * 200: + * description: Scope details + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/scope.json" + * 404: + * description: Scope not found + */ +\App::$slim->get( + '/scope-by-id/', + '\BO\Zmscitizenapi\ScopeByIdGet' +) + ->setName("ScopeByIdGet"); + +/** + * @swagger + * /services-by-office/: + * get: + * summary: Get the services offered by a specific office + * tags: + * - services + * parameters: + * - name: officeId + * description: Office ID + * in: query + * required: true + * type: integer + * responses: + * 200: + * description: List of services for the office + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/servicesByOffice.json" + */ +\App::$slim->get( + '/services-by-office/', + '\BO\Zmscitizenapi\ServicesByOfficeList' +) + ->setName("ServicesByOfficeList"); + +/** + * @swagger + * /offices-by-service/: + * get: + * summary: Get the offices that offer a specific service + * tags: + * - offices + * parameters: + * - name: serviceId + * description: Service ID + * in: query + * required: true + * type: integer + * responses: + * 200: + * description: List of offices offering the service + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/officesByService.json" + */ +\App::$slim->get( + '/offices-by-service/', + '\BO\Zmscitizenapi\OfficesByServiceList' +) + ->setName("OfficesByServiceList"); + +/** + * @swagger + * /available-days/: + * get: + * summary: Get the list of available days for appointments + * tags: + * - appointments + * parameters: + * - name: officeId + * description: Office ID + * in: query + * required: true + * type: integer + * - name: serviceId + * description: Service ID + * in: query + * required: true + * type: integer + * responses: + * 200: + * description: List of available days + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/availableDays.json" + */ +\App::$slim->get( + '/available-days/', + '\BO\Zmscitizenapi\AvailableDaysList' +) + ->setName("AvailableDaysList"); + +/** + * @swagger + * /available-appointments/: + * get: + * summary: Get available appointments for a specific day + * tags: + * - appointments + * parameters: + * - name: date + * description: Date in format YYYY-MM-DD + * in: query + * required: true + * type: string + * - name: officeId + * description: Office ID + * in: query + * required: true + * type: integer + * - name: serviceId + * description: Service ID + * in: query + * required: true + * type: integer + * responses: + * 200: + * description: List of available appointments + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/availableAppointments.json" + */ +\App::$slim->get( + '/available-appointments/', + '\BO\Zmscitizenapi\AvailableAppointmentsList' +) + ->setName("AvailableAppointmentsList"); + +/** + * @swagger + * /appointment/: + * get: + * summary: Get an appointment by process ID + * tags: + * - appointments + * parameters: + * - name: processId + * description: The unique identifier for the process. Must be an integer starting with 10 or 11, e.g., 100348. + * in: query + * required: true + * type: integer + * - name: authKey + * description: The authentication key consisting of 4 to 5 alphanumeric characters, e.g., 42a3. + * in: query + * required: true + * type: string + * responses: + * 200: + * description: Appointment details + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/appointment.json" + * 400: + * description: Invalid input + * schema: + * type: object + * properties: + * errors: + * type: array + * items: + * type: object + * properties: + * type: + * type: string + * msg: + * type: string + * path: + * type: string + * location: + * type: string + * 404: + * description: Appointment not found + */ +\App::$slim->get( + '/appointment/', + '\BO\Zmscitizenapi\AppointmentGet' +) + ->setName("AppointmentGet"); + +/** + * @swagger + * /captcha-details/: + * get: + * summary: Get CAPTCHA details + * tags: + * - captcha + * responses: + * 200: + * description: CAPTCHA details + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/captchaDetails.json" + */ +\App::$slim->get( + '/captcha-details/', + '\BO\Zmscitizenapi\CaptchaGet' +) + ->setName("CaptchaGet"); + +/** + * @swagger + * /reserve-appointment/: + * post: + * summary: Reserve an appointment + * tags: + * - appointments + * parameters: + * - name: appointment + * description: Appointment reservation data + * in: body + * required: true + * schema: + * $ref: "schema/citizenapi/appointmentReserve.json" + * responses: + * 200: + * description: Reservation successful + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/appointment.json" + * 400: + * description: Invalid input + * schema: + * type: object + * properties: + * errors: + * type: array + * items: + * type: object + * properties: + * type: + * type: string + * msg: + * type: string + * path: + * type: string + * location: + * type: string + * 404: + * description: Appointment not found + */ +\App::$slim->post( + '/reserve-appointment/', + '\BO\Zmscitizenapi\AppointmentReserve' +) + ->setName("AppointmentReserve"); + +/** + * @swagger + * /update-appointment/: + * post: + * summary: Update an appointment + * tags: + * - appointments + * parameters: + * - name: appointment + * description: Appointment update data + * in: body + * required: true + * schema: + * $ref: "schema/citizenapi/appointmentUpdate.json" + * responses: + * 200: + * description: Update successful + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/appointment.json" + * 400: + * description: Invalid input + * schema: + * type: object + * properties: + * errors: + * type: array + * items: + * type: object + * properties: + * type: + * type: string + * msg: + * type: string + * path: + * type: string + * location: + * type: string + * 404: + * description: Appointment not found + */ +\App::$slim->post( + '/update-appointment/', + '\BO\Zmscitizenapi\AppointmentUpdate' +) + ->setName("AppointmentUpdate"); + +/** + * @swagger + * /confirm-appointment/: + * post: + * summary: Confirm an appointment + * tags: + * - appointments + * parameters: + * - name: appointment + * description: Appointment confirmation data + * in: body + * required: true + * schema: + * $ref: "schema/citizenapi/appointmentConfirm.json" + * responses: + * 200: + * description: Confirmation successful + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/appointment.json" + * 400: + * description: Invalid input + * schema: + * type: object + * properties: + * errors: + * type: array + * items: + * type: object + * properties: + * type: + * type: string + * msg: + * type: string + * path: + * type: string + * location: + * type: string + * 404: + * description: Appointment not found + */ +\App::$slim->post( + '/confirm-appointment/', + '\BO\Zmscitizenapi\AppointmentConfirm' +) + ->setName("AppointmentConfirm"); + +/** + * @swagger + * /preconfirm-appointment/: + * post: + * summary: Preconfirm an appointment + * tags: + * - appointments + * parameters: + * - name: appointment + * description: Appointment preconfirmation data + * in: body + * required: true + * schema: + * $ref: "schema/citizenapi/appointmentPreconfirm.json" + * responses: + * 200: + * description: Preconfirmation successful + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/appointment.json" + * 400: + * description: Invalid input + * schema: + * type: object + * properties: + * errors: + * type: array + * items: + * type: object + * properties: + * type: + * type: string + * msg: + * type: string + * path: + * type: string + * location: + * type: string + * 404: + * description: Appointment not found + */ +\App::$slim->post( + '/preconfirm-appointment/', + '\BO\Zmscitizenapi\AppointmentPreconfirm' +) + ->setName("AppointmentPreconfirm"); + +/** + * @swagger + * /cancel-appointment/: + * post: + * summary: Cancel an appointment + * tags: + * - appointments + * parameters: + * - name: appointment + * description: Appointment cancellation data + * in: body + * required: true + * schema: + * $ref: "schema/citizenapi/appointmentCancel.json" + * responses: + * 200: + * description: Cancellation successful + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/appointment.json" + * 400: + * description: Invalid input + * schema: + * type: object + * properties: + * errors: + * type: array + * items: + * type: object + * properties: + * type: + * type: string + * msg: + * type: string + * path: + * type: string + * location: + * type: string + * 404: + * description: Appointment not found + */ +\App::$slim->post( + '/cancel-appointment/', + '\BO\Zmscitizenapi\AppointmentCancel' +) + ->setName("AppointmentCancel"); diff --git a/zmscitizenapi/src/Zmscitizenapi/Application.php b/zmscitizenapi/src/Zmscitizenapi/Application.php new file mode 100644 index 000000000..3b0396397 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Application.php @@ -0,0 +1,56 @@ +getQueryParams(); + $processId = isset($queryParams['processId']) && is_numeric($queryParams['processId']) ? (int)$queryParams['processId'] : null; + $authKey = isset($queryParams['authKey']) && is_string($queryParams['authKey']) && trim($queryParams['authKey']) !== '' ? $queryParams['authKey'] : null; + + $result = ZmsApiFacadeService::getProcessById($processId, $authKey); + + return $this->createJsonResponse($response, $result['data'] ?? $result, $result['status']); + } + +} diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php new file mode 100644 index 000000000..5477a4422 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php @@ -0,0 +1,16 @@ +getParsedBody(); + + $officeId = isset($body['officeId']) && is_numeric($body['officeId']) ? (int) $body['officeId'] : null; + $serviceIds = $body['serviceId'] ?? null; + $serviceCounts = $body['serviceCount'] ?? [1]; + $captchaSolution = $body['captchaSolution'] ?? null; + $timestamp = isset($body['timestamp']) && is_numeric($body['timestamp']) ? (int) $body['timestamp'] : null; + + $errors = ValidationService::validatePostAppointmentReserve($officeId, $serviceIds, $serviceCounts, $timestamp); + if (!empty($errors['errors'])) { + return $this->createJsonResponse($response, $errors, 400); + } + + try { + $providerScope = ZmsApiFacadeService::getScopeByOfficeId($officeId); + $captchaRequired = Application::$CAPTCHA_ENABLED === true && isset($providerScope['captchaActivatedRequired']) && $providerScope['captchaActivatedRequired'] === "1"; + + if ($captchaRequired) { + $captchaVerificationResult = FriendlyCaptchaService::verifyCaptcha($captchaSolution); + if (!$captchaVerificationResult['success']) { + return $this->createJsonResponse($response, [ + 'errorCode' => 'captchaVerificationFailed', + 'errorMessage' => 'Captcha verification failed' + ], 400); + } + } + + $serviceValidationResult = ValidationService::validateServiceLocationCombination($officeId, $serviceIds); + if ($serviceValidationResult['status'] !== 200) { + return $this->createJsonResponse($response, $serviceValidationResult, 400); + } + + $freeAppointments = new ProcessList(); + $freeAppointments = ZmsApiFacadeService::getFreeAppointments([ + 'officeId' => $officeId, + 'serviceIds' => $serviceIds, + 'serviceCounts' => $serviceCounts, + 'date' => UtilityHelper::getInternalDateFromTimestamp($timestamp) + ]); + + $processArray = json_decode(json_encode($freeAppointments), true); + + $filteredProcesses = array_filter($processArray, function ($process) use ($timestamp) { + if (!isset($process['appointments']) || !is_array($process['appointments'])) { + return false; + } + return in_array($timestamp, array_column($process['appointments'], 'date')); + }); + + $selectedProcess = $filteredProcesses ? new Process() : null; + + if (!empty($filteredProcesses)) { + $selectedProcessData = array_values($filteredProcesses)[0]; + + $scopeData = $selectedProcessData['scope'] ?? null; + $scope = $scopeData ? new Scope($scopeData) : null; + + $selectedProcess->withUpdatedData($selectedProcessData, new \DateTime("@$timestamp"), $scope); + } + + $errors = ValidationService::validateGetProcessNotFound($selectedProcess); + if (!empty($errors['errors'])) { + return $this->createJsonResponse($response, $errors, 404); + } + + $selectedProcess->clients = [ + [ + 'email' => 'default@example.com' + ] + ]; + + $reservedProcess = new Process(); + $reservedProcess = ZmsApiFacadeService::reserveTimeslot($selectedProcess, $serviceIds, $serviceCounts); + + if ($reservedProcess && $reservedProcess->scope && $reservedProcess->scope->id) { + $scopeIds = [$reservedProcess->scope->id]; + $scopesData = ZmsApiFacadeService::getScopeByIds($scopeIds); + + if ($scopesData['status'] === 200 && isset($scopesData['scopes']['scopes']) && !empty($scopesData['scopes']['scopes'])) { + $reservedProcess->scope = MapperService::mapScope($scopesData['scopes']['scopes'][0]); + } + } + + $thinnedProcessData = UtilityHelper::getThinnedProcessData($reservedProcess); + $thinnedProcessData = array_merge($thinnedProcessData, ['officeId' => $officeId]); + + return $this->createJsonResponse($response, $thinnedProcessData, 200); + + } catch (\Exception $e) { + return $this->createJsonResponse($response, [ + 'errorCode' => 'internalServerError', + 'errorMessage' => $e->getMessage() + ], 500); + } + } +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php new file mode 100644 index 000000000..c779eab45 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php @@ -0,0 +1,59 @@ +getParsedBody(); + $processId = $body['processId'] ?? null; + $authKey = $body['authKey'] ?? null; + $familyName = $body['familyName'] ?? null; + $email = $body['email'] ?? null; + $telephone = $body['telephone'] ?? null; + $customTextfield = $body['customTextfield'] ?? null; + + $errors = ValidationService::validateUpdateAppointmentInputs($processId, $authKey, $familyName, $email, $telephone, $customTextfield); + if (!empty($errors['errors'])) { + return $this->createJsonResponse($response, $errors, 400); + } + + try { + + $reservedProcess = ZmsApiFacadeService::getProcessById($processId, $authKey); + if (!empty($reservedProcess['errors'])) { + return $this->createJsonResponse($response, $reservedProcess, 404); + } + + $reservedProcess['clients'][0]['familyName'] = $familyName; + $reservedProcess['clients'][0]['email'] = $email; + $reservedProcess['clients'][0]['telephone'] = $telephone; + $reservedProcess['customTextfield'] = $customTextfield; + + $updatedProcess = ZmsApiFacadeService::updateClientData(new Process($reservedProcess)); + + if (isset($updatedProcess['error']) && $updatedProcess['error'] === 'tooManyAppointmentsWithSameMail') { + return $this->createJsonResponse($response, ExceptionService::tooManyAppointmentsWithSameMail(), 406); + } + + $thinnedProcessData = UtilityHelper::getThinnedProcessData($updatedProcess); + return $this->createJsonResponse($response, $thinnedProcessData, 200); + + } catch (\Exception $e) { + throw $e; + } + } +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php b/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php new file mode 100644 index 000000000..544c45b8a --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php @@ -0,0 +1,21 @@ +getQueryParams(); + + $result = ZmsApiFacadeService::getAvailableAppointments($queryParams); + + return $this->createJsonResponse($response, $result, statusCode: $result['status']); + } + +} diff --git a/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php b/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php new file mode 100644 index 000000000..ef4a9014c --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php @@ -0,0 +1,22 @@ +getQueryParams(); + + $result = ZmsApiFacadeService::getBookableFreeDays($queryParams); + + return $this->createJsonResponse($response, $result, $result['status']); + } + +} diff --git a/zmscitizenapi/src/Zmscitizenapi/BaseController.php b/zmscitizenapi/src/Zmscitizenapi/BaseController.php new file mode 100644 index 000000000..e9c4c07ae --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/BaseController.php @@ -0,0 +1,52 @@ +initRequest($request); + $noCacheResponse = \BO\Slim\Render::withLastModified($response, time(), '0'); + return $this->readResponse($request, $noCacheResponse, $args); + } + + /** + * Hook method for handling responses in child controllers. + * Child classes should override this method to implement their specific response logic. + * + * @param RequestInterface $request The HTTP request + * @param ResponseInterface $response The HTTP response + * @param array $args Route parameters + * @return ResponseInterface The modified response + */ + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface + { + return parent::__invoke($request, $response, $args); + } + + protected function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface + { + if ($statusCode < 100 || $statusCode > 599) { + throw new \InvalidArgumentException('Invalid HTTP status code'); + } + + $response = $response->withStatus($statusCode) + ->withHeader('Content-Type', 'application/json; charset=utf-8'); + + try { + // Add JSON_UNESCAPED_SLASHES to ensure slashes in HTML are not escaped + $json = json_encode($content, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } catch (\JsonException $e) { + throw new \RuntimeException('Failed to encode JSON response: ' . $e->getMessage(), 0, $e); + } + + $response->getBody()->write($json); + + return $response; + } + +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php new file mode 100644 index 000000000..9d5818ecb --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php @@ -0,0 +1,18 @@ +createJsonResponse($response, $captchaDetails, statusCode: $captchaDetails['status']); + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php b/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php new file mode 100644 index 000000000..7f60aa440 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php @@ -0,0 +1,82 @@ +getCode(); + } + + $error = [ + 'message' => $this->getErrorMessage($exception, $displayErrorDetails), + 'code' => $statusCode, + ]; + + if ($displayErrorDetails) { + $error['trace'] = $exception->getTraceAsString(); + } + + if ($logErrors) { + $this->logError($exception, $logErrorDetails); + } + + $payload = json_encode($error, JSON_PRETTY_PRINT); + + $response = new Response(); + $response->getBody()->write($payload); + + return $response + ->withStatus($statusCode) + ->withHeader('Content-Type', 'application/json'); + } + + /** + * Get appropriate error message based on environment. + */ + private function getErrorMessage(Throwable $exception, bool $displayErrorDetails): string + { + if ($displayErrorDetails) { + return $exception->getMessage(); + } + + // Generic message in production + return 'An error has occurred. Please try again later.'; + } + + /** + * Log the error with appropriate detail level. + */ + private function logError(Throwable $exception, bool $logErrorDetails): void + { + $message = $exception->getMessage(); + if ($logErrorDetails) { + $message .= "\n" . $exception->getTraceAsString(); + } + error_log($message); + } +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php b/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php new file mode 100644 index 000000000..9a7a552c4 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php @@ -0,0 +1,94 @@ + (int) $date->format('d'), + 'month' => (int) $date->format('m'), + 'year' => (int) $date->format('Y'), + ]; + } + + public static function getInternalDateFromISO($dateString): array + { + try { + if (!is_string($dateString)) { + throw new \InvalidArgumentException('Date string must be a string'); + } + $date = new \DateTime($dateString); + return self::formatDateArray($date); + } catch (\Exception $e) { + throw new \InvalidArgumentException('Invalid ISO date format: ' . $e->getMessage()); + } + } + + public static function getInternalDateFromTimestamp(int $timestamp): array + { + try { + $date = (new \DateTime())->setTimestamp($timestamp); + return self::formatDateArray($date); + } catch (\Exception $e) { + throw new \InvalidArgumentException('Invalid timestamp: ' . $e->getMessage()); + } + } + + public static function uniqueElementsFilter($value, $index, $self): bool + { + return array_search($value, $self) === $index; + } + + public static function getThinnedProcessData(?Process $myProcess): array + { + if (!$myProcess || !isset($myProcess->id)) { + return []; + } + + $subRequestCounts = []; + $mainServiceId = null; + $mainServiceCount = 0; + + $requests = $myProcess->requests ?? []; + if ($requests) { + $requests = is_array($requests) ? $requests : iterator_to_array($requests); + if (count($requests) > 0) { + $mainServiceId = $requests[0]->id; + foreach ($requests as $request) { + if ($request->id === $mainServiceId) { + $mainServiceCount++; + } else { + if (!isset($subRequestCounts[$request->id])) { + $subRequestCounts[$request->id] = [ + 'id' => $request->id, + 'count' => 0, + ]; + } + $subRequestCounts[$request->id]['count']++; + } + } + } + } + + return [ + 'processId' => $myProcess->id, + 'timestamp' => isset($myProcess->appointments[0]) ? $myProcess->appointments[0]->date : null, + 'authKey' => $myProcess->authKey ?? null, + 'familyName' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->familyName : null, + 'customTextfield' => $myProcess->customTextfield ?? null, + 'email' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->email : null, + 'telephone' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->telephone : null, + 'officeName' => $myProcess->scope->contact->name ?? null, + 'officeId' => $myProcess->scope->provider->id ?? null, + 'scope' => $myProcess->scope ?? null, + 'subRequestCounts' => array_values($subRequestCounts), + 'serviceId' => $mainServiceId, + 'serviceCount' => $mainServiceCount, + ]; + } + +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Middleware/MaintenanceMiddleware.php b/zmscitizenapi/src/Zmscitizenapi/Middleware/MaintenanceMiddleware.php new file mode 100644 index 000000000..cad14beba --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Middleware/MaintenanceMiddleware.php @@ -0,0 +1,28 @@ + self::ERROR_UNAVAILABLE, + 'errorMessage' => 'Service Unavailable: The application is under maintenance.', + 'status' => self::HTTP_UNAVAILABLE, + ]; + + return ['errors' => $errors, 'status' => self::HTTP_UNAVAILABLE]; + } + return $next->handle($request); + } +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php new file mode 100644 index 000000000..651632432 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php @@ -0,0 +1,39 @@ +getQueryParams()['serviceId'] ?? []; + + if (is_string($serviceIdParam)) { + $serviceIdParam = explode(',', $serviceIdParam); + } + + $errors = ValidationService::validateServiceIdParam($serviceIdParam); + if (!empty($errors)) { + return $this->createJsonResponse($response, $errors, 400); + } + + $result = ZmsApiFacadeService::getOfficesByServiceIds($serviceIdParam); + + if (isset($result['errors'])) { + return $this->createJsonResponse($response, $result, $result['status']); + } + + return $this->createJsonResponse($response, $result['offices'], $result['status']); + } + + + +} diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesList.php b/zmscitizenapi/src/Zmscitizenapi/OfficesList.php new file mode 100644 index 000000000..cf9042077 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesList.php @@ -0,0 +1,18 @@ +createJsonResponse($response, $offices, statusCode: $offices['status']); + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php b/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php new file mode 100644 index 000000000..71b13cf63 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php @@ -0,0 +1,18 @@ +createJsonResponse($response, $officesAndServices, statusCode: $officesAndServices['status']); + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php b/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php new file mode 100644 index 000000000..415be20a6 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php @@ -0,0 +1,34 @@ +getQueryParams()['scopeId'] ?? []; + + if (is_string($scopeIdParam)) { + $scopeIdParam = explode(',', $scopeIdParam); + } + + $errors = ValidationService::validateScopeIdParam($scopeIdParam); + if (!empty($errors)) { + return $this->createJsonResponse($response, $errors, 400); + } + + $result = ZmsApiFacadeService::getScopeByIds($scopeIdParam); + + if (isset($result['errors'])) { + return $this->createJsonResponse($response, $result, $result['status']); + } + + return $this->createJsonResponse($response, $result['scopes'], $result['status']); + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/ScopesList.php b/zmscitizenapi/src/Zmscitizenapi/ScopesList.php new file mode 100644 index 000000000..52c13cfdd --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/ScopesList.php @@ -0,0 +1,18 @@ +createJsonResponse($response, $scopes, statusCode: $scopes['status']); + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaServiceInterface.php b/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaServiceInterface.php new file mode 100644 index 000000000..ba2e0a13b --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaServiceInterface.php @@ -0,0 +1,22 @@ + self::ERROR_NO_APPOINTMENTS, + 'errorMessage' => 'An diesem Standort gibt es aktuell leider keine freien Termine.', + 'status' => self::HTTP_NOT_FOUND, + ]; + + return ['errors' => $errors, 'status' => self::HTTP_NOT_FOUND]; + + } + + public static function appointmentNotFound(): array + { + + $errors[] = [ + 'errorCode' => self::ERROR_APPOINTMENT_NOT_FOUND, + 'errorMessage' => 'Termin wurde nicht gefunden.', + 'status' => self::HTTP_NOT_FOUND, + ]; + + return ['errors' => $errors, 'status' => self::HTTP_NOT_FOUND]; + + } + + public static function tooManyAppointmentsWithSameMail(): array + { + $errors[] = [ + 'errorCode' => self::ERROR_TOO_MANY_APPOINTMENTS, + 'errorMessage' => 'Zu viele Termine mit gleicher E-Mail- Adresse.', + 'status' => self::HTTP_NOT_ACCEPTABLE, + ]; + + return ['errors' => $errors, 'status' => self::HTTP_NOT_ACCEPTABLE]; + + } + +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/FriendlyCaptchaService.php b/zmscitizenapi/src/Zmscitizenapi/Services/FriendlyCaptchaService.php new file mode 100644 index 000000000..65435ed9b --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/FriendlyCaptchaService.php @@ -0,0 +1,40 @@ + Application::$CAPTCHA_SITEKEY, + 'captchaEndpoint' => Application::$CAPTCHA_ENDPOINT, + 'puzzle' => Application::$CAPTCHA_ENDPOINT_PUZZLE, + 'captchaEnabled' => Application::$CAPTCHA_ENABLED, + 'status' => 200 + ]; + } + + public static function verifyCaptcha(string $solution): bool + { + try { + $response = \App::$http->post(Application::$CAPTCHA_ENDPOINT, [ + 'form_params' => [ + 'secret' => Application::$CAPTCHA_SECRET, + 'solution' => $solution + ] + ]); + + $responseBody = json_decode($response->getBody(), true); + + return $responseBody; + } catch (RequestException $e) { + $errorMessage = $e->hasResponse() ? $e->getResponse()->getBody()->getContents() : $e->getMessage(); + throw new Exception('Captcha verification failed: ' . $errorMessage); + } + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php new file mode 100644 index 000000000..2ee751749 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php @@ -0,0 +1,105 @@ + $scope->id ?? null, + "provider" => $scope->getProvider() ?? null, + "shortName" => $scope->getShortName() ?? null, + 'telephoneActivated' => $scope->getTelephoneActivated() ?? null, + 'telephoneRequired' => $scope->getTelephoneRequired() ?? null, + 'customTextfieldActivated' => $scope->getCustomTextfieldActivated() ?? null, + 'customTextfieldRequired' => $scope->getCustomTextfieldRequired() ?? null, + 'customTextfieldLabel' => $scope->getCustomTextfieldLabel() ?? null, + 'captchaActivatedRequired' => $scope->getCaptchaActivatedRequired() ?? null, + 'displayInfo' => $scope->getDisplayInfo() ?? null, + ]; + } + + public static function mapOfficesWithScope(ProviderList $providerList): array + { + $offices = []; + + $scopes = new ScopeList(ZmsApiClientService::getScopes() ?? []); + + foreach ($providerList as $provider) { + $officeData = array_merge( + [ + "id" => $provider->id, + "name" => $provider->displayName ?? $provider->name, + ], + !empty($provider->data['address']) ? ["address" => $provider->data['address']] : [], + !empty($provider->data['geo']) ? ["geo" => $provider->data['geo']] : [] + ); + + $providerScope = ZmsApiFacadeService::getScopeForProvider($provider->id, $scopes); + if (isset($providerScope['scope']) && !isset($providerScope['errors'])) { + $officeData['scope'] = $providerScope['scope']; + } + + $offices[] = $officeData; + } + return $offices; + } + + public static function mapServicesWithCombinations(RequestList $requestList, RequestRelationList $relationList): array + { + $servicesProviderIds = []; + foreach ($relationList as $relation) { + if (!isset($servicesProviderIds[$relation->request->id])) { + $servicesProviderIds[$relation->request->id] = []; + } + $servicesProviderIds[$relation->request->id][] = $relation->provider->id; + } + + $services = []; + foreach ($requestList as $service) { + $serviceCombinations = []; + $mappedService = [ + "id" => $service->getId(), + "name" => $service->getName(), + "maxQuantity" => $service->getAdditionalData()['maxQuantity'] ?? 1, + ]; + + if (isset($service->getAdditionalData()['combinable'])) { + foreach ($service->getAdditionalData()['combinable'] as $combinationServiceId) { + $commonProviders = array_intersect( + $servicesProviderIds[$service->getId()] ?? [], + $servicesProviderIds[$combinationServiceId] ?? [] + ); + $serviceCombinations[$combinationServiceId] = !empty($commonProviders) ? array_values($commonProviders) : []; + } + $mappedService['combinable'] = $serviceCombinations; + } + + $services[] = $mappedService; + } + + return $services; + } + + public static function mapRelations(RequestRelationList $relationList): array + { + $relations = []; + foreach ($relationList as $relation) { + $relations[] = [ + "officeId" => $relation->provider->id, + "serviceId" => $relation->request->id, + "slots" => intval($relation->slots) + ]; + } + return $relations; + } + +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php new file mode 100644 index 000000000..53e467262 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php @@ -0,0 +1,453 @@ + 400, + 'errorCode' => 'invalidLocationAndServiceCombination', + 'errorMessage' => 'The provided service(s) do not exist at the given location.', + 'invalidServiceIds' => $invalidServiceIds, + 'locationId' => $officeId + ]; + } + + return [ + 'status' => 200, + 'message' => 'Valid service-location combination.', + ]; + } + + public static function validateGetBookableFreeDays(?int $officeId, ?int $serviceId, ?string $startDate, ?string $endDate, ?array $serviceCounts): array + { + $errors = []; + if (!$startDate) { + $errors[] = ['status' => 400, 'errorCode' => 'invalidStartDate', 'errorMessage' => 'startDate is required and must be a valid date.']; + } + if (!$endDate) { + $errors[] = ['status' => 400, 'errorCode' => 'invalidEndDate', 'errorMessage' => 'endDate is required and must be a valid date.']; + } + if (!$officeId || !is_numeric($officeId)) { + $errors[] = ['status' => 400, 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.']; + } + if (!$serviceId || !is_numeric($serviceId)) { + $errors[] = ['status' => 400, 'errorCode' => 'invalidServiceId', 'errorMessage' => 'serviceId should be a 32-bit integer.']; + } + if (empty($serviceCounts[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceCounts))) { + $errors[] = ['status' => 400, 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.']; + } + + return ['errors' => $errors, 'status' => 400]; + } + + public static function validateGetProcessById(?int $processId, ?string $authKey): array + { + $errors = []; + if (!$processId || !is_numeric($processId) || intval($processId) <= 0) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidProcessId', + 'errorMessage' => 'processId should be a 32-bit integer.', + ]; + } + + if (!$authKey || !is_string($authKey)) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidAuthKey', + 'errorMessage' => 'authKey should be a string.', + ]; + } + + return ['errors' => $errors, 'status' => 400]; + } + + public static function validateGetAvailableAppointments(?string $date, ?int $officeId, ?array $serviceIds, ?array $serviceCounts): array + { + $errors = []; + if (!$date) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidDate', + 'errorMessage' => 'date is required and must be a valid date.', + ]; + } + + if (!$officeId || !is_numeric($officeId)) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + ]; + } + + if (empty($serviceIds) || !is_array($serviceIds) || array_filter($serviceIds, fn($id) => !is_numeric($id))) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + ]; + } + + if (empty($serviceCounts[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceCounts))) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + ]; + } + + return ['errors' => $errors, 'status' => 400]; + } + + public static function validatePostAppointmentReserve(?int $officeId, ?array $serviceIds, ?array $serviceCounts, ?int $timestamp): array + { + $errors = []; + if (!$officeId || !is_numeric($officeId)) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + ]; + } + + if (empty($serviceIds)) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + ]; + } elseif (!is_array($serviceIds) || array_filter($serviceIds, fn($id) => !is_numeric($id))) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + ]; + } + + if (!$timestamp || !is_numeric($timestamp) || $timestamp < 0) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidTimestamp', + 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', + ]; + } + + if (!is_array($serviceCounts) || array_filter($serviceCounts, fn($count) => !is_numeric($count) || $count < 0)) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + ]; + } + + return ['errors' => $errors, 'status' => 400]; + } + + public static function validateGetOfficesByServiceIds(?array $serviceIds): array + { + $errors = []; + if (empty($serviceIds) || $serviceIds == ['']) { + $errors[] = [ + 'offices' => [], + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400 + ]; + } + + return ['errors' => $errors, 'status' => 400]; + } + + public static function validateGetScopeByIds(?array $scopeIds): array + { + $errors = []; + if (empty($scopeIds) || $scopeIds == ['']) { + $errors[] = [ + 'scopes' => [], + 'errorCode' => 'invalidScopeId', + 'errorMessage' => "scopeId should be a 32-bit integer.", + 'status' => 400 + ]; + } + + return ['errors' => $errors, 'status' => 400]; + } + + public static function validateGetServicesByOfficeIds(?array $officeIds): array + { + + $errors = []; + + if (empty($officeIds) || !is_array($officeIds)) { + $errors[] = [ + 'services' => [], + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ]; + } + + foreach ($officeIds as $id) { + if (!is_numeric($id)) { + $errors[] = [ + 'services' => [], + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => "officeId should be a 32-bit integer.", + 'status' => 400, + ]; + } + } + + return ['errors' => $errors, 'status' => 400]; + } + + public static function validateGetProcessFreeSlots(?ProcessList $freeSlots): array + { + $errors = []; + if (empty($freeSlots) || !is_iterable($freeSlots)) { + $errors[] = [ + 'appointmentTimestamps' => [], + 'errorCode' => 'appointmentNotAvailable', + 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar.', + 'status' => 404, + ]; + } + + return ['errors' => $errors, 'status' => 404]; + } + + public static function validateGetProcessByIdTimestamps(?array $appointmentTimestamps): array + { + $errors = []; + if (empty($appointmentTimestamps)) { + $errors[] = [ + 'appointmentTimestamps' => [], + 'errorCode' => 'appointmentNotAvailable', + 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar.', + 'status' => 404, + ]; + } + + return ['errors' => $errors, 'status' => 404]; + } + + public static function validateGetProcessNotFound(?Process $process): array + { + $errors = []; + if (!$process) { + $errors[] = [ + 'errorCode' => 'appointmentNotAvailable', + 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar.', + 'status' => 404, + ]; + } + + return ['errors' => $errors, 'status' => 404]; + } + + public static function validateScopesNotFound(?ScopeList $scopes): array + { + $errors = []; + if (empty($scopes) || $scopes === null || $scopes->count() === 0) { + $errors[] = [ + 'errorCode' => 'scopesNotFound', + 'errorMessage' => 'Scope(s) not found.', + 'status' => 404, + ]; + } + + return ['errors' => $errors, 'status' => 404]; + } + + public static function validateServicesNotFound(?array $services): array + { + $errors = []; + if (empty($services)) { + $errors[] = [ + 'errorCode' => 'servicesNotFound', + 'errorMessage' => 'Service(s) not found for the provided officeId(s).', + 'status' => 404, + ]; + } + + return ['errors' => $errors, 'status' => 404]; + } + + public static function validateOfficesNotFound(?array $offices): array + { + $errors = []; + if (empty($offices)) { + $errors[] = [ + 'errorCode' => 'officesNotFound', + 'errorMessage' => 'Office(s) not found for the provided serviceId(s).', + 'status' => 404, + ]; + } + + return ['errors' => $errors, 'status' => 404]; + } + + public static function validateAppointmentDaysNotFound(?array $formattedDays): array + { + $errors = []; + if (empty($formattedDays)) { + $errors[] = [ + 'errorCode' => 'noAppointmentForThisDay', + 'errorMessage' => 'No available days found for the given criteria.', + 'status' => 404, + ]; + } + + return ['errors' => $errors, 'status' => 404]; + } + + public static function validateNoAppointmentsAtLocation(): array + { + + $errors[] = [ + 'errorCode' => 'noAppointmentForThisScope', + 'errorMessage' => 'An diesem Standort gibt es aktuell leider keine freien Termine.', + 'status' => 404, + ]; + + return ['errors' => $errors, 'status' => 404]; + + } + + public static function validateUpdateAppointmentInputs(?int $processId, ?string $authKey, ?string $familyName, ?string $email, ?string $telephone, ?string $customTextfield): array + { + $errors = []; + + if (!$processId || !is_numeric($processId) || intval($processId) <= 0) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidProcessId', + 'errorMessage' => 'processId should be a positive 32-bit integer.', + ]; + } + + if (!$authKey || !is_string($authKey)) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidAuthKey', + 'errorMessage' => 'authKey should be a non-empty string.', + ]; + } + + if (!$familyName || !is_string($familyName)) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidFamilyName', + 'errorMessage' => 'familyName should be a non-empty string.', + ]; + } + + if (!$email || !filter_var($email, FILTER_VALIDATE_EMAIL)) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidEmail', + 'errorMessage' => 'email should be a valid email address.', + ]; + } + + if ($telephone !== null && (!$telephone || !preg_match('/^\d{7,15}$/', $telephone))) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidTelephone', + 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.', + ]; + } + + if ($customTextfield !== null && (!is_string($customTextfield) || is_numeric($customTextfield))) { + $errors[] = [ + 'status' => 400, + 'errorCode' => 'invalidCustomTextfield', + 'errorMessage' => 'customTextfield should be a string.', + ]; + } + + if (!empty($errors)) { + return ['errors' => $errors, 'status' => 400]; + } + + return ['status' => 200, 'message' => 'Valid input for updating appointment.']; + } + + public static function validateServiceIdParam(array $serviceIds): array + { + $errors = []; + + if (empty($serviceIds) || !is_array($serviceIds)) { + $errors[] = [ + 'offices' => [], + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ]; + return ['errors' => $errors, 'status' => 400]; + } + + foreach ($serviceIds as $id) { + if (!is_numeric($id)) { + $errors[] = [ + 'offices' => [], + 'errorCode' => 'invalidServiceId', + 'errorMessage' => "serviceId should be a 32-bit integer.", + 'status' => 400, + ]; + } + } + + return empty($errors) ? [] : ['errors' => $errors, 'status' => 400]; + } + + public static function validateScopeIdParam(array $scopeIds): array + { + $errors = []; + + if (empty($scopeIds) || !is_array($scopeIds)) { + $errors[] = [ + 'scopes' => [], + 'errorCode' => 'invalidScopeId', + 'errorMessage' => 'scopeId should be a 32-bit integer.', + 'status' => 400, + ]; + return ['errors' => $errors, 'status' => 400]; + } + + foreach ($scopeIds as $id) { + if (!is_numeric($id)) { + $errors[] = [ + 'scopes' => [], + 'errorCode' => 'invalidScopeId', + 'errorMessage' => "scopeId should be a 32-bit integer.", + 'status' => 400, + ]; + } + } + + return empty($errors) ? [] : ['errors' => $errors, 'status' => 400]; + } + +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php new file mode 100644 index 000000000..9bbfc5306 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php @@ -0,0 +1,225 @@ +readGetResult('/source/' . \App::$source_name . '/', [ + 'resolveReferences' => 2, + ])->getEntity(); + + $providerList = new ProviderList(); + $providerList = $sources->getProviderList() ?? $providerList; + + return $providerList; + + } + + public static function getScopes(): ScopeList + { + $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ + 'resolveReferences' => 2, + ])->getEntity(); + + $scopeList = new ScopeList(); + $scopeList = $sources->getScopeList() ?? $scopeList; + + return $scopeList; + + } + + public static function getServices(): RequestList + { + $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ + 'resolveReferences' => 2, + ])->getEntity(); + + $requestList = new RequestList(); + $requestList = $sources->getRequestList() ?? $requestList; + + return $requestList; + + } + + public static function getRequestRelationList(): RequestRelationList + { + + $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ + 'resolveReferences' => 2, + ])->getEntity(); + + $requestRelationList = new RequestRelationList(); + $requestRelationList = $sources->getRequestRelationList() ?? $requestRelationList; + + return $requestRelationList; + } + + public static function getFreeDays(ProviderList $providers, RequestList $requests, array $firstDay, array $lastDay): Calendar + { + $calendar = new Calendar(); + $calendar->firstDay = $firstDay; + $calendar->lastDay = $lastDay; + $calendar->providers = $providers; + $calendar->requests = $requests; + + $result = new Calendar(); + $result = \App::$http->readPostResult('/calendar/', $calendar)->getEntity() ?? $result; + return $result; + } + + public static function getFreeTimeslots(ProviderList $providers, RequestList $requests, array $firstDay, array $lastDay): ProcessList + { + + $calendar = new Calendar(); + $calendar->firstDay = $firstDay; + $calendar->lastDay = $lastDay; + $calendar->providers = $providers; + $calendar->requests = $requests; + + + $result = \App::$http->readPostResult('/process/status/free/', $calendar); + if (!$result || !method_exists($result, 'getCollection')) { + throw new \Exception('Invalid response from API'); + } + return $result->getCollection(); + } + + public static function reserveTimeslot(Process $appointmentProcess, array $serviceIds, array $serviceCounts): Process + { + $requests = []; + + foreach ($serviceIds as $index => $serviceId) { + $count = intval($serviceCounts[$index]); + for ($i = 0; $i < $count; $i++) { + $requests[] = [ + 'id' => $serviceId, + 'source' => \App::$source_name + ]; + } + } + + $processEntity = new Process(); + $processEntity->appointments = $appointmentProcess['appointments'] ?? []; + $processEntity->authKey = $appointmentProcess['authKey'] ?? null; + $processEntity->clients = $appointmentProcess['clients'] ?? []; + + $processEntity->scope = $appointmentProcess['scope'] ?? null; + $processEntity->requests = $requests; + $processEntity->lastChange = $appointmentProcess['lastChange'] ?? time(); + + $processEntity->createIP = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'; + $processEntity->createTimestamp = time(); + + if (isset($appointmentProcess['queue'])) { + $processEntity->queue = $appointmentProcess['queue']; + } + + $result = new Process(); + $result = \App::$http->readPostResult('/process/status/reserved/', $processEntity); + + return $result->getEntity(); + } + + public static function submitClientData(Process $process): Process|array + { + $processEntity = new Process(); + $processEntity->id = $process['data']['processId'] ?? null; + $processEntity->authKey = $process['data']['authKey'] ?? null; + $processEntity->appointments = $process['appointments'] ?? []; + $processEntity->clients = $process['clients'] ?? []; + $processEntity->scope = $process['data']['scope'] ?? null; + $processEntity->customTextfield = $process['customTextfield'] ?? null; + $processEntity->lastChange = $process['lastChange'] ?? time(); + + if (isset($process['queue'])) { + $processEntity->queue = $process['queue']; + } + + $processEntity->createIP = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'; + $processEntity->createTimestamp = time(); + + $url = "/process/{$processEntity->id}/{$processEntity->authKey}/"; + + try { + $result = \App::$http->readPostResult($url, $processEntity); + return $result->getEntity(); + } catch (\Exception $e) { + $exceptionName = json_decode(json_encode($e), true)['template'] ?? null; + if ($exceptionName === 'BO\\Zmsapi\\Exception\\Process\\MoreThanAllowedAppointmentsPerMail') { + $exception = [ + 'error' => 'tooManyAppointmentsWithSameMail' + ]; + return $exception; + } else { + throw $e; + } + } + } + + public function preconfirmProcess(?Process $process): Process + { + $url = '/process/status/preconfirmed/'; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + public function confirmProcess(?Process $process): Process + { + $url = '/process/status/confirmed/'; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + public function cancelAppointment(?Process $process): Process + { + $url = "/process/{$process['id']}/{$process['authKey']}/"; + return \App::$http->readDeleteResult($url, $process)->getEntity(); + } + + public function sendConfirmationEmail(?Process $process): Process + { + $url = "/process/{$process['id']}/{$process['authKey']}/confirmation/mail/"; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + public function sendPreconfirmationEmail(?Process $process): Process + { + $url = "/process/{$process['id']}/{$process['authKey']}/preconfirmation/mail/"; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + public function sendCancelationEmail(?Process $process): Process + { + $url = "/process/{$process['id']}/{$process['authKey']}/delete/mail/"; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + public static function getProcessById(?int $processId, ?string $authKey): Process + { + $resolveReferences = 2; + $process = new Process(); + $process = \App::$http->readGetResult("/process/{$processId}/{$authKey}/", [ + 'resolveReferences' => $resolveReferences + ])->getEntity(); + + return $process; + } + +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php new file mode 100644 index 000000000..c2d53237c --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php @@ -0,0 +1,635 @@ +withProviderID($provider->source, $provider->id)->getIterator()->current(); + + $providerData = array_merge( + [ + "id" => $provider->id, + "name" => $provider->displayName ?? $provider->name, + ], + !empty($provider->data['address']) ? ["address" => $provider->data['address']] : [], + !empty($provider->data['geo']) ? ["geo" => $provider->data['geo']] : [] + ); + + if ($matchingScope instanceof Scope) { + $providerData["scope"] = [ + "id" => $matchingScope->id, + "provider" => $matchingScope->getProvider() ?? null, + "shortName" => $matchingScope->getShortName() ?? null, + "telephoneActivated" => $matchingScope->getTelephoneActivated() ?? null, + "telephoneRequired" => $matchingScope->getTelephoneRequired() ?? null, + "customTextfieldActivated" => $matchingScope->getCustomTextfieldActivated() ?? null, + "customTextfieldRequired" => $matchingScope->getCustomTextfieldRequired() ?? null, + "customTextfieldLabel" => $matchingScope->getCustomTextfieldLabel() ?? null, + "captchaActivatedRequired" => $matchingScope->getCaptchaActivatedRequired() ?? null, + "displayInfo" => $matchingScope->getDisplayInfo() ?? null + ]; + } + + $providerProjectionList[] = $providerData; + } + + return [ + "offices" => $providerProjectionList, + "status" => 200 + ]; + } + + public static function getScopes(): array + { + $scopeList = new ScopeList(ZmsApiClientService::getScopes() ?? []); + $scopesProjectionList = []; + + foreach ($scopeList as $scope) { + $scopesProjectionList[] = [ + "id" => $scope->id, + "provider" => $scope->getProvider() ?? null, + "shortName" => $scope->getShortName() ?? null, + "telephoneActivated" => $scope->getTelephoneActivated() ?? null, + "telephoneRequired" => $scope->getTelephoneRequired() ?? null, + "customTextfieldActivated" => $scope->getCustomTextfieldActivated() ?? null, + "customTextfieldRequired" => $scope->getCustomTextfieldRequired() ?? null, + "customTextfieldLabel" => $scope->getCustomTextfieldLabel() ?? null, + "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired() ?? null, + "displayInfo" => $scope->getDisplayInfo() ?? null + ]; + } + + return [ + "scopes" => $scopesProjectionList, + "status" => 200 + ]; + } + + public static function getServices(): array + { + + $requestList = ZmsApiClientService::getServices() ?? []; + $servicesProjectionList = []; + + foreach ($requestList as $request) { + $additionalData = $request->getAdditionalData(); + $servicesProjectionList[] = [ + "id" => $request->getId(), + "name" => $request->getName(), + "maxQuantity" => $additionalData['maxQuantity'] ?? 1, + ]; + } + + return [ + "services" => $servicesProjectionList, + "status" => 200 + ]; + } + + public static function getScopeForProvider(int $providerId, ?ScopeList $scopes): array + { + $filteredScopes = $scopes->withProviderID(\App::$source_name, (string) $providerId); + + if ($filteredScopes->count() === 0) { + $errors = ValidationService::validateScopesNotFound($filteredScopes); + if (!empty($errors['errors'])) { + return $errors; + } + } + + $result = []; + foreach ($filteredScopes as $scope) { + $result = [ + "id" => $scope->id, + "provider" => $scope->getProvider(), + "shortName" => $scope->getShortName() ?? null, + "telephoneActivated" => $scope->getTelephoneActivated() ?? null, + "telephoneRequired" => $scope->getTelephoneRequired() ?? null, + "customTextfieldActivated" => $scope->getCustomTextfieldActivated() ?? null, + "customTextfieldRequired" => $scope->getCustomTextfieldRequired() ?? null, + "customTextfieldLabel" => $scope->getCustomTextfieldLabel() ?? null, + "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired() ?? null, + "displayInfo" => $scope->getDisplayInfo() ?? null, + ]; + } + + return [ + 'scope' => $result, + 'status' => 200, + ]; + } + + public static function getServicesAndOffices(): array + { + $providerList = ZmsApiClientService::getOffices() ?? []; + $requestList = ZmsApiClientService::getServices() ?? []; + $relationList = ZmsApiClientService::getRequestRelationList() ?? []; + + $offices = MapperService::mapOfficesWithScope($providerList); + $services = MapperService::mapServicesWithCombinations($requestList, $relationList); + $relations = MapperService::mapRelations($relationList); + + return [ + "offices" => $offices, + "services" => $services, + "relations" => $relations, + "status" => 200 + ]; + } + + /* Todo add method + * getCombinableServicesByIds + * + * + * + */ + + public static function getScopeByOfficeId(int $officeId): array + { + $scopeList = new ScopeList(ZmsApiClientService::getScopes() ?? []); + $matchingScope = $scopeList->withProviderID(\App::$source_name, $officeId)->getIterator()->current(); + + if ($matchingScope instanceof Scope) { + return [ + "id" => $matchingScope->id, + "provider" => $matchingScope->getProvider() ?? null, + "shortName" => $matchingScope->getShortName() ?? null, + "telephoneActivated" => $matchingScope->getTelephoneActivated() ?? null, + "telephoneRequired" => $matchingScope->getTelephoneRequired() ?? null, + "customTextfieldActivated" => $matchingScope->getCustomTextfieldActivated() ?? null, + "customTextfieldRequired" => $matchingScope->getCustomTextfieldRequired() ?? null, + "customTextfieldLabel" => $matchingScope->getCustomTextfieldLabel() ?? null, + "captchaActivatedRequired" => $matchingScope->getCaptchaActivatedRequired() ?? null, + "displayInfo" => $matchingScope->getDisplayInfo() ?? null, + ]; + } + + return [ + 'error' => 'Scope not found for the provided office ID', + 'status' => 404, + ]; + } + + /* Todo add method + * getOfficeByIds + * + * + * + */ + + public static function getOfficesByServiceIds(array $serviceIds): array + { + $serviceIds = array_unique($serviceIds); + + $errors = ValidationService::validateGetOfficesByServiceIds($serviceIds); + if (!empty($errors['errors'])) { + return $errors; + } + + $providerList = ZmsApiClientService::getOffices(); + $requestRelationList = ZmsApiClientService::getRequestRelationList(); + + $offices = []; + $notFoundIds = []; + $addedOfficeIds = []; + + foreach ($serviceIds as $serviceId) { + $found = false; + foreach ($requestRelationList as $relation) { + if ($relation->request->id == $serviceId) { + if (!in_array($relation->provider->id, $addedOfficeIds)) { + foreach ($providerList as $provider) { + if ($provider->id == $relation->provider->id) { + $offices[] = [ + "id" => $provider->id, + "name" => $provider->name, + ]; + $addedOfficeIds[] = $provider->id; + $found = true; + break; + } + } + } else { + $found = true; + } + } + } + if (!$found) { + $notFoundIds[] = $serviceId; + } + } + + $errors = ValidationService::validateOfficesNotFound($offices); + if (!empty($errors['errors'])) { + return $errors; + } + + $responseContent = ['offices' => $offices]; + if (!empty($notFoundIds)) { + $responseContent['warning'] = 'The following serviceId(s) were not found: ' . implode(', ', $notFoundIds); + } + + return [ + 'offices' => $responseContent, + 'status' => 200 + ]; + } + + public static function getScopeByIds(array $scopeIds): array + { + $scopeIds = array_unique($scopeIds); + + $errors = ValidationService::validateGetScopeByIds($scopeIds); + if (!empty($errors['errors'])) { + return $errors; + } + + $scopeList = new ScopeList(ZmsApiClientService::getScopes() ?? []); + $filteredScopes = new ScopeList(); + + foreach ($scopeList as $scope) { + if (in_array($scope->id, $scopeIds)) { + $filteredScopes->addEntity($scope); + } + } + + $errors = ValidationService::validateScopesNotFound($filteredScopes); + if (!empty($errors['errors'])) { + return $errors; + } + + $scopes = []; + foreach ($filteredScopes as $scope) { + $scopes[] = [ + "id" => $scope->id, + "provider" => $scope->getProvider() ?? null, + "shortName" => $scope->getShortName() ?? null, + "telephoneActivated" => $scope->getTelephoneActivated() ?? null, + "telephoneRequired" => $scope->getTelephoneRequired() ?? null, + "customTextfieldActivated" => $scope->getCustomTextfieldActivated() ?? null, + "customTextfieldRequired" => $scope->getCustomTextfieldRequired() ?? null, + "customTextfieldLabel" => $scope->getCustomTextfieldLabel() ?? null, + "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired() ?? null, + "displayInfo" => $scope->getDisplayInfo() ?? null, + ]; + } + + return [ + 'scopes' => $scopes, + 'status' => 200, + ]; + } + + public static function getServicesByOfficeIds(array $officeIds): array + { + $officeIds = array_unique($officeIds); + + $errors = ValidationService::validateGetServicesByOfficeIds($officeIds); + if (!empty($errors['errors'])) { + return $errors; + } + + $requestList = ZmsApiClientService::getServices() ?? []; + $requestRelationList = ZmsApiClientService::getRequestRelationList(); + + $services = []; + $notFoundIds = []; + $addedServices = []; + + foreach ($officeIds as $officeId) { + $found = false; + foreach ($requestRelationList as $relation) { + if ($relation->provider->id == $officeId) { + foreach ($requestList as $request) { + if ($request->id == $relation->request->id && !in_array($request->id, $addedServices)) { + $services[] = [ + "id" => $request->id, + "name" => $request->name, + "maxQuantity" => $request->getAdditionalData()['maxQuantity'] ?? 1, + ]; + $addedServices[] = $request->id; + $found = true; + } + } + } + } + if (!$found) { + $notFoundIds[] = $officeId; + } + } + + $errors = ValidationService::validateServicesNotFound($services); + if (!empty($errors['errors'])) { + return $errors; + } + + $responseContent = ['services' => $services]; + if (!empty($notFoundIds)) { + $responseContent['warning'] = 'The following officeId(s) were not found: ' . implode(', ', $notFoundIds); + } + + return [ + 'services' => $responseContent, + 'status' => 200, + ]; + } + + /* Todo add method + * getOfficesThatProvideService + * + * + * + */ + + public static function getServicesProvidedAtOffice(int $officeId): array + { + $requestRelationList = ZmsApiClientService::getRequestRelationList(); + + $requestRelationArray = []; + foreach ($requestRelationList as $relation) { + $requestRelationArray[] = $relation; + } + + $serviceIds = array_filter($requestRelationArray, function ($relation) use ($officeId) { + return $relation->provider->id === $officeId || (string)$relation->provider->id === (string)$officeId; + }); + + $serviceIds = array_map(function ($relation) { + return $relation->request->id; + }, $serviceIds); + + $requestList = ZmsApiClientService::getServices() ?? []; + $requestArray = []; + foreach ($requestList as $request) { + $requestArray[] = $request; + } + + return array_filter($requestArray, function ($request) use ($serviceIds) { + return in_array($request->id, $serviceIds); + }); + } + + + public static function getBookableFreeDays(array $queryParams): array + { + $officeId = $queryParams['officeId'] ?? null; + $serviceId = $queryParams['serviceId'] ?? null; + $serviceCounts = isset($queryParams['serviceCount']) ? explode(',', $queryParams['serviceCount']) : []; + $startDate = $queryParams['startDate'] ?? null; + $endDate = $queryParams['endDate'] ?? null; + + $errors = ValidationService::validateGetBookableFreeDays($officeId, $serviceId, $startDate, $endDate, $serviceCounts); + if (!empty($errors['errors'])) { + return $errors; + } + + try { + $firstDay = UtilityHelper::getInternalDateFromISO($startDate); + $lastDay = UtilityHelper::getInternalDateFromISO($endDate); + + $freeDays = ZmsApiClientService::getFreeDays( + new ProviderList([['id' => $officeId, 'source' => \App::$source_name]]), + new RequestList([ + [ + 'id' => $serviceId, + 'source' => \App::$source_name, + 'slotCount' => $serviceCounts, + ] + ]), + $firstDay, + $lastDay, + ); + + $daysCollection = $freeDays->days; + $formattedDays = []; + + foreach ($daysCollection as $day) { + $formattedDays[] = sprintf('%04d-%02d-%02d', $day->year, $day->month, $day->day); + } + + $errors = ValidationService::validateAppointmentDaysNotFound($formattedDays); + if (!empty($errors['errors'])) { + return $errors; + } + + return [ + 'availableDays' => $formattedDays, + 'status' => 200, + ]; + + } catch (\Exception $e) { + return ExceptionService::noAppointmentsAtLocation(); + } + } + + public static function getFreeAppointments(array $params): ProcessList|array + { + $office = [ + 'id' => $params['officeId'], + 'source' => \App::$source_name + ]; + + $requests = []; + + foreach ($params['serviceIds'] as $index => $serviceId) { + $service = [ + 'id' => $serviceId, + 'source' => \App::$source_name, + 'slotCount' => $params['serviceCounts'][$index] + ]; + $requests = array_merge($requests, array_fill(0, $service['slotCount'], $service)); + } + + try { + + $freeSlots = new ProcessList(); + $freeSlots = ZmsApiClientService::getFreeTimeslots( + new ProviderList([$office]), + new RequestList($requests), + $params['date'], + $params['date'] + ); + + return $freeSlots; + + } catch (\Exception $e) { + return [ + 'appointmentTimestamps' => [], + 'errorCode' => 'internalError', + 'errorMessage' => 'An error occurred while fetching available free appointments', + 'status' => 500, + ]; + } + } + + public static function getAvailableAppointments(array $queryParams): array + { + $date = $queryParams['date'] ?? null; + $officeId = $queryParams['officeId'] ?? null; + $serviceIds = isset($queryParams['serviceId']) ? explode(',', $queryParams['serviceId']) : []; + $serviceCounts = isset($queryParams['serviceCount']) ? explode(',', $queryParams['serviceCount']) : []; + + $errors = ValidationService::validateGetAvailableAppointments($date, $officeId, $serviceIds, $serviceCounts); + if (!empty($errors['errors'])) { + return $errors; + } + + try { + $requests = []; + foreach ($serviceIds as $index => $serviceId) { + $slotCount = isset($serviceCounts[$index]) ? intval($serviceCounts[$index]) : 1; + for ($i = 0; $i < $slotCount; $i++) { + $requests[] = [ + 'id' => $serviceId, + 'source' => \App::$source_name, + 'slotCount' => 1, + ]; + } + } + + $freeSlots = new ProcessList(); + $freeSlots = ZmsApiClientService::getFreeTimeslots( + new ProviderList([['id' => $officeId, 'source' => \App::$source_name]]), + new RequestList($requests), + UtilityHelper::getInternalDateFromISO($date), + UtilityHelper::getInternalDateFromISO($date) + ); + + return self::processFreeSlots($freeSlots); + + } catch (\Exception $e) { + return [ + 'appointmentTimestamps' => [], + 'errorCode' => 'internalError', + 'errorMessage' => 'An error occurred while fetching available appointments', + 'status' => 500, + ]; + } + } + + private static function processFreeSlots(ProcessList $freeSlots): array + { + + $errors = ValidationService::validateGetProcessFreeSlots($freeSlots); + if (!empty($errors['errors'])) { + return $errors; + } + + $currentTimestamp = time(); + $appointmentTimestamps = []; + + foreach ($freeSlots as $slot) { + if (!isset($slot->appointments) || !is_iterable($slot->appointments)) { + continue; + } + + foreach ($slot->appointments as $appointment) { + + if (isset($appointment->date)) { + $timestamp = (int) $appointment->date; + + if (!in_array($timestamp, $appointmentTimestamps) && $timestamp > $currentTimestamp) { + $appointmentTimestamps[] = $timestamp; + } + } + } + } + + $errors = ValidationService::validateGetProcessByIdTimestamps($appointmentTimestamps); + if (!empty($errors['errors'])) { + return $errors; + } + + sort($appointmentTimestamps); + + return [ + 'appointmentTimestamps' => $appointmentTimestamps, + 'status' => 200, + ]; + } + + public static function reserveTimeslot(Process $appointmentProcess, array $serviceIds, array $serviceCounts): Process + { + return ZmsApiClientService::reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts); + } + + public static function getProcessById(?int $processId, ?string $authKey): array + { + $errors = ValidationService::validateGetProcessById($processId, $authKey); + if (!empty($errors['errors'])) { + return $errors; + } + + try { + $process = new Process; + $process = ZmsApiClientService::getProcessById($processId, $authKey); + + $errors = ValidationService::validateGetProcessNotFound($process); + if (!empty($errors['errors'])) { + return $errors; + } + + $responseData = UtilityHelper::getThinnedProcessData($process); + return ['data' => $responseData, 'status' => 200]; + + } catch (\Exception $e) { + if (strpos($e->getMessage(), 'kein Termin gefunden') !== false) { + return ExceptionService::appointmentNotFound(); + } else { + throw $e; + } + } + } + + public static function updateClientData(Process $reservedProcess): Process|array + { + $clientUpdateResult = ZmsApiClientService::submitClientData($reservedProcess); + + if (isset($clientUpdateResult['error'])) { + return $clientUpdateResult; + } + + return $clientUpdateResult; + } + + /* Todo add method + * preconfirmAppointment + * + * + * + */ + + + /* Todo add method + * confirmAppointment + * + * + * + */ + + /* Todo add method + * cancelAppointment + * + * + * + */ + +} + + diff --git a/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php b/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php new file mode 100644 index 000000000..31d6ea1df --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php @@ -0,0 +1,26 @@ +getQueryParams()['officeId'] ?? ''); + + $result = ZmsApiFacadeService::getServicesByOfficeIds($officeIds); + + if (isset($result['errors'])) { + return $this->createJsonResponse($response, $result, $result['status']); + } + + return $this->createJsonResponse($response, $result['services'], $result['status']); + } + +} diff --git a/zmscitizenapi/src/Zmscitizenapi/ServicesList.php b/zmscitizenapi/src/Zmscitizenapi/ServicesList.php new file mode 100644 index 000000000..421719bd2 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/ServicesList.php @@ -0,0 +1,18 @@ +createJsonResponse($response, $services, statusCode: $services['status']); + } +} diff --git a/zmscitizenapi/templates/.keep b/zmscitizenapi/templates/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php new file mode 100644 index 000000000..a38e2e30b --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php @@ -0,0 +1,14 @@ +renderJson(method: 'POST'); + $this->assertEqualsCanonicalizing([], $responseData); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php new file mode 100644 index 000000000..04d3d9e25 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php @@ -0,0 +1,13 @@ +renderJson(method: 'POST'); + $this->assertEqualsCanonicalizing([], $responseData); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php new file mode 100644 index 000000000..2e4b60bd8 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php @@ -0,0 +1,313 @@ +setApiCalls( + [ + [ + 'function' => 'readGetResult', + 'url' => '/process/101002/fb43/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_process.json") + ] + ] + ); + + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + 'processId' => '101002', + 'timestamp' => 1724907600, + 'authKey' => 'fb43', + 'familyName' => 'Doe', + 'customTextfield' => '', + 'email' => 'johndoe@example.com', + 'telephone' => '0123456789', + 'officeName' => 'Bürgerbüro Orleansplatz DEV (KVR-II/231 DEV)', + 'officeId' => '102522', + 'scope' => [ + '$schema' => 'https://schema.berlin.de/queuemanagement/scope.json', + 'id' => '64', + 'source' => 'dldb', + 'contact' => [ + 'name' => 'Bürgerbüro Orleansplatz DEV (KVR-II/231 DEV)', + 'street' => 'Orleansstraße 50', + 'email' => '', + 'country' => 'Germany' + ], + 'provider' => [ + 'contact' => [ + 'city' => 'Muenchen', + 'country' => 'Germany', + 'name' => 'Bürgerbüro Orleansplatz DEV (KVR-II/231 DEV)', + 'postalCode' => '81667', + 'region' => 'Muenchen', + 'street' => 'Orleansstraße', + 'streetNumber' => '50' + ], + 'id' => '102522', + 'link' => 'https://service.berlin.de/standort/102522/', + 'name' => 'Bürgerbüro Orleansplatz DEV (KVR-II/231 DEV)', + 'displayName' => 'Bürgerbüro Orleansplatz DEV', + 'source' => 'dldb' + ], + 'hint' => '', + 'lastChange' => 1724192287, + 'preferences' => [ + 'appointment' => [ + 'deallocationDuration' => '15', + 'endInDaysDefault' => '60', + 'multipleSlotsEnabled' => '0', + 'reservationDuration' => '15', + 'activationDuration' => '15', + 'startInDaysDefault' => '2', + 'notificationConfirmationEnabled' => '0', + 'notificationHeadsUpEnabled' => '0' + ], + 'client' => [ + 'alternateAppointmentUrl' => '', + 'amendmentActivated' => '0', + 'amendmentLabel' => '', + 'emailFrom' => 'terminvereinbarung@muenchen.de', + 'emailRequired' => '0', + 'telephoneActivated' => '1', + 'telephoneRequired' => '1', + 'appointmentsPerMail' => '1', + 'customTextfieldActivated' => '1', + 'customTextfieldRequired' => '1', + 'customTextfieldLabel' => 'Nachname des Kindes', + 'captchaActivatedRequired' => '0', + 'adminMailOnAppointment' => 0, + 'adminMailOnDeleted' => 0, + 'adminMailOnUpdated' => 0, + 'adminMailOnMailSent' => 0 + ], + 'notifications' => [ + 'confirmationContent' => '', + 'headsUpContent' => '', + 'headsUpTime' => '10' + ], + 'pickup' => [ + 'alternateName' => 'Ausgabe', + 'isDefault' => '0' + ], + 'queue' => [ + 'callCountMax' => '1', + 'callDisplayText' => 'Herzlich Willkommen', + 'firstNumber' => '1', + 'lastNumber' => '999', + 'maxNumberContingent' => '999', + 'processingTimeAverage' => '12', + 'publishWaitingTimeEnabled' => '0', + 'statisticsEnabled' => '0' + ], + 'survey' => [ + 'emailContent' => '', + 'enabled' => '0', + 'label' => '' + ], + 'ticketprinter' => [ + 'buttonName' => 'Bürgerbüro Orleansplatz (KVR-II/231)', + 'confirmationEnabled' => '0', + 'deactivatedText' => 'dasdsa', + 'notificationsAmendmentEnabled' => '0', + 'notificationsEnabled' => '0', + 'notificationsDelay' => '0' + ], + 'workstation' => [ + 'emergencyEnabled' => '0', + 'emergencyRefreshInterval' => '5' + ] + ], + 'shortName' => 'DEVV', + 'status' => [ + 'emergency' => [ + 'activated' => '0' + ], + 'queue' => [ + 'ghostWorkstationCount' => '-1', + 'givenNumberCount' => '4', + 'lastGivenNumber' => '4', + 'lastGivenNumberTimestamp' => 1715292000 + ], + 'ticketprinter' => [ + 'deactivated' => '0' + ] + ] + ], + 'subRequestCounts' => [], + 'serviceId' => '1063424', + 'serviceCount' => 1 + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingProcessId() + { + $parameters = [ + 'authKey' => 'fb43', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidProcessId', + 'errorMessage' => 'processId should be a 32-bit integer.' + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingAuthKey() + { + $parameters = [ + 'processId' => '101002', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidAuthKey', + 'errorMessage' => 'authKey should be a string.' + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessId() + { + $parameters = [ + 'processId' => 'invalid', + 'authKey' => 'fb43', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidProcessId', + 'errorMessage' => 'processId should be a 32-bit integer.' + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidAuthKey() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 12345, + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidAuthKey', + 'errorMessage' => 'authKey should be a string.' + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testBothParametersMissing() + { + $parameters = []; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidProcessId', + 'errorMessage' => 'processId should be a 32-bit integer.', + ], + [ + 'status' => 400, + 'errorCode' => 'invalidAuthKey', + 'errorMessage' => 'authKey should be a string.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + + } + + public function testAppointmentNotFound() + { + $this->setApiCalls( + [ + [ + 'function' => 'readGetResult', + 'url' => '/process/101002/fb43/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'exception' => new \Exception('API-Error: Zu den angegebenen Daten konnte kein Termin gefunden werden.') + ] + ] + ); + + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'appointmentNotFound', + 'errorMessage' => 'Termin wurde nicht gefunden.', + 'status' => 404, + ] + ], + 'status' => 404 + ]; + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php new file mode 100644 index 000000000..3208be31f --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php @@ -0,0 +1,14 @@ +renderJson(method: 'POST'); + $this->assertEqualsCanonicalizing([], $responseData); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php new file mode 100644 index 000000000..fa0798672 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php @@ -0,0 +1,459 @@ +setApiCalls( + [ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_reserve_SourceGet_dldb.json"), + ], + [ + 'function' => 'readPostResult', + 'url' => '/process/status/free/', + 'response' => $this->readFixture("GET_appointments_free.json") + ], + [ + 'function' => 'readPostResult', + 'url' => '/process/status/reserved/', + 'response' => $this->readFixture("POST_reserve_appointment.json") + ] + ] + ); + + $parameters = [ + 'officeId' => 10546, + 'serviceId' => ['1063423'], + 'serviceCount' => [0], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + 'processId' => '101002', + 'timestamp' => 32526616522, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'customTextfield' => '', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'officeName' => null, + 'officeId' => 10546, + 'scope' => [ + '$schema' => 'https://schema.berlin.de/queuemanagement/scope.json', + 'id' => '58', + 'source' => 'dldb', + ], + 'subRequestCounts' => [], + 'serviceId' => null, + 'serviceCount' => 0 + ]; + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testAppointmentNotAvailable() + { + $this->setApiCalls( + [ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_reserve_SourceGet_dldb.json"), + ], + [ + 'function' => 'readPostResult', + 'url' => '/process/status/free/', + 'response' => $this->readFixture("GET_appointments_free.json") + ] + ] + ); + + $parameters = [ + 'officeId' => 10546, + 'serviceId' => ['1063423'], + 'serviceCount' => [0], + 'timestamp' => "32526616300", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'appointmentNotAvailable', + 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar.', + 'status' => 404, + ] + ], + 'status' => 404 + ]; + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingOfficeId() + { + $this->setApiCalls([]); + + $parameters = [ + 'serviceId' => ['1063423'], + 'serviceCount' => [0], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingServiceId() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => 10546, + 'serviceCount' => [0], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingTimestamp() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => 10546, + 'serviceId' => ['1063423'], + 'serviceCount' => [0], + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidTimestamp', + 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingOfficeIdAndServiceId() + { + $this->setApiCalls([]); + + $parameters = [ + 'serviceCount' => [0], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + ], + [ + 'status' => 400, + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingOfficeIdAndTimestamp() + { + $this->setApiCalls([]); + + $parameters = [ + 'serviceId' => ['1063423'], + 'serviceCount' => [0], + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + ], + [ + 'status' => 400, + 'errorCode' => 'invalidTimestamp', + 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingServiceIdAndTimestamp() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => 10546, + 'serviceCount' => [0], + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + ], + [ + 'status' => 400, + 'errorCode' => 'invalidTimestamp', + 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingAllFields() + { + $this->setApiCalls([]); + + $parameters = []; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + ], + [ + 'status' => 400, + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + ], + [ + 'status' => 400, + 'errorCode' => 'invalidTimestamp', + 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidOfficeIdFormat() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => 'invalid_id', + 'serviceId' => ['1063423'], + 'serviceCount' => [0], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidServiceIdFormat() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => 10546, + 'serviceId' => ['invalid_service_id'], + 'serviceCount' => [0], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidTimestampFormat() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => 10546, + 'serviceId' => ['1063423'], + 'serviceCount' => [0], + 'timestamp' => 'invalid_timestamp', + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidTimestamp', + 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + public function testEmptyServiceIdArray() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => 10546, + 'serviceId' => [], + 'serviceCount' => [0], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidServiceCount() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => 10546, + 'serviceId' => ['1063423'], + 'serviceCount' => ['invalid'], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php new file mode 100644 index 000000000..057f94b84 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php @@ -0,0 +1,1797 @@ +setApiCalls( + [ + [ + 'function' => 'readGetResult', + 'url' => '/process/101002/fb43/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_process.json") + ], + [ + 'function' => 'readPostResult', + 'url' => '/process/101002/fb43/', + 'response' => $this->readFixture("POST_update_appointment.json") + ] + ] + ); + + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => "default@example.com", + 'telephone' => '123456789', + 'customTextfield' => "Some custom text", + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + "processId" => "101002", + "timestamp" => 1727865900, + "authKey" => "fb43", + "familyName" => "Smith", + "customTextfield" => "Some custom text", + "email" => "default@example.com", + "telephone" => "123456789", + "officeName" => null, + "officeId" => null, + "scope" => [ + '$schema' => "https://schema.berlin.de/queuemanagement/scope.json", + "id" => 0, + "source" => "dldb" + ], + "subRequestCounts" => [], + "serviceId" => "10242339", + "serviceCount" => 1 + ]; + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + + } + + public function testTooManyEmailsAtLocation() + { + + $exception = new \BO\Zmsclient\Exception(); + $exception->template = 'BO\\Zmsapi\\Exception\\Process\\MoreThanAllowedAppointmentsPerMail'; + + $this->setApiCalls( + [ + [ + 'function' => 'readGetResult', + 'url' => '/process/101002/fb43/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_process.json") + ], + [ + 'function' => 'readPostResult', + 'url' => '/process/101002/fb43/', + 'exception' => $exception + ] + ] + ); + + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => "default@example.com", + 'telephone' => '123456789', + 'customTextfield' => "Some custom text", + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'tooManyAppointmentsWithSameMail', + 'errorMessage' => 'Zu viele Termine mit gleicher E-Mail- Adresse.', + 'status' => 406, + ] + ], + 'status' => 406 + ]; + + $this->assertEquals(406, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testAppointmentNotFound() + { + $this->setApiCalls( + [ + [ + 'function' => 'readGetResult', + 'url' => '/process/101003/fb43/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'exception' => new \Exception('API-Error: Zu den angegebenen Daten konnte kein Termin gefunden werden.') + ] + ] + ); + + $parameters = [ + 'processId' => '101003', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => "default@example.com", + 'telephone' => '123456789', + 'customTextfield' => "Some custom text", + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'appointmentNotFound', + 'errorMessage' => 'Termin wurde nicht gefunden.', + 'status' => 404, + ] + ], + 'status' => 404 + ]; + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'default@example.com', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php b/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php new file mode 100644 index 000000000..34017a8a9 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php @@ -0,0 +1,337 @@ +setApiCalls( + [ + [ + 'function' => 'readPostResult', + 'url' => '/process/status/free/', + 'response' => $this->readFixture("GET_appointments.json") + ] + ] + ); + + $parameters = [ + 'date' => '3000-09-21', + 'officeId' => 10546, + 'serviceId' => '1063423', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'appointmentTimestamps' => [32526616522], + 'status' => 200, + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + + public function testEmptyAppointments() + { + $this->setApiCalls( + [ + [ + 'function' => 'readPostResult', + 'url' => '/process/status/free/', + 'response' => $this->readFixture("GET_appointments_empty.json") + ] + ] + ); + + $parameters = [ + 'date' => '3000-09-21', + 'officeId' => 10546, + 'serviceId' => '1063423', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $expectedResponse = [ + 'errors' => [ + [ + 'appointmentTimestamps' => [], + 'errorCode' => "appointmentNotAvailable", + 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar.', + 'status' => 404, + ] + ], + 'status' => 404 + ]; + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } + + public function testDateMissing() + { + $parameters = [ + 'officeId' => 10546, + 'serviceId' => '1063423', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidDate', + 'errorMessage' => 'date is required and must be a valid date.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testOfficeIdMissing() + { + $parameters = [ + 'date' => '3000-09-21', + 'serviceId' => '1063423', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testServiceIdMissing() + { + $parameters = [ + 'date' => '3000-09-21', + 'officeId' => 10546, + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testServiceCountMissing() + { + $parameters = [ + 'date' => '3000-09-21', + 'officeId' => 10546, + 'serviceId' => '1063423', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testDateAndOfficeIdMissing() + { + $parameters = [ + 'serviceId' => '1063423', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidDate', + 'errorMessage' => 'date is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testDateAndServiceIdMissing() + { + $parameters = [ + 'officeId' => 10546, + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidDate', + 'errorMessage' => 'date is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testDateAndServiceCountMissing() + { + $parameters = [ + 'officeId' => 10546, + 'serviceId' => '1063423', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidDate', + 'errorMessage' => 'date is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testOfficeIdAndServiceIdMissing() + { + $parameters = [ + 'date' => '3000-09-21', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testOfficeIdAndServiceCountMissing() + { + $parameters = [ + 'date' => '3000-09-21', + 'serviceId' => '1063423', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testServiceIdAndServiceCountMissing() + { + $parameters = [ + 'date' => '3000-09-21', + 'officeId' => 10546, + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php b/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php new file mode 100644 index 000000000..95f4defa3 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php @@ -0,0 +1,684 @@ +setApiCalls( + [ + [ + 'function' => 'readPostResult', + 'url' => '/calendar/', + 'response' => $this->readFixture("GET_calendar.json") + ] + ] + ); + $parameters = [ + 'officeId' => '9999998', + 'serviceId' => '1', + 'startDate' => '2024-08-21', + 'endDate' => '2024-08-23', + 'serviceCount' => '1', + ]; + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'availableDays' => [ + "2024-08-21", "2024-08-22", "2024-08-23", "2024-08-26", "2024-08-27", "2024-08-28", "2024-08-29", "2024-08-30", + "2024-09-02", "2024-09-03", "2024-09-04", "2024-09-05", "2024-09-06", "2024-09-09", "2024-09-10", "2024-09-11", + "2024-09-12", "2024-09-13", "2024-09-16", "2024-09-17", "2024-09-18", "2024-09-19", "2024-09-20" + ], + 'status' => 200, + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + + } + + public function testNoAvailableDays() + { + $this->setApiCalls( + [ + [ + 'function' => 'readPostResult', + 'url' => '/calendar/', + 'response' => $this->readFixture("GET_calendar_empty_days.json") // Simulate a response with no available days + ] + ] + ); + + $parameters = [ + 'officeId' => '9999998', + 'serviceId' => '1', + 'serviceCount' => '1', + 'startDate' => '2024-08-21', + 'endDate' => '2024-08-23', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'noAppointmentForThisDay', + 'errorMessage' => 'No available days found for the given criteria.', + 'status' => 404, + ] + ], + 'status' => 404, + ]; + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidDateFormat() + { + $parameters = [ + 'officeId' => '9999998', + 'serviceId' => '1', + 'serviceCount' => '1', + 'startDate' => 'invalid-date', + 'endDate' => 'invalid-date', + ]; + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'noAppointmentForThisOffice', + 'errorMessage' => 'An diesem Standort gibt es aktuell leider keine freien Termine.', + 'status' => 404, + ] + ], + 'status' => 404, + ]; + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + + } + + public function testMissingStartDate() + { + $parameters = [ + 'endDate' => '2024-09-04', + 'officeId' => '102522', + 'serviceId' => '1063424', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidStartDate', + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingEndDate() + { + $parameters = [ + 'startDate' => '2024-08-29', + 'officeId' => '102522', + 'serviceId' => '1063424', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidEndDate', + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingOfficeId() + { + $parameters = [ + 'startDate' => '2024-08-29', + 'endDate' => '2024-09-04', + 'serviceId' => '1063424', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingServiceId() + { + $parameters = [ + 'startDate' => '2024-08-29', + 'endDate' => '2024-09-04', + 'officeId' => '102522', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingServiceCount() + { + $parameters = [ + 'startDate' => '2024-08-29', + 'endDate' => '2024-09-04', + 'officeId' => '102522', + 'serviceId' => '1063424', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testEmptyServiceCount() + { + $parameters = [ + 'startDate' => '2024-08-29', + 'endDate' => '2024-09-04', + 'officeId' => '102522', + 'serviceId' => '1063424', + 'serviceCount' => '', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidServiceCountFormat() + { + $parameters = [ + 'startDate' => '2024-08-29', + 'endDate' => '2024-09-04', + 'officeId' => '102522', + 'serviceId' => '1063424', + 'serviceCount' => 'one,two', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testAllParametersMissing() + { + $parameters = []; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidStartDate', + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidEndDate', + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingStartDateAndEndDate() + { + $parameters = [ + 'officeId' => '102522', + 'serviceId' => '1063424', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidStartDate', + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidEndDate', + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingOfficeIdAndServiceId() + { + $parameters = [ + 'startDate' => '2024-08-29', + 'endDate' => '2024-09-04', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingServiceIdAndServiceCount() + { + $parameters = [ + 'startDate' => '2024-08-29', + 'endDate' => '2024-09-04', + 'officeId' => '102522', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingStartDateAndOfficeId() + { + $parameters = [ + 'endDate' => '2024-09-04', + 'serviceId' => '1063424', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidStartDate', + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingEndDateAndServiceCount() + { + $parameters = [ + 'startDate' => '2024-08-29', + 'officeId' => '102522', + 'serviceId' => '1063424', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidEndDate', + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingOfficeIdAndServiceCount() + { + $parameters = [ + 'startDate' => '2024-08-29', + 'endDate' => '2024-09-04', + 'serviceId' => '1063424', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingStartDateEndDateAndOfficeId() + { + $parameters = [ + 'serviceId' => '1063424', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidStartDate', + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidEndDate', + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingStartDateEndDateAndServiceId() + { + $parameters = [ + 'officeId' => '102522', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidStartDate', + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidEndDate', + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingStartDateOfficeIdAndServiceCount() + { + $parameters = [ + 'endDate' => '2024-09-04', + 'serviceId' => '1063424', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidStartDate', + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testMissingEndDateOfficeIdAndServiceCount() + { + $parameters = [ + 'startDate' => '2024-08-29', + 'serviceId' => '1063424', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidEndDate', + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testEmptyStartDateAndEndDate() + { + $parameters = [ + 'startDate' => '', + 'endDate' => '', + 'officeId' => '102522', + 'serviceId' => '1063424', + 'serviceCount' => '1', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidStartDate', + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorCode' => 'invalidEndDate', + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testNonNumericServiceCount() + { + $parameters = [ + 'startDate' => '2024-08-29', + 'endDate' => '2024-09-04', + 'officeId' => '102522', + 'serviceId' => '1063424', + 'serviceCount' => 'abc,123', + ]; + + $response = $this->render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/Base.php b/zmscitizenapi/tests/Zmscitizenapi/Base.php new file mode 100644 index 000000000..d06e1a796 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/Base.php @@ -0,0 +1,29 @@ +render($arguments, $parameters, $sessionData, $method); + $this->assertContains($response->getStatusCode(), $assertStatusCodes); + return json_decode($response->getBody(), true); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php new file mode 100644 index 000000000..140dd9eca --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php @@ -0,0 +1,51 @@ +render([], $parameters, []); + $responseBody = json_decode((string)$response->getBody(), true); + + $expectedResponse = [ + 'siteKey' => 'FAKE_SITE_KEY', + 'captchaEndpoint' => 'https://api.friendlycaptcha.com/api/v1/siteverify', + 'puzzle' => 'https://api.friendlycaptcha.com/api/v1/puzzle', + 'captchaEnabled' => true, + 'status' => 200 + ]; + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php b/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php new file mode 100644 index 000000000..b889e59da --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php @@ -0,0 +1,293 @@ +setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + $response = $this->render([], [ + 'serviceId' => '2' + ], []); + $expectedResponse = [ + 'offices' => [ + [ + 'id' => '9999999', + 'name' => 'Unittest Source Dienstleister 2', + ] + ] + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } + + public function testRenderingRequestRelation() + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + $response = $this->render([], [ + 'serviceId' => '1' + ], []); + $expectedResponse = [ + 'offices' => [ + [ + 'id' => '9999998', + 'name' => 'Unittest Source Dienstleister', + ], + [ + 'id' => '9999999', + 'name' => 'Unittest Source Dienstleister 2', + ] + ] + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } + + public function testRenderingMulti() + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + $response = $this->render([], [ + 'serviceId' => '1,2' + ], []); + $expectedResponse = [ + 'offices' => [ + [ + 'id' => '9999998', + 'name' => 'Unittest Source Dienstleister', + ], + [ + 'id' => '9999999', + 'name' => 'Unittest Source Dienstleister 2', + ] + ] + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } + + public function testServiceNotFound() + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + + $response = $this->render([], [ + 'serviceId' => '99999999' + ], []); + + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'officesNotFound', + 'errorMessage' => 'Office(s) not found for the provided serviceId(s).', + 'status' => 404, + ] + ], + 'status' => 404 + ]; + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + + } + + public function testNoServiceIdProvided() + { + $response = $this->render([], [], []); + + $expectedResponse = [ + 'errors' => [ + [ + 'offices' => [], + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400 + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + + } + + public function testPartialResultsWithWarning() + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + $response = $this->render([], [ + 'serviceId' => '2,99999999' + ], []); + $expectedResponse = [ + 'offices' => [ + [ + 'id' => '9999999', + 'name' => 'Unittest Source Dienstleister 2', + ] + ], + 'warning' => 'The following serviceId(s) were not found: 99999999' + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + + } + + public function testPartialResultsWithWarningRequestRelation() + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + $response = $this->render([], [ + 'serviceId' => '1,99999999' + ], []); + $expectedResponse = [ + 'offices' => [ + [ + 'id' => '9999998', + 'name' => 'Unittest Source Dienstleister', + ], + [ + 'id' => '9999999', + 'name' => 'Unittest Source Dienstleister 2', + ] + ], + 'warning' => 'The following serviceId(s) were not found: 99999999' + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + + } + + public function testDuplicateServiceIds() + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + $response = $this->render([], [ + 'serviceId' => '2,2' + ], []); + $expectedResponse = [ + 'offices' => [ + [ + 'id' => '9999999', + 'name' => 'Unittest Source Dienstleister 2', + ] + ] + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } + + public function testDuplicateServiceIdsCombinable() + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + $response = $this->render([], [ + 'serviceId' => '1,1' + ], []); + $expectedResponse = [ + 'offices' => [ + [ + 'id' => '9999998', + 'name' => 'Unittest Source Dienstleister', + ], + [ + 'id' => '9999999', + 'name' => 'Unittest Source Dienstleister 2', + ] + ] + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + + } + + public function testInvalidServiceId() + { + $response = $this->render([], [ + 'serviceId' => 'blahblahblah' + ], []); + + $expectedResponse = [ + 'errors' => [ + [ + 'offices' => [], + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400 + ] + ], + "status" => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } + +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/OfficesListTest.php b/zmscitizenapi/tests/Zmscitizenapi/OfficesListTest.php new file mode 100644 index 000000000..d04746041 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/OfficesListTest.php @@ -0,0 +1,74 @@ +setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json") + ] + ]); + + $response = $this->render(); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + "offices" => [ + [ + "id" => "9999998", + "name" => "Unittest Source Dienstleister", + "scope" => [ + "id" => "1", + "provider" => [ + '$schema' => "https://schema.berlin.de/queuemanagement/provider.json", + "id" => "9999998", + "source" => "unittest" + ], + "shortName" => "Scope 1", + "telephoneActivated" => "1", + "telephoneRequired" => "0", + "customTextfieldActivated" => "1", + "customTextfieldRequired" => "0", + "customTextfieldLabel" => "Custom Label", + "captchaActivatedRequired" => "1", + "displayInfo" => null + ] + ], + [ + "id" => "9999999", + "name" => "Unittest Source Dienstleister 2", + "scope" => [ + "id" => "2", + "provider" => [ + '$schema' => "https://schema.berlin.de/queuemanagement/provider.json", + "id" => "9999999", + "source" => "unittest" + ], + "shortName" => "Scope 2", + "telephoneActivated" => "0", + "telephoneRequired" => "1", + "customTextfieldActivated" => "0", + "customTextfieldRequired" => "1", + "customTextfieldLabel" => "", + "captchaActivatedRequired" => "0", + "displayInfo" => null + ] + ] + ], + "status" => 200 + ]; + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/OfficesServicesRelationsTest.php b/zmscitizenapi/tests/Zmscitizenapi/OfficesServicesRelationsTest.php new file mode 100644 index 000000000..965dc9e33 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/OfficesServicesRelationsTest.php @@ -0,0 +1,107 @@ +setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json") + ] + ]); + + $response = $this->render(); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + "offices" => [ + [ + "id" => "9999998", + "name" => "Unittest Source Dienstleister", + "scope" => [ + "id" => "1", + "provider" => [ + '$schema' => "https://schema.berlin.de/queuemanagement/provider.json", + "id" => "9999998", + "source" => "unittest" + ], + "shortName" => "Scope 1", + "telephoneActivated" => "1", + "telephoneRequired" => "0", + "customTextfieldActivated" => "1", + "customTextfieldRequired" => "0", + "customTextfieldLabel" => "Custom Label", + "captchaActivatedRequired" => "1", + "displayInfo" => null + ] + ], + [ + "id" => "9999999", + "name" => "Unittest Source Dienstleister 2", + "scope" => [ + "id" => "2", + "provider" => [ + '$schema' => "https://schema.berlin.de/queuemanagement/provider.json", + "id" => "9999999", + "source" => "unittest" + ], + "shortName" => "Scope 2", + "telephoneActivated" => "0", + "telephoneRequired" => "1", + "customTextfieldActivated" => "0", + "customTextfieldRequired" => "1", + "customTextfieldLabel" => "", + "captchaActivatedRequired" => "0", + "displayInfo" => null + ] + ] + ], + "services" => [ + [ + "id" => "1", + "name" => "Unittest Source Dienstleistung", + "maxQuantity" => 1 + ], + [ + "id" => "2", + "name" => "Unittest Source Dienstleistung 2", + "maxQuantity" => 1, + "combinable" => [ + "1" => ["9999999"], + "2" => ["9999999"] + ] + ] + ], + "relations" => [ + [ + "officeId" => "9999998", + "serviceId" => "1", + "slots" => 2 + ], + [ + "officeId" => "9999999", + "serviceId" => "1", + "slots" => 1 + ], + [ + "officeId" => "9999999", + "serviceId" => "2", + "slots" => 1 + ] + ], + "status" => 200 + ]; + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php new file mode 100644 index 000000000..10557ec2e --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php @@ -0,0 +1,257 @@ +setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + $response = $this->render([], [ + 'scopeId' => '1' + ], []); + $expectedResponse = [ + [ + 'id' => '1', + 'provider' => [ + '$schema' => 'https://schema.berlin.de/queuemanagement/provider.json', + 'id' => '9999998', + 'source' => 'unittest', + ], + 'shortName' => 'Scope 1', + 'telephoneActivated' => '1', + 'telephoneRequired' => '0', + 'customTextfieldActivated' => '1', + 'customTextfieldRequired' => '0', + 'customTextfieldLabel' => 'Custom Label', + 'captchaActivatedRequired' => '1', + 'displayInfo' => null + ] + ]; + $responseBody = json_decode((string) $response->getBody(), true); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testRenderingMulti() + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + $response = $this->render([], [ + 'scopeId' => '1,2' + ], []); + $expectedResponse = [ + [ + 'id' => '1', + 'provider' => [ + '$schema' => 'https://schema.berlin.de/queuemanagement/provider.json', + 'id' => '9999998', + 'source' => 'unittest', + ], + 'shortName' => 'Scope 1', + 'telephoneActivated' => '1', + 'telephoneRequired' => '0', + 'customTextfieldActivated' => '1', + 'customTextfieldRequired' => '0', + 'customTextfieldLabel' => 'Custom Label', + 'captchaActivatedRequired' => '1', + 'displayInfo' => null + ], + [ + 'id' => '2', + 'provider' => [ + '$schema' => 'https://schema.berlin.de/queuemanagement/provider.json', + 'id' => '9999999', + 'source' => 'unittest', + ], + 'shortName' => 'Scope 2', + 'telephoneActivated' => '0', + 'telephoneRequired' => '1', + 'customTextfieldActivated' => '0', + 'customTextfieldRequired' => '1', + 'customTextfieldLabel' => '', + 'captchaActivatedRequired' => '0', + 'displayInfo' => null + ] + ]; + $responseBody = json_decode((string) $response->getBody(), true); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testScopeNotFound() + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + + $response = $this->render([], [ + 'scopeId' => '99' + ], []); + + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => "scopesNotFound", + 'errorMessage' => 'Scope(s) not found.', + 'status' => 404 + ] + ], + 'status' => 404 + ]; + + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + + } + + public function testNoScopeIdProvided() + { + $response = $this->render([], [], []); + $expectedResponse = [ + 'errors' => [ + [ + 'services' => [], + 'errorCode' => 'invalidScopeId', + 'errorMessage' => "scopeId should be a 32-bit integer.", + 'status' => 400 + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + + } + + public function testPartialResultsWithWarning() //Don't return invalid scopes thta don't exist like 99 + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + $response = $this->render([], [ + 'scopeId' => '1,99' + ], []); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + [ + 'id' => '1', + 'provider' => [ + '$schema' => 'https://schema.berlin.de/queuemanagement/provider.json', + 'id' => '9999998', + 'source' => 'unittest', + ], + 'shortName' => 'Scope 1', + 'telephoneActivated' => '1', + 'telephoneRequired' => '0', + 'customTextfieldActivated' => '1', + 'customTextfieldRequired' => '0', + 'customTextfieldLabel' => 'Custom Label', + 'captchaActivatedRequired' => '1', + 'displayInfo' => null + ] + ]; + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + + public function testDuplicateScopeIds() + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + + $response = $this->render([], [ + 'scopeId' => '1,1,1' + ], []); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + [ + 'id' => '1', + 'provider' => [ + '$schema' => 'https://schema.berlin.de/queuemanagement/provider.json', + 'id' => '9999998', + 'source' => 'unittest', + ], + 'shortName' => 'Scope 1', + 'telephoneActivated' => '1', + 'telephoneRequired' => '0', + 'customTextfieldActivated' => '1', + 'customTextfieldRequired' => '0', + 'customTextfieldLabel' => 'Custom Label', + 'captchaActivatedRequired' => '1', + 'displayInfo' => null + ] + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + + public function testInvalidScopeId() + { + $response = $this->render([], [ + 'scopeId' => 'blahblahblah' + ], []); + + $expectedResponse = [ + 'errors' => [ + [ + 'scopes' => [], + 'errorCode' => 'invalidScopeId', + 'errorMessage' => 'scopeId should be a 32-bit integer.', + 'status' => 400 + ] + ], + "status" => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } + +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/ScopesListTest.php b/zmscitizenapi/tests/Zmscitizenapi/ScopesListTest.php new file mode 100644 index 000000000..d1b019f42 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/ScopesListTest.php @@ -0,0 +1,66 @@ +setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json") + ] + ]); + + $response = $this->render(); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + "scopes" => [ + [ + "id" => "1", + "provider" => [ + '$schema' => "https://schema.berlin.de/queuemanagement/provider.json", + "id" => "9999998", + "source" => "unittest" + ], + "shortName" => "Scope 1", + "telephoneActivated" => "1", + "telephoneRequired" => "0", + "customTextfieldActivated" => "1", + "customTextfieldRequired" => "0", + "customTextfieldLabel" => "Custom Label", + "captchaActivatedRequired" => "1", + "displayInfo" => null + ], + [ + "id" => "2", + "provider" => [ + '$schema' => "https://schema.berlin.de/queuemanagement/provider.json", + "id" => "9999999", + "source" => "unittest" + ], + "shortName" => "Scope 2", + "telephoneActivated" => "0", + "telephoneRequired" => "1", + "customTextfieldActivated" => "0", + "customTextfieldRequired" => "1", + "customTextfieldLabel" => "", + "captchaActivatedRequired" => "0", + "displayInfo" => null + ] + ], + "status" => 200, + ]; + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php b/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php new file mode 100644 index 000000000..348d7fe1d --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php @@ -0,0 +1,184 @@ +setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + $response = $this->render([], [ + 'officeId' => '9999998' + ], []); + $expectedResponse = [ + 'services' => [ + [ + 'id' => '1', + 'name' => 'Unittest Source Dienstleistung', + 'maxQuantity' => 1, + ] + ] + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } + + public function testRenderingMulti() + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + $response = $this->render([], [ + 'officeId' => '9999998,9999999' + ], []); + $expectedResponse = [ + 'services' => [ + [ + 'id' => '1', + 'name' => 'Unittest Source Dienstleistung', + 'maxQuantity' => 1, + ], + [ + 'id' => '2', + 'name' => 'Unittest Source Dienstleistung 2', + 'maxQuantity' => 1, + ] + ] + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } + + + public function testServiceNotFound() + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + + $response = $this->render([], [ + 'officeId' => '99999999' + ], []); + + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'servicesNotFound', + 'errorMessage' => 'Service(s) not found for the provided officeId(s).', + 'status' => 404 + ] + ], + 'status' => 404 + ]; + + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + + } + + public function testNoOfficeIdProvided() + { + $response = $this->render([], [], []); + $expectedResponse = [ + 'errors' => [ + [ + 'services' => [], + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400 + ] + ], + 'status' => 400 + ]; + + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + + } + + public function testPartialResultsWithWarning() + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + $response = $this->render([], [ + 'officeId' => '9999998,99999999' + ], []); + $expectedResponse = [ + 'services' => [ + [ + 'id' => '1', + 'name' => 'Unittest Source Dienstleistung', + 'maxQuantity' => 1, + ] + ], + 'warning' => 'The following officeId(s) were not found: 99999999' + ]; + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + + } + + public function testDuplicateOfficeIds() + { + $this->setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + ] + ]); + $response = $this->render([], [ + 'officeId' => '9999998,9999998' + ], []); + $expectedResponse = [ + 'services' => [ + [ + 'id' => '1', + 'name' => 'Unittest Source Dienstleistung', + 'maxQuantity' => 1, + ] + ] + ]; + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/ServicesListTest.php b/zmscitizenapi/tests/Zmscitizenapi/ServicesListTest.php new file mode 100644 index 000000000..dc4911ea4 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/ServicesListTest.php @@ -0,0 +1,43 @@ +setApiCalls([ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_SourceGet_dldb.json") + ] + ]); + + $response = $this->render(); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + "services" => [ + [ + "id" => "1", + "name" => "Unittest Source Dienstleistung", + "maxQuantity" => 1 + ], + [ + "id" => "2", + "name" => "Unittest Source Dienstleistung 2", + "maxQuantity" => 1 + ] + ], + "status" => 200, + ]; + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/bootstrap.php b/zmscitizenapi/tests/Zmscitizenapi/bootstrap.php new file mode 100644 index 000000000..f3a67cbae --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/bootstrap.php @@ -0,0 +1,10 @@ +Es empfiehlt sich, die Adresse in sämtlichen Ausweisdokumenten gleichzeitig mit der Anmeldung zu ändern.

", + "links": [ + { + "name": "Datenschutzgrundverordnung", + "link": "https://stadt.muenchen.de/infos/dsgvo-datenschutzgrundverordnung.html", + "description": false + } + ], + "forms": [ + { + "name": "Datenschutzgrundverordnung", + "link": "https://stadt.muenchen.de/infos/dsgvo-datenschutzgrundverordnung.html", + "description": false + } + ], + "name": "Adressänderung Personalausweis, Reisepass, eAT", + "appointment": { + "link": "https://zms-demo.muenchen.de/buergeransicht/#/services/10242339" + }, + "maxQuantity": 5, + "public": true, + "fees": "gebührenfrei", + "duration": 5 + } + } + ] + } +} \ No newline at end of file diff --git a/zmsclient/phpunit.xml b/zmsclient/phpunit.xml index 3857e96b5..93362f937 100644 --- a/zmsclient/phpunit.xml +++ b/zmsclient/phpunit.xml @@ -10,7 +10,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" forceCoversAnnotation="false" - stopOnFailure="true" + stopOnFailure="false" verbose="true" processIsolation="false" > diff --git a/zmsdb/paratest.xml b/zmsdb/paratest.xml index 082572423..440f4821e 100644 --- a/zmsdb/paratest.xml +++ b/zmsdb/paratest.xml @@ -7,7 +7,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="true" - stopOnFailure="true" + stopOnFailure="false" syntaxCheck="false" bootstrap="tests/Zmsdb/bootstrap.php" > diff --git a/zmsdldb/phpunit.xml.dist b/zmsdldb/phpunit.xml.dist index a025a9a30..a744641f1 100644 --- a/zmsdldb/phpunit.xml.dist +++ b/zmsdldb/phpunit.xml.dist @@ -7,7 +7,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="true" + stopOnFailure="false" bootstrap="tests/bootstrap.php" > diff --git a/zmsentities/schema/citizenapi/appointment.json b/zmsentities/schema/citizenapi/appointment.json new file mode 100644 index 000000000..f34a3d0ae --- /dev/null +++ b/zmsentities/schema/citizenapi/appointment.json @@ -0,0 +1,506 @@ +{ + "type": "object", + "description": "Includes a message that indicates the result or status of an appointment-related operation.", + "properties": { + "processId": { + "type": "string" + }, + "timestamp": { + "type": "number" + }, + "authKey": { + "type": "string" + }, + "familyName": { + "type": "string" + }, + "customTextfield": { + "type": "string" + }, + "email": { + "type": "string", + "pattern": "^[a-zA-Z0-9_\\-\\.]{2,}@[a-zA-Z0-9_\\-\\.]{2,}\\.[a-z]{2,}$|^$" + }, + "telephone": { + "type": "string" + }, + "officeName": { + "type": "string" + }, + "officeId": { + "type": "string" + }, + "scope": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + }, + "contact": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "street": { + "type": "string" + }, + "email": { + "type": "string" + }, + "country": { + "type": "string" + } + }, + "required": [ + "name", + "street", + "email", + "country" + ] + }, + "provider": { + "type": "object", + "properties": { + "contact": { + "type": "object", + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "name": { + "type": "string" + }, + "postalCode": { + "type": "string" + }, + "region": { + "type": "string" + }, + "street": { + "type": "string" + }, + "streetNumber": { + "type": "string" + } + }, + "required": [ + "city", + "country", + "name", + "postalCode", + "region", + "street", + "streetNumber" + ] + }, + "id": { + "type": "string" + }, + "link": { + "type": "string" + }, + "name": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "contact", + "id", + "link", + "name", + "displayName", + "source" + ] + }, + "hint": { + "type": "string" + }, + "lastChange": { + "type": "number" + }, + "preferences": { + "type": "object", + "properties": { + "appointment": { + "type": "object", + "properties": { + "deallocationDuration": { + "type": "string" + }, + "infoForAppointment": { + "type": "string" + }, + "endInDaysDefault": { + "type": "string" + }, + "multipleSlotsEnabled": { + "type": "string" + }, + "reservationDuration": { + "type": "string" + }, + "activationDuration": { + "type": "string" + }, + "startInDaysDefault": { + "type": "string" + }, + "notificationConfirmationEnabled": { + "type": "string" + }, + "notificationHeadsUpEnabled": { + "type": "string" + } + }, + "required": [ + "deallocationDuration", + "infoForAppointment", + "endInDaysDefault", + "multipleSlotsEnabled", + "reservationDuration", + "activationDuration", + "startInDaysDefault", + "notificationConfirmationEnabled", + "notificationHeadsUpEnabled" + ] + }, + "client": { + "type": "object", + "properties": { + "alternateAppointmentUrl": { + "type": "string" + }, + "amendmentActivated": { + "type": "string" + }, + "amendmentLabel": { + "type": "string" + }, + "emailFrom": { + "type": "string" + }, + "emailRequired": { + "type": "string" + }, + "emailConfirmationActivated": { + "type": "string" + }, + "telephoneActivated": { + "type": "string" + }, + "telephoneRequired": { + "type": "number" + }, + "appointmentsPerMail": { + "type": "string" + }, + "slotsPerAppointment": { + "type": "string" + }, + "customTextfieldActivated": { + "type": "string" + }, + "customTextfieldRequired": { + "type": "string" + }, + "customTextfieldLabel": { + "type": "string" + }, + "captchaActivatedRequired": { + "type": "string" + }, + "adminMailOnAppointment": { + "type": "number" + }, + "adminMailOnDeleted": { + "type": "number" + }, + "adminMailOnUpdated": { + "type": "number" + }, + "adminMailOnMailSent": { + "type": "number" + } + }, + "required": [ + "alternateAppointmentUrl", + "amendmentActivated", + "amendmentLabel", + "emailFrom", + "emailRequired", + "emailConfirmationActivated", + "telephoneActivated", + "telephoneRequired", + "appointmentsPerMail", + "slotsPerAppointment", + "customTextfieldActivated", + "customTextfieldRequired", + "customTextfieldLabel", + "captchaActivatedRequired", + "adminMailOnAppointment", + "adminMailOnDeleted", + "adminMailOnUpdated", + "adminMailOnMailSent" + ] + }, + "notifications": { + "type": "object", + "properties": { + "confirmationContent": { + "type": "string" + }, + "headsUpContent": { + "type": "string" + }, + "headsUpTime": { + "type": "string" + } + }, + "required": [ + "confirmationContent", + "headsUpContent", + "headsUpTime" + ] + }, + "pickup": { + "type": "object", + "properties": { + "alternateName": { + "type": "string" + }, + "isDefault": { + "type": "string" + } + }, + "required": [ + "alternateName", + "isDefault" + ] + }, + "queue": { + "type": "object", + "properties": { + "callCountMax": { + "type": "string" + }, + "callDisplayText": { + "type": "string" + }, + "firstNumber": { + "type": "string" + }, + "lastNumber": { + "type": "string" + }, + "maxNumberContingent": { + "type": "string" + }, + "processingTimeAverage": { + "type": "string" + }, + "publishWaitingTimeEnabled": { + "type": "string" + }, + "statisticsEnabled": { + "type": "string" + } + }, + "required": [ + "callCountMax", + "callDisplayText", + "firstNumber", + "lastNumber", + "maxNumberContingent", + "processingTimeAverage", + "publishWaitingTimeEnabled", + "statisticsEnabled" + ] + }, + "survey": { + "type": "object", + "properties": { + "emailContent": { + "type": "string" + }, + "enabled": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "emailContent", + "enabled", + "label" + ] + }, + "ticketprinter": { + "type": "object", + "properties": { + "buttonName": { + "type": "string" + }, + "confirmationEnabled": { + "type": "string" + }, + "deactivatedText": { + "type": "string" + }, + "notificationsAmendmentEnabled": { + "type": "string" + }, + "notificationsEnabled": { + "type": "string" + }, + "notificationsDelay": { + "type": "string" + } + }, + "required": [ + "buttonName", + "confirmationEnabled", + "deactivatedText", + "notificationsAmendmentEnabled", + "notificationsEnabled", + "notificationsDelay" + ] + }, + "workstation": { + "type": "object", + "properties": { + "emergencyEnabled": { + "type": "string" + }, + "emergencyRefreshInterval": { + "type": "string" + } + }, + "required": [ + "emergencyEnabled", + "emergencyRefreshInterval" + ] + } + }, + "required": [ + "appointment", + "client", + "notifications", + "pickup", + "queue", + "survey", + "ticketprinter", + "workstation" + ] + }, + "shortName": { + "type": "string" + }, + "status": { + "type": "object", + "properties": { + "emergency": { + "type": "object", + "properties": { + "activated": { + "type": "string" + } + }, + "required": [ + "activated" + ] + }, + "queue": { + "type": "object", + "properties": { + "ghostWorkstationCount": { + "type": "string" + }, + "givenNumberCount": { + "type": "string" + }, + "lastGivenNumber": { + "type": "string" + }, + "lastGivenNumberTimestamp": { + "type": "number" + } + }, + "required": [ + "ghostWorkstationCount", + "givenNumberCount", + "lastGivenNumber", + "lastGivenNumberTimestamp" + ] + }, + "ticketprinter": { + "type": "object", + "properties": { + "deactivated": { + "type": "string" + } + }, + "required": [ + "deactivated" + ] + } + }, + "required": [ + "emergency", + "queue", + "ticketprinter" + ] + } + }, + "required": [ + "id", + "source", + "contact", + "provider", + "hint", + "lastChange", + "preferences", + "shortName", + "status" + ] + }, + "subRequestCounts": { + "type": "array", + "items": {} + }, + "serviceId": { + "type": "string" + }, + "serviceCount": { + "type": "number" + } + }, + "required": [ + "processId", + "timestamp", + "authKey", + "familyName", + "customTextfield", + "email", + "telephone", + "officeName", + "officeId", + "scope", + "subRequestCounts", + "serviceId", + "serviceCount" + ] +} \ No newline at end of file diff --git a/zmsentities/schema/citizenapi/appointmentCancel.json b/zmsentities/schema/citizenapi/appointmentCancel.json new file mode 100644 index 000000000..49ed26c25 --- /dev/null +++ b/zmsentities/schema/citizenapi/appointmentCancel.json @@ -0,0 +1,16 @@ +{ + "type": "object", + "description": "Includes a message indicating the result of a canceled appointment request.", + "properties": { + "message": { + "type": "string", + "description": "A human-readable message describing the cancellation result", + "minLength": 1, + "maxLength": 1000, + "pattern": "^[\\p{L}\\p{N}\\s.,!?()-]+$" + } + }, + "required": [ + "message" + ] +} \ No newline at end of file diff --git a/zmsentities/schema/citizenapi/appointmentConfirm.json b/zmsentities/schema/citizenapi/appointmentConfirm.json new file mode 100644 index 000000000..f77648b3a --- /dev/null +++ b/zmsentities/schema/citizenapi/appointmentConfirm.json @@ -0,0 +1,10 @@ +{ + "type": "object", + "description": "Contains a message confirming the successful confirmation of an appointment.", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"] +} diff --git a/zmsentities/schema/citizenapi/appointmentPreconfirm.json b/zmsentities/schema/citizenapi/appointmentPreconfirm.json new file mode 100644 index 000000000..e2537a038 --- /dev/null +++ b/zmsentities/schema/citizenapi/appointmentPreconfirm.json @@ -0,0 +1,10 @@ +{ + "type": "object", + "description": "Provides a message indicating the successful pre-confirmation of an appointment.", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"] +} diff --git a/zmsentities/schema/citizenapi/appointmentReserve.json b/zmsentities/schema/citizenapi/appointmentReserve.json new file mode 100644 index 000000000..001ee30b8 --- /dev/null +++ b/zmsentities/schema/citizenapi/appointmentReserve.json @@ -0,0 +1,13 @@ +{ + "type": "object", + "description": "Includes a message confirming that an appointment has been successfully reserved.", + "properties": { + "message": { + "type": "string", + "description": "Confirmation message for the appointment reservation", + "minLength": 1, + "maxLength": 500 + } + }, + "required": ["message"] +} diff --git a/zmsentities/schema/citizenapi/appointmentUpdate.json b/zmsentities/schema/citizenapi/appointmentUpdate.json new file mode 100644 index 000000000..2a68fad92 --- /dev/null +++ b/zmsentities/schema/citizenapi/appointmentUpdate.json @@ -0,0 +1,10 @@ +{ + "type": "object", + "description": "Provides a message indicating the result of an appointment update operation.", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"] +} diff --git a/zmsentities/schema/citizenapi/availableAppointments.json b/zmsentities/schema/citizenapi/availableAppointments.json new file mode 100644 index 000000000..d3c5fdecf --- /dev/null +++ b/zmsentities/schema/citizenapi/availableAppointments.json @@ -0,0 +1,19 @@ +{ + "type": "object", + "description": "Lists available appointment timestamps and the last modification time for the appointment data.", + "properties": { + "appointmentTimestamps": { + "type": "array", + "items": { + "type": "number" + } + }, + "lastModified": { + "type": "number" + } + }, + "required": [ + "appointmentTimestamps", + "lastModified" + ] +} diff --git a/zmsentities/schema/citizenapi/availableDays.json b/zmsentities/schema/citizenapi/availableDays.json new file mode 100644 index 000000000..35924d7e0 --- /dev/null +++ b/zmsentities/schema/citizenapi/availableDays.json @@ -0,0 +1,22 @@ +{ + "type": "object", + "description": "Contains available days for scheduling appointments and the last modification time for this data.", + "properties": { + "availableDays": { + "type": "array", + "items": { + "type": "string", + "format": "date", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "description": "Date in YYYY-MM-DD format" + } + }, + "lastModified": { + "type": "number" + } + }, + "required": [ + "availableDays", + "lastModified" + ] +} diff --git a/zmsentities/schema/citizenapi/captchaDetails.json b/zmsentities/schema/citizenapi/captchaDetails.json new file mode 100644 index 000000000..970059275 --- /dev/null +++ b/zmsentities/schema/citizenapi/captchaDetails.json @@ -0,0 +1,27 @@ +{ + "type": "object", + "description": "Provides CAPTCHA configuration details including site key, endpoint, puzzle status, and CAPTCHA enablement.", + "properties": { + "siteKey": { + "type": "string" + }, + "captchaEndpoint": { + "type": "string", + "format": "uri", + "pattern": "^https?://.+", + "description": "The CAPTCHA verification endpoint URL (must be HTTP/HTTPS)" + }, + "puzzle": { + "type": "boolean" + }, + "captchaEnabled": { + "type": "boolean" + } + }, + "required": [ + "siteKey", + "captchaEndpoint", + "puzzle", + "captchaEnabled" + ] +} \ No newline at end of file diff --git a/zmsentities/schema/citizenapi/offices.json b/zmsentities/schema/citizenapi/offices.json new file mode 100644 index 000000000..fd5d9828c --- /dev/null +++ b/zmsentities/schema/citizenapi/offices.json @@ -0,0 +1,86 @@ +{ + "type": "object", + "description": "Lists all offices and includes details, such as their IDs, names, and configuration options.", + "properties": { + "offices": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "scope": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "provider": { + "type": "object", + "properties": { + "$schema": { + "type": "string" + }, + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "$schema", + "id", + "source" + ] + }, + "shortName": { + "type": "string" + }, + "telephoneActivated": { + "type": "string" + }, + "telephoneRequired": { + "type": "number" + }, + "customTextfieldActivated": { + "type": "string" + }, + "customTextfieldRequired": { + "type": "number" + }, + "customTextfieldLabel": { + "type": "string" + }, + "captchaActivatedRequired": { + "type": "string" + } + }, + "required": [ + "id", + "provider", + "shortName", + "telephoneActivated", + "telephoneRequired", + "customTextfieldActivated", + "customTextfieldRequired", + "customTextfieldLabel", + "captchaActivatedRequired" + ] + } + }, + "required": [ + "id", + "name" + ] + } + } + }, + "required": [ + "offices" + ] +} diff --git a/zmsentities/schema/citizenapi/officesAndServices.json b/zmsentities/schema/citizenapi/officesAndServices.json new file mode 100644 index 000000000..366869f04 --- /dev/null +++ b/zmsentities/schema/citizenapi/officesAndServices.json @@ -0,0 +1,160 @@ +{ + "type": "object", + "description": "Contains information about offices, services, and their relations. Includes details about each office, available services, and how they are related.", + "properties": { + "offices": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "scope": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "provider": { + "type": "object", + "properties": { + "$schema": { + "type": "string" + }, + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "$schema", + "id", + "source" + ] + }, + "shortName": { + "type": "string" + }, + "telephoneActivated": { + "type": "string" + }, + "telephoneRequired": { + "type": "number" + }, + "customTextfieldActivated": { + "type": "string" + }, + "customTextfieldRequired": { + "type": "number" + }, + "customTextfieldLabel": { + "type": "string" + }, + "captchaActivatedRequired": { + "type": "string" + } + }, + "required": [ + "id", + "provider", + "shortName", + "telephoneActivated", + "telephoneRequired", + "customTextfieldActivated", + "customTextfieldRequired", + "customTextfieldLabel", + "captchaActivatedRequired" + ] + } + }, + "required": [ + "id", + "name" + ] + } + }, + "services": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "maxQuantity": { + "type": "number" + }, + "combinable": { + "type": "object", + "properties": { + "10300808": { + "type": "array", + "items": { + "type": "string" + } + }, + "10300814": { + "type": "array", + "items": { + "type": "string" + } + }, + "10300817": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "10300814" + ] + } + }, + "required": [ + "id", + "name", + "maxQuantity" + ] + } + }, + "relations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "officeId": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "serviceId": { + "type": "string", + "pattern": "^[0-9]+$" + }, + "slots": { + "type": "number" + } + }, + "required": [ + "officeId", + "serviceId", + "slots" + ] + } + } + }, + "required": [ + "offices", + "services", + "relations" + ] +} diff --git a/zmsentities/schema/citizenapi/officesByService.json b/zmsentities/schema/citizenapi/officesByService.json new file mode 100644 index 000000000..f735809f6 --- /dev/null +++ b/zmsentities/schema/citizenapi/officesByService.json @@ -0,0 +1,27 @@ +{ + "type": "object", + "description": "Lists offices associated with specific services.", + "properties": { + "offices": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ] + } + } + }, + "required": [ + "offices" + ] +} diff --git a/zmsentities/schema/citizenapi/scope.json b/zmsentities/schema/citizenapi/scope.json new file mode 100644 index 000000000..36e188a73 --- /dev/null +++ b/zmsentities/schema/citizenapi/scope.json @@ -0,0 +1,68 @@ +{ + "type": "object", + "description": "Defines the scope, which represents a location or unit for processing client requests, including its configuration and availability options.", + "properties": { + "scopes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "provider": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "id", + "source" + ] + }, + "shortName": { + "type": "string" + }, + "telephoneActivated": { + "type": "string" + }, + "telephoneRequired": { + "type": "number" + }, + "customTextfieldActivated": { + "type": "string" + }, + "customTextfieldRequired": { + "type": "number" + }, + "customTextfieldLabel": { + "type": "string" + }, + "captchaActivatedRequired": { + "type": "string" + } + }, + "required": [ + "id", + "provider", + "shortName", + "telephoneActivated", + "telephoneRequired", + "customTextfieldActivated", + "customTextfieldRequired", + "customTextfieldLabel", + "captchaActivatedRequired" + ] + } + } + }, + "required": [ + "scopes" + ] + } + \ No newline at end of file diff --git a/zmsentities/schema/citizenapi/scopes.json b/zmsentities/schema/citizenapi/scopes.json new file mode 100644 index 000000000..88a84007e --- /dev/null +++ b/zmsentities/schema/citizenapi/scopes.json @@ -0,0 +1,71 @@ +{ + "type": "object", + "description": "Details a list of all scopes, which are locations or units handling client requests, including their configuration and availability options.", + "properties": { + "scopes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "provider": { + "type": "object", + "properties": { + "$schema": { + "type": "string" + }, + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "$schema", + "id", + "source" + ] + }, + "shortName": { + "type": "string" + }, + "telephoneActivated": { + "type": "string" + }, + "telephoneRequired": { + "type": "number" + }, + "customTextfieldActivated": { + "type": "string" + }, + "customTextfieldRequired": { + "type": "number" + }, + "customTextfieldLabel": { + "type": "string" + }, + "captchaActivatedRequired": { + "type": "string" + } + }, + "required": [ + "id", + "provider", + "shortName", + "telephoneActivated", + "telephoneRequired", + "customTextfieldActivated", + "customTextfieldRequired", + "customTextfieldLabel", + "captchaActivatedRequired" + ] + } + } + }, + "required": [ + "scopes" + ] +} diff --git a/zmsentities/schema/citizenapi/services.json b/zmsentities/schema/citizenapi/services.json new file mode 100644 index 000000000..184301797 --- /dev/null +++ b/zmsentities/schema/citizenapi/services.json @@ -0,0 +1,38 @@ +{ + "type": "object", + "description": "Lists all services and includes details, including their IDs, names, and maximum quantities.", + "properties": { + "services": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "pattern": "^[0-9]+$", + "minLength": 1, + "maxLength": 50 + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 200 + }, + "maxQuantity": { + "type": "number", + "minimum": 1, + "maximum": 100 + } + }, + "required": [ + "id", + "name", + "maxQuantity" + ] + } + } + }, + "required": [ + "services" + ] +} diff --git a/zmsentities/schema/citizenapi/servicesByOffice.json b/zmsentities/schema/citizenapi/servicesByOffice.json new file mode 100644 index 000000000..754ff4a38 --- /dev/null +++ b/zmsentities/schema/citizenapi/servicesByOffice.json @@ -0,0 +1,38 @@ +{ + "type": "object", + "description": "Lists services associated with specific offices.", + "properties": { + "services": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the service", + "pattern": "^[A-Za-z0-9_-]+$", + "minLength": 1 + }, + "name": { + "type": "string", + "description": "Display name of the service", + "minLength": 1 + }, + "maxQuantity": { + "type": "number", + "description": "Maximum number of appointments that can be booked for this service", + "minimum": 1 + } + }, + "required": [ + "id", + "name", + "maxQuantity" + ] + } + } + }, + "required": [ + "services" + ] +} diff --git a/zmsentities/src/Zmsentities/Scope.php b/zmsentities/src/Zmsentities/Scope.php index 3238f9769..60d50494b 100644 --- a/zmsentities/src/Zmsentities/Scope.php +++ b/zmsentities/src/Zmsentities/Scope.php @@ -25,6 +25,46 @@ public function getSource() return $this->toProperty()->source->get(); } + public function getShortName() + { + return $this->toProperty()->shortName->get(); + } + + public function getTelephoneActivated() + { + return $this->getPreference('client', 'telephoneActivated', null); + } + + public function getTelephoneRequired() + { + return $this->getPreference('client', 'telephoneRequired', null); + } + + public function getCustomTextfieldActivated() + { + return $this->getPreference('client', 'customTextfieldActivated', null); + } + + public function getCustomTextfieldRequired() + { + return $this->getPreference('client', 'customTextfieldRequired', null); + } + + public function getCustomTextfieldLabel() + { + return $this->getPreference('client', 'customTextfieldLabel', ''); + } + + public function getCaptchaActivatedRequired() + { + return $this->getPreference('client', 'captchaActivatedRequired', null); + } + + public function getDisplayInfo() + { + return $this->getPreference('appointment', 'infoForAppointment', null); + } + public function getProvider() { if (!$this->provider instanceof Provider) { diff --git a/zmsentities/src/Zmsentities/Source.php b/zmsentities/src/Zmsentities/Source.php index b0cfaa397..fe6a8fb30 100644 --- a/zmsentities/src/Zmsentities/Source.php +++ b/zmsentities/src/Zmsentities/Source.php @@ -51,6 +51,18 @@ public function getProviderList() return $providerList; } + public function getScopeList() + { + $scopeList = new Collection\ScopeList(); + foreach ($this->toProperty()->scopes->get() as $scope) { + if (!$scope instanceof Scope) { + $scope = new Scope($scope); + } + $scopeList->addEntity($scope); + } + return $scopeList; + } + public function getRequestList() { $requestList = new Collection\RequestList(); diff --git a/zmsslim/phpunit.xml.dist b/zmsslim/phpunit.xml.dist index 62e86e02a..cacae0417 100644 --- a/zmsslim/phpunit.xml.dist +++ b/zmsslim/phpunit.xml.dist @@ -10,7 +10,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" forceCoversAnnotation="false" - stopOnFailure="true" + stopOnFailure="false" verbose="true" processIsolation="true" >