/**
  * @param CheckAction $action
  */
 public function perform(CheckAction $action)
 {
     $parser = new Parser();
     foreach ($this->getFiles($action->getBasePath()) as $file) {
         try {
             $parser->parse(file_get_contents($file->getRealPath()));
         } catch (ParseException $e) {
             $action->addError('parser', $e->getMessage(), ['file_name' => $file->getFileName(), 'file_path' => $file->getPathName(), 'training' => $this->getCurrentTraining($file), 'discriminator' => 'training']);
             $action->addSkip('file', $file->getFileName());
         }
     }
 }
 /**
  * @param CheckAction $action
  */
 public function perform(CheckAction $action)
 {
     $files = $this->getFiles($action->getBasePath());
     $parser = new Parser();
     foreach ($files as $file) {
         if (in_array($file->getFileName(), $action->getSkip('file'))) {
             continue;
         }
         $data = $parser->parse(file_get_contents($file->getRealPath()));
         $context = ['file_name' => $file->getFileName(), 'file_path' => $file->getPathName(), 'discriminator' => 'category', 'training' => $this->getCurrentTraining($file)];
         if (!is_array($data)) {
             $action->addError('structure', 'Output must be type of array, given ' . gettype($data), $context);
             $action->addSkip('category', $file->getFileName());
             continue;
         }
         if (empty($data)) {
             $action->addError('structure', 'Resource file cannot be empty', $context);
             continue;
         }
         //Structure without data can pass
         if (1 === count($data) && isset($data['category'])) {
             $action->addSkip('category', $data['category']);
             continue;
         }
         if (empty($data['category'])) {
             $action->addError('structure', 'Cannot have empty label', $context);
         }
         if (!isset($data['questions']) || empty($data['questions'])) {
             $action->addError('structure', 'Missing questions', $context);
             $action->addSkip('category', $data['category']);
             continue;
         }
         foreach ($data['questions'] as $questionNode) {
             $context['discriminator'] = 'question';
             if (!isset($questionNode['question'])) {
                 $action->addError('structure', 'Required a question', $context);
                 continue 2;
             }
             if (!is_string($questionNode['question'])) {
                 $action->addError('structure', 'Should be a string', $context);
                 continue 2;
             }
             if (empty($questionNode['question'])) {
                 $action->addError('structure', 'Cannot have empty label', $context);
             }
             if (!isset($questionNode['answers']) || count($questionNode['answers']) <= 1) {
                 $action->addError('structure', 'Must contains at least 2 answers to be valid', $context);
                 continue 2;
             }
             $good = 0;
             foreach ($questionNode['answers'] as $answerNode) {
                 $context['discriminator'] = 'answer';
                 if (!isset($answerNode['value']) || !is_string($answerNode['value'])) {
                     $action->addError('structure', 'Missing or invalid [value] field', $context);
                     continue 3;
                 }
                 if (empty($answerNode['value'])) {
                     $action->addError('structure', sprintf('Cannot have empty label, related question : "%s"', $questionNode['question']), $context);
                 }
                 if (!isset($answerNode['correct']) || !is_bool($answerNode['correct'])) {
                     $action->addError('structure', 'Missing or invalid [correct] field', $context);
                     continue 3;
                 }
                 if (true === $answerNode['correct']) {
                     $good++;
                 }
             }
             if (0 === $good) {
                 $action->addError('structure', 'Must have at least one valid answer', $context);
                 continue 2;
             }
         }
     }
 }
 /**
  * @param CheckAction $action
  *
  * @return Finder
  */
 public function perform(CheckAction $action)
 {
     $finder = new Finder();
     $files = $finder->in($action->getBasePath() . '/*')->name('context.yml');
     $parser = new Parser();
     $collector = [];
     foreach ($files as $context) {
         $errorContext = ['file_name' => $context->getFileName(), 'file_path' => $context->getPathName(), 'discriminator' => 'context', 'training' => $this->getCurrentTraining($context)];
         try {
             $contextData = $parser->parse(file_get_contents($context->getRealPath()));
         } catch (ParseException $e) {
             $action->addError('parser', $e->getMessage(), $errorContext);
             $action->addSkip('file', $context->getFileName());
             continue;
         }
         $mandatory = ['name', 'icons', 'label', 'availableLanguages', 'customize', 'threshold', 'defaults'];
         foreach ($mandatory as $key) {
             if (!isset($contextData[$key])) {
                 $action->addError('structure', sprintf('Missing required key "%s"', $key), $errorContext);
             }
         }
         if (isset($contextData['name']) && isset($contextData['label'])) {
             $collector[$this->getCurrentTraining($context) . '=name'][] = $contextData['name'];
             $collector[$this->getCurrentTraining($context) . '=label'][] = $contextData['label'];
         }
         foreach ($contextData as $key => $value) {
             if (in_array($key, ['name', 'label'])) {
                 if (!is_string($key) || empty($key)) {
                     $action->addError('structure', sprintf('Key "%s" must be type of "string" and not empty', $key), $errorContext);
                 }
                 continue;
             }
             if ('availableLanguages' === $key) {
                 if (!is_array($value)) {
                     $action->addError('structure', sprintf('Key "%s" must be type of array with at least one element', $key), $errorContext);
                     continue;
                 }
                 if (0 === count($value)) {
                     $action->addError('structure', sprintf('Key "%s" must contain at least one element', $key), $errorContext);
                     continue;
                 }
                 foreach ($value as $vlKey => $vlValue) {
                     if (!is_string($vlValue)) {
                         $action->addError('structure', sprintf('Child %s of key "%s" must be a string, given "%s"', $vlKey, $key, gettype($vlValue)), $errorContext);
                         continue 2;
                     }
                 }
             }
             if ('availableLevels' === $key && null !== $value) {
                 foreach ($value as $vlKey => $vlValue) {
                     if (!is_string($vlValue)) {
                         $action->addError('structure', $errorContext);
                         continue 2;
                     }
                 }
             }
             if ('customize' === $key) {
                 $requiredKeys = ['exclude_categories', 'number_of_questions'];
                 foreach ($requiredKeys as $vlValue) {
                     if (!array_key_exists($vlValue, $value)) {
                         $action->addError('structure', sprintf('Mandatory key "%s" missing for parameter "%s"', $vlValue, $key), $errorContext);
                         continue 2;
                     }
                     if (!is_bool($value[$vlValue])) {
                         $action->addError('structure', sprintf('Child "%s" of "%s" must be type of "bool", given "%s"', $vlValue, $key, gettype($value)), $errorContext);
                         continue 2;
                     }
                 }
             }
             if ('threshold' === $key && null !== $value) {
                 if (!is_array($value) || empty($value)) {
                     $action->addError('structure', sprintf('Key "%s" must be type of array with at least one element, given "%s"', $key, gettype($value)), $errorContext);
                     continue;
                 }
                 foreach ($value as $vlKey => $vlValue) {
                     if (!is_string($vlKey) || !is_int($vlValue)) {
                         $action->addError('structure', sprintf('Threshold must be like "(string) level_id => (int) score_trigger, given %s => %s"', $vlKey, $vlValue), $errorContext);
                     }
                 }
             }
             if ('defaults' === $key) {
                 if (!is_array($value)) {
                     $action->addError('structure', sprintf('Key "%s" must be type of array, given "%s"', $key, gettype($value)), $errorContext);
                     continue;
                 }
                 $childrenRequired = ['language', 'questions_peer_category'];
                 foreach ($childrenRequired as $mandatoryKey) {
                     if (!array_key_exists($mandatoryKey, $value)) {
                         $action->addError('structure', sprintf('Child %s of %s is required', $mandatoryKey, $key), $errorContext);
                     }
                 }
                 foreach ($value as $vlKey => $vlValue) {
                     if (!in_array($vlKey, $childrenRequired)) {
                         $action->addError('structure', sprintf('Unknown %s key', $vlKey), $errorContext);
                         continue 2;
                     }
                     if ('language' === $vlKey) {
                         if (!is_string($vlValue)) {
                             if (!in_array($vlKey, $childrenRequired)) {
                                 $action->addError('structure', sprintf('Child %s of %s must be type of "string", given %s', $vlKey, $key, gettype($value)), $errorContext);
                             }
                         }
                         continue 2;
                     }
                     if ('questions_peer_category' === $vlKey) {
                         if (!is_int($vlValue)) {
                             $action->addError('structure', sprintf('Child %s of %s must be type of "integer", given %s', $vlKey, $key, gettype($value)), $errorContext);
                         }
                         continue 2;
                     }
                 }
             }
         }
     }
     foreach ($collector as $properties => $elements) {
         list($training, $property) = explode('=', $properties);
         foreach (array_count_values($elements) as $count) {
             if ($count > 1) {
                 $action->addError('integrity', sprintf('A training has already the same %s', $property), ['file_name' => 'context.yml', 'training' => $training]);
             }
         }
     }
 }
 /**
  * @param CheckAction $action
  */
 public function perform(CheckAction $action)
 {
     $files = $this->getFiles($action->getBasePath());
     $parser = new Parser();
     $collector = ['category' => [], 'question' => [], 'answer' => []];
     foreach ($files as $file) {
         if (in_array($file->getFileName(), $action->getSkip('file'))) {
             continue 1;
         }
         $data = $parser->parse(file_get_contents($file->getRealPath()));
         if (in_array($data['category'], $action->getSkip('category'))) {
             continue 1;
         }
         $trainingName = $this->getCurrentTraining($file);
         $identifier = implode('=', [$trainingName, $file->getFileName()]);
         $collector['category'][$identifier] = $data['category'];
         foreach ($data['questions'] as $questionNode) {
             if (in_array($questionNode['question'], $action->getSkip('question'))) {
                 continue 2;
             }
             $collector['question'][$identifier][] = $questionNode['question'];
             foreach ($questionNode['answers'] as $answerNode) {
                 if (in_array($answerNode['value'], $action->getSkip('answer'))) {
                     continue 3;
                 }
                 $collector['answer'][$identifier][$questionNode['question']][] = $answerNode['value'];
             }
         }
     }
     foreach ($collector as $type => $node) {
         if ($type === 'category') {
             //flatten
             foreach (array_count_values($collector[$type]) as $category => $count) {
                 if ($count > 1) {
                     $affected = array_keys($collector[$type], $category);
                     $errorFiles = [];
                     foreach ($affected as $value) {
                         list($training, $file) = explode('=', $value);
                         $errorFiles[] = $file;
                     }
                     $action->addError('integrity', sprintf('Category "%s" was find several times', $category), ['file_name' => $errorFiles, 'discriminator' => 'category', 'training' => $training]);
                 }
             }
         }
         if ($type === 'question') {
             //nested
             foreach ($node as $context => $questionsNodes) {
                 list($training, $fileName) = explode('=', $context);
                 foreach (array_count_values($questionsNodes) as $question => $count) {
                     if ($count > 1) {
                         $action->addError('integrity', sprintf('Question "%s" was find several times', $question), ['file_name' => $fileName, 'discriminator' => 'question', 'training' => $training]);
                     }
                 }
             }
         }
         if ($type === 'answer') {
             //nested
             foreach ($node as $context => $answerNode) {
                 list($training, $fileName) = explode('=', $context);
                 foreach ($answerNode as $question => $answers) {
                     foreach (array_count_values($answers) as $answer => $count) {
                         if ($count > 1) {
                             $action->addError('integrity', sprintf('Answer "%s" was find several times for question "%s"', $answer, $question), ['file_name' => $fileName, 'discriminator' => 'answer', 'training' => $training]);
                         }
                     }
                 }
             }
         }
     }
 }
 /**
  * 1 Set commit status to pending
  * 2 Save status in DB (mongo)
  * 3 Clone the branch of fork who is pulled
  * 4 Check commit data
  * 5 Save report inside MongoDB
  * 6 Update commit status success|error|failure
  *
  * @param Request  $request
  * @param array    $data
  * @param Response $response
  *
  * @return Response
  */
 protected function doReview(Request $request, array $data, Response $response)
 {
     if (null !== $this->logger) {
         $this->logger->debug(sprintf('Set commit status pending on certificationy'), ['delivery_uuid' => $data['delivery_uuid']]);
     }
     $persistenceAction = new PersistenceAction($this->client, $data, ['total' => 0], PersistenceAction::TASK_START);
     //Save in db (mongo)
     $this->actionDispatcher->dispatch(ReviewerBotActions::PERSIST, $persistenceAction);
     if (null !== $this->logger) {
         $this->logger->debug(sprintf('Set commit status pending on github'), ['delivery_uuid' => $data['delivery_uuid']]);
     }
     //Set commit status to pending
     $this->actionDispatcher->dispatch(BotActions::SET_COMMIT_STATUS_PENDING, new SwitchCommitStatusAction($this->client, $data, 'Certificationy CI is currently working'));
     if (null !== $this->logger) {
         $this->logger->debug(sprintf('Clone locally'), ['delivery_uuid' => $data['delivery_uuid']]);
     }
     //Clone locally last commit on pull request
     $this->actionDispatcher->dispatch(ReviewerBotActions::GIT_CLONE, new GitLocaleCloneAction($this->client, $data));
     $basePath = sprintf('%s/../web/analyze/%s/%s', $this->kernelRootDir, $data['content']['pull_request']['head']['sha'], $data['content']['pull_request']['head']['repo']['name']);
     if (null !== $this->logger) {
         $this->logger->debug(sprintf('Check specification'), ['delivery_uuid' => $data['delivery_uuid']]);
     }
     //Check certificationy
     $this->actionDispatcher->dispatch(ReviewerBotActions::CHECK, $checkAction = new CheckAction($this->client, $data, $basePath));
     if (null !== $this->logger) {
         $this->logger->debug(sprintf('Set commit status finished on certificationy'), ['delivery_uuid' => $data['delivery_uuid']]);
     }
     $stopwatchEvent = $this->stopwatch->stop('bot');
     //Save in db (mongo)
     $this->actionDispatcher->dispatch(ReviewerBotActions::PERSIST, new PersistenceAction($this->client, $data, $checkAction->getErrors(), PersistenceAction::TASK_END, $stopwatchEvent));
     if (0 === $checkAction->getErrors()['total']) {
         if (null !== $this->logger) {
             $this->logger->debug(sprintf('Set commit status successfull on github'), ['delivery_uuid' => $data['delivery_uuid']]);
         }
         $this->actionDispatcher->dispatch(BotActions::SET_COMMIT_STATUS_SUCCESS, new SwitchCommitStatusAction($this->client, $data, 'Everything is OK, well done.'));
     } else {
         if (null !== $this->logger) {
             $this->logger->debug(sprintf('Set commit status is errored on github'), ['delivery_uuid' => $data['delivery_uuid']]);
         }
         $this->actionDispatcher->dispatch(BotActions::SET_COMMIT_STATUS_ERROR, new SwitchCommitStatusAction($this->client, $data, 'The test as failed, please look details to correct it'));
     }
     if (null !== $this->logger) {
         $this->logger->debug(sprintf('Perform clean'), ['delivery_uuid' => $data['delivery_uuid']]);
     }
     //Clean up
     $this->actionDispatcher->dispatch(ReviewerBotActions::CLEAN, new RemoveFolderAction($this->client, $data));
     return $response;
 }