diff --git a/plugins/API/ProcessedReport.php b/plugins/API/ProcessedReport.php index f5384be8393..0a5a34bfa28 100644 --- a/plugins/API/ProcessedReport.php +++ b/plugins/API/ProcessedReport.php @@ -71,6 +71,7 @@ public function getMetadata( } } + $looseMatchingReports = []; foreach ($reportsMetadata as $report) { // See ArchiveProcessor/Aggregator.php - unique visitors are not processed for period != day // todo: should use SettingsPiwik::isUniqueVisitorsEnabled instead @@ -87,23 +88,38 @@ public function getMetadata( empty($apiParameters) && empty($report['parameters']) ) { - return array($report); + return [$report]; } if (empty($report['parameters'])) { continue; } - $diff = array_diff($report['parameters'], $apiParameters); - if (empty($diff)) { - return array($report); + + $isExactMatchingReport = $report['parameters'] == $apiParameters; + if ($isExactMatchingReport) { + return [$report]; + } + $isLooseMatchingReport = empty(array_diff_assoc($report['parameters'], $apiParameters)); + if ($isLooseMatchingReport) { + $matchingParams = count(array_intersect_assoc($report['parameters'], $apiParameters)); + if (!array_key_exists($matchingParams, $looseMatchingReports)) { + $looseMatchingReports[$matchingParams] = []; + } + $looseMatchingReports[$matchingParams][] = $report; } } } + if (count($looseMatchingReports)) { + // most matching params are last + $reports = end($looseMatchingReports); + return [reset($reports)]; + } + return false; } /** - * Verfies whether the given report exists for the given site. + * Verifies whether the given report exists for the given site. * * @param int $idSite * @param string $apiMethodUniqueId For example 'MultiSites_getAll' diff --git a/plugins/API/tests/Integration/ProcessedReportTest.php b/plugins/API/tests/Integration/ProcessedReportTest.php new file mode 100644 index 00000000000..2c3b50cdccc --- /dev/null +++ b/plugins/API/tests/Integration/ProcessedReportTest.php @@ -0,0 +1,161 @@ +idSite = Fixture::createWebsite('2020-02-03 03:04:05'); + + $this->processedReport = StaticContainer::get(ProcessedReport::class); + + $this->addTestReports(); + } + + public function test_getMetadata_matchesCorrectReport_whenExactNoParametersProvided() + { + $reports = $this->getTestReportMetadata([]); + $this->assertNotEmpty($reports); + $this->assertCount(1, $reports); + + $report = $reports[0]; + $this->assertNotEmpty($report); + $this->assertEquals('TestPlugin', $report['module']); + $this->assertEquals('getTestReport', $report['action']); + $this->assertEmpty($report['parameters'] ?? []); + } + + public function test_getMetadata_matchesCorrectReport_whenExactOneApiParameterProvided() + { + $reports = $this->getTestReportMetadata(['param0' => 'testvalue']); + $this->assertNotEmpty($reports); + $this->assertCount(1, $reports); + + $report = $reports[0]; + $this->assertNotEmpty($report); + $this->assertEquals('TestPlugin', $report['module']); + $this->assertEquals('getTestReport', $report['action']); + $this->assertEquals(['param0' => 'testvalue'], $report['parameters']); + } + + public function test_getMetadata_matchesCorrectReport_whenExactTwoApiParametersProvided() + { + $reports = $this->getTestReportMetadata(['param0' => 'testvalue', 'param1' => 'testvalue']); + $this->assertNotEmpty($reports); + $this->assertCount(1, $reports); + + $report = $reports[0]; + $this->assertNotEmpty($report); + $this->assertEquals('TestPlugin', $report['module']); + $this->assertEquals('getTestReport', $report['action']); + $this->assertEquals(['param0' => 'testvalue', 'param1' => 'testvalue'], $report['parameters']); + } + + public function test_getMetadata_returnsFirstMatchedReportWithParams_whenUnneededApiParametersProvided() + { + $reports = $this->getTestReportMetadata(['param0' => 'testvalue', 'param1' => 'testvalue', 'extraParam' => 'value']); + + $report = $reports[0]; + $this->assertNotEmpty($report); + $this->assertEquals('TestPlugin', $report['module']); + $this->assertEquals('getTestReport', $report['action']); + $this->assertEquals(['param0' => 'testvalue', 'param1' => 'testvalue'], $report['parameters']); + } + + public function test_getMetadata_doesNotMatch_whenMissingApiParameters() + { + $reports = $this->getTestReportMetadata(['param1' => 'testvalue']); + $this->assertEmpty($reports); + } + + private function addTestReports() + { + Piwik::addAction('Report.addReports', function (&$reports) { + $reports[] = $this->makeTestReportWithParameters(0); + $reports[] = $this->makeTestReportWithParameters(1); + $reports[] = $this->makeTestReportWithParameters(2); + }); + + Fixture::clearInMemoryCaches(); + + $reports = StaticContainer::get(ReportsProvider::class)->getAllReports(); + $reports = array_filter($reports, function (Report $r) { + return $r->getModule() === 'TestPlugin'; + }); + + // sanity check + $addedReports = 3; + $this->assertCount($addedReports, $reports); + } + + private function makeTestReportWithParameters(int $paramCount): Report + { + $report = new class () extends Report { + public $paramCount = 0; + + public function init() + { + parent::init(); + + $this->module = 'TestPlugin'; + $this->action = 'getTestReport'; + $this->order = 1 + $this->paramCount; + + for ($i = 0; $i < $this->paramCount; ++$i) { + $this->parameters['param' . $i] = 'testvalue'; + } + } + }; + + // have to set the property this way since Report::__construct() is final + $report->paramCount = $paramCount; + $report->init(); + return $report; + } + + private function getTestReportMetadata(array $parameters) + { + return $this->processedReport->getMetadata( + $this->idSite, + 'TestPlugin', + 'getTestReport', + $parameters, + false, + false, + false, + false, + false + ); + } +}