diff --git a/.gitignore b/.gitignore index b0544dc..869584e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,9 +8,14 @@ test.php tests/data/config.yml tests/data/out/ tests/data/connectionAction/config.yml +tests/data/connectionAction/config.json tests/data/connectionAction/out/ tests/data/runAction/config.yml +tests/data/runAction/config.json tests/data/runAction/out/ +tests/data/getTablesAction/config.yml +tests/data/getTablesAction/config.json +tests/data/getTablesAction/out/ snowflake_linux_x8664_odbc.tgz snowsql-linux_x86_64.bash snowflake-odbc-x86_64.deb diff --git a/.travis.yml b/.travis.yml index 8576999..059d68b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,8 +12,7 @@ install: - docker-compose run --rm drivers-download composer install - docker-compose run --rm drivers-download php driver/downloadDriver.php - docker-compose build app - - docker-compose run --rm app vendor/bin/phpcs --standard=psr2 --ignore=vendor -n . - - docker-compose run --rm app vendor/bin/phpunit + - docker-compose run --rm app script: - docker pull quay.io/keboola/developer-portal-cli-v2:latest diff --git a/Dockerfile b/Dockerfile index d105816..fe22cf3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,11 @@ FROM php:7.1 -MAINTAINER Erik Zigo + +ENV COMPOSER_ALLOW_SUPERUSER=1 ENV DEBIAN_FRONTEND noninteractive # Install Dependencies RUN apt-get update \ - && apt-get install unzip git unixODBC-dev libpq-dev -y + && apt-get install unzip git unixodbc unixodbc-dev libpq-dev -y RUN docker-php-ext-install pdo_pgsql pdo_mysql RUN pecl install xdebug \ diff --git a/composer.json b/composer.json index b54d14d..f2dc02c 100644 --- a/composer.json +++ b/composer.json @@ -6,23 +6,22 @@ "keywords": ["keboola", "snowflake", "db", "extractor"], "authors": [ { - "name": "Erik Žigo", - "email": "erik@keboola.com" + "name": "Keboola", + "email": "devel@keboola.com" } ], "require": { - "php": ">=5.6", - "keboola/db-extractor-common": "~5.2", - "keboola/php-temp": "^0.1.6", - "keboola/php-csv-db-import": "~2.3.0", - "squizlabs/php_codesniffer": "^2.6", - "vkartaviy/retry": "^0.2" + "php": ">=7.1", + "keboola/db-extractor-common": "~9.1", + "keboola/php-temp": "^1.0", + "keboola/php-csv-db-import": "~2.3.0" }, "require-dev": { "phpunit/phpunit": "4.6.*", "codeclimate/php-test-reporter": "^0.3", + "keboola/coding-standard": "^6.0", "keboola/storage-api-client": "^4.14", - "keboola/php-csv-db-import": "~2.3.0" + "phpstan/phpstan-shim": "^0.10" }, "autoload": { "psr-4": { @@ -31,7 +30,26 @@ }, "autoload-dev": { "psr-4": { - "Keboola\\Test\\": "tests/" + "Keboola\\DbExtractor\\Tests\\": "tests/" } + }, + "scripts": { + "tests": "phpunit", + "phpstan": "phpstan analyse --no-progress --level=max -c phpstan.neon ./src ./tests", + "phpcs": "phpcs -n --ignore=vendor --extensions=php .", + "phpcbf": "phpcbf -n --ignore=vendor --extensions=php .", + "build": [ + "@phpcs", + "@phpstan", + "@tests" + ], + "ci": [ + "@composer validate --no-check-all --strict", + "@build" + ] + }, + "config": { + "process-timeout": 0, + "sort-packages": true } } diff --git a/composer.lock b/composer.lock index 2c93f90..dfa6fe0 100644 --- a/composer.lock +++ b/composer.lock @@ -1,23 +1,23 @@ { "_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#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6de760d3b2a19b7030f9868832cb96d7", + "content-hash": "b05532007e3541c75d25904dd83868a5", "packages": [ { "name": "aws/aws-sdk-php", - "version": "3.52.13", + "version": "3.69.4", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "6d2f46b55a9620e41e2fc71eb9993d43ebb352c7" + "reference": "8e66716ee7bf6a4a4881647c6d96aa8619e82152" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6d2f46b55a9620e41e2fc71eb9993d43ebb352c7", - "reference": "6d2f46b55a9620e41e2fc71eb9993d43ebb352c7", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/8e66716ee7bf6a4a4881647c6d96aa8619e82152", + "reference": "8e66716ee7bf6a4a4881647c6d96aa8619e82152", "shasum": "" }, "require": { @@ -38,6 +38,8 @@ "doctrine/cache": "~1.4", "ext-dom": "*", "ext-openssl": "*", + "ext-pcntl": "*", + "ext-sockets": "*", "nette/neon": "^2.3", "phpunit/phpunit": "^4.8.35|^5.4.3", "psr/cache": "^1.0" @@ -46,7 +48,8 @@ "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", "doctrine/cache": "To use the DoctrineCacheAdapter", "ext-curl": "To send requests using cURL", - "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages" + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-sockets": "To use client-side monitoring" }, "type": "library", "extra": { @@ -84,20 +87,20 @@ "s3", "sdk" ], - "time": "2018-02-20T20:46:59+00:00" + "time": "2018-10-10T22:18:53+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "6.3.0", + "version": "6.3.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699" + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699", - "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", "shasum": "" }, "require": { @@ -107,7 +110,7 @@ }, "require-dev": { "ext-curl": "*", - "phpunit/phpunit": "^4.0 || ^5.0", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", "psr/log": "^1.0" }, "suggest": { @@ -116,7 +119,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.2-dev" + "dev-master": "6.3-dev" } }, "autoload": { @@ -149,7 +152,7 @@ "rest", "web service" ], - "time": "2017-06-22T18:50:49+00:00" + "time": "2018-04-22T15:46:56+00:00" }, { "name": "guzzlehttp/promises", @@ -300,34 +303,39 @@ }, { "name": "keboola/db-extractor-common", - "version": "5.3.1", + "version": "9.1.11", "source": { "type": "git", "url": "https://github.com/keboola/db-extractor-common.git", - "reference": "7df4dfb44ec37cd1699bc57349d85f7d10bc9ef0" + "reference": "a67d229d9672158ffb349cb3c5f24029e68bc570" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/keboola/db-extractor-common/zipball/7df4dfb44ec37cd1699bc57349d85f7d10bc9ef0", - "reference": "7df4dfb44ec37cd1699bc57349d85f7d10bc9ef0", + "url": "https://api.github.com/repos/keboola/db-extractor-common/zipball/a67d229d9672158ffb349cb3c5f24029e68bc570", + "reference": "a67d229d9672158ffb349cb3c5f24029e68bc570", "shasum": "" }, "require": { "keboola/csv": "^1.1", - "keboola/php-datatypes": "^2.0.1", + "keboola/php-datatypes": "^3.2", + "keboola/php-utils": "^2.3", "keboola/ssh-tunnel": "^1.0", "monolog/monolog": "^1.17", "nette/utils": "^2.4", - "php": ">=5.6", + "php": ">=7.1", "pimple/pimple": "^3.0", "symfony/config": "^3.0", "symfony/process": "^3.0", - "symfony/yaml": "^3.0" + "symfony/serializer": "^3.1", + "symfony/yaml": "^3.0", + "vkartaviy/retry": "^0.2" }, "require-dev": { + "aws/aws-sdk-php": "^3.67", + "keboola/coding-standard": "^2.0", "phpstan/phpstan": "^0.8", - "phpunit/phpunit": "4.6.*", - "squizlabs/php_codesniffer": "^2.6" + "phpunit/phpunit": "^5.0", + "symfony/finder": "*" }, "type": "library", "autoload": { @@ -336,13 +344,17 @@ } }, "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], "authors": [ { - "name": "Miroslav Cillik", - "email": "miro@keboola.com" + "name": "keboola", + "email": "devel@keboola.com" } ], - "time": "2018-01-29T09:58:19+00:00" + "description": "Common library from Keboola Database Extractors", + "time": "2018-09-22T12:54:02+00:00" }, { "name": "keboola/php-csv-db-import", @@ -377,7 +389,7 @@ "notification-url": "https://packagist.org/downloads/", "authors": [ { - "name": "Martin Halamíček", + "name": "Martin Halamicek", "email": "martin@keboola.com" } ], @@ -385,21 +397,21 @@ }, { "name": "keboola/php-datatypes", - "version": "2.1.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/keboola/php-datatypes.git", - "reference": "4da9c61e8f711123ef3c1e041564557a79c0fcf8" + "reference": "f16967a5da1b67e4a9fb38b07a1b0df3e588abb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/keboola/php-datatypes/zipball/4da9c61e8f711123ef3c1e041564557a79c0fcf8", - "reference": "4da9c61e8f711123ef3c1e041564557a79c0fcf8", + "url": "https://api.github.com/repos/keboola/php-datatypes/zipball/f16967a5da1b67e4a9fb38b07a1b0df3e588abb8", + "reference": "f16967a5da1b67e4a9fb38b07a1b0df3e588abb8", "shasum": "" }, "require-dev": { "codeclimate/php-test-reporter": "~0.4", - "phpstan/phpstan": "~0.6", + "phpstan/phpstan": "~0.9", "phpunit/phpunit": "^5.0", "squizlabs/php_codesniffer": "^2.6" }, @@ -412,24 +424,24 @@ "notification-url": "https://packagist.org/downloads/", "authors": [ { - "name": "Ondrej Hlavacek", + "name": "Ondřej Hlaváček", "email": "ondrej.hlavacek@keboola.com" } ], - "time": "2017-05-30T16:30:33+00:00" + "time": "2018-08-02T15:13:46+00:00" }, { "name": "keboola/php-temp", - "version": "0.1.6", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/keboola/php-temp.git", - "reference": "ffe345e78ba0d35d970fc26fe2c1a36f7d20ca8c" + "reference": "2e3c2fc4cce8536a84cbad2a1586eb2eaebe5d3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/keboola/php-temp/zipball/ffe345e78ba0d35d970fc26fe2c1a36f7d20ca8c", - "reference": "ffe345e78ba0d35d970fc26fe2c1a36f7d20ca8c", + "url": "https://api.github.com/repos/keboola/php-temp/zipball/2e3c2fc4cce8536a84cbad2a1586eb2eaebe5d3b", + "reference": "2e3c2fc4cce8536a84cbad2a1586eb2eaebe5d3b", "shasum": "" }, "require": { @@ -465,7 +477,73 @@ "filesystem", "temp" ], - "time": "2016-03-23T17:48:01+00:00" + "time": "2017-11-13T13:02:19+00:00" + }, + { + "name": "keboola/php-utils", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/keboola/php-utils.git", + "reference": "ec4d582994f2961bd16a6f6be40d3e8608ef5ec3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/keboola/php-utils/zipball/ec4d582994f2961bd16a6f6be40d3e8608ef5ec3", + "reference": "ec4d582994f2961bd16a6f6be40d3e8608ef5ec3", + "shasum": "" + }, + "require": { + "keboola/php-temp": "^1.0", + "php": ">=5.6.0", + "seld/jsonlint": "^1.4" + }, + "require-dev": { + "codeclimate/php-test-reporter": "~0.2", + "phpunit/phpunit": "~5.0", + "squizlabs/php_codesniffer": "~2.4" + }, + "type": "library", + "autoload": { + "files": [ + "src/Keboola/Utils/jsonDecode.php", + "src/Keboola/Utils/sanitizeUtf8.php", + "src/Keboola/Utils/objectToArray.php", + "src/Keboola/Utils/formatDateTime.php", + "src/Keboola/Utils/replaceDates.php", + "src/Keboola/Utils/replaceDatesInArray.php", + "src/Keboola/Utils/buildUrl.php", + "src/Keboola/Utils/httpBuildUrl.php", + "src/Keboola/Utils/returnBytes.php", + "src/Keboola/Utils/camelize.php", + "src/Keboola/Utils/getDataFromPath.php", + "src/Keboola/Utils/isValidDateTimeString.php", + "src/Keboola/Utils/flattenArray.php", + "src/Keboola/Utils/arrayToObject.php", + "src/Keboola/Utils/isEmptyObject.php", + "src/Keboola/Utils/stripInvalidUtf16.php", + "src/Keboola/Utils/toAscii.php", + "src/Keboola/Utils/sanitizeColumnName.php" + ], + "psr-0": { + "Keboola\\Utils": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Keboola", + "email": "devel@keboola.com" + } + ], + "description": "Misc utility functions", + "keywords": [ + "utility" + ], + "time": "2018-04-11T14:40:53+00:00" }, { "name": "keboola/ssh-tunnel", @@ -499,7 +577,7 @@ ], "authors": [ { - "name": "Miroslav Cillik", + "name": "Miroslav Čillík", "email": "miro@keboola.com" } ], @@ -641,16 +719,16 @@ }, { "name": "nette/utils", - "version": "v2.5.1", + "version": "v2.5.3", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "8a85ce76298c8a8941f912b8fa3ee93ca17d2ebc" + "reference": "17b9f76f2abd0c943adfb556e56f2165460b15ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/8a85ce76298c8a8941f912b8fa3ee93ca17d2ebc", - "reference": "8a85ce76298c8a8941f912b8fa3ee93ca17d2ebc", + "url": "https://api.github.com/repos/nette/utils/zipball/17b9f76f2abd0c943adfb556e56f2165460b15ce", + "reference": "17b9f76f2abd0c943adfb556e56f2165460b15ce", "shasum": "" }, "require": { @@ -719,7 +797,7 @@ "utility", "validation" ], - "time": "2018-02-19T14:42:42+00:00" + "time": "2018-09-18T10:22:16+00:00" }, { "name": "pimple/pimple", @@ -918,100 +996,72 @@ "time": "2016-10-10T12:19:37+00:00" }, { - "name": "squizlabs/php_codesniffer", - "version": "2.9.1", + "name": "seld/jsonlint", + "version": "1.7.1", "source": { "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "dcbed1074f8244661eecddfc2a675430d8d33f62" + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/dcbed1074f8244661eecddfc2a675430d8d33f62", - "reference": "dcbed1074f8244661eecddfc2a675430d8d33f62", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/d15f59a67ff805a44c50ea0516d2341740f81a38", + "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38", "shasum": "" }, "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.1.2" + "php": "^5.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, "bin": [ - "scripts/phpcs", - "scripts/phpcbf" + "bin/jsonlint" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, "autoload": { - "classmap": [ - "CodeSniffer.php", - "CodeSniffer/CLI.php", - "CodeSniffer/Exception.php", - "CodeSniffer/File.php", - "CodeSniffer/Fixer.php", - "CodeSniffer/Report.php", - "CodeSniffer/Reporting.php", - "CodeSniffer/Sniff.php", - "CodeSniffer/Tokens.php", - "CodeSniffer/Reports/", - "CodeSniffer/Tokenizers/", - "CodeSniffer/DocGenerators/", - "CodeSniffer/Standards/AbstractPatternSniff.php", - "CodeSniffer/Standards/AbstractScopeSniff.php", - "CodeSniffer/Standards/AbstractVariableSniff.php", - "CodeSniffer/Standards/IncorrectPatternException.php", - "CodeSniffer/Standards/Generic/Sniffs/", - "CodeSniffer/Standards/MySource/Sniffs/", - "CodeSniffer/Standards/PEAR/Sniffs/", - "CodeSniffer/Standards/PSR1/Sniffs/", - "CodeSniffer/Standards/PSR2/Sniffs/", - "CodeSniffer/Standards/Squiz/Sniffs/", - "CodeSniffer/Standards/Zend/Sniffs/" - ] + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Greg Sherwood", - "role": "lead" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "http://www.squizlabs.com/php-codesniffer", + "description": "JSON Linter", "keywords": [ - "phpcs", - "standards" + "json", + "linter", + "parser", + "validator" ], - "time": "2017-05-22T02:43:20+00:00" + "time": "2018-01-24T12:46:19+00:00" }, { "name": "symfony/config", - "version": "v3.4.4", + "version": "v3.4.17", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "72689b934d6c6ecf73eca874e98933bf055313c9" + "reference": "e5389132dc6320682de3643091121c048ff796b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/72689b934d6c6ecf73eca874e98933bf055313c9", - "reference": "72689b934d6c6ecf73eca874e98933bf055313c9", + "url": "https://api.github.com/repos/symfony/config/zipball/e5389132dc6320682de3643091121c048ff796b3", + "reference": "e5389132dc6320682de3643091121c048ff796b3", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8", - "symfony/filesystem": "~2.8|~3.0|~4.0" + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/dependency-injection": "<3.3", @@ -1019,6 +1069,7 @@ }, "require-dev": { "symfony/dependency-injection": "~3.3|~4.0", + "symfony/event-dispatcher": "~3.3|~4.0", "symfony/finder": "~3.3|~4.0", "symfony/yaml": "~3.0|~4.0" }, @@ -1055,24 +1106,25 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-01-21T19:05:02+00:00" + "time": "2018-09-08T13:15:14+00:00" }, { "name": "symfony/filesystem", - "version": "v3.4.4", + "version": "v3.4.17", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "e078773ad6354af38169faf31c21df0f18ace03d" + "reference": "d69930fc337d767607267d57c20a7403d0a822a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/e078773ad6354af38169faf31c21df0f18ace03d", - "reference": "e078773ad6354af38169faf31c21df0f18ace03d", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/d69930fc337d767607267d57c20a7403d0a822a4", + "reference": "d69930fc337d767607267d57c20a7403d0a822a4", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { @@ -1104,20 +1156,78 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-01-03T07:37:34+00:00" + "time": "2018-10-02T12:28:39+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.9.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2018-08-06T14:22:27+00:00" }, { "name": "symfony/process", - "version": "v3.4.4", + "version": "v3.4.17", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "09a5172057be8fc677840e591b17f385e58c7c0d" + "reference": "1dc2977afa7d70f90f3fefbcd84152813558910e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/09a5172057be8fc677840e591b17f385e58c7c0d", - "reference": "09a5172057be8fc677840e591b17f385e58c7c0d", + "url": "https://api.github.com/repos/symfony/process/zipball/1dc2977afa7d70f90f3fefbcd84152813558910e", + "reference": "1dc2977afa7d70f90f3fefbcd84152813558910e", "shasum": "" }, "require": { @@ -1153,24 +1263,104 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-01-29T09:03:43+00:00" + "time": "2018-10-02T12:28:39+00:00" + }, + { + "name": "symfony/serializer", + "version": "v3.4.17", + "source": { + "type": "git", + "url": "https://github.com/symfony/serializer.git", + "reference": "8bc00ef47a428bfebc4641f29d158e7c56137fcb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/serializer/zipball/8bc00ef47a428bfebc4641f29d158e7c56137fcb", + "reference": "8bc00ef47a428bfebc4641f29d158e7c56137fcb", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "phpdocumentor/type-resolver": "<0.2.1", + "symfony/dependency-injection": "<3.2", + "symfony/property-access": ">=3.0,<3.0.4|>=2.8,<2.8.4", + "symfony/property-info": "<3.1", + "symfony/yaml": "<3.4" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/cache": "~1.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0", + "symfony/cache": "~3.1|~4.0", + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.2|~4.0", + "symfony/http-foundation": "~2.8|~3.0|~4.0", + "symfony/property-access": "~2.8|~3.0|~4.0", + "symfony/property-info": "~3.1|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.", + "doctrine/cache": "For using the default cached annotation reader and metadata cache.", + "psr/cache-implementation": "For using the metadata cache.", + "symfony/config": "For using the XML mapping loader.", + "symfony/http-foundation": "To use the DataUriNormalizer.", + "symfony/property-access": "For using the ObjectNormalizer.", + "symfony/property-info": "To deserialize relations.", + "symfony/yaml": "For using the default YAML mapping loader." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Serializer\\": "" + }, + "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": "Symfony Serializer Component", + "homepage": "https://symfony.com", + "time": "2018-10-02T12:28:39+00:00" }, { "name": "symfony/yaml", - "version": "v3.4.4", + "version": "v3.4.17", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "eab73b6c21d27ae4cd037c417618dfd4befb0bfe" + "reference": "640b6c27fed4066d64b64d5903a86043f4a4de7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/eab73b6c21d27ae4cd037c417618dfd4befb0bfe", - "reference": "eab73b6c21d27ae4cd037c417618dfd4befb0bfe", + "url": "https://api.github.com/repos/symfony/yaml/zipball/640b6c27fed4066d64b64d5903a86043f4a4de7f", + "reference": "640b6c27fed4066d64b64d5903a86043f4a4de7f", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/console": "<3.4" @@ -1211,20 +1401,20 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-01-21T19:05:02+00:00" + "time": "2018-10-02T16:33:53+00:00" }, { "name": "tracy/tracy", - "version": "v2.4.11", + "version": "v2.5.3", "source": { "type": "git", "url": "https://github.com/nette/tracy.git", - "reference": "bcb93a9d4347be8779c83b200b64ea6f52d6f9ed" + "reference": "7f670b08a7f4ed0243373f9d003d139196273861" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/tracy/zipball/bcb93a9d4347be8779c83b200b64ea6f52d6f9ed", - "reference": "bcb93a9d4347be8779c83b200b64ea6f52d6f9ed", + "url": "https://api.github.com/repos/nette/tracy/zipball/7f670b08a7f4ed0243373f9d003d139196273861", + "reference": "7f670b08a7f4ed0243373f9d003d139196273861", "shasum": "" }, "require": { @@ -1234,7 +1424,8 @@ }, "require-dev": { "nette/di": "~2.3", - "nette/tester": "~1.7" + "nette/tester": "~1.7", + "nette/utils": "~2.3" }, "suggest": { "https://nette.org/donate": "Please support Tracy via a donation" @@ -1242,7 +1433,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.4-dev" + "dev-master": "2.5-dev" } }, "autoload": { @@ -1276,7 +1467,7 @@ "nette", "profiler" ], - "time": "2018-02-01T18:11:38+00:00" + "time": "2018-09-24T22:35:07+00:00" }, { "name": "vkartaviy/retry", @@ -1387,32 +1578,32 @@ }, { "name": "doctrine/instantiator", - "version": "1.0.5", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": "^7.1" }, "require-dev": { "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "phpunit/phpunit": "^6.2.3", + "squizlabs/php_codesniffer": "^3.0.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -1437,7 +1628,7 @@ "constructor", "instantiate" ], - "time": "2015-06-14T21:17:01+00:00" + "time": "2017-07-22T11:58:36+00:00" }, { "name": "guzzle/guzzle", @@ -1535,6 +1726,32 @@ "abandoned": "guzzlehttp/guzzle", "time": "2015-03-18T18:23:50+00:00" }, + { + "name": "keboola/coding-standard", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/keboola/phpcs-standard.git", + "reference": "4c247e4ecf8f9c134efc6b7efe30593923415655" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/keboola/phpcs-standard/zipball/4c247e4ecf8f9c134efc6b7efe30593923415655", + "reference": "4c247e4ecf8f9c134efc6b7efe30593923415655", + "shasum": "" + }, + "require": { + "slevomat/coding-standard": "4.4.6", + "squizlabs/php_codesniffer": "^3.2" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Keboola coding standard", + "time": "2018-09-03T10:12:38+00:00" + }, { "name": "keboola/storage-api-client", "version": "4.20.1", @@ -1634,29 +1851,35 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "3.3.2", + "version": "4.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2" + "reference": "94fd0001232e47129dd3504189fa1c7225010d08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2", - "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", + "reference": "94fd0001232e47129dd3504189fa1c7225010d08", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", + "php": "^7.0", "phpdocumentor/reflection-common": "^1.0.0", "phpdocumentor/type-resolver": "^0.4.0", "webmozart/assert": "^1.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" + "doctrine/instantiator": "~1.0.5", + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.4" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, "autoload": { "psr-4": { "phpDocumentor\\Reflection\\": [ @@ -1675,7 +1898,7 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-10T14:09:06+00:00" + "time": "2017-11-30T07:14:17+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -1726,33 +1949,33 @@ }, { "name": "phpspec/prophecy", - "version": "1.7.5", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", - "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0", + "sebastian/comparator": "^1.1|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.8.x-dev" } }, "autoload": { @@ -1785,7 +2008,51 @@ "spy", "stub" ], - "time": "2018-02-19T10:16:54+00:00" + "time": "2018-08-05T17:53:17+00:00" + }, + { + "name": "phpstan/phpstan-shim", + "version": "0.10.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-shim.git", + "reference": "1b12b7b684201385c1bd4bdb8e50ff062b33bb5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-shim/zipball/1b12b7b684201385c1bd4bdb8e50ff062b33bb5c", + "reference": "1b12b7b684201385c1bd4bdb8e50ff062b33bb5c", + "shasum": "" + }, + "require": { + "php": "~7.1" + }, + "replace": { + "nikic/php-parser": "^4.0.2", + "phpstan/phpdoc-parser": "^0.3", + "phpstan/phpstan": "self.version" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.10-dev" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan Phar distribution", + "time": "2018-08-12T16:21:59+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2222,6 +2489,7 @@ "github", "test" ], + "abandoned": "php-coveralls/php-coveralls", "time": "2017-10-14T23:15:34+00:00" }, { @@ -2597,107 +2865,141 @@ "time": "2015-06-21T13:59:46+00:00" }, { - "name": "symfony/console", - "version": "v3.4.4", + "name": "slevomat/coding-standard", + "version": "4.4.6", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "26b6f419edda16c19775211987651cb27baea7f1" + "url": "https://github.com/slevomat/coding-standard.git", + "reference": "861e7b55d348c81a9dd0b3655dbbc83076d60c05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/26b6f419edda16c19775211987651cb27baea7f1", - "reference": "26b6f419edda16c19775211987651cb27baea7f1", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/861e7b55d348c81a9dd0b3655dbbc83076d60c05", + "reference": "861e7b55d348c81a9dd0b3655dbbc83076d60c05", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/debug": "~2.8|~3.0|~4.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" + "php": "^7.1", + "squizlabs/php_codesniffer": "^3.0.2" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.3|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.3|~4.0" + "jakub-onderka/php-parallel-lint": "0.9.2", + "phing/phing": "2.16", + "phpstan/phpstan": "0.9.2", + "phpstan/phpstan-phpunit": "0.9.4", + "phpstan/phpstan-strict-rules": "0.9", + "phpunit/php-code-coverage": "6.0.1", + "phpunit/phpunit": "7.0.0" + }, + "type": "phpcodesniffer-standard", + "autoload": { + "psr-4": { + "SlevomatCodingStandard\\": "SlevomatCodingStandard" + } }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "time": "2018-02-15T17:13:28+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "6ad28354c04b364c3c71a34e4a18b629cc3b231e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/6ad28354c04b364c3c71a34e4a18b629cc3b231e", + "reference": "6ad28354c04b364c3c71a34e4a18b629cc3b231e", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "3.x-dev" } }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Greg Sherwood", + "role": "lead" } ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "time": "2018-01-29T09:03:43+00:00" + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "http://www.squizlabs.com/php-codesniffer", + "keywords": [ + "phpcs", + "standards" + ], + "time": "2018-09-23T23:08:17+00:00" }, { - "name": "symfony/debug", - "version": "v3.4.4", + "name": "symfony/console", + "version": "v4.1.6", "source": { "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "53f6af2805daf52a43b393b93d2f24925d35c937" + "url": "https://github.com/symfony/console.git", + "reference": "dc7122fe5f6113cfaba3b3de575d31112c9aa60b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/53f6af2805daf52a43b393b93d2f24925d35c937", - "reference": "53f6af2805daf52a43b393b93d2f24925d35c937", + "url": "https://api.github.com/repos/symfony/console/zipball/dc7122fe5f6113cfaba3b3de575d31112c9aa60b", + "reference": "dc7122fe5f6113cfaba3b3de575d31112c9aa60b", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "psr/log": "~1.0" + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" }, "require-dev": { - "symfony/http-kernel": "~2.8|~3.0|~4.0" + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0" + }, + "suggest": { + "psr/log-implementation": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Debug\\": "" + "Symfony\\Component\\Console\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2717,22 +3019,22 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Debug Component", + "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-01-18T22:16:57+00:00" + "time": "2018-10-03T08:15:46+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v2.8.34", + "version": "v2.8.46", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "d64be24fc1eba62f9daace8a8918f797fc8e87cc" + "reference": "84ae343f39947aa084426ed1138bb96bf94d1f12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d64be24fc1eba62f9daace8a8918f797fc8e87cc", - "reference": "d64be24fc1eba62f9daace8a8918f797fc8e87cc", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/84ae343f39947aa084426ed1138bb96bf94d1f12", + "reference": "84ae343f39947aa084426ed1138bb96bf94d1f12", "shasum": "" }, "require": { @@ -2779,20 +3081,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-01-03T07:36:31+00:00" + "time": "2018-07-26T09:03:18+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.7.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" + "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", - "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8", + "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8", "shasum": "" }, "require": { @@ -2804,7 +3106,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7-dev" + "dev-master": "1.9-dev" } }, "autoload": { @@ -2838,29 +3140,29 @@ "portable", "shim" ], - "time": "2018-01-30T19:27:44+00:00" + "time": "2018-08-06T14:22:27+00:00" }, { "name": "symfony/stopwatch", - "version": "v3.4.4", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "c865551df7c17e63fc1f09f763db04387f91ae4d" + "reference": "5bfc064125b73ff81229e19381ce1c34d3416f4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/c865551df7c17e63fc1f09f763db04387f91ae4d", - "reference": "c865551df7c17e63fc1f09f763db04387f91ae4d", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5bfc064125b73ff81229e19381ce1c34d3416f4b", + "reference": "5bfc064125b73ff81229e19381ce1c34d3416f4b", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -2887,7 +3189,7 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2018-01-03T07:37:34+00:00" + "time": "2018-10-02T12:40:59+00:00" }, { "name": "webmozart/assert", @@ -2946,7 +3248,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.6" + "php": ">=7.1" }, "platform-dev": [] } diff --git a/docker-compose.yml b/docker-compose.yml index 20c5f02..a5ed102 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,8 +3,7 @@ services: app: build: . image: keboola/ex-db-snowflake - tty: true - stdin_open: true + command: composer ci environment: - STORAGE_API_TOKEN - SNOWFLAKE_DB_HOST @@ -16,9 +15,7 @@ services: - SNOWFLAKE_DB_WAREHOUSE dev: image: keboola/ex-db-snowflake - tty: true - stdin_open: true - command: vendor/bin/phpunit + command: composer ci volumes: - .:/code environment: diff --git a/driver/downloadDriver.php b/driver/downloadDriver.php index f27a3a8..2005327 100644 --- a/driver/downloadDriver.php +++ b/driver/downloadDriver.php @@ -1,6 +1,9 @@ getObject([ 'Bucket' => 'keboola-configs', 'Key' => 'drivers/snowflake/snowflake-odbc-2.13.17.x86_64.deb', - 'SaveAs' => './snowflake-odbc-x86_64.deb' + 'SaveAs' => './snowflake-odbc-x86_64.deb', ]); $client->getObject([ 'Bucket' => 'keboola-configs', 'Key' => 'drivers/snowflake-snowsql/snowsql-1.1.50-linux_x86_64.bash', - 'SaveAs' => './snowsql-linux_x86_64.bash' + 'SaveAs' => './snowsql-linux_x86_64.bash', ]); diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..67e37cf --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,4 @@ + + + + diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..f318682 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,3 @@ +parameters: + ignoreErrors: + - '#Cannot call method scalarNode()#' diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 3a5c07d..a4af4f7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -8,11 +8,7 @@ processIsolation="false" stopOnFailure="false" syntaxCheck="false" - bootstrap="vendor/keboola/db-extractor-common/bootstrap.php"> - - - - + bootstrap="vendor/autoload.php"> tests diff --git a/run.php b/run.php index d3a62e7..fc2f609 100644 --- a/run.php +++ b/run.php @@ -1,17 +1,16 @@ setHandlers(array(new NullHandler(Logger::INFO))); @@ -43,20 +50,20 @@ exit(0); } catch (UserException $e) { $logger->log('error', $e->getMessage(), (array) $e->getData()); - if (!$runAction) { echo $e->getMessage(); } - exit(1); -} catch (ApplicationException $e) { - $logger->log('error', $e->getMessage(), (array) $e->getData()); - exit(2); -} catch (\Exception $e) { - $logger->log('error', $e->getMessage(), [ - 'errFile' => $e->getFile(), - 'errLine' => $e->getLine(), - 'trace' => $e->getTrace() - ]); +} catch (\Throwable $e) { + $logger->critical( + get_class($e) . ':' . $e->getMessage(), + [ + 'errFile' => $e->getFile(), + 'errLine' => $e->getLine(), + 'errCode' => $e->getCode(), + 'errTrace' => $e->getTraceAsString(), + 'errPrevious' => $e->getPrevious() ? get_class($e->getPrevious()) : '', + ] + ); exit(2); } diff --git a/src/Keboola/DbExtractor/Configuration/SnowflakeConfigDefinition.php b/src/Keboola/DbExtractor/Configuration/SnowflakeConfigDefinition.php index 5fa06f9..f22a728 100644 --- a/src/Keboola/DbExtractor/Configuration/SnowflakeConfigDefinition.php +++ b/src/Keboola/DbExtractor/Configuration/SnowflakeConfigDefinition.php @@ -1,21 +1,21 @@ root('parameters'); + // @formatter:off $rootNode ->children() ->scalarNode('data_dir') @@ -52,14 +52,8 @@ public function getConfigTreeBuilder() ->arrayNode('tables') ->prototype('array') ->children() - ->integerNode('id') - ->isRequired() - ->min(0) - ->end() - ->scalarNode('name') - ->isRequired() - ->cannotBeEmpty() - ->end() + ->integerNode('id')->isRequired()->min(0)->end() + ->scalarNode('name')->isRequired()->cannotBeEmpty()->end() ->scalarNode('query')->end() ->arrayNode('table') ->children() @@ -68,65 +62,20 @@ public function getConfigTreeBuilder() ->end() ->end() ->arrayNode('columns') - ->prototype('scalar') - ->end() - ->end() - ->scalarNode('outputTable') - ->isRequired() - ->cannotBeEmpty() - ->end() - ->booleanNode('incremental') - ->defaultValue(false) - ->end() - ->booleanNode('enabled') - ->defaultValue(true) + ->prototype('scalar')->end() ->end() + ->scalarNode('outputTable')->isRequired()->cannotBeEmpty()->end() + ->booleanNode('incremental')->defaultValue(false)->end() + ->booleanNode('enabled')->defaultValue(true)->end() ->arrayNode('primaryKey') - ->prototype('scalar') - ->end() - ->end() - ->integerNode('retries') - ->min(1) + ->prototype('scalar')->end() ->end() + ->integerNode('retries')->min(1)->end() ->end() ->end() ->end() - ->end() - ; - + ->end(); + // @formatter:on return $treeBuilder; } - - public function addSshNode() - { - $builder = new TreeBuilder(); - $node = $builder->root('ssh'); - - $node - ->children() - ->booleanNode('enabled')->end() - ->arrayNode('keys') - ->children() - ->scalarNode('private')->end() - ->scalarNode('#private')->end() - ->scalarNode('public')->end() - ->end() - ->end() - ->scalarNode('sshHost')->end() - ->scalarNode('sshPort') - ->defaultValue("22") - ->end() - ->scalarNode('remoteHost') - ->end() - ->scalarNode('remotePort') - ->end() - ->scalarNode('localPort') - ->defaultValue("33006") - ->end() - ->scalarNode('user')->end() - ->end() - ; - - return $node; - } } diff --git a/src/Keboola/DbExtractor/Extractor/Snowflake.php b/src/Keboola/DbExtractor/Extractor/Snowflake.php index 0862400..6097727 100644 --- a/src/Keboola/DbExtractor/Extractor/Snowflake.php +++ b/src/Keboola/DbExtractor/Extractor/Snowflake.php @@ -1,8 +1,10 @@ temp = new Temp('ex-snowflake'); - parent::__construct($parameters, $logger); + parent::__construct($parameters, $state, $logger); } - public function createConnection($dbParams) + public function createConnection(array $dbParams): Connection { - $this->snowSqlConfig = $this->crateSnowSqlConfig($dbParams); + $this->snowSqlConfig = $this->createSnowSqlConfig($dbParams); $connection = new Connection($dbParams); @@ -71,7 +79,7 @@ public function createConnection($dbParams) return $connection; } - public function testConnection() + public function testConnection(): void { $this->db->query('SELECT current_date;'); @@ -90,7 +98,7 @@ public function testConnection() 'USE WAREHOUSE %s;', $this->db->quoteIdentifier($warehouse) )); - } catch (\Exception $e) { + } catch (\Throwable $e) { if (preg_match('/Object does not exist/ui', $e->getMessage())) { throw new UserException(sprintf('Invalid warehouse "%s" specified', $warehouse)); } else { @@ -99,18 +107,21 @@ public function testConnection() } } - public function export(array $table) + public function export(array $table): array { $outputTable = $table['outputTable']; $this->logger->info("Exporting to " . $outputTable); - $this->exportAndDownload($table); + $rowCount = $this->exportAndDownload($table); - return $outputTable; + return [ + "outputTable"=> $outputTable, + "rows" => $rowCount, + ]; } - private function getColumnInfo(string $query) + private function getColumnInfo(string $query): array { // Create temporary view from the supplied query $sql = sprintf( @@ -120,7 +131,7 @@ private function getColumnInfo(string $query) try { $this->db->query($sql); - } catch (\Exception $e) { + } catch (\Throwable $e) { throw new UserException( sprintf('DB query "%s" failed: %s', rtrim(trim($query), ';'), $e->getMessage()), 0, @@ -131,12 +142,12 @@ private function getColumnInfo(string $query) return $this->db->fetchAll("DESC RESULT LAST_QUERY_ID()"); } - private function exportAndDownload(array $table) + private function exportAndDownload(array $table): int { if (!isset($table['query']) || $table['query'] === '') { $query = $this->simpleQuery($table['table'], $table['columns']); $columnInfo = $this->getColumnInfo($query); - $objectColumns = array_filter($columnInfo, function ($column) { + $objectColumns = array_filter($columnInfo, function ($column): bool { return in_array($column['type'], self::SEMI_STRUCTURED_TYPES); }); if (!empty($objectColumns)) { @@ -155,7 +166,7 @@ private function exportAndDownload(array $table) try { $res = $this->executeCopyCommand($copyCommand); - } catch (\Exception $e) { + } catch (\Throwable $e) { throw new UserException( sprintf('Copy Command: %s failed with message: %s', $copyCommand, $e->getMessage()) ); @@ -163,9 +174,11 @@ private function exportAndDownload(array $table) if (count($res) > 0 && (int) $res[0]['rows_unloaded'] === 0) { // query resulted in no rows, nothing left to do - return; + return 0; } + $rowCount = (int) $res[0]['rows_unloaded']; + $this->logger->info("Downloading data from Snowflake"); $outputDataDir = $this->dataDir . '/out/tables/' . $tmpTableName . ".csv.gz"; @@ -190,7 +203,7 @@ private function exportAndDownload(array $table) ); $snowSql = $this->temp->createTmpFile('snowsql.sql'); - file_put_contents($snowSql, implode("\n", $sql)); + file_put_contents($snowSql->getPathname(), implode("\n", $sql)); $this->logger->debug(trim(implode("\n", $sql))); @@ -221,24 +234,31 @@ private function exportAndDownload(array $table) file_put_contents( $outputDataDir . '.manifest', - Yaml::dump($this->createTableManifest($table, array_map( - function ($column) { - return $column['name']; - }, - $columnInfo - ))) + json_encode( + $this->createTableManifest( + $table, + array_map( + function ($column): string { + return $column['name']; + }, + $columnInfo + ) + ) + ) ); $this->logger->info(sprintf( "%d files (%s) downloaded", count($csvFiles), - $this->dataSizeFormatted($bytesDownloaded) + $this->dataSizeFormatted((int) $bytesDownloaded) )); $this->cleanupTableStage($tmpTableName); + + return $rowCount; } - private function generateCopyCommand($stageTmpPath, $query) + private function generateCopyCommand(string $stageTmpPath, string $query): string { $csvOptions = []; $csvOptions[] = sprintf('FIELD_DELIMITER = %s', $this->quote(CsvFile::DEFAULT_DELIMITER)); @@ -264,13 +284,7 @@ private function generateCopyCommand($stageTmpPath, $query) ); } - /** - * @param $copyCommand - * @param int $maxTries - * @return array - * @throws \Exception - */ - private function executeCopyCommand($copyCommand, $maxTries = 5): array + private function executeCopyCommand(string $copyCommand, int $maxTries = 5): array { $retryPolicy = new SimpleRetryPolicy($maxTries, ['PDOException', 'ErrorException', 'Exception']); $backOffPolicy = new ExponentialBackOffPolicy(1000); @@ -285,7 +299,7 @@ private function executeCopyCommand($copyCommand, $maxTries = 5): array } try { return $this->db->fetchAll($copyCommand); - } catch (\Exception $e) { + } catch (\Throwable $e) { $lastException = new UserException( sprintf("Copy Command failed: %s", $e->getMessage()), 0, @@ -295,8 +309,8 @@ private function executeCopyCommand($copyCommand, $maxTries = 5): array throw $e; } }); - } catch (\Exception $e) { - if ($lastException) { + } catch (\Throwable $e) { + if ($lastException !== null) { throw $lastException; } throw $e; @@ -312,7 +326,7 @@ private function createTableManifest(array $table, array $columns): array 'enclosure' => CsvFile::DEFAULT_ENCLOSURE, 'primary_key' => $table['primaryKey'], 'incremental' => $table['incremental'], - 'columns' => $columns + 'columns' => $columns, ]; if (isset($table['table']) && isset($table['table']['tableName'])) { @@ -348,7 +362,7 @@ private function createTableManifest(array $table, array $columns): array if ($key !== 'name') { $columnMetadata[$column['name']][] = [ 'key' => "KBC." . $key, - 'value'=> $value + 'value'=> $value, ]; } } @@ -357,7 +371,7 @@ private function createTableManifest(array $table, array $columns): array foreach ($tableDetails as $key => $value) { $manifestData['metadata'][] = [ "key" => "KBC." . $key, - "value" => $value + "value" => $value, ]; } $manifestData['column_metadata'] = $columnMetadata; @@ -366,15 +380,15 @@ private function createTableManifest(array $table, array $columns): array return $manifestData; } - - private function dataSizeFormatted(int $bytes) + + private function dataSizeFormatted(int $bytes): string { $base = log($bytes) / log(1024); $suffixes = [' B', ' KB', ' MB', ' GB', ' TB']; return round(pow(1024, $base - floor($base)), 2) . $suffixes[(int) floor($base)]; } - public function getTables(array $tables = null) + public function getTables(?array $tables = null): array { $sql = $this->schema ? "SHOW TABLES IN SCHEMA" : "SHOW TABLES IN DATABASE"; $arr = $this->db->fetchAll($sql); @@ -386,7 +400,7 @@ public function getTables(array $tables = null) $tableNameArray = []; $tableDefs = []; foreach ($arr as $table) { - if (($this->schema && $table['schema_name'] !== $this->schema) || $table['schema_name'] === 'INFORMATION_SCHEMA') { + if ($this->shouldTableBeSkipped($table)) { continue; } if (is_null($tables) || !(array_search($table['name'], array_column($tables, 'tableName')) === false)) { @@ -396,7 +410,7 @@ public function getTables(array $tables = null) 'name' => $table['name'], 'catalog' => (isset($table['database_name'])) ? $table['database_name'] : null, 'schema' => (isset($table['schema_name'])) ? $table['schema_name'] : null, - 'type' => $isView ? 'VIEW' : (isset($table['kind']) ? $table['kind'] : null) + 'type' => $isView ? 'VIEW' : (isset($table['kind']) ? $table['kind'] : null), ]; if (isset($table['rows'])) { $tableDefs[$table['schema_name'] . '.' . $table['name']]['rowCount'] = $table['rows']; @@ -415,8 +429,8 @@ public function getTables(array $tables = null) "SELECT * FROM information_schema.columns WHERE TABLE_NAME IN (%s) ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION", - implode(', ', array_map(function ($tableName) { - return "'" . $tableName . "'"; + implode(', ', array_map(function ($tableName): string { + return $this->quote($tableName); }, $tableNameArray)) ); @@ -438,7 +452,7 @@ public function getTables(array $tables = null) "length" => $length, "nullable" => (trim($column['IS_NULLABLE']) === "NO") ? false : true, "type" => $column['DATA_TYPE'], - "ordinalPosition" => $column['ORDINAL_POSITION'] + "ordinalPosition" => $column['ORDINAL_POSITION'], ]; if (!array_key_exists('columns', $tableDefs[$curTable])) { @@ -449,12 +463,19 @@ public function getTables(array $tables = null) return array_values($tableDefs); } - public function simpleQuery(array $table, array $columns = array()) + private function shouldTableBeSkipped(array $table): bool + { + $isFromDifferentSchema = $this->schema && $table['schema_name'] !== $this->schema; + $isFromInformationSchema = $table['schema_name'] === 'INFORMATION_SCHEMA'; + return $isFromDifferentSchema || $isFromInformationSchema; + } + + public function simpleQuery(array $table, array $columns = array()): string { if (count($columns) > 0) { return sprintf( "SELECT %s FROM %s.%s", - implode(', ', array_map(function ($column) { + implode(', ', array_map(function ($column): string { return $this->db->quoteIdentifier($column); }, $columns)), $this->db->quoteIdentifier($table['schema']), @@ -473,7 +494,7 @@ private function simpleQueryWithCasting(array $table, array $columnInfo) : strin { return sprintf( "SELECT %s FROM %s.%s", - implode(', ', array_map(function ($column) { + implode(', ', array_map(function ($column): string { if (in_array($column['type'], self::SEMI_STRUCTURED_TYPES)) { return sprintf( 'CAST(%s AS TEXT) AS %s', @@ -488,25 +509,19 @@ private function simpleQueryWithCasting(array $table, array $columnInfo) : strin ); } - /** - * @param $output - * @param $path - * @return \SplFileInfo[] - * @throws \Exception - */ - private function parseFiles($output, $path) + private function parseFiles(string $output, string $path): array { $files = []; $lines = explode("\n", $output); $lines = array_map( - function ($item) { + function ($item): array { $item = trim($item, '|'); return array_map('trim', explode('|', $item)); }, array_filter( $lines, - function ($item) { + function ($item): bool { $item = trim($item); return preg_match('/^\|.+\|$/ui', $item) && preg_match('/([a-z0-9\_\-\.]+\.gz)/ui', $item); } @@ -535,16 +550,12 @@ function ($item) { return $files; } - private function quote($value) + private function quote(string $value): string { return "'" . addslashes($value) . "'"; } - /** - * @param $dbParams - * @return \SplFileInfo - */ - private function crateSnowSqlConfig($dbParams) + private function createSnowSqlConfig(array $dbParams): \SplFileInfo { $cliConfig[] = ''; $cliConfig[] = '[options]'; @@ -565,12 +576,12 @@ private function crateSnowSqlConfig($dbParams) } $file = $this->temp->createFile('snowsql.config'); - file_put_contents($file, implode("\n", $cliConfig)); + file_put_contents($file->getPathname(), implode("\n", $cliConfig)); return $file; } - private function getUserDefaultWarehouse() + private function getUserDefaultWarehouse(): ?string { $sql = sprintf( "DESC USER %s;", @@ -588,11 +599,11 @@ private function getUserDefaultWarehouse() return null; } - private function execQuery($query) + private function execQuery(string $query): void { try { $this->db->query($query); - } catch (\Exception $e) { + } catch (\Throwable $e) { throw new UserException("Query execution error: " . $e->getMessage(), 0, $e); } } diff --git a/src/Keboola/DbExtractor/SnowflakeApplication.php b/src/Keboola/DbExtractor/SnowflakeApplication.php index 156fa5a..2e86b03 100644 --- a/src/Keboola/DbExtractor/SnowflakeApplication.php +++ b/src/Keboola/DbExtractor/SnowflakeApplication.php @@ -1,16 +1,19 @@ setConfigDefinition(new SnowflakeConfigDefinition()); } diff --git a/src/Keboola/DbExtractor/Utils/AccountUrlParser.php b/src/Keboola/DbExtractor/Utils/AccountUrlParser.php index 37d759c..4d77bef 100644 --- a/src/Keboola/DbExtractor/Utils/AccountUrlParser.php +++ b/src/Keboola/DbExtractor/Utils/AccountUrlParser.php @@ -1,9 +1,12 @@ storageApiClient = new Client([ - 'token' => getenv('STORAGE_API_TOKEN') + 'token' => getenv('STORAGE_API_TOKEN'), ]); $this->setupTables(); $fileSystem = new Filesystem(); - $fileSystem->remove(__DIR__ . '/data/out'); - $fileSystem->remove(__DIR__ . '/data/runAction/config.yml'); - $fileSystem->remove(__DIR__ . '/data/runAction/out'); - $fileSystem->remove(__DIR__ . '/data/connectionAction/config.yml'); - $fileSystem->remove(__DIR__ . '/tests/data/connectionAction/out'); + $fileSystem->remove($this->dataDir . '/out'); + $fileSystem->remove($this->dataDir . '/runAction/config.yml'); + $fileSystem->remove($this->dataDir . '/runAction/config.json'); + $fileSystem->remove($this->dataDir . '/runAction/out'); + $fileSystem->remove($this->dataDir . '/connectionAction/config.yml'); + $fileSystem->remove($this->dataDir . '/connectionAction/config.json'); + $fileSystem->remove($this->dataDir . '/connectionAction/out'); + $fileSystem->remove($this->dataDir . '/getTablesAction/config.yml'); + $fileSystem->remove($this->dataDir . '/getTablesAction/config.json'); + $fileSystem->remove($this->dataDir . '/getTablesAction/out'); } - /** - * @param string $driver - * @return mixed - */ - public function getConfig($driver = 'snowflake') + public function getConfig(string $driver = 'snowflake', string $format = 'json'): array { $config = parent::getConfig($driver); @@ -72,22 +80,15 @@ public function getConfig($driver = 'snowflake') return $config; } - /** - * @param array $config - * @return SnowflakeApplication - */ - public function createApplication(array $config) + public function createApplication(array $config): SnowflakeApplication { - $app = new SnowflakeApplication($config, $this->dataDir); + $logger = new Logger('ex-db-snowflake-tests'); + $app = new SnowflakeApplication($config, $logger, [], $this->dataDir); return $app; } - /** - * @param CsvFile $file - * @return string - */ - protected function generateTableName(CsvFile $file) + protected function generateTableName(CsvFile $file): string { $tableName = sprintf( '%s', @@ -97,7 +98,7 @@ protected function generateTableName(CsvFile $file) return $tableName; } - private function setupTables() + private function setupTables(): void { $salescsv = new CsvFile($this->dataDir . '/snowflake/sales.csv'); $this->createTextTable($salescsv); @@ -114,12 +115,17 @@ private function setupTables() $this->connection->query( sprintf( - 'CREATE TABLE %s ("character" VARCHAR(100) NOT NULL, "integer" NUMBER(6,0), "decimal" NUMBER(10,2), "date" DATE);', + 'CREATE TABLE %s ( + "character" VARCHAR(100) NOT NULL, + "integer" NUMBER(6,0), + "decimal" NUMBER(10,2), + "date" DATE + );', $this->connection->quoteIdentifier('types') ) ); $storageFileInfo = $this->storageApiClient->getFile( - $this->storageApiClient->uploadFile( + (string) $this->storageApiClient->uploadFile( (string) $types, new FileUploadOptions() ), @@ -135,11 +141,13 @@ private function setupTables() // create a view with a json object column $this->connection->query('DROP TABLE IF EXISTS "semi-structured"'); - $this->connection->query('CREATE TABLE "semi-structured" ( - "var" VARIANT, - "obj" OBJECT, - "arr" ARRAY - )'); + $this->connection->query( + 'CREATE TABLE "semi-structured" ( + "var" VARIANT, + "obj" OBJECT, + "arr" ARRAY + )' + ); $this->connection->query( 'INSERT INTO "semi-structured" SELECT @@ -149,12 +157,12 @@ private function setupTables() ); } - private function quote($value) + private function quote(string $value): string { return "'" . addslashes($value) . "'"; } - private function generateCreateCommand($tableName, CsvFile $csv, $fileInfo) + private function generateCreateCommand(string $tableName, CsvFile $csv, array $fileInfo): string { $csvOptions = []; $csvOptions[] = sprintf('FIELD_DELIMITER = %s', $this->connection->quoteIdentifier($csv->getDelimiter())); @@ -189,7 +197,7 @@ private function generateCreateCommand($tableName, CsvFile $csv, $fileInfo) * @param string $tableName - optional name override (default uses filename) * @param string $schemaName - optional schema in which to create the table */ - protected function createTextTable(CsvFile $file, $tableName = null, $schemaName = null) + protected function createTextTable(CsvFile $file, ?string $tableName = null, ?string $schemaName = null): void { if (!$tableName) { $tableName = $this->generateTableName($file); @@ -215,12 +223,11 @@ protected function createTextTable(CsvFile $file, $tableName = null, $schemaName $q = '"'; return ($q . str_replace("$q", "$q$q", $column) . $q) . ' VARCHAR(200) NOT NULL'; }, $file->getHeader()) - ), - $tableName + ) )); $storageFileInfo = $this->storageApiClient->getFile( - $this->storageApiClient->uploadFile( + (string) $this->storageApiClient->uploadFile( (string) $file, new FileUploadOptions() ), @@ -238,13 +245,7 @@ protected function createTextTable(CsvFile $file, $tableName = null, $schemaName $this->assertEquals($this->countTable($file), (int) $result[0]['ROWCOUNT']); } - /** - * Count records in CSV (with headers) - * - * @param CsvFile $file - * @return int - */ - protected function countTable(CsvFile $file) + protected function countTable(CsvFile $file): int { $linesCount = 0; foreach ($file as $i => $line) { @@ -258,4 +259,12 @@ protected function countTable(CsvFile $file) return $linesCount; } + + public function configTypesProvider(): array + { + return [ + ['yaml'], + ['json'], + ]; + } } diff --git a/tests/AccountParseTest.php b/tests/AccountParseTest.php index 2bab01e..21c7b6f 100644 --- a/tests/AccountParseTest.php +++ b/tests/AccountParseTest.php @@ -1,11 +1,14 @@ assertEquals( 'something', diff --git a/tests/SnowflakeEntrypointTest.php b/tests/SnowflakeEntrypointTest.php index dba052b..9971a51 100644 --- a/tests/SnowflakeEntrypointTest.php +++ b/tests/SnowflakeEntrypointTest.php @@ -1,33 +1,52 @@ getEnv($driver, 'DB_USER', true); - $config['parameters']['db']['#password'] = $this->getEnv($driver, 'DB_PASSWORD', true); - $config['parameters']['db']['schema'] = $this->getEnv($driver, 'DB_SCHEMA'); - $config['parameters']['db']['host'] = $this->getEnv($driver, 'DB_HOST'); - $config['parameters']['db']['port'] = $this->getEnv($driver, 'DB_PORT'); - $config['parameters']['db']['database'] = $this->getEnv($driver, 'DB_DATABASE'); - $config['parameters']['db']['warehouse'] = $this->getEnv($driver, 'DB_WAREHOUSE'); + public const ROOT_PATH = __DIR__ . '/..'; + + private function createConfigFile(string $rootPath, string $configType = 'yaml'): void + { + $config = Yaml::parse((string) file_get_contents($rootPath . '/config.template.yml')); + $config['parameters']['db']['user'] = $this->getEnv(self::DRIVER, 'DB_USER', true); + $config['parameters']['db']['#password'] = $this->getEnv(self::DRIVER, 'DB_PASSWORD', true); + $config['parameters']['db']['schema'] = $this->getEnv(self::DRIVER, 'DB_SCHEMA'); + $config['parameters']['db']['host'] = $this->getEnv(self::DRIVER, 'DB_HOST'); + $config['parameters']['db']['port'] = $this->getEnv(self::DRIVER, 'DB_PORT'); + $config['parameters']['db']['database'] = $this->getEnv(self::DRIVER, 'DB_DATABASE'); + $config['parameters']['db']['warehouse'] = $this->getEnv(self::DRIVER, 'DB_WAREHOUSE'); if (isset($config['parameters']['tables'][2]['table'])) { - $config['parameters']['tables'][2]['table']['schema'] = $this->getEnv($driver, 'DB_SCHEMA'); + $config['parameters']['tables'][2]['table']['schema'] = $this->getEnv(self::DRIVER, 'DB_SCHEMA'); } - file_put_contents($rootPath . '/config.yml', Yaml::dump($config)); - return new \SplFileInfo($rootPath . '/config.yml'); + // unlink any old configs written here + @unlink($rootPath . '/config.yml'); + @unlink($rootPath . '/config.json'); + + if ($configType === 'yaml') { + file_put_contents($rootPath . '/config.yml', Yaml::dump($config)); + } else if ($configType === 'json') { + file_put_contents($rootPath . '/config.json', json_encode($config)); + } else { + throw new UserException(sprintf("Unsupported configType [%s]", $configType)); + } } - public function testRunAction() + /** + * @dataProvider configTypesProvider + * @param string $configType + */ + public function testRunAction(string $configType): void { $dataPath = __DIR__ . '/data/runAction'; @@ -40,12 +59,13 @@ public function testRunAction() @unlink($dataPath . "/out/tables/in.c-main_tableColumns.csv.gz"); @unlink($dataPath . "/out/tables/in.c-main_tableColumns.csv.gz.manifest"); - $this->createConfigFile($dataPath); + $this->createConfigFile($dataPath, $configType); - $process = new Process('php ' . ROOT_PATH . '/run.php --data=' . $dataPath . ' 2>&1'); + $process = new Process('php ' . self::ROOT_PATH . '/run.php --data=' . $dataPath . ' 2>&1'); $process->setTimeout(300); $process->run(); - $this->assertEquals(0, $process->getExitCode(), sprintf('error output: ', $process->getErrorOutput())); + + $this->assertEquals(0, $process->getExitCode(), sprintf('error output: %s', $process->getErrorOutput())); $this->assertFileExists($dataPath . "/out/tables/in_c-main_sales.csv.gz"); $this->assertFileExists($dataPath . "/out/tables/in_c-main_sales.csv.gz.manifest"); $this->assertFileExists($dataPath . "/out/tables/in_c-main_escaping.csv.gz"); @@ -54,13 +74,17 @@ public function testRunAction() $this->assertFileExists($dataPath . "/out/tables/in_c-main_tableColumns.csv.gz.manifest"); } - public function testConnectionAction() + /** + * @dataProvider configTypesProvider + * @param string $configType + */ + public function testConnectionAction(string $configType): void { $dataPath = __DIR__ . '/data/connectionAction'; - $this->createConfigFile($dataPath); + $this->createConfigFile($dataPath, $configType); - $process = new Process('php ' . ROOT_PATH . '/run.php --data=' . $dataPath . ' 2>&1'); + $process = new Process('php ' . self::ROOT_PATH . '/run.php --data=' . $dataPath . ' 2>&1'); $process->run(); $output = $process->getOutput(); @@ -73,28 +97,31 @@ public function testConnectionAction() $this->assertEquals('success', $data['status']); } - public function testNonexistingTable() + public function testNonexistingTable(): void { $config = $this->getConfig(); $config['parameters']['tables'][0]['query'] = "SELECT * FROM non_existing_table"; @unlink($this->dataDir . '/config.yml'); file_put_contents($this->dataDir . '/config.yml', Yaml::dump($config)); - $process = new Process('php ' . ROOT_PATH . '/run.php --data=' . $this->dataDir); + $process = new Process('php ' . self::ROOT_PATH . '/run.php --data=' . $this->dataDir); $process->setTimeout(300); $process->run(); $this->assertEquals(1, $process->getExitCode()); } - public function testGetTablesAction() + /** + * @dataProvider configTypesProvider + * @param string $configType + */ + public function testGetTablesAction(string $configType): void { - $config = $this->getConfig(); - @unlink($this->dataDir . '/config.yml'); - $config['action'] = 'getTables'; - file_put_contents($this->dataDir . '/config.yml', Yaml::dump($config)); + $dataPath = __DIR__ . '/data/getTablesAction'; - $process = new Process('php ' . ROOT_PATH . '/run.php --data=' . $this->dataDir); + $this->createConfigFile($dataPath, $configType); + + $process = new Process('php ' . self::ROOT_PATH . '/run.php --data=' . $dataPath); $process->setTimeout(300); $process->run(); @@ -103,7 +130,7 @@ public function testGetTablesAction() $this->assertEquals("", $process->getErrorOutput()); } - public function testBadTypesRetries() + public function testBadTypesRetries(): void { $config = $this->getConfig(); $this->createTextTable(new \Keboola\Csv\CsvFile($this->dataDir . '/snowflake/badTypes.csv'), 'types'); @@ -116,7 +143,7 @@ public function testBadTypesRetries() @unlink($this->dataDir . '/config.yml'); file_put_contents($this->dataDir . '/config.yml', Yaml::dump($config)); - $process = new Process('php ' . ROOT_PATH . '/run.php --data=' . $this->dataDir); + $process = new Process('php ' . self::ROOT_PATH . '/run.php --data=' . $this->dataDir); $process->setTimeout(300); $process->run(); diff --git a/tests/SnowflakeTest.php b/tests/SnowflakeTest.php index 4dd13b2..ab535ef 100644 --- a/tests/SnowflakeTest.php +++ b/tests/SnowflakeTest.php @@ -1,5 +1,8 @@ getConfig(); $user = $config['parameters']['db']['user']; @@ -22,7 +25,7 @@ public function testDefaultWarehouse() try { $app->run(); $this->fail('Run extractor without warehouse should fail'); - } catch (\Exception $e) { + } catch (\Throwable $e) { $this->assertRegExp('/No active warehouse/ui', $e->getMessage()); } @@ -37,7 +40,7 @@ public function testDefaultWarehouse() $this->setUserDefaultWarehouse($user, $warehouse); } - public function testCredentials() + public function testCredentials(): void { $config = $this->getConfig(); $config['action'] = 'testConnection'; @@ -50,7 +53,7 @@ public function testCredentials() $this->assertEquals('success', $result['status']); } - public function testCredentialsWithoutSchema() + public function testCredentialsWithoutSchema(): void { $config = $this->getConfig(); $config['action'] = 'testConnection'; @@ -64,7 +67,7 @@ public function testCredentialsWithoutSchema() $this->assertEquals('success', $result['status']); } - public function testCredentialsDefaultWarehouse() + public function testCredentialsDefaultWarehouse(): void { $config = $this->getConfig(); $config['action'] = 'testConnection'; @@ -107,7 +110,7 @@ public function testCredentialsDefaultWarehouse() $this->setUserDefaultWarehouse($user, $warehouse); } - public function testRunWithoutTables() + public function testRunWithoutTables(): void { $config = $this->getConfig(); @@ -120,7 +123,7 @@ public function testRunWithoutTables() $this->assertEquals('success', $result['status']); } - public function testRun() + public function testRunMain(): void { $config = $this->getConfig(); $app = $this->createApplication($config); @@ -155,19 +158,22 @@ public function testRun() array_shift($csv1arr); $outCsv1 = new CsvFile($this->dataDir . '/out/tables/in_c-main_sales.csv.gz/part_0_0_0.csv'); $this->assertEquals($csv1arr, iterator_to_array($outCsv1)); + $this->assertEquals(100, $result['imported']['0']['rows']); $csv2arr = iterator_to_array($csv2); array_shift($csv2arr); $outCsv2 = new CsvFile($this->dataDir . '/out/tables/in_c-main_escaping.csv.gz/part_0_0_0.csv'); $this->assertEquals($csv2arr, iterator_to_array($outCsv2)); + $this->assertEquals(7, $result['imported']['1']['rows']); $csv3arr = iterator_to_array($csv3); array_shift($csv3arr); $outCsv3 = new CsvFile($this->dataDir . '/out/tables/in_c-main_tableColumns.csv.gz/part_0_0_0.csv'); $this->assertEquals($csv3arr, iterator_to_array($outCsv3)); + $this->assertEquals(4, $result['imported']['2']['rows']); } - public function testRunWithoutSchema() + public function testRunWithoutSchema(): void { $config = $this->getConfig(); unset($config['parameters']['db']['schema']); @@ -180,7 +186,7 @@ public function testRunWithoutSchema() try { $result = $app->run(); $this->fail('The query does not specify schema and no schema is specified in the connection.'); - } catch (\Exception $e) { + } catch (\Throwable $e) { $this->assertContains('no schema is specified', $e->getMessage()); } @@ -195,7 +201,7 @@ public function testRunWithoutSchema() $this->validateExtraction($config['parameters']['tables'][0]); } - public function testRunEmptyQuery() + public function testRunEmptyQuery(): void { $csv = new CsvFile($this->dataDir . '/snowflake/escaping.csv'); $this->createTextTable($csv); @@ -216,7 +222,7 @@ public function testRunEmptyQuery() $this->assertFileNotExists($outputManifestFile); } - public function testGetTables() + public function testGetTables(): void { $config = $this->getConfig(); $config['action'] = 'getTables'; @@ -507,7 +513,7 @@ public function testGetTables() $this->assertEquals($expectedData, $result['tables']); } - public function testGetTablesWithoutSchema() + public function testGetTablesWithoutSchema(): void { $config = $this->getConfig(); $config['action'] = 'getTables'; @@ -829,7 +835,7 @@ public function testGetTablesWithoutSchema() $this->assertEquals($expectedData, $result['tables']); } - public function testManifestMetadata() + public function testManifestMetadata(): void { $config = $this->getConfig(); @@ -842,7 +848,7 @@ public function testManifestMetadata() $result = $app->run(); $outputManifest = Yaml::parse( - file_get_contents($this->dataDir . '/out/tables/in_c-main_tableColumns.csv.gz.manifest') + (string) file_get_contents($this->dataDir . '/out/tables/in_c-main_tableColumns.csv.gz.manifest') ); $this->assertArrayHasKey('destination', $outputManifest); @@ -1018,14 +1024,14 @@ public function testManifestMetadata() $this->assertEquals($expectedColumnMetadata, $outputManifest['column_metadata']); } - public function testSemiStructured() + public function testSemiStructured(): void { $config = $this->getConfig(); $table = $config['parameters']['tables'][0]; unset($table['query']); $table['table'] = [ 'tableName' => 'semi-structured', - 'schema' => $this->getEnv('snowflake', 'DB_SCHEMA') + 'schema' => $this->getEnv('snowflake', 'DB_SCHEMA'), ]; $table['outputTable'] = 'in.c-main.semi-structured'; $table['primaryKey'] = null; @@ -1042,14 +1048,14 @@ public function testSemiStructured() exec("gunzip -d " . escapeshellarg($archiveFile), $output, $return); $this->assertEquals(0, $return); - $rawFile = new \SplFileInfo($this->dataDir . '/out/tables/in_c-main_semi-structured.csv.gz/part_0_0_0.csv'); + $rawFile = $this->dataDir . '/out/tables/in_c-main_semi-structured.csv.gz/part_0_0_0.csv'; $this->assertEquals( file_get_contents($this->dataDir . '/snowflake/expected-semi-structured.csv'), file_get_contents($rawFile) ); } - private function getUserDefaultWarehouse($user) + private function getUserDefaultWarehouse(string $user): ?string { $sql = sprintf( "DESC USER %s;", @@ -1067,7 +1073,7 @@ private function getUserDefaultWarehouse($user) return null; } - private function setUserDefaultWarehouse($user, $warehouse = null) + private function setUserDefaultWarehouse(string $user, ?string $warehouse = null): void { if ($warehouse) { $sql = sprintf( @@ -1089,7 +1095,7 @@ private function setUserDefaultWarehouse($user, $warehouse = null) } } - private function validateExtraction(array $query, $expectedFiles = 1) + private function validateExtraction(array $query, int $expectedFiles = 1): ?array { $dirPath = $this->dataDir . '/out/tables'; @@ -1100,7 +1106,7 @@ function ($manifestFileName) use ($dirPath) { return $dirPath . '/' . $manifestFileName; }, array_filter( - scandir($dirPath), + (array) scandir($dirPath), function ($fileName) use ($dirPath, $outputTable) { $filePath = $dirPath . '/' . $fileName; if (is_dir($filePath)) { @@ -1112,21 +1118,21 @@ function ($fileName) use ($dirPath, $outputTable) { return false; } - $manifest = Yaml::parse(file_get_contents($file)); + $manifest = Yaml::parse((string) file_get_contents($file->getPathname())); return $manifest['destination'] === $outputTable; } ) ); if (!$expectedFiles) { - return; + return null; } $this->assertCount($expectedFiles, $manifestFiles); $columns = []; foreach ($manifestFiles as $file) { // manifest validation - $params = Yaml::parse(file_get_contents($file)); + $params = Yaml::parse((string) file_get_contents($file)); $this->assertArrayHasKey('destination', $params); $this->assertArrayHasKey('incremental', $params); @@ -1146,13 +1152,13 @@ function ($fileName) use ($dirPath, $outputTable) { $this->assertEquals($query['outputTable'], $params['destination']); } - $csvDir = new \SplFileInfo(str_replace('.manifest', '', $file)); + $csvDir = str_replace('.manifest', '', $file); $this->assertTrue(is_dir($csvDir)); - foreach (array_diff(scandir($csvDir), array('..', '.')) as $csvFile) { + foreach (array_diff((array) scandir($csvDir), array('..', '.')) as $csvFile) { // archive validation - $archiveFile = new \SplFileInfo($csvDir . "/" . $csvFile); + $archiveFile = $csvDir . "/" . $csvFile; $pos = strrpos($archiveFile, ".gz"); $rawFile = new \SplFileInfo(substr_replace($archiveFile, '', $pos, strlen(".gz"))); diff --git a/tests/data/getTablesAction/config.template.yml b/tests/data/getTablesAction/config.template.yml new file mode 100644 index 0000000..03474c9 --- /dev/null +++ b/tests/data/getTablesAction/config.template.yml @@ -0,0 +1,29 @@ +action: getTables +parameters: + tables: + - + id: 1 + name: sales + query: SELECT * FROM "sales" + outputTable: in.c-main.sales + incremental: false + primaryKey: null + enabled: true + - + id: 2 + name: escaping + query: SELECT * FROM "escaping" + outputTable: in.c-main.escaping + incremental: false + primaryKey: + - col1 + enabled: true + - + id: 3 + name: somedata + query: SELECT * FROM "somedata" + outputTable: in.c-main.somedata + incremental: true + primaryKey: + - somedataId + enabled: false