├── .github └── workflows │ ├── ci.yaml │ └── only-main.yaml ├── .gitignore ├── .php-cs-fixer.dist.php ├── Cli ├── Application.php ├── Command │ ├── AnalysisCommand.php │ ├── AnalyzeCommand.php │ ├── ConfigureCommand.php │ ├── NeedConfigurationInterface.php │ ├── ProjectsCommand.php │ └── SelfUpdateCommand.php ├── Configuration.php ├── Descriptor │ ├── AbstractDescriptor.php │ ├── JsonDescriptor.php │ ├── PmdDescriptor.php │ ├── TextDescriptor.php │ └── XmlDescriptor.php └── Helper │ ├── ConfigurationHelper.php │ ├── DescriptorHelper.php │ └── FailConditionHelper.php ├── LICENSE ├── README.md ├── Sdk ├── Api.php ├── Exception │ ├── ApiClientException.php │ ├── ApiParserException.php │ ├── ApiServerException.php │ └── ExceptionInterface.php ├── Model │ ├── Analyses.php │ ├── Analysis.php │ ├── Error.php │ ├── Link.php │ ├── Project.php │ ├── Projects.php │ ├── Violation.php │ └── Violations.php ├── Parser.php └── Tests │ ├── ApiTest.php │ ├── Model │ └── ViolationsTest.php │ ├── ParserTest.php │ └── fixtures │ ├── analyses.xml │ ├── analysis.xml │ ├── errors.xml │ ├── project.xml │ ├── projects.xml │ ├── projects2.xml │ └── status.xml ├── bin └── insight ├── box.json ├── composer.json └── phpunit.xml.dist /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | on: [pull_request] 2 | name: Insight SDK 3 | 4 | jobs: 5 | php-cs: 6 | name: Coding style 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@main 10 | - uses: shivammathur/setup-php@v2 11 | with: 12 | php-version: '7.2' 13 | tools: php-cs-fixer:3.2.1 14 | coverage: none 15 | - name: php-cs-fixer 16 | run: php-cs-fixer fix --dry-run --diff 17 | 18 | tests-phpunit: 19 | name: Unit tests 20 | runs-on: ubuntu-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | sf-version: [ '^5.4', '^6.4', '^7.2' ] 26 | php-version: [ '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3' ] 27 | exclude: 28 | - sf-version: '^6.4' 29 | php-version: '7.2' 30 | - sf-version: '^6.4' 31 | php-version: '7.3' 32 | - sf-version: '^6.4' 33 | php-version: '7.4' 34 | - sf-version: '^6.4' 35 | php-version: '8.0' 36 | - sf-version: '^7.2' 37 | php-version: '7.2' 38 | - sf-version: '^7.2' 39 | php-version: '7.3' 40 | - sf-version: '^7.2' 41 | php-version: '7.4' 42 | - sf-version: '^7.2' 43 | php-version: '8.0' 44 | - sf-version: '^7.2' 45 | php-version: '8.1' 46 | 47 | steps: 48 | - uses: actions/checkout@main 49 | - name: Setup PHP 50 | uses: shivammathur/setup-php@v2 51 | with: 52 | php-version: ${{ matrix.php-version }} 53 | tools: phpunit-bridge 54 | coverage: none 55 | 56 | - name: Get Composer cache directory 57 | id: composer-cache 58 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 59 | 60 | - name: Mount PHP dependencies cache 61 | uses: actions/cache@v4 62 | with: 63 | path: ${{ steps.composer-cache.outputs.dir }} 64 | key: ${{ runner.os }}-composer-${{ hashFiles('console/composer.lock') }} 65 | restore-keys: ${{ runner.os }}-composer- 66 | 67 | - name: Install dependencies 68 | run: | 69 | composer global config --no-plugins allow-plugins.symfony/flex true 70 | composer global require symfony/flex:^1.0 71 | export SYMFONY_REQUIRE="${{ matrix.sf-version }}" 72 | composer update --prefer-dist --no-interaction --no-ansi --no-progress 73 | 74 | - name: Run the tests suite 75 | run: | 76 | php vendor/bin/simple-phpunit -v --log-junit ./phpunit/junit.xml 77 | 78 | - name: Run cmd 79 | run: bin/insight list 80 | -------------------------------------------------------------------------------- /.github/workflows/only-main.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | name: Insight SDK (release) 6 | 7 | jobs: 8 | release: 9 | name: Release 10 | runs-on: ubuntu-latest 11 | 12 | env: 13 | AWS_DEFAULT_REGION: 'eu-west-1' 14 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 15 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 16 | 17 | steps: 18 | - uses: actions/checkout@main 19 | - name: Setup PHP 20 | uses: shivammathur/setup-php@v2 21 | with: 22 | php-version: '7.4' 23 | coverage: none 24 | 25 | - name: Get Composer cache directory 26 | id: composer-cache 27 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 28 | 29 | - name: Mount PHP dependencies cache 30 | uses: actions/cache@v4 31 | with: 32 | path: ${{ steps.composer-cache.outputs.dir }} 33 | key: ${{ runner.os }}-composer-${{ hashFiles('console/composer.lock') }} 34 | restore-keys: ${{ runner.os }}-composer- 35 | 36 | - name: Install dependencies 37 | run: composer install 38 | 39 | - name: Build 40 | run: | 41 | wget https://github.com/box-project/box/releases/download/3.13.0/box.phar -O /tmp/box.phar 42 | php /tmp/box.phar build 43 | 44 | - name: Test 45 | run: build/insight.phar list 46 | 47 | - name: Release 48 | run: | 49 | aws s3 cp build/insight.phar s3://get.insight.symfony.com/insight.phar 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /cache/ 2 | /composer.lock 3 | /build/ 4 | /insight.log 5 | /vendor/ 6 | /.php_cs.cache 7 | /.phpunit.result.cache 8 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | in([__DIR__.'/Cli', __DIR__.'/Sdk']); 5 | 6 | $config = new PhpCsFixer\Config(); 7 | $config 8 | ->setRules(array( 9 | '@Symfony' => true, 10 | '@Symfony:risky' => true, 11 | 'array_syntax' => ['syntax' => 'short'], 12 | 'class_attributes_separation' => ['elements' => ['const' => 'one']], 13 | )) 14 | ->setRiskyAllowed(true) 15 | ->setFinder($finder) 16 | ; 17 | 18 | return $config; 19 | -------------------------------------------------------------------------------- /Cli/Application.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Cli; 13 | 14 | use Monolog\Handler\StreamHandler; 15 | use Monolog\Logger; 16 | use SensioLabs\Insight\Cli\Command as LocalCommand; 17 | use SensioLabs\Insight\Cli\Helper\ConfigurationHelper; 18 | use SensioLabs\Insight\Cli\Helper\FailConditionHelper; 19 | use SensioLabs\Insight\Sdk\Api; 20 | use Symfony\Component\Console\Application as SymfonyApplication; 21 | use Symfony\Component\Console\Command\Command; 22 | use Symfony\Component\Console\Helper\HelperSet; 23 | use Symfony\Component\Console\Input\InputDefinition; 24 | use Symfony\Component\Console\Input\InputInterface; 25 | use Symfony\Component\Console\Input\InputOption; 26 | use Symfony\Component\Console\Output\OutputInterface; 27 | 28 | class Application extends SymfonyApplication 29 | { 30 | const APPLICATION_NAME = 'SymfonyInsight CLI'; 31 | const APPLICATION_VERSION = '1.7.4'; 32 | 33 | private $api; 34 | private $apiConfig; 35 | private $logFile; 36 | 37 | public function __construct() 38 | { 39 | $this->apiConfig = [ 40 | 'base_url' => Api::ENDPOINT, 41 | ]; 42 | 43 | parent::__construct(static::APPLICATION_NAME, static::APPLICATION_VERSION); 44 | } 45 | 46 | public function getApi() 47 | { 48 | if ($this->api) { 49 | return $this->api; 50 | } 51 | 52 | $config = $this->apiConfig; 53 | if (\array_key_exists('api_endpoint', $config)) { 54 | $config['base_url'] = $config['api_endpoint']; 55 | } 56 | 57 | $this->api = new Api($config); 58 | 59 | if ($this->logFile) { 60 | if (!class_exists('Monolog\Logger')) { 61 | throw new \InvalidArgumentException('You must include monolog if you want to log (run "composer install --dev")'); 62 | } 63 | $logger = new Logger('insight'); 64 | $logger->pushHandler(new StreamHandler($this->logFile, Logger::DEBUG)); 65 | 66 | $this->api->setLogger($logger); 67 | } 68 | 69 | return $this->api; 70 | } 71 | 72 | public function getLongVersion(): string 73 | { 74 | $version = parent::getLongVersion().' by Symfony'; 75 | $commit = '@git-commit@'; 76 | 77 | if ('@'.'git-commit@' !== $commit) { 78 | $version .= ' ('.substr($commit, 0, 7).')'; 79 | } 80 | 81 | return $version; 82 | } 83 | 84 | protected function getDefaultHelperSet(): HelperSet 85 | { 86 | $helperSet = parent::getDefaultHelperSet(); 87 | 88 | $helperSet->set(new ConfigurationHelper(Api::ENDPOINT)); 89 | $helperSet->set(new FailConditionHelper()); 90 | 91 | return $helperSet; 92 | } 93 | 94 | protected function getDefaultInputDefinition(): InputDefinition 95 | { 96 | $definition = parent::getDefaultInputDefinition(); 97 | 98 | $definition->addOption(new InputOption('api-token', null, InputOption::VALUE_REQUIRED, 'Your api token.')); 99 | $definition->addOption(new InputOption('user-uuid', null, InputOption::VALUE_REQUIRED, 'Your user uuid.')); 100 | $definition->addOption(new InputOption('api-endpoint', null, InputOption::VALUE_REQUIRED, 'The api endpoint.')); 101 | $definition->addOption(new InputOption('log', null, InputOption::VALUE_OPTIONAL, 'Add some log capability. Specify a log file if you want to change the log location.')); 102 | 103 | return $definition; 104 | } 105 | 106 | protected function getDefaultCommands(): array 107 | { 108 | $defaultCommands = parent::getDefaultCommands(); 109 | 110 | $defaultCommands[] = new LocalCommand\AnalysisCommand(); 111 | $defaultCommands[] = new LocalCommand\AnalyzeCommand(); 112 | $defaultCommands[] = new LocalCommand\ConfigureCommand(); 113 | $defaultCommands[] = new LocalCommand\ProjectsCommand(); 114 | $defaultCommands[] = new LocalCommand\SelfUpdateCommand(); 115 | 116 | return $defaultCommands; 117 | } 118 | 119 | protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output): int 120 | { 121 | if (!$command instanceof LocalCommand\NeedConfigurationInterface) { 122 | return parent::doRunCommand($command, $input, $output); 123 | } 124 | 125 | $configuration = $this->getHelperSet()->get('configuration')->getConfiguration($input, $output); 126 | $this->apiConfig = array_merge($this->apiConfig, $configuration->toArray()); 127 | 128 | if (false !== $input->getParameterOption('--log')) { 129 | $this->logFile = $input->getParameterOption('--log') ?: getcwd().'/insight.log'; 130 | } 131 | 132 | return parent::doRunCommand($command, $input, $output); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /Cli/Command/AnalysisCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Cli\Command; 13 | 14 | use SensioLabs\Insight\Cli\Helper\DescriptorHelper; 15 | use Symfony\Component\Console\Command\Command; 16 | use Symfony\Component\Console\Input\InputArgument; 17 | use Symfony\Component\Console\Input\InputInterface; 18 | use Symfony\Component\Console\Input\InputOption; 19 | use Symfony\Component\Console\Output\OutputInterface; 20 | 21 | class AnalysisCommand extends Command implements NeedConfigurationInterface 22 | { 23 | protected function configure(): void 24 | { 25 | $this 26 | ->setName('analysis') 27 | ->addArgument('project-uuid', InputArgument::REQUIRED) 28 | ->addOption('format', null, InputOption::VALUE_REQUIRED, 'To output in other formats', 'txt') 29 | ->addOption('fail-condition', null, InputOption::VALUE_REQUIRED, '') 30 | ->addOption('show-ignored-violations', null, InputOption::VALUE_NONE, 'Show ignored violations') 31 | ->setDescription('Show the last project analysis') 32 | ; 33 | } 34 | 35 | protected function execute(InputInterface $input, OutputInterface $output): int 36 | { 37 | $api = $this->getApplication()->getApi(); 38 | $analysis = $api->getProject($input->getArgument('project-uuid'))->getLastAnalysis(); 39 | 40 | if (!$analysis) { 41 | $output->writeln('There are no analyses'); 42 | 43 | return 1; 44 | } 45 | 46 | $helper = new DescriptorHelper($api->getSerializer()); 47 | $helper->describe($output, $analysis, $input->getOption('format'), $input->getOption('show-ignored-violations')); 48 | 49 | if ('txt' === $input->getOption('format') && OutputInterface::VERBOSITY_VERBOSE > $output->getVerbosity()) { 50 | $output->writeln(''); 51 | $output->writeln('Re-run this command with -v option to get the full report'); 52 | } 53 | 54 | if (!$expr = $input->getOption('fail-condition')) { 55 | return 1; 56 | } 57 | 58 | return $this->getHelperSet()->get('fail_condition')->evaluate($analysis, $expr); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Cli/Command/AnalyzeCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Cli\Command; 13 | 14 | use SensioLabs\Insight\Cli\Helper\DescriptorHelper; 15 | use SensioLabs\Insight\Sdk\Api; 16 | use Symfony\Component\Console\Command\Command; 17 | use Symfony\Component\Console\Input\InputArgument; 18 | use Symfony\Component\Console\Input\InputInterface; 19 | use Symfony\Component\Console\Input\InputOption; 20 | use Symfony\Component\Console\Output\OutputInterface; 21 | use Symfony\Component\Console\Style\SymfonyStyle; 22 | 23 | class AnalyzeCommand extends Command implements NeedConfigurationInterface 24 | { 25 | protected function configure(): void 26 | { 27 | $this 28 | ->setName('analyze') 29 | ->addArgument('project-uuid', InputArgument::REQUIRED) 30 | ->addOption('format', null, InputOption::VALUE_REQUIRED, 'To output in other formats', 'txt') 31 | ->addOption('reference', null, InputOption::VALUE_REQUIRED, 'The git reference to analyze') 32 | ->addOption('branch', null, InputOption::VALUE_REQUIRED, 'The analysis current branch') 33 | ->addOption('show-ignored-violations', null, InputOption::VALUE_NONE, 'Show ignored violations') 34 | ->addOption('poll-period', null, InputOption::VALUE_REQUIRED, 'How regularly should the analysis status be checked in seconds', 30) 35 | ->addOption('fail-condition', null, InputOption::VALUE_REQUIRED, '') 36 | ->addOption('no-wait', null, InputOption::VALUE_NONE, 'Do not wait for analysis result') 37 | ->setDescription('Analyze a project') 38 | ; 39 | } 40 | 41 | protected function execute(InputInterface $input, OutputInterface $output): int 42 | { 43 | $projectUuid = $input->getArgument('project-uuid'); 44 | 45 | $pollPeriod = (int) $input->getOption('poll-period'); 46 | if ($pollPeriod < 30) { 47 | $pollPeriod = 30; 48 | } 49 | 50 | /** @var Api $api */ 51 | $api = $this->getApplication()->getApi(); 52 | 53 | if (false !== strpos($api->getBaseUrl(), '.sensiolabs.com')) { 54 | $io = new SymfonyStyle($input, $output); 55 | $io->warning('You are using the legacy URL of SymfonyInsight which may stop working in the future. You should reconfigure this tool by running the "configure" command and use "https://insight.symfony.com" as endpoint.'); 56 | } 57 | 58 | $analysis = $api->analyze($projectUuid, $input->getOption('reference'), $input->getOption('branch')); 59 | 60 | $chars = ['-', '\\', '|', '/']; 61 | $noAnsiStatus = 'Analysis running'; 62 | $output->getErrorOutput()->writeln($noAnsiStatus); 63 | 64 | if ($input->getOption('no-wait')) { 65 | $output->writeln('The analysis is launched. Please check the result in you SymfonyInsight notifications.'); 66 | 67 | return 0; 68 | } 69 | 70 | $position = 1; 71 | 72 | while (true) { 73 | // Check the status every poll-period * 5 loops (5 * 200ms = 1s) 74 | if (0 === $position % (5 * $pollPeriod)) { 75 | $analysis = $api->getAnalysisStatus($projectUuid, $analysis->getNumber()); 76 | } 77 | 78 | if ('txt' === $input->getOption('format')) { 79 | if (!$output->isDecorated()) { 80 | if ($noAnsiStatus !== $analysis->getStatusMessage()) { 81 | $output->writeln($noAnsiStatus = $analysis->getStatusMessage()); 82 | } 83 | } else { 84 | $output->write(sprintf("%s %-80s\r", $chars[$position % 4], $analysis->getStatusMessage())); 85 | } 86 | } 87 | 88 | if ($analysis->isFinished()) { 89 | break; 90 | } 91 | 92 | usleep(200000); 93 | 94 | ++$position; 95 | } 96 | 97 | $analysis = $api->getAnalysis($projectUuid, $analysis->getNumber()); 98 | if ($analysis->isFailed()) { 99 | $output->writeln(sprintf('There was an error: "%s"', $analysis->getFailureMessage())); 100 | 101 | return 1; 102 | } 103 | 104 | $helper = new DescriptorHelper($api->getSerializer()); 105 | $helper->describe($output, $analysis, $input->getOption('format'), $input->getOption('show-ignored-violations')); 106 | 107 | if ('txt' === $input->getOption('format') && OutputInterface::VERBOSITY_VERBOSE > $output->getVerbosity()) { 108 | $output->writeln(''); 109 | $output->writeln(sprintf('Run %s %s %s -v to get the full report', $_SERVER['PHP_SELF'], 'analysis', $projectUuid)); 110 | } 111 | 112 | if (!$expr = $input->getOption('fail-condition')) { 113 | return 0; 114 | } 115 | 116 | return $this->getHelperSet()->get('fail_condition')->evaluate($analysis, $expr); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Cli/Command/ConfigureCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Cli\Command; 13 | 14 | use Symfony\Component\Console\Command\Command; 15 | use Symfony\Component\Console\Input\InputInterface; 16 | use Symfony\Component\Console\Output\OutputInterface; 17 | 18 | class ConfigureCommand extends Command 19 | { 20 | protected function configure(): void 21 | { 22 | $this 23 | ->setName('configure') 24 | ->setDescription('(Re-)Configure your credentials.') 25 | ; 26 | } 27 | 28 | protected function execute(InputInterface $input, OutputInterface $output): int 29 | { 30 | $this->getHelperSet()->get('configuration')->updateConfigurationManually($input, $output); 31 | 32 | return 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Cli/Command/NeedConfigurationInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Cli\Command; 13 | 14 | interface NeedConfigurationInterface 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /Cli/Command/ProjectsCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Cli\Command; 13 | 14 | use Symfony\Component\Console\Command\Command; 15 | use Symfony\Component\Console\Helper\Table; 16 | use Symfony\Component\Console\Input\InputInterface; 17 | use Symfony\Component\Console\Output\OutputInterface; 18 | 19 | class ProjectsCommand extends Command implements NeedConfigurationInterface 20 | { 21 | protected function configure(): void 22 | { 23 | $this 24 | ->setName('projects') 25 | ->setDescription('List all your projects') 26 | ; 27 | } 28 | 29 | protected function execute(InputInterface $input, OutputInterface $output): int 30 | { 31 | $api = $this->getApplication()->getApi(); 32 | 33 | $projectsResource = $api->getProjects(); 34 | $projects = $projectsResource->getProjects(); 35 | $nbPage = ceil($projectsResource->getTotal() / 10); 36 | $page = 1; 37 | while ($page < $nbPage) { 38 | ++$page; 39 | $projects = array_merge($projects, $api->getProjects($page)->getProjects()); 40 | } 41 | 42 | if (!$projects) { 43 | $output->writeln('There are no projects'); 44 | } 45 | 46 | $rows = []; 47 | foreach ($projects as $project) { 48 | if ($project->getLastAnalysis()) { 49 | $grade = $project->getLastAnalysis()->getGrade(); 50 | } else { 51 | $grade = 'This project has no analyses'; 52 | } 53 | $rows[] = [$project->getName(), $project->getUuid(), $grade]; 54 | } 55 | 56 | $table = new Table($output); 57 | $table->setHeaders(['name', 'uuid', 'grade']) 58 | ->setRows($rows) 59 | ->render() 60 | ; 61 | 62 | return 0; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Cli/Command/SelfUpdateCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Cli\Command; 13 | 14 | use Symfony\Component\Console\Command\Command; 15 | use Symfony\Component\Console\Input\InputInterface; 16 | use Symfony\Component\Console\Output\OutputInterface; 17 | 18 | /** 19 | * @author Igor Wiedler 20 | * @author Stephane PY 21 | * @author Grégoire Pineau 22 | */ 23 | class SelfUpdateCommand extends Command 24 | { 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | protected function configure(): void 29 | { 30 | $this 31 | ->setName('self-update') 32 | ->setDescription('Update insight.phar to the latest version.') 33 | ->setHelp(<<%command.name% command replace your insight.phar by the latest 35 | version. 36 | 37 | php insight.phar %command.name% 38 | 39 | EOT 40 | ) 41 | ; 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | protected function execute(InputInterface $input, OutputInterface $output): int 48 | { 49 | $remoteFilename = 'http://get.insight.sensiolabs.com/insight.phar'; 50 | $localFilename = $_SERVER['argv'][0]; 51 | $tempFilename = basename($localFilename, '.phar').'-temp.phar'; 52 | 53 | try { 54 | copy($remoteFilename, $tempFilename); 55 | 56 | if (md5_file($localFilename) == md5_file($tempFilename)) { 57 | $output->writeln('insight is already up to date.'); 58 | unlink($tempFilename); 59 | 60 | return 0; 61 | } 62 | 63 | chmod($tempFilename, 0777 & ~umask()); 64 | 65 | // test the phar validity 66 | $phar = new \Phar($tempFilename); 67 | // free the variable to unlock the file 68 | unset($phar); 69 | rename($tempFilename, $localFilename); 70 | 71 | $output->writeln('insight updated.'); 72 | 73 | return 0; 74 | } catch (\Exception $e) { 75 | if (!$e instanceof \UnexpectedValueException && !$e instanceof \PharException) { 76 | throw $e; 77 | } 78 | unlink($tempFilename); 79 | $output->writeln('The download is corrupt ('.$e->getMessage().').'); 80 | $output->writeln('Please re-run the self-update command to try again.'); 81 | 82 | return 1; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Cli/Configuration.php: -------------------------------------------------------------------------------- 1 | storagePath = $this->getStoragePath(); 15 | $this->load(); 16 | } 17 | 18 | public function getUserUuid() 19 | { 20 | return $this->userUuid; 21 | } 22 | 23 | public function setUserUuid($userUuid) 24 | { 25 | $this->userUuid = $userUuid; 26 | } 27 | 28 | public function getApiToken() 29 | { 30 | return $this->apiToken; 31 | } 32 | 33 | public function setApiToken($apiToken) 34 | { 35 | $this->apiToken = $apiToken; 36 | } 37 | 38 | public function getApiEndpoint() 39 | { 40 | return $this->apiEndpoint; 41 | } 42 | 43 | public function setApiEndpoint($apiEndpoint) 44 | { 45 | $this->apiEndpoint = $apiEndpoint; 46 | } 47 | 48 | public function toArray() 49 | { 50 | return [ 51 | 'user_uuid' => $this->userUuid, 52 | 'api_token' => $this->apiToken, 53 | 'api_endpoint' => $this->apiEndpoint, 54 | ]; 55 | } 56 | 57 | public function save() 58 | { 59 | file_put_contents($this->storagePath, json_encode($this->toArray())); 60 | } 61 | 62 | public function equals(self $configuration) 63 | { 64 | if ($this->userUuid !== $configuration->userUuid) { 65 | return false; 66 | } 67 | if ($this->apiToken !== $configuration->apiToken) { 68 | return false; 69 | } 70 | if ($this->apiEndpoint !== $configuration->apiEndpoint) { 71 | return false; 72 | } 73 | 74 | return true; 75 | } 76 | 77 | private function load() 78 | { 79 | if (!file_exists($this->storagePath)) { 80 | return; 81 | } 82 | 83 | $data = json_decode(file_get_contents($this->storagePath), true); 84 | 85 | if (\array_key_exists('user_uuid', $data)) { 86 | $this->userUuid = $data['user_uuid']; 87 | } 88 | if (\array_key_exists('api_token', $data)) { 89 | $this->apiToken = $data['api_token']; 90 | } 91 | if (\array_key_exists('api_endpoint', $data)) { 92 | $this->apiEndpoint = $data['api_endpoint']; 93 | } 94 | } 95 | 96 | private function getStoragePath() 97 | { 98 | $storagePath = getenv('INSIGHT_HOME'); 99 | 100 | if (!$storagePath) { 101 | if (\defined('PHP_WINDOWS_VERSION_MAJOR')) { 102 | if (!getenv('APPDATA')) { 103 | throw new \RuntimeException('The APPDATA or INSIGHT_HOME environment variable must be set for insight to run correctly'); 104 | } 105 | $storagePath = strtr(getenv('APPDATA'), '\\', '/').'/Sensiolabs'; 106 | } else { 107 | if (!getenv('HOME')) { 108 | throw new \RuntimeException('The HOME or INSIGHT_HOME environment variable must be set for insight to run correctly'); 109 | } 110 | $storagePath = rtrim(getenv('HOME'), '/').'/.sensiolabs'; 111 | } 112 | } 113 | 114 | if (!is_dir($storagePath) && !@mkdir($storagePath, 0777, true)) { 115 | throw new \RuntimeException(sprintf('The directory "%s" does not exist and could not be created.', $storagePath)); 116 | } 117 | 118 | if (!is_writable($storagePath)) { 119 | throw new \RuntimeException(sprintf('The directory "%s" is not writable.', $storagePath)); 120 | } 121 | 122 | return $storagePath.'/insight.json'; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Cli/Descriptor/AbstractDescriptor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Cli\Descriptor; 13 | 14 | use SensioLabs\Insight\Sdk\Model\Analysis; 15 | use SensioLabs\Insight\Sdk\Model\Violation; 16 | 17 | abstract class AbstractDescriptor 18 | { 19 | public function describe($object, array $options = []) 20 | { 21 | if ($object instanceof Analysis) { 22 | if (!$options['show_ignored_violations'] && $object->getViolations()) { 23 | $object->getViolations()->filter(function (Violation $v) { 24 | return !$v->isIgnored(); 25 | }); 26 | } 27 | 28 | return $this->describeAnalysis($object, $options); 29 | } 30 | 31 | throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', \get_class($object))); 32 | } 33 | 34 | abstract protected function describeAnalysis(Analysis $argument, array $options = []); 35 | } 36 | -------------------------------------------------------------------------------- /Cli/Descriptor/JsonDescriptor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Cli\Descriptor; 13 | 14 | use JMS\Serializer\Serializer; 15 | use SensioLabs\Insight\Sdk\Model\Analysis; 16 | 17 | class JsonDescriptor extends AbstractDescriptor 18 | { 19 | private $serializer; 20 | 21 | public function __construct(Serializer $serializer) 22 | { 23 | $this->serializer = $serializer; 24 | } 25 | 26 | protected function describeAnalysis(Analysis $analysis, array $options = []) 27 | { 28 | $output = $options['output']; 29 | $output->writeln($this->serializer->serialize($analysis, 'json')); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Cli/Descriptor/PmdDescriptor.php: -------------------------------------------------------------------------------- 1 | formatOutput = true; 24 | $xml->preserveWhiteSpace = true; 25 | 26 | $pmd = $xml->createElement('pmd'); 27 | $pmd->setAttribute('timestamp', $analysis->getEndAt()->format('c')); 28 | 29 | $xml->appendChild($pmd); 30 | 31 | $violations = $analysis->getViolations(); 32 | if ($violations) { 33 | foreach ($violations as $violation) { 34 | /* 35 | * @var $violation \SensioLabs\Insight\Sdk\Model\Violation 36 | */ 37 | $filename = $violation->getResource(); 38 | 39 | $nodes = $xpath->query(sprintf('//file[@name="%s"]', $filename)); 40 | 41 | if ($nodes->length > 0) { 42 | $node = $nodes->item(0); 43 | } else { 44 | $node = $xml->createElement('file'); 45 | $node->setAttribute('name', $filename); 46 | 47 | $pmd->appendChild($node); 48 | } 49 | 50 | $violationNode = $xml->createElement('violation', $violation->getMessage()); 51 | $node->appendChild($violationNode); 52 | 53 | $violationNode->setAttribute('beginline', $violation->getLine()); 54 | $violationNode->setAttribute('endline', $violation->getLine()); 55 | $violationNode->setAttribute('rule', $violation->getTitle()); 56 | $violationNode->setAttribute('ruleset', $violation->getCategory()); 57 | $violationNode->setAttribute('priority', $this->getPriority($violation)); 58 | } 59 | } 60 | 61 | $output->writeln($xml->saveXML()); 62 | } 63 | 64 | private function getPriority(Violation $violation) 65 | { 66 | switch ($violation->getSeverity()) { 67 | case 'critical': 68 | return self::PHPMD_PRIORITY_HIGH; 69 | 70 | case 'major': 71 | return self::PHPMD_PRIORITY_MEDIUM; 72 | 73 | default: 74 | return self::PHPMD_PRIORITY_LOW; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Cli/Descriptor/TextDescriptor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Cli\Descriptor; 13 | 14 | use SensioLabs\Insight\Sdk\Model\Analysis; 15 | use Symfony\Component\Console\Output\OutputInterface; 16 | 17 | class TextDescriptor extends AbstractDescriptor 18 | { 19 | protected function describeAnalysis(Analysis $analysis, array $options = []) 20 | { 21 | $output = $options['output']; 22 | if (OutputInterface::VERBOSITY_VERY_VERBOSE <= $output->getVerbosity()) { 23 | $output->write(sprintf('Began at: %s', $analysis->getBeginAt()->format('Y-m-d h:i:s'))); 24 | } 25 | if (!$analysis->isFinished()) { 26 | if (OutputInterface::VERBOSITY_VERY_VERBOSE <= $output->getVerbosity()) { 27 | $output->writeln(''); 28 | } 29 | $output->writeln('The analysis is not finished yet.'); 30 | 31 | return; 32 | } 33 | if (OutputInterface::VERBOSITY_VERY_VERBOSE <= $output->getVerbosity()) { 34 | $output->write(sprintf(' Ended at: %s', $analysis->getEndAt()->format('Y-m-d h:i:s'))); 35 | $output->writeln(sprintf(' Real duration: %s.', $analysis->getEndAt()->format('Y-m-d h:i:s'))); 36 | $output->writeln(''); 37 | } 38 | $output->write(sprintf( 39 | 'The project has %d violations, it got the %s grade.', 40 | $analysis->getNbViolations(), 41 | $analysis->getGrade() 42 | )); 43 | 44 | $grades = $analysis->getGrades(); 45 | $bestGrade = end($grades); 46 | if ($bestGrade == $analysis->getGrade()) { 47 | $output->writeln(''); 48 | 49 | return; 50 | } 51 | 52 | $output->writeln(sprintf( 53 | ' %d hours to get the %s grade and %d hours to get the %s grade', 54 | $analysis->getRemediationCostForNextGrade(), 55 | $analysis->getNextGrade(), 56 | $analysis->getRemediationCost(), 57 | $bestGrade 58 | )); 59 | $output->writeln(''); 60 | 61 | if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity() && $analysis->getViolations()) { 62 | $template = <<{{ resource }}:{{ line }} 64 | Category: {{ category }} Severity: {{ severity }} 65 | Title: {{ title }}{{ ignored }} 66 | Message: {{ message }} 67 | 68 | EOL; 69 | foreach ($analysis->getViolations() as $violation) { 70 | $output->writeln(strtr($template, [ 71 | '{{ resource }}' => $violation->getResource(), 72 | '{{ line }}' => $violation->getLine(), 73 | '{{ category }}' => $violation->getCategory(), 74 | '{{ severity }}' => $violation->getSeverity(), 75 | '{{ title }}' => $violation->getTitle(), 76 | '{{ message }}' => $violation->getMessage(), 77 | '{{ ignored }}' => $violation->isIgnored() ? ' (ignored)' : null, 78 | ])); 79 | } 80 | } 81 | 82 | foreach ($analysis->getLinks() as $link) { 83 | if ('self' == $link->getRel() && 'text/html' == $link->getType()) { 84 | $output->writeln(sprintf('You can get the full report at %s', $link->getHref())); 85 | 86 | break; 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Cli/Descriptor/XmlDescriptor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Cli\Descriptor; 13 | 14 | use JMS\Serializer\Serializer; 15 | use SensioLabs\Insight\Sdk\Model\Analysis; 16 | 17 | class XmlDescriptor extends AbstractDescriptor 18 | { 19 | private $serializer; 20 | 21 | public function __construct(Serializer $serializer) 22 | { 23 | $this->serializer = $serializer; 24 | } 25 | 26 | protected function describeAnalysis(Analysis $analysis, array $options = []) 27 | { 28 | $output = $options['output']; 29 | $output->writeln($this->serializer->serialize($analysis, 'xml')); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Cli/Helper/ConfigurationHelper.php: -------------------------------------------------------------------------------- 1 | apiEndpoint = $apiEndpoint; 20 | } 21 | 22 | public function updateConfigurationManually(InputInterface $input, OutputInterface $output) 23 | { 24 | $configuration = new Configuration(); 25 | 26 | $userUuid = $input->getOption('user-uuid') ?: $configuration->getUserUuid(); 27 | $apiToken = $input->getOption('api-token') ?: $configuration->getApiToken(); 28 | 29 | // Avoid saving again a legacy URL 30 | $defaultEndpoint = $configuration->getApiEndpoint(); 31 | if (false !== strpos($defaultEndpoint, '.sensiolabs.com')) { 32 | $defaultEndpoint = null; 33 | } 34 | 35 | $apiEndpoint = $input->getOption('api-endpoint') ?: $defaultEndpoint; 36 | 37 | $configuration->setUserUuid($this->askValue($input, $output, 'User Uuid', $userUuid)); 38 | $configuration->setApiToken($this->askValue($input, $output, 'Api Token', $apiToken)); 39 | $configuration->setApiEndpoint($this->askValue($input, $output, 'Api Endpoint', $apiEndpoint ?: $this->apiEndpoint)); 40 | 41 | if (false !== strpos($configuration->getApiEndpoint(), '.sensiolabs.com')) { 42 | $io = new SymfonyStyle($input, $output); 43 | $io->warning('You are using the legacy URL of SymfonyInsight which may stop working in the future. You should reconfigure this tool by running the "configure" command and use "https://insight.symfony.com" as endpoint.'); 44 | } 45 | 46 | $this->saveConfiguration($input, $output, $configuration); 47 | } 48 | 49 | public function getConfiguration(InputInterface $input, OutputInterface $output) 50 | { 51 | $previousConfiguration = new Configuration(); 52 | $configuration = clone $previousConfiguration; 53 | 54 | $this->resolveValue($input, $output, $configuration, 'User Uuid', null); 55 | $this->resolveValue($input, $output, $configuration, 'Api Token', null); 56 | $this->resolveValue($input, $output, $configuration, 'Api Endpoint', $this->apiEndpoint); 57 | 58 | if (!$configuration->equals($previousConfiguration)) { 59 | $this->saveConfiguration($input, $output, $configuration); 60 | } 61 | 62 | return $configuration; 63 | } 64 | 65 | public function getName(): string 66 | { 67 | return 'configuration'; 68 | } 69 | 70 | private function resolveValue(InputInterface $input, OutputInterface $output, Configuration $configuration, $varName, $default = null) 71 | { 72 | $configurationProperty = str_replace(' ', '', $varName); 73 | 74 | $value = $this->getValue($input, $varName); 75 | 76 | if (!$value) { 77 | $value = $configuration->{'get'.$configurationProperty}(); 78 | } 79 | if (!$value) { 80 | $value = $default; 81 | } 82 | if (!$value && $input->isInteractive()) { 83 | $value = $this->askValue($input, $output, $varName); 84 | } 85 | if (!$value) { 86 | throw new \InvalidArgumentException(sprintf('You should provide your %s.', $varName)); 87 | } 88 | $configuration->{'set'.$configurationProperty}($value); 89 | } 90 | 91 | private function getValue(InputInterface $input, $varName) 92 | { 93 | $envVarName = sprintf('INSIGHT_%s', str_replace(' ', '_', strtoupper($varName))); 94 | if ($value = getenv($envVarName)) { 95 | return $value; 96 | } 97 | 98 | $cliVarName = sprintf('--%s', str_replace(' ', '-', strtolower($varName))); 99 | 100 | return $input->getParameterOption($cliVarName); 101 | } 102 | 103 | private function askValue(InputInterface $input, OutputInterface $output, $varname, $default = null) 104 | { 105 | $validator = static function ($v) use ($varname) { 106 | if (!$v) { 107 | throw new \InvalidArgumentException(sprintf('Your must provide a %s!', $varname)); 108 | } 109 | 110 | return $v; 111 | }; 112 | 113 | if (!$input->isInteractive()) { 114 | return $validator($default); 115 | } 116 | 117 | $question = new Question( 118 | $default ? sprintf('What is your %s? [%s] ', $varname, $default) : sprintf('What is your %s? ', $varname), 119 | $default 120 | ); 121 | 122 | $question->setValidator($validator); 123 | 124 | return $this->getHelperSet()->get('question')->ask($input, $output, $question); 125 | } 126 | 127 | private function saveConfiguration(InputInterface $input, OutputInterface $output, Configuration $configuration) 128 | { 129 | if (!$input->isInteractive()) { 130 | $configuration->save(); 131 | 132 | return; 133 | } 134 | 135 | $question = new ConfirmationQuestion('Do you want to save this new configuration? [Y/n] '); 136 | 137 | if ($this->getHelperSet()->get('question')->ask($input, $output, $question)) { 138 | $configuration->save(); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Cli/Helper/DescriptorHelper.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Cli\Helper; 13 | 14 | use JMS\Serializer\Serializer; 15 | use SensioLabs\Insight\Cli\Descriptor\AbstractDescriptor; 16 | use SensioLabs\Insight\Cli\Descriptor\JsonDescriptor; 17 | use SensioLabs\Insight\Cli\Descriptor\PmdDescriptor; 18 | use SensioLabs\Insight\Cli\Descriptor\TextDescriptor; 19 | use SensioLabs\Insight\Cli\Descriptor\XmlDescriptor; 20 | use Symfony\Component\Console\Helper\Helper; 21 | use Symfony\Component\Console\Output\OutputInterface; 22 | 23 | class DescriptorHelper extends Helper 24 | { 25 | private $descriptors = []; 26 | 27 | public function __construct(Serializer $serializer) 28 | { 29 | $this 30 | ->register('json', new JsonDescriptor($serializer)) 31 | ->register('pmd', new PmdDescriptor()) 32 | ->register('txt', new TextDescriptor()) 33 | ->register('xml', new XmlDescriptor($serializer)) 34 | ; 35 | } 36 | 37 | public function describe(OutputInterface $output, $object, $format = null, $showIgnoredViolation = false) 38 | { 39 | $options = [ 40 | 'raw_text' => false, 41 | 'format' => $format ?: 'txt', 42 | 'output' => $output, 43 | 'show_ignored_violations' => $showIgnoredViolation, 44 | ]; 45 | $options['type'] = 'txt' === $options['format'] ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW; 46 | 47 | if (!isset($this->descriptors[$options['format']])) { 48 | throw new \InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format'])); 49 | } 50 | 51 | $this->descriptors[$options['format']]->describe($object, $options); 52 | } 53 | 54 | public function register($format, AbstractDescriptor $descriptor) 55 | { 56 | $this->descriptors[$format] = $descriptor; 57 | 58 | return $this; 59 | } 60 | 61 | /** 62 | * {@inheritdoc} 63 | */ 64 | public function getName(): string 65 | { 66 | return 'descriptor'; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Cli/Helper/FailConditionHelper.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Cli\Helper; 13 | 14 | use SensioLabs\Insight\Sdk\Model\Analysis; 15 | use Symfony\Component\Console\Helper\Helper; 16 | use Symfony\Component\ExpressionLanguage\ExpressionLanguage; 17 | 18 | class FailConditionHelper extends Helper 19 | { 20 | private $el; 21 | 22 | public function __construct() 23 | { 24 | $this->el = new ExpressionLanguage(); 25 | } 26 | 27 | public function evaluate(Analysis $analysis, $expr) 28 | { 29 | $analysisData = [ 30 | 'grade' => $analysis->getGrade(), 31 | 'nbViolations' => 0, 32 | 'remediationCost' => $analysis->getRemediationCost(), 33 | ]; 34 | 35 | $counts = [ 36 | // Category 37 | 'architecture' => 0, 38 | 'bugrisk' => 0, 39 | 'codestyle' => 0, 40 | 'deadcode' => 0, 41 | 'performance' => 0, 42 | 'readability' => 0, 43 | 'security' => 0, 44 | 45 | // Severity 46 | 'critical' => 0, 47 | 'major' => 0, 48 | 'minor' => 0, 49 | 'info' => 0, 50 | ]; 51 | 52 | $violations = $analysis->getViolations() ?: []; 53 | 54 | foreach ($violations as $violation) { 55 | ++$counts[$violation->getCategory()]; 56 | ++$counts[$violation->getSeverity()]; 57 | ++$analysisData['nbViolations']; 58 | } 59 | 60 | $vars = [ 61 | 'analysis' => (object) $analysisData, 62 | 'counts' => (object) $counts, 63 | ]; 64 | 65 | if ($this->el->evaluate($expr, $vars)) { 66 | return 70; 67 | } 68 | 69 | return 0; 70 | } 71 | 72 | public function getName(): string 73 | { 74 | return 'fail_condition'; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Symfony 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SymfonyInsight SDK 2 | ================== 3 | 4 | About 5 | ----- 6 | 7 | This is the official SDK for the [SymfonyInsight](https://insight.symfony.com/) API. 8 | 9 | Installation 10 | ------------ 11 | 12 | To install the SDK, run the command below and you will get the latest version: 13 | 14 | composer require sensiolabs/insight 15 | 16 | Command Line Tool 17 | ----------------- 18 | 19 | The easiest way to use the SymfonyInsight API is via the built-in command line tool. 20 | 21 | A phar version of the command line tool exists to avoid installation of this 22 | project. Download it, then use it like the command line tool: 23 | 24 | $ curl -o insight.phar -s https://get.insight.symfony.com/insight.phar 25 | # or 26 | $ wget https://get.insight.symfony.com/insight.phar 27 | 28 | # Then 29 | $ php insight.phar 30 | 31 | List all the projects in your account: 32 | 33 | $ php insight.phar projects 34 | 35 | The first time, you will be prompted for your SymfonyInsight API key and 36 | user UUID (which can be found under the ["Account" section](https://insight.symfony.com/account) on the website). 37 | 38 | These information are then stored locally, but can still be overridden via the 39 | `--api-token` and `--user-uuid` options. 40 | 41 | To run an analysis: 42 | 43 | $ php insight.phar analyze UUID 44 | 45 | where `UUID` is the UUID of the project you want to analyze (the UUIDs are 46 | listed by the `projects` command). 47 | 48 | To export an analysis report: 49 | 50 | $ php insight.phar analysis UUID --format="xml" # or --format="json" or --format="pmd" 51 | 52 | Configuration 53 | ------------- 54 | 55 | use SensioLabs\Insight\Sdk\Api; 56 | 57 | $api = new Api(array( 58 | 'api_token' => 'your api token', 59 | 'user_uuid' => 'your user uuid', 60 | )); 61 | 62 | Usage 63 | ----- 64 | 65 | ### List all projects: 66 | 67 | $api->getProjects(); 68 | $api->getProjects(2); // For the second page 69 | 70 | ### Get a project: 71 | 72 | $project = $api->getProject('project uuid'); 73 | 74 | ### Update a project 75 | 76 | $api->updateProject($project); 77 | 78 | Note: If something went wrong, see *Error management* section 79 | 80 | ### Post a project 81 | 82 | use SensioLabs\Insight\Sdk\Model\Project; 83 | 84 | $project = new Project(); 85 | $project 86 | ->setName('Foo') 87 | ->setDescription('Foo') 88 | ->setType(TYPE_WEBSITE::TYPE_WEBSITE) 89 | ; 90 | 91 | $api->createProject($project) 92 | 93 | Note: If something went wrong, see *Error management* section 94 | 95 | ### Run an analysis 96 | 97 | // on the default branch 98 | $api->analyze('project uuid', 'master'); 99 | 100 | // for a specific branch or reference 101 | $api->analyze('project uuid', '1.0'); 102 | 103 | ### Get all analyses 104 | 105 | $api->getAnalyses('project uuid'); 106 | 107 | // for a specific branch 108 | $api->getAnalyses('project uuid', 'branch name'); 109 | 110 | ### Get an analysis 111 | 112 | $api->getAnalysis('project uuid', 'analysis id'); 113 | 114 | ### Get a status analysis 115 | 116 | $api->getAnalysisStatus('project uuid', 'analysis id'); 117 | 118 | ### Error management 119 | 120 | If something went wrong, an 121 | `SensioLabs\Insight\Sdk\Exception\ExceptionInterface` will be throw: 122 | 123 | * `ApiClientException` If you did something wrong. This exception contains the 124 | previous exception throw by guzzle. You can easily check if it is a: 125 | * 403: In this case, check your credentials 126 | * 404: In this case, check your request 127 | * 400: In this case, check the data sent. In this case, the Exception will 128 | contains a `SensioLabs\Insight\Sdk\Model\Error` object. Which will contains 129 | all form errors. 130 | * `ApiServerException` If something went wrong with the API. 131 | 132 | Jenkins/Hudson Integration 133 | -------------------------- 134 | 135 | Thanks to [Jenkins PMD Plugin](https://wiki.jenkins-ci.org/display/JENKINS/PMD+Plugin) and 136 | SymfonyInsight SDK PMD output you can easily embed SymfonyInsight reports into your build 137 | workflow, following these steps: 138 | 139 | *It is assumed you already have your project up and building in Jenkins and SymfonyInsight SDK installed*. 140 | 141 | 1. Retrieve your `SymfonyInsight API Token`, `User UUID` and `Project UUID` 142 | on your [account page](https://insight.symfony.com/account) 143 | 2. Install the Jenkins `PMD plugin`: 144 | [How to install a jenkins plugin](https://wiki.jenkins-ci.org/display/JENKINS/Plugins#Plugins-Howtoinstallplugins) 145 | 3. Optionally you can also install the `EnvInject Plugin` 146 | 4. Edit your project configuration 147 | 5. If you have EnvInject Plugin installed, 148 | enabled `Set environment variables` then add and adapt the following lines to variables name: 149 | 150 | INSIGHT_API_TOKEN="Your API Token" 151 | INSIGHT_USER_UUID="Your user UUID" 152 | INSIGHT_PROJECT_UUID="Your project UUID" 153 | 154 | 6. Add a `Execute shell` build step 155 | 7. In the new shell step add and adapt the following command (if you don't have EnvInject plugin, replace variables by plain values): 156 | 157 | /path/to/insight-sdk/bin/insight analysis \ 158 | --user-uuid $INSIGHT_USER_UUID \ 159 | --api-token $INSIGHT_API_TOKEN \ 160 | $INSIGHT_PROJECT_UUID --format=pmd > insight-pmd.xml 161 | 162 | 8. Enable `Publish PMD analysis results` using `insight-pmd.xml` as PMD result filename 163 | 9. Optionally, you can add the `insight-pmd.xml` file to artifacts to archive 164 | 10. Save and build! 165 | 166 | License 167 | ------- 168 | 169 | This library is licensed under the MIT license. 170 | -------------------------------------------------------------------------------- /Sdk/Api.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk; 13 | 14 | use JMS\Serializer\Serializer; 15 | use JMS\Serializer\SerializerBuilder; 16 | use Psr\Log\LoggerInterface; 17 | use SensioLabs\Insight\Sdk\Exception\ApiClientException; 18 | use SensioLabs\Insight\Sdk\Exception\ApiServerException; 19 | use SensioLabs\Insight\Sdk\Model\Analyses; 20 | use SensioLabs\Insight\Sdk\Model\Analysis; 21 | use SensioLabs\Insight\Sdk\Model\Project; 22 | use SensioLabs\Insight\Sdk\Model\Projects; 23 | use Symfony\Component\HttpClient\HttpClient; 24 | use Symfony\Component\HttpClient\ScopingHttpClient; 25 | use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; 26 | use Symfony\Contracts\HttpClient\Exception\ExceptionInterface; 27 | use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface; 28 | use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; 29 | use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; 30 | use Symfony\Contracts\HttpClient\HttpClientInterface; 31 | 32 | class Api 33 | { 34 | const ENDPOINT = 'https://insight.symfony.com'; 35 | 36 | private $baseUrl; 37 | private $httpClient; 38 | private $serializer; 39 | private $parser; 40 | private $logger; 41 | 42 | public function __construct(array $options = [], ?HttpClientInterface $httpClient = null, ?Parser $parser = null, ?LoggerInterface $logger = null) 43 | { 44 | $this->httpClient = $httpClient ?: HttpClient::create(); 45 | $this->parser = $parser ?: new Parser(); 46 | 47 | $defaultOptions = [ 48 | 'base_url' => static::ENDPOINT, 49 | 'cache' => false, 50 | 'debug' => false, 51 | ]; 52 | 53 | $required = ['api_token', 'base_url', 'user_uuid']; 54 | $options = array_merge($defaultOptions, $options); 55 | 56 | if ($missing = array_diff($required, array_keys($options))) { 57 | throw new \Exception('Config is missing the following keys: '.implode(', ', $missing)); 58 | } 59 | 60 | $this->baseUrl = $options['base_url']; 61 | 62 | $this->httpClient = new ScopingHttpClient( 63 | $this->httpClient, 64 | [ 65 | '.+' => [ 66 | 'base_uri' => $this->baseUrl, 67 | 'auth_basic' => [$options['user_uuid'], $options['api_token']], 68 | 'headers' => ['accept' => 'application/vnd.com.sensiolabs.insight+xml'], 69 | ], 70 | ], 71 | '.+' 72 | ); 73 | 74 | $serializerBuilder = SerializerBuilder::create() 75 | ->addMetadataDir(__DIR__.'/Model') 76 | ->setDebug($options['debug']) 77 | ; 78 | 79 | if ($cache = $options['cache']) { 80 | $serializerBuilder = $serializerBuilder->setCacheDir($cache); 81 | } 82 | 83 | $this->serializer = $serializerBuilder->build(); 84 | $this->logger = $logger; 85 | } 86 | 87 | public function getBaseUrl(): string 88 | { 89 | return $this->baseUrl; 90 | } 91 | 92 | /** 93 | * @param int $page 94 | * 95 | * @return Projects 96 | */ 97 | public function getProjects($page = 1) 98 | { 99 | return $this->serializer->deserialize( 100 | $this->send('GET', '/api/projects?page='.$page), 101 | Projects::class, 102 | 'xml' 103 | ); 104 | } 105 | 106 | /** 107 | * @param string $uuid 108 | * 109 | * @return Project 110 | */ 111 | public function getProject($uuid) 112 | { 113 | return $this->serializer->deserialize( 114 | $this->send('GET', sprintf('/api/projects/%s', $uuid)), 115 | Project::class, 116 | 'xml' 117 | ); 118 | } 119 | 120 | /** 121 | * @return Project 122 | */ 123 | public function updateProject(Project $project) 124 | { 125 | return $this->serializer->deserialize( 126 | $this->send('PUT', sprintf('/api/projects/%s', $project->getUuid()), ['insight_project' => $project->toArray()]), 127 | Project::class, 128 | 'xml' 129 | ); 130 | } 131 | 132 | /** 133 | * @return Project 134 | */ 135 | public function createProject(Project $project) 136 | { 137 | return $this->serializer->deserialize( 138 | $this->send('POST', '/api/projects', ['insight_project' => $project->toArray()]), 139 | Project::class, 140 | 'xml' 141 | ); 142 | } 143 | 144 | /** 145 | * @param string $projectUuid 146 | * 147 | * @return Analyses 148 | */ 149 | public function getAnalyses($projectUuid, $branch = null) 150 | { 151 | $url = sprintf('/api/projects/%s/analyses', $projectUuid); 152 | 153 | if ($branch) { 154 | $url .= '?branch='.$branch; 155 | } 156 | 157 | return $this->serializer->deserialize( 158 | $this->send('GET', $url), 159 | Analyses::class, 160 | 'xml' 161 | ); 162 | } 163 | 164 | /** 165 | * @param string $projectUuid 166 | * @param int $analysesNumber 167 | * 168 | * @return Analysis 169 | */ 170 | public function getAnalysis($projectUuid, $analysesNumber) 171 | { 172 | return $this->serializer->deserialize( 173 | $this->send('GET', sprintf('/api/projects/%s/analyses/%s', $projectUuid, $analysesNumber), null), 174 | Analysis::class, 175 | 'xml' 176 | ); 177 | } 178 | 179 | /** 180 | * @param string $projectUuid 181 | * @param int $analysesNumber 182 | * 183 | * @return Analysis an incomplete Analysis object 184 | */ 185 | public function getAnalysisStatus($projectUuid, $analysesNumber) 186 | { 187 | return $this->serializer->deserialize( 188 | $this->send('GET', sprintf('/api/projects/%s/analyses/%s/status', $projectUuid, $analysesNumber)), 189 | Analysis::class, 190 | 'xml' 191 | ); 192 | } 193 | 194 | /** 195 | * @param string $projectUuid 196 | * @param string|null $reference A git reference. It can be a commit sha, a tag name or a branch name 197 | * @param string|null $branch Current analysis branch, used by SymfonyInsight to distinguish between the main branch and PRs 198 | * 199 | * @return Analysis 200 | */ 201 | public function analyze($projectUuid, $reference = null, $branch = null) 202 | { 203 | return $this->serializer->deserialize( 204 | $this->send( 205 | 'POST', 206 | sprintf('/api/projects/%s/analyses', $projectUuid), 207 | $branch ? ['reference' => $reference, 'branch' => $branch] : ['reference' => $reference] 208 | ), 209 | Analysis::class, 210 | 'xml' 211 | ); 212 | } 213 | 214 | /** 215 | * Use this method to call a specific API resource. 216 | */ 217 | public function call($method = 'GET', $uri = null, $headers = null, $body = null, array $options = [], $classToUnserialize = null) 218 | { 219 | if ($classToUnserialize) { 220 | return $this->serializer->deserialize( 221 | $this->send($method, $uri, $body), 222 | $classToUnserialize, 223 | 'xml' 224 | ); 225 | } 226 | 227 | return $this->send($method, $uri, $body); 228 | } 229 | 230 | public function setLogger(LoggerInterface $logger) 231 | { 232 | $this->logger = $logger; 233 | 234 | return $this; 235 | } 236 | 237 | /** 238 | * @return Serializer 239 | */ 240 | public function getSerializer() 241 | { 242 | return $this->serializer; 243 | } 244 | 245 | private function send($method, $url, $body = null): string 246 | { 247 | try { 248 | $option = []; 249 | if ($body) { 250 | $option['body'] = $body; 251 | } 252 | 253 | $this->logger && $this->logger->debug(sprintf('%s "%s"', $method, $url)); 254 | $response = $this->httpClient->request($method, $url, $option); 255 | 256 | // block until headers arrive 257 | $response->getStatusCode(); 258 | $this->logger && $this->logger->debug(sprintf("Request:\n%s", (string) $response->getInfo('debug'))); 259 | 260 | return $response->getContent(); 261 | } catch (ClientExceptionInterface $e) { 262 | $this->logException($e); 263 | 264 | $this->processClientError($e); 265 | } catch (TransportExceptionInterface $e) { 266 | $this->logException($e); 267 | 268 | throw new ApiServerException('Something went wrong with upstream', 0, $e); 269 | } catch (ServerExceptionInterface $e) { 270 | $this->logException($e); 271 | 272 | throw new ApiServerException('Something went wrong with upstream', 0, $e); 273 | } 274 | } 275 | 276 | private function processClientError(HttpExceptionInterface $e) 277 | { 278 | $statusCode = $e->getResponse()->getStatusCode(); 279 | $error = null; 280 | $message = sprintf('Your request in not valid (status code: "%d").', $statusCode); 281 | 282 | if (400 === $statusCode) { 283 | $error = $this->parser->parseError($e->getResponse()->getContent(false)); 284 | $message .= 'See $error attached to the exception'; 285 | } 286 | 287 | throw new ApiClientException($message, $error, 0, $e); 288 | } 289 | 290 | private function logException(ExceptionInterface $e) 291 | { 292 | $message = sprintf("Exception: Class: \"%s\", Message: \"%s\", Response:\n%s", 293 | \get_class($e), 294 | $e->getMessage(), 295 | $e->getResponse()->getInfo('debug') 296 | ); 297 | 298 | $this->logger && $this->logger->error($message, ['exception' => $e]); 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /Sdk/Exception/ApiClientException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk\Exception; 13 | 14 | use SensioLabs\Insight\Sdk\Model\Error; 15 | 16 | class ApiClientException extends \LogicException implements ExceptionInterface 17 | { 18 | private $error; 19 | 20 | public function __construct($message = '', Error $error = null, $code = 0, $e = null) 21 | { 22 | $this->error = $error; 23 | 24 | parent::__construct($message, $code, $e); 25 | } 26 | 27 | public function getError() 28 | { 29 | return $this->error; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sdk/Exception/ApiParserException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk\Exception; 13 | 14 | class ApiParserException extends \RuntimeException implements ExceptionInterface 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /Sdk/Exception/ApiServerException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk\Exception; 13 | 14 | class ApiServerException extends \RuntimeException implements ExceptionInterface 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /Sdk/Exception/ExceptionInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk\Exception; 13 | 14 | interface ExceptionInterface 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /Sdk/Model/Analyses.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk\Model; 13 | 14 | use JMS\Serializer\Annotation\Type; 15 | use JMS\Serializer\Annotation\XmlList; 16 | 17 | class Analyses 18 | { 19 | /** 20 | * @Type("array") 21 | * @XmlList(inline = true, entry = "link") 22 | */ 23 | #[Type("array")] 24 | #[XmlList(inline: true, entry: "link")] 25 | private $links = []; 26 | 27 | /** 28 | * @Type("array") 29 | * @XmlList(inline = true, entry = "analysis") 30 | */ 31 | #[Type("array")] 32 | #[XmlList(inline: true, entry: "analysis")] 33 | private $analyses = []; 34 | 35 | /** 36 | * @return Link[] 37 | */ 38 | public function getLinks() 39 | { 40 | return $this->links; 41 | } 42 | 43 | /** 44 | * @return Analysis[] 45 | */ 46 | public function getAnalyses() 47 | { 48 | return $this->analyses; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sdk/Model/Analysis.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk\Model; 13 | 14 | use JMS\Serializer\Annotation\SerializedName; 15 | use JMS\Serializer\Annotation\Type; 16 | use JMS\Serializer\Annotation\XmlList; 17 | 18 | class Analysis 19 | { 20 | const STATUS_ORDERED = 'ordered'; 21 | const STATUS_RUNNING = 'running'; 22 | const STATUS_MEASURED = 'measured'; 23 | const STATUS_ANALYZED = 'analyzed'; 24 | const STATUS_FINISHED = 'finished'; 25 | 26 | /** 27 | * @Type("array") 28 | * @XmlList(inline = true, entry = "link") 29 | */ 30 | #[Type("array")] 31 | #[XmlList(inline: true, entry: "link")] 32 | private $links = []; 33 | 34 | /** @Type("integer") */ 35 | #[Type("integer")] 36 | private $number; 37 | 38 | /** @Type("string") */ 39 | #[Type("string")] 40 | private $grade; 41 | 42 | /** 43 | * @Type("string") 44 | * @SerializedName("next-grade") 45 | */ 46 | #[Type("string")] 47 | #[SerializedName("next-grade")] 48 | private $nextGrade; 49 | 50 | /** @Type("array") */ 51 | #[Type("array")] 52 | private $grades = []; 53 | 54 | /** 55 | * @Type("float") 56 | * @SerializedName("remediation-cost") 57 | */ 58 | #[Type("float")] 59 | #[SerializedName("remediation-cost")] 60 | private $remediationCost; 61 | 62 | /** 63 | * @Type("float") 64 | * @SerializedName("remediation-cost-for-next-grade") 65 | */ 66 | #[Type("float")] 67 | #[SerializedName("remediation-cost-for-next-grade")] 68 | private $remediationCostForNextGrade; 69 | 70 | /** 71 | * @Type("integer") 72 | * @SerializedName("nb-violations") 73 | */ 74 | #[Type("integer")] 75 | #[SerializedName("nb-violations")] 76 | private $nbViolations; 77 | 78 | /** 79 | * @Type("DateTime") 80 | * @SerializedName("begin-at") 81 | */ 82 | #[Type("DateTime")] 83 | #[SerializedName("begin-at")] 84 | private $beginAt; 85 | 86 | /** 87 | * @Type("DateTime") 88 | * @SerializedName("end-at") 89 | */ 90 | #[Type("DateTime")] 91 | #[SerializedName("end-at")] 92 | private $endAt; 93 | 94 | /** @Type("integer") */ 95 | #[Type("integer")] 96 | private $duration; 97 | 98 | /** 99 | * @Type("string") 100 | * @SerializedName("failure-message") 101 | */ 102 | #[Type("string")] 103 | #[SerializedName("failure-message")] 104 | private $failureMessage; 105 | 106 | /** 107 | * @Type("string") 108 | * @SerializedName("failure-code") 109 | */ 110 | #[Type("string")] 111 | #[SerializedName("failure-code")] 112 | private $failureCode; 113 | 114 | /** @Type("boolean") */ 115 | #[Type("boolean")] 116 | private $failed; 117 | 118 | /** @Type("string") */ 119 | #[Type("string")] 120 | private $status; 121 | 122 | /** 123 | * @Type("string") 124 | * @SerializedName("status-message") 125 | */ 126 | #[Type("string")] 127 | #[SerializedName("status-message")] 128 | private $statusMessage; 129 | 130 | /** 131 | * @Type("boolean") 132 | * @SerializedName("altered") 133 | */ 134 | #[Type("boolean")] 135 | #[SerializedName("altered")] 136 | private $isAltered; 137 | 138 | /** @Type("SensioLabs\Insight\Sdk\Model\Violations") */ 139 | #[Type("SensioLabs\Insight\Sdk\Model\Violations")] 140 | private $violations; 141 | 142 | /** @Type("string") */ 143 | #[Type("string")] 144 | private $branch; 145 | 146 | /** @Type("string") */ 147 | #[Type("string")] 148 | private $reference; 149 | 150 | /** 151 | * @return Link[] 152 | */ 153 | public function getLinks() 154 | { 155 | return $this->links; 156 | } 157 | 158 | /** 159 | * @return int 160 | */ 161 | public function getNumber() 162 | { 163 | return $this->number; 164 | } 165 | 166 | /** 167 | * @return string 168 | */ 169 | public function getGrade() 170 | { 171 | return $this->grade; 172 | } 173 | 174 | /** 175 | * @return string 176 | */ 177 | public function getNextGrade() 178 | { 179 | return $this->nextGrade; 180 | } 181 | 182 | /** 183 | * @return string[] 184 | */ 185 | public function getGrades() 186 | { 187 | return $this->grades; 188 | } 189 | 190 | /** 191 | * @return float 192 | */ 193 | public function getRemediationCost() 194 | { 195 | return $this->remediationCost; 196 | } 197 | 198 | /** 199 | * @return float 200 | */ 201 | public function getRemediationCostForNextGrade() 202 | { 203 | return $this->remediationCostForNextGrade; 204 | } 205 | 206 | /** 207 | * @return int 208 | */ 209 | public function getNbViolations() 210 | { 211 | return $this->nbViolations; 212 | } 213 | 214 | /** 215 | * @return \DateTime 216 | */ 217 | public function getBeginAt() 218 | { 219 | return $this->beginAt; 220 | } 221 | 222 | /** 223 | * @return \DateTime|null 224 | */ 225 | public function getEndAt() 226 | { 227 | return $this->endAt; 228 | } 229 | 230 | /** 231 | * @return \DateInterval 232 | */ 233 | public function getDuration() 234 | { 235 | return new \DateInterval('PT'.($this->duration ?: '0').'S'); 236 | } 237 | 238 | /** 239 | * @return string 240 | */ 241 | public function getFailureMessage() 242 | { 243 | return $this->failureMessage; 244 | } 245 | 246 | /** 247 | * @return string 248 | */ 249 | public function getFailureCode() 250 | { 251 | return $this->failureCode; 252 | } 253 | 254 | /** 255 | * @return bool 256 | */ 257 | public function isFailed() 258 | { 259 | return $this->failed; 260 | } 261 | 262 | /** 263 | * @return bool 264 | */ 265 | public function isFinished() 266 | { 267 | return static::STATUS_FINISHED == $this->status; 268 | } 269 | 270 | /** 271 | * @return string One of the STATUS_* constants 272 | */ 273 | public function getStatus() 274 | { 275 | return $this->status; 276 | } 277 | 278 | /** 279 | * @return string 280 | */ 281 | public function getStatusMessage() 282 | { 283 | return $this->statusMessage; 284 | } 285 | 286 | /** 287 | * @return bool 288 | */ 289 | public function isAltered() 290 | { 291 | return $this->isAltered; 292 | } 293 | 294 | /** 295 | * @return Violations|null 296 | */ 297 | public function getViolations() 298 | { 299 | return $this->violations; 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /Sdk/Model/Error.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk\Model; 13 | 14 | class Error 15 | { 16 | private $entityBodyParameters = []; 17 | 18 | public function getEntityBodyParameters() 19 | { 20 | return $this->entityBodyParameters; 21 | } 22 | 23 | public function hasEntityBodyParameter($name) 24 | { 25 | return \array_key_exists($name, $this->entityBodyParameters); 26 | } 27 | 28 | public function addEntityBodyParameter($name) 29 | { 30 | if (!$this->hasEntityBodyParameter($name)) { 31 | $this->entityBodyParameters[$name] = []; 32 | } 33 | 34 | return $this; 35 | } 36 | 37 | public function addEntityBodyParameterError($name, $message) 38 | { 39 | $this->entityBodyParameters[$name][] = $message; 40 | 41 | return $this; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sdk/Model/Link.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk\Model; 13 | 14 | use JMS\Serializer\Annotation\Type; 15 | use JMS\Serializer\Annotation\XmlAttribute; 16 | 17 | class Link 18 | { 19 | /** 20 | * @XmlAttribute 21 | * @Type("string") 22 | */ 23 | #[XmlAttribute] 24 | #[Type("string")] 25 | private $href; 26 | 27 | /** 28 | * @XmlAttribute 29 | * @Type("string") 30 | */ 31 | #[XmlAttribute] 32 | #[Type("string")] 33 | private $rel; 34 | 35 | /** 36 | * @XmlAttribute 37 | * @Type("string") 38 | */ 39 | #[XmlAttribute] 40 | #[Type("string")] 41 | private $type; 42 | 43 | /** 44 | * @return string 45 | */ 46 | public function getHref() 47 | { 48 | return $this->href; 49 | } 50 | 51 | /** 52 | * @return string 53 | */ 54 | public function getRel() 55 | { 56 | return $this->rel; 57 | } 58 | 59 | /** 60 | * @return string 61 | */ 62 | public function getType() 63 | { 64 | return $this->type; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sdk/Model/Project.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk\Model; 13 | 14 | use JMS\Serializer\Annotation\Exclude; 15 | use JMS\Serializer\Annotation\SerializedName; 16 | use JMS\Serializer\Annotation\Type; 17 | use JMS\Serializer\Annotation\XmlList; 18 | 19 | class Project 20 | { 21 | /** 22 | * @see https://github.com/sensiolabs/connect/blob/master/src/SensioLabs/Connect/Api/Entity/Project.php 23 | */ 24 | const TYPE_PHP_WEBSITE = 0; 25 | const TYPE_PHP_LIBRARY = 1; 26 | const TYPE_SYMFONY2_BUNDLE = 2; 27 | const TYPE_SYMFONY1_PLUGIN = 4; 28 | const TYPE_OTHER = 6; 29 | const TYPE_DRUPAL_MODULE = 7; 30 | const TYPE_LARAVAL_WEB_PROJECT = 8; 31 | const TYPE_SILEX_WEB_PROJECT = 9; 32 | const TYPE_SYMFONY2_WEB_PROJECT = 10; 33 | const TYPE_SYMFONY1_WEB_PROJECT = 11; 34 | 35 | /** 36 | * @Exclude() 37 | */ 38 | #[Exclude] 39 | public static $types = [ 40 | self::TYPE_SYMFONY2_WEB_PROJECT => 'Symfony2 Web Project', 41 | self::TYPE_SYMFONY1_WEB_PROJECT => 'symfony1 Web Project', 42 | self::TYPE_SILEX_WEB_PROJECT => 'Silex Web Project', 43 | self::TYPE_LARAVAL_WEB_PROJECT => 'Laravel Web Project', 44 | self::TYPE_SYMFONY2_BUNDLE => 'Symfony2 Bundle', 45 | self::TYPE_SYMFONY1_PLUGIN => 'symfony1 Plugin', 46 | self::TYPE_DRUPAL_MODULE => 'Drupal Module', 47 | self::TYPE_PHP_WEBSITE => 'PHP Web Project', 48 | self::TYPE_PHP_LIBRARY => 'PHP Library', 49 | self::TYPE_OTHER => 'Other', 50 | ]; 51 | 52 | /** 53 | * @Type("array") 54 | * @XmlList(inline = true, entry = "link") 55 | */ 56 | #[Type("array")] 57 | #[XmlList(inline: true, entry: "link")] 58 | private $links = []; 59 | 60 | /** 61 | * @Type("string") 62 | * @SerializedName("id") 63 | */ 64 | #[Type("string")] 65 | #[SerializedName("id")] 66 | private $uuid; 67 | 68 | /** @Type("string") */ 69 | #[Type("string")] 70 | private $name; 71 | 72 | /** @Type("string") */ 73 | #[Type("string")] 74 | private $configuration; 75 | 76 | /** @Type("string") */ 77 | #[Type("string")] 78 | private $description; 79 | 80 | /** @Type("integer") */ 81 | #[Type("integer")] 82 | private $type; 83 | 84 | /** 85 | * @Type("string") 86 | * @SerializedName("repository-url") 87 | */ 88 | #[Type("string")] 89 | #[SerializedName("repository-url")] 90 | private $repositoryUrl; 91 | 92 | /** @Type("boolean") */ 93 | #[Type("boolean")] 94 | private $private; 95 | 96 | /** 97 | * @Type("boolean") 98 | * @SerializedName("report-available") 99 | */ 100 | #[Type("boolean")] 101 | #[SerializedName("report-available")] 102 | private $reportAvailable; 103 | 104 | /** 105 | * @Type("SensioLabs\Insight\Sdk\Model\Analysis") 106 | * @SerializedName("last-analysis") 107 | */ 108 | #[Type("SensioLabs\Insight\Sdk\Model\Analysis")] 109 | #[SerializedName("last-analysis")] 110 | private $lastAnalysis; 111 | 112 | public function toArray() 113 | { 114 | return [ 115 | 'name' => $this->name, 116 | 'public' => !$this->private, 117 | 'description' => $this->description, 118 | 'repositoryUrl' => $this->repositoryUrl, 119 | 'type' => $this->type, 120 | 'configuration' => $this->configuration, 121 | ]; 122 | } 123 | 124 | /** 125 | * @return Link[] 126 | */ 127 | public function getLinks() 128 | { 129 | return $this->links; 130 | } 131 | 132 | /** 133 | * @return string 134 | */ 135 | public function getUuid() 136 | { 137 | return $this->uuid; 138 | } 139 | 140 | /** 141 | * @return string 142 | */ 143 | public function getName() 144 | { 145 | return $this->name; 146 | } 147 | 148 | public function setName($name) 149 | { 150 | $this->name = $name; 151 | 152 | return $this; 153 | } 154 | 155 | /** 156 | * @return string 157 | */ 158 | public function getConfiguration() 159 | { 160 | return $this->configuration; 161 | } 162 | 163 | public function setConfiguration($configuration) 164 | { 165 | $this->configuration = $configuration; 166 | 167 | return $this; 168 | } 169 | 170 | /** 171 | * @return string 172 | */ 173 | public function getDescription() 174 | { 175 | return $this->description; 176 | } 177 | 178 | public function setDescription($description) 179 | { 180 | $this->description = $description; 181 | 182 | return $this; 183 | } 184 | 185 | /** 186 | * @return int 187 | */ 188 | public function getType() 189 | { 190 | return $this->type; 191 | } 192 | 193 | public function setType($type) 194 | { 195 | if (!\array_key_exists($type, static::$types)) { 196 | throw new \InvalidArgumentException(sprintf('"%s" is not a valid type. You must pick one among "%"', $type, implode('", "', array_keys(static::$types)))); 197 | } 198 | 199 | $this->type = $type; 200 | 201 | return $this; 202 | } 203 | 204 | /** 205 | * @return string 206 | */ 207 | public function getRepositoryUrl() 208 | { 209 | return $this->repositoryUrl; 210 | } 211 | 212 | public function setRepositoryUrl($repositoryUrl) 213 | { 214 | $this->repositoryUrl = $repositoryUrl; 215 | 216 | return $this; 217 | } 218 | 219 | /** 220 | * @return bool 221 | */ 222 | public function isPublic() 223 | { 224 | return !$this->private; 225 | } 226 | 227 | public function setPublic($isPublic = false) 228 | { 229 | $this->private = !$isPublic; 230 | 231 | return $this; 232 | } 233 | 234 | /** 235 | * @return bool 236 | */ 237 | public function isPrivate() 238 | { 239 | return $this->private; 240 | } 241 | 242 | public function setPrivate($isPrivate = true) 243 | { 244 | $this->private = $isPrivate; 245 | 246 | return $this; 247 | } 248 | 249 | /** 250 | * @return bool 251 | */ 252 | public function isReportAvailable() 253 | { 254 | return $this->reportAvailable; 255 | } 256 | 257 | /** 258 | * @return Analysis|null 259 | */ 260 | public function getLastAnalysis() 261 | { 262 | return $this->lastAnalysis; 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /Sdk/Model/Projects.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk\Model; 13 | 14 | use JMS\Serializer\Annotation\Type; 15 | use JMS\Serializer\Annotation\XmlAttribute; 16 | use JMS\Serializer\Annotation\XmlList; 17 | use JMS\Serializer\Annotation\XmlRoot; 18 | 19 | /** 20 | * @XmlRoot("projects") 21 | */ 22 | #[XmlRoot("projects")] 23 | class Projects 24 | { 25 | /** 26 | * @XmlAttribute 27 | * @Type("integer") 28 | */ 29 | #[XmlAttribute] 30 | #[Type("integer")] 31 | private $page; 32 | 33 | /** 34 | * @XmlAttribute 35 | * @Type("integer") 36 | */ 37 | #[XmlAttribute] 38 | #[Type("integer")] 39 | private $total; 40 | 41 | /** 42 | * @XmlAttribute 43 | * @Type("integer") 44 | */ 45 | #[XmlAttribute] 46 | #[Type("integer")] 47 | private $limit; 48 | 49 | /** 50 | * @Type("array") 51 | * @XmlList(inline = true, entry = "link") 52 | */ 53 | #[Type("array")] 54 | #[XmlList(inline: true, entry: "link")] 55 | private $links = []; 56 | 57 | /** 58 | * @Type("array") 59 | * @XmlList(inline = true, entry = "project") 60 | */ 61 | #[Type("array")] 62 | #[XmlList(inline: true, entry: "project")] 63 | private $projects = []; 64 | 65 | /** 66 | * @return int 67 | */ 68 | public function getPage() 69 | { 70 | return $this->page; 71 | } 72 | 73 | /** 74 | * @return int 75 | */ 76 | public function getTotal() 77 | { 78 | return $this->total; 79 | } 80 | 81 | /** 82 | * @return int 83 | */ 84 | public function getLimit() 85 | { 86 | return $this->limit; 87 | } 88 | 89 | /** 90 | * @return Link[] 91 | */ 92 | public function getLinks() 93 | { 94 | return $this->links; 95 | } 96 | 97 | /** 98 | * @return Project[] 99 | */ 100 | public function getProjects() 101 | { 102 | return $this->projects; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Sdk/Model/Violation.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk\Model; 13 | 14 | use JMS\Serializer\Annotation\Type; 15 | use JMS\Serializer\Annotation\XmlAttribute; 16 | 17 | class Violation 18 | { 19 | /** @Type("string") */ 20 | #[Type("string")] 21 | private $title; 22 | 23 | /** @Type("string") */ 24 | #[Type("string")] 25 | private $message; 26 | 27 | /** @Type("string") */ 28 | #[Type("string")] 29 | private $resource; 30 | 31 | /** @Type("integer") */ 32 | #[Type("integer")] 33 | private $line; 34 | 35 | /** 36 | * @Type("string") 37 | * @XmlAttribute 38 | */ 39 | #[Type("string")] 40 | #[XmlAttribute] 41 | private $severity; 42 | 43 | /** 44 | * @Type("string") 45 | * @XmlAttribute 46 | */ 47 | #[Type("string")] 48 | #[XmlAttribute] 49 | private $category; 50 | 51 | /** 52 | * @Type("boolean") 53 | * @XmlAttribute 54 | */ 55 | #[Type("boolean")] 56 | #[XmlAttribute] 57 | private $ignored; 58 | 59 | /** 60 | * @return string 61 | */ 62 | public function getTitle() 63 | { 64 | return $this->title; 65 | } 66 | 67 | /** 68 | * @return string 69 | */ 70 | public function getMessage() 71 | { 72 | return $this->message; 73 | } 74 | 75 | /** 76 | * @return string 77 | */ 78 | public function getResource() 79 | { 80 | return $this->resource; 81 | } 82 | 83 | /** 84 | * @return int 85 | */ 86 | public function getLine() 87 | { 88 | return $this->line; 89 | } 90 | 91 | /** 92 | * @return string 93 | */ 94 | public function getSeverity() 95 | { 96 | return $this->severity; 97 | } 98 | 99 | /** 100 | * @return string 101 | */ 102 | public function getCategory() 103 | { 104 | return $this->category; 105 | } 106 | 107 | /** 108 | * @return bool 109 | */ 110 | public function isIgnored() 111 | { 112 | return $this->ignored; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /Sdk/Model/Violations.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk\Model; 13 | 14 | use JMS\Serializer\Annotation\Type; 15 | use JMS\Serializer\Annotation\XmlList; 16 | 17 | class Violations implements \Countable, \IteratorAggregate 18 | { 19 | /** 20 | * @Type("array") 21 | * @XmlList(inline = true, entry = "violation") 22 | */ 23 | #[Type("array")] 24 | #[XmlList(inline: true, entry: "violation")] 25 | private $violations = []; 26 | 27 | public function count(): int 28 | { 29 | return \count($this->violations); 30 | } 31 | 32 | public function getIterator(): \Traversable 33 | { 34 | return new \ArrayIterator($this->violations); 35 | } 36 | 37 | /** 38 | * @return Violation[] 39 | */ 40 | public function getViolations() 41 | { 42 | return $this->violations; 43 | } 44 | 45 | /** 46 | * @param callable $callback 47 | */ 48 | public function filter($callback) 49 | { 50 | if (!\is_callable($callback)) { 51 | throw new \InvalidArgumentException('The callback is not callable.'); 52 | } 53 | 54 | $this->violations = array_filter($this->violations, $callback); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sdk/Parser.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk; 13 | 14 | use SensioLabs\Insight\Sdk\Exception\ApiParserException; 15 | use SensioLabs\Insight\Sdk\Model\Error; 16 | 17 | class Parser 18 | { 19 | public function parseError($content) 20 | { 21 | if (!$content) { 22 | throw new ApiParserException('Could not transform this xml to a \DOMDocument instance.'); 23 | } 24 | 25 | $internalErrors = libxml_use_internal_errors(true); 26 | $disableEntities = libxml_disable_entity_loader(true); 27 | libxml_clear_errors(); 28 | 29 | $document = new \DOMDocument(); 30 | $document->validateOnParse = true; 31 | if (!$document->loadXML($content, \LIBXML_NONET | (\defined('LIBXML_COMPACT') ? \LIBXML_COMPACT : 0))) { 32 | libxml_disable_entity_loader($disableEntities); 33 | 34 | libxml_clear_errors(); 35 | libxml_use_internal_errors($internalErrors); 36 | 37 | throw new ApiParserException('Could not transform this xml to a \DOMDocument instance.'); 38 | } 39 | 40 | $document->normalizeDocument(); 41 | 42 | libxml_use_internal_errors($internalErrors); 43 | libxml_disable_entity_loader($disableEntities); 44 | 45 | $xpath = new \DOMXpath($document); 46 | 47 | $nodes = $xpath->evaluate('./error'); 48 | if (1 === $nodes->length) { 49 | throw new ApiParserException('The dom contains more than one error node.'); 50 | } 51 | 52 | $error = new Error(); 53 | 54 | $parameters = $xpath->query('./entity/body/parameter', $nodes->item(0)); 55 | foreach ($parameters as $parameter) { 56 | $name = $parameter->getAttribute('name'); 57 | $error->addEntityBodyParameter($name); 58 | 59 | $messages = $xpath->query('./message', $parameter); 60 | foreach ($messages as $message) { 61 | $error->addEntityBodyParameterError($name, $this->sanitizeValue($message->nodeValue)); 62 | } 63 | } 64 | 65 | return $error; 66 | } 67 | 68 | protected function sanitizeValue($value) 69 | { 70 | if ('true' === $value) { 71 | $value = true; 72 | } elseif ('false' === $value) { 73 | $value = false; 74 | } elseif (empty($value)) { 75 | $value = null; 76 | } 77 | 78 | return $value; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Sdk/Tests/ApiTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk\Tests; 13 | 14 | use PHPUnit\Framework\TestCase; 15 | use SensioLabs\Insight\Sdk\Api; 16 | use SensioLabs\Insight\Sdk\Model\Project; 17 | use Symfony\Component\HttpClient\MockHttpClient; 18 | use Symfony\Component\HttpClient\Response\MockResponse; 19 | 20 | class ApiTest extends TestCase 21 | { 22 | /** 23 | * @var Api 24 | */ 25 | private $api; 26 | private $logger; 27 | 28 | protected function setUp(): void 29 | { 30 | $this->logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); 31 | } 32 | 33 | public function testConstructorWithoutOption() 34 | { 35 | $this->expectException(\Exception::class); 36 | $this->expectExceptionMessage('Config is missing the following keys: api_token, user_uuid'); 37 | new Api(); 38 | } 39 | 40 | public function testGetProjects() 41 | { 42 | $api = $this->createApi('projects'); 43 | 44 | $projects = $api->getProjects(); 45 | 46 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Projects', $projects); 47 | $this->assertCount(10, $projects->getProjects()); 48 | $this->assertSame(1, $projects->getPage()); 49 | $this->assertSame(12, $projects->getTotal()); 50 | $this->assertSame(10, $projects->getLimit()); 51 | 52 | $projects = $projects->getProjects(); 53 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Project', reset($projects)); 54 | } 55 | 56 | public function testGetProjectsWithPage() 57 | { 58 | $this->logger 59 | ->expects($this->exactly(2)) 60 | ->method('debug') 61 | ; 62 | $this->logger 63 | ->expects($this->at(1)) 64 | ->method('debug') 65 | ->with($this->stringContains('/api/projects?page=2')) 66 | ; 67 | $api = $this->createApi('projects2', ['debug' => '/api/projects?page=2']); 68 | 69 | $projects = $api->getProjects(2); 70 | 71 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Projects', $projects); 72 | $this->assertCount(2, $projects->getProjects()); 73 | $this->assertSame(2, $projects->getPage()); 74 | $this->assertSame(12, $projects->getTotal()); 75 | $this->assertSame(10, $projects->getLimit()); 76 | 77 | $projects = $projects->getProjects(); 78 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Project', reset($projects)); 79 | } 80 | 81 | public function testGetProject() 82 | { 83 | $api = $this->createApi('project'); 84 | 85 | $project = $api->getProject('6718526f-ecdf-497d-bffb-8512f0b402ea'); 86 | 87 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Project', $project); 88 | $this->assertSame('demo', $project->getName()); 89 | $this->assertNotnull($project->getConfiguration()); 90 | $this->assertSame('git@github.com:lyrixx/demoer.git', $project->getRepositoryUrl()); 91 | $this->assertTrue($project->isPublic()); 92 | $this->assertTrue($project->isReportAvailable()); 93 | $this->assertSame(1, $project->getType()); 94 | 95 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Analysis', $project->getLastAnalysis()); 96 | } 97 | 98 | public function testCreateProjectOk() 99 | { 100 | $project = new Project(); 101 | 102 | $api = $this->createApi('project'); 103 | 104 | $project = $api->createProject($project); 105 | 106 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Project', $project); 107 | } 108 | 109 | public function testCreateProjectNOk() 110 | { 111 | $project = new Project(); 112 | 113 | $api = $this->createApi('errors', ['http_code' => 400]); 114 | 115 | try { 116 | $project = $api->createProject($project); 117 | $this->fail('Something should go wrong'); 118 | } catch (\Exception $e) { 119 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Exception\ApiClientException', $e); 120 | $this->assertSame('Your request in not valid (status code: "400").See $error attached to the exception', $e->getMessage()); 121 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Error', $e->getError()); 122 | } 123 | } 124 | 125 | public function testupdateProjectOk() 126 | { 127 | $project = new Project(); 128 | 129 | $api = $this->createApi('project'); 130 | 131 | $project = $api->updateProject($project); 132 | 133 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Project', $project); 134 | } 135 | 136 | public function testupdateProjectNOk() 137 | { 138 | $project = new Project(); 139 | $api = $this->createApi('errors', ['http_code' => 400]); 140 | 141 | try { 142 | $project = $api->updateProject($project); 143 | $this->fail('Something should go wrong'); 144 | } catch (\Exception $e) { 145 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Exception\ApiClientException', $e); 146 | $this->assertSame('Your request in not valid (status code: "400").See $error attached to the exception', $e->getMessage()); 147 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Error', $e->getError()); 148 | } 149 | } 150 | 151 | public function testGetAnalyses() 152 | { 153 | $api = $this->createApi('analyses'); 154 | 155 | $analyses = $api->getAnalyses('6718526f-ecdf-497d-bffb-8512f0b402ea'); 156 | 157 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Analyses', $analyses); 158 | $this->assertCount(2, $analyses->getAnalyses()); 159 | 160 | $analyses = $analyses->getAnalyses(); 161 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Analysis', reset($analyses)); 162 | } 163 | 164 | public function testGetAnalysis() 165 | { 166 | $api = $this->createApi('analysis'); 167 | 168 | $analysis = $api->getAnalysis('6718526f-ecdf-497d-bffb-8512f0b402ea', 1); 169 | 170 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Analysis', $analysis); 171 | $this->assertSame(49, $analysis->getNumber()); 172 | $this->assertSame('error', $analysis->getGrade()); 173 | $this->assertSame('bronze', $analysis->getNextGrade()); 174 | $this->assertSame(['error', 'bronze', 'silver', 'gold', 'platinum'], $analysis->getGrades()); 175 | $this->assertSame(181.75, $analysis->getRemediationCost()); 176 | $this->assertSame(55.5, $analysis->getRemediationCostForNextGrade()); 177 | $this->assertSame('2013-06-25T19:37:20+02:00', $analysis->getBeginAt()->format('c')); 178 | $this->assertSame('2013-06-25T19:37:53+02:00', $analysis->getEndAt()->format('c')); 179 | $this->assertSame('0', $analysis->getDuration()->format('%s')); 180 | $this->assertSame(250, $analysis->getNbViolations()); 181 | $this->assertNull($analysis->getFailureMessage()); 182 | $this->assertNull($analysis->getFailureCode()); 183 | $this->assertFalse($analysis->isAltered()); 184 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Violations', $analysis->getViolations()); 185 | $this->assertCount(250, $analysis->getViolations()->getViolations()); 186 | 187 | $violations = $analysis->getViolations()->getViolations(); 188 | $firstViolation = reset($violations); 189 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Violation', $firstViolation); 190 | 191 | $this->assertSame(7, $firstViolation->getLine()); 192 | $this->assertSame('critical', $firstViolation->getSeverity()); 193 | $this->assertSame('security', $firstViolation->getCategory()); 194 | $this->assertSame('snippets/001-HelloWorld.php', $firstViolation->getResource()); 195 | } 196 | 197 | public function testGetAnalysisStatus() 198 | { 199 | $api = $this->createApi('status'); 200 | 201 | $analysis = $api->getAnalysisStatus('6718526f-ecdf-497d-bffb-8512f0b402ea', 1); 202 | 203 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Analysis', $analysis); 204 | $this->assertSame(49, $analysis->getNumber()); 205 | $this->assertSame('2013-06-25T19:37:20+02:00', $analysis->getBeginAt()->format('c')); 206 | $this->assertSame('2013-06-25T19:37:53+02:00', $analysis->getEndAt()->format('c')); 207 | $this->assertSame('finished', $analysis->getStatus()); 208 | } 209 | 210 | public function testAnalyze() 211 | { 212 | $api = $this->createApi('analysis'); 213 | 214 | $analysis = $api->analyze('6718526f-ecdf-497d-bffb-8512f0b402ea', 'SHA'); 215 | 216 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Analysis', $analysis); 217 | } 218 | 219 | protected function tearDown(): void 220 | { 221 | $this->logger = null; 222 | $this->api = null; 223 | $this->pluginMockResponse = null; 224 | } 225 | 226 | private function createResponse($fixture, $statusCode = 200) 227 | { 228 | return file_get_contents(sprintf('%s/fixtures/%s.xml', __DIR__, $fixture)); 229 | } 230 | 231 | private function createApi($fixture, $option = []) 232 | { 233 | $client = new MockHttpClient([new MockResponse($this->createResponse($fixture), $option)]); 234 | 235 | return new Api(['api_token' => 'my-token', 'user_uuid' => 'my-user-uuid'], $client, null, $this->logger); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /Sdk/Tests/Model/ViolationsTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Tests\Sdk\Model; 13 | 14 | use PHPUnit\Framework\TestCase; 15 | use SensioLabs\Insight\Sdk\Model\Violations; 16 | 17 | class ViolationsTest extends TestCase 18 | { 19 | public function testCount() 20 | { 21 | $violations = new Violations(); 22 | 23 | $reflector = new \ReflectionObject($violations); 24 | $violationsAttr = $reflector->getProperty('violations'); 25 | $violationsAttr->setAccessible(true); 26 | $violationsAttr->setValue($violations, range(1, 10)); 27 | $violationsAttr->setAccessible(false); 28 | 29 | $this->assertSame(10, \count($violations)); 30 | } 31 | 32 | public function testIterable() 33 | { 34 | $violations = new Violations(); 35 | 36 | $reflector = new \ReflectionObject($violations); 37 | $violationsAttr = $reflector->getProperty('violations'); 38 | $violationsAttr->setAccessible(true); 39 | $violationsAttr->setValue($violations, range(0, 10)); 40 | $violationsAttr->setAccessible(false); 41 | 42 | foreach ($violations as $k => $violation) { 43 | $this->assertSame($k, $violation); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sdk/Tests/ParserTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace SensioLabs\Insight\Sdk\Tests; 13 | 14 | use PHPUnit\Framework\TestCase; 15 | use SensioLabs\Insight\Sdk\Exception\ApiParserException; 16 | use SensioLabs\Insight\Sdk\Parser; 17 | 18 | class ParserTest extends TestCase 19 | { 20 | /** 21 | * @var Parser 22 | */ 23 | private $parser; 24 | 25 | protected function setUp(): void 26 | { 27 | $this->parser = new Parser(); 28 | } 29 | 30 | public function getParseErrorsFailedIfDocumentIfInvalidTests() 31 | { 32 | return [ 33 | [null], 34 | [''], 35 | ['403'], 36 | ]; 37 | } 38 | 39 | /** 40 | * @dataProvider getParseErrorsFailedIfDocumentIfInvalidTests 41 | */ 42 | public function testParseErrorsFailedIfDocumentIfInvalid($xml) 43 | { 44 | $this->expectException(ApiParserException::class); 45 | $this->expectExceptionMessage('Could not transform this xml to a \DOMDocument instance.'); 46 | $this->parser->parseError($xml); 47 | } 48 | 49 | public function testParseErrors() 50 | { 51 | $xml = file_get_contents(__DIR__.'/fixtures/errors.xml'); 52 | 53 | $error = $this->parser->parseError($xml); 54 | 55 | $expectedFields = [ 56 | 'foo' => [ 57 | 0 => 'This value should not be null.', 58 | 1 => 'This value should not be blank.', 59 | ], 60 | 'bar' => [ 61 | 0 => 'This value should be equals to 6.', 62 | ], 63 | ]; 64 | 65 | $this->assertInstanceOf('SensioLabs\Insight\Sdk\Model\Error', $error); 66 | $this->assertSame($expectedFields, $error->getEntityBodyParameters()); 67 | } 68 | 69 | protected function tearDown(): void 70 | { 71 | $this->parser = null; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Sdk/Tests/fixtures/analyses.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 49 18 | false 19 | 20 | 21 | 22 | 23 | 0 24 | false 25 | 26 | 181.75 27 | 55.5 28 | 250 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 48 45 | true 46 | 58 | 59 | 60 | 61 | 0 62 | false 63 | 64 | 0 65 | 0 66 | 0 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /Sdk/Tests/fixtures/errors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | This value should not be null. 7 | This value should not be blank. 8 | 9 | 10 | This value should be equals to 6. 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Sdk/Tests/fixtures/projects.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | false 15 | true 16 | 11 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | false 25 | false 26 | 1 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 47 42 | true 43 | 44 | 45 | false 46 | 47 | 0 48 | 0 49 | 0 50 | 51 | 52 | 53 | false 54 | false 55 | 6 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | false 65 | false 66 | 4 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | false 75 | false 76 | 10 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | false 85 | false 86 | 10 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | false 95 | false 96 | 7 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | false 105 | false 106 | 9 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | false 115 | false 116 | 11 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 49 132 | false 133 | 134 | 135 | 136 | 137 | 0 138 | false 139 | 140 | 181.75 141 | 55.5 142 | 250 143 | 144 | 145 | 146 | true 147 | true 148 | 1 149 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 177 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /Sdk/Tests/fixtures/projects2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | false 15 | false 16 | 7 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | false 25 | false 26 | 4 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Sdk/Tests/fixtures/status.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 49 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /bin/insight: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | // installed via composer? 14 | if (file_exists($a = __DIR__.'/../../../autoload.php')) { 15 | require_once $a; 16 | } else { 17 | require_once __DIR__.'/../vendor/autoload.php'; 18 | } 19 | 20 | use SensioLabs\Insight\Cli\Application; 21 | 22 | $application = new Application(); 23 | 24 | $application->run(); 25 | -------------------------------------------------------------------------------- /box.json: -------------------------------------------------------------------------------- 1 | { 2 | "check-requirements": false, 3 | "output": "build/insight.phar", 4 | "git-commit": "git-commit", 5 | "finder": [ 6 | {"in": "bin"}, 7 | {"in": "Cli"}, 8 | {"in": "Sdk", "exclude": ["Tests"]}, 9 | {"in": "vendor", "notName": "/LICENSE|.*\\.md|.*\\.dist|Makefile|composer\\.json|composer\\.lock/"} 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sensiolabs/insight", 3 | "description": "SymfonyInsight SDK", 4 | "license": "MIT", 5 | "require": { 6 | "php": ">=7.2", 7 | "ext-json": "*", 8 | "doctrine/annotations": "^1.14|^2.0", 9 | "jms/serializer": "^3.29", 10 | "psr/log": "^1.0", 11 | "symfony/http-client": "^5.4|^6.4|^7.0", 12 | "symfony/console": "^5.4|^6.4|^7.0", 13 | "symfony/expression-language": "^5.4|^6.4|^7.0" 14 | }, 15 | "require-dev": { 16 | "monolog/monolog": "^1.4", 17 | "symfony/phpunit-bridge": "^5.4|^6.4|^7.0", 18 | "symfony/var-dumper": "^5.4|^6.4|^7.0" 19 | }, 20 | "suggest": { 21 | "monolog/monolog": "Will add some log capability to this library" 22 | }, 23 | "autoload": { 24 | "psr-4": { 25 | "SensioLabs\\Insight\\": "" 26 | } 27 | }, 28 | "bin": ["bin/insight"], 29 | "extra": { 30 | "branch-alias": { 31 | "dev-main": "1.x-dev" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Sdk/Tests 23 | 24 | 25 | 26 | 27 | 28 | Sdk 29 | 30 | Sdk/Tests 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | --------------------------------------------------------------------------------