Proxy receives all the API calls requests via call() and forwards them to the right object, with the parameters in the right order. It will also log the performance of API calls (time spent, parameter values, etc.) if logger available
Inheritance: extends Piwik\Singleton
 public function test_CheckIfMethodComment_DoesNotContainHideAnnotation()
 {
     $annotation = '@not found here';
     EventDispatcher::getInstance()->addObserver('API.DocumentationGenerator.@hello', function (&$hide) {
         $hide = true;
     });
     $this->assertEquals(Proxy::getInstance()->shouldHideAPIMethod($annotation), false);
 }
Example #2
0
 public function listAllAPI()
 {
     $view = new View("@API/listAllAPI");
     $this->setGeneralVariablesView($view);
     $ApiDocumentation = new DocumentationGenerator();
     $view->countLoadedAPI = Proxy::getInstance()->getCountRegisteredClasses();
     $view->list_api_methods_with_links = $ApiDocumentation->getAllInterfaceString();
     return $view->render();
 }
Example #3
0
 /**
  * @internal
  */
 protected function loadDataTableFromAPI()
 {
     if (!is_null($this->dataTable)) {
         // data table is already there
         // this happens when setDataTable has been used
         return $this->dataTable;
     }
     // we build the request (URL) to call the API
     $request = $this->buildApiRequestArray();
     $module = $this->requestConfig->getApiModuleToRequest();
     $method = $this->requestConfig->getApiMethodToRequest();
     PluginManager::getInstance()->checkIsPluginActivated($module);
     $class = ApiRequest::getClassNameAPI($module);
     $dataTable = Proxy::getInstance()->call($class, $method, $request);
     $response = new ResponseBuilder($format = 'original', $request);
     $response->disableSendHeader();
     $response->disableDataTablePostProcessor();
     $this->dataTable = $response->getResponse($dataTable, $module, $method);
 }
Example #4
0
 /**
  * Dispatches the API request to the appropriate API method and returns the result
  * after post-processing.
  *
  * Post-processing includes:
  *
  * - flattening if **flat** is 0
  * - running generic filters unless **disable_generic_filters** is set to 1
  * - URL decoding label column values
  * - running queued filters unless **disable_queued_filters** is set to 1
  * - removing columns based on the values of the **hideColumns** and **showColumns** query parameters
  * - filtering rows if the **label** query parameter is set
  * - converting the result to the appropriate format (ie, XML, JSON, etc.)
  *
  * If `'original'` is supplied for the output format, the result is returned as a PHP
  * object.
  *
  * @throws PluginDeactivatedException if the module plugin is not activated.
  * @throws Exception if the requested API method cannot be called, if required parameters for the
  *                   API method are missing or if the API method throws an exception and the **format**
  *                   query parameter is **original**.
  * @return DataTable|Map|string The data resulting from the API call.
  */
 public function process()
 {
     // read the format requested for the output data
     $outputFormat = strtolower(Common::getRequestVar('format', 'xml', 'string', $this->request));
     // create the response
     $response = new ResponseBuilder($outputFormat, $this->request);
     $corsHandler = new CORSHandler();
     $corsHandler->handle();
     try {
         // read parameters
         $moduleMethod = Common::getRequestVar('method', null, 'string', $this->request);
         list($module, $method) = $this->extractModuleAndMethod($moduleMethod);
         $module = $this->renameModule($module);
         if (!\Piwik\Plugin\Manager::getInstance()->isPluginActivated($module)) {
             throw new PluginDeactivatedException($module);
         }
         $apiClassName = $this->getClassNameAPI($module);
         self::reloadAuthUsingTokenAuth($this->request);
         // call the method
         $returnedValue = Proxy::getInstance()->call($apiClassName, $method, $this->request);
         $toReturn = $response->getResponse($returnedValue, $module, $method);
     } catch (Exception $e) {
         Log::debug($e);
         $toReturn = $response->getResponseException($e);
     }
     return $toReturn;
 }
Example #5
0
 public function test_generateReport_CatchesIndividualReportProcessExceptions_WithoutFailingToGenerateWholeReport()
 {
     $realProxy = new Proxy();
     $mockProxy = $this->getMock('Piwik\\API\\Proxy', array('call'));
     $mockProxy->expects($this->any())->method('call')->willReturnCallback(function ($className, $methodName, $parametersRequest) use($realProxy) {
         switch ($className) {
             case '\\Piwik\\Plugins\\VisitsSummary\\API':
                 $result = new DataTable();
                 $result->addRowFromSimpleArray(array('label' => 'visits label', 'nb_visits' => 1));
                 return $result;
             case '\\Piwik\\Plugins\\UserCountry\\API':
                 throw new \Exception("error");
             case '\\Piwik\\Plugins\\Referrers\\API':
                 $result = new DataTable();
                 $result->addRowFromSimpleArray(array('label' => 'referrers label', 'nb_visits' => 1));
                 return $result;
             case '\\Piwik\\Plugins\\API\\API':
                 return $realProxy->call($className, $methodName, $parametersRequest);
             default:
                 throw new \Exception("Unexpected method {$className}::{$methodName}.");
         }
     });
     Proxy::setSingletonInstance($mockProxy);
     $idReport = APIScheduledReports::getInstance()->addReport(1, '', Schedule::PERIOD_DAY, 0, ScheduledReports::EMAIL_TYPE, ReportRenderer::HTML_FORMAT, array('VisitsSummary_get', 'UserCountry_getCountry', 'Referrers_getWebsites'), array(ScheduledReports::DISPLAY_FORMAT_PARAMETER => ScheduledReports::DISPLAY_FORMAT_TABLES_ONLY));
     ob_start();
     $result = APIScheduledReports::getInstance()->generateReport($idReport, Date::factory('now')->toString(), $language = false, $outputType = APIScheduledReports::OUTPUT_RETURN);
     ob_end_clean();
     $this->assertContains('id="VisitsSummary_get"', $result);
     $this->assertContains('id="Referrers_getWebsites"', $result);
     $this->assertNotContains('id="UserCountry_getCountry"', $result);
 }
Example #6
0
 /**
  * Renders a report depending on the configured ViewDataTable see {@link configureView()} and
  * {@link getDefaultTypeViewDataTable()}. If you want to customize the render process or just render any custom view
  * you can overwrite this method.
  *
  * @return string
  * @throws \Exception In case the given API action does not exist yet.
  * @api
  */
 public function render()
 {
     $viewDataTable = Common::getRequestVar('viewDataTable', false, 'string');
     $fixed = Common::getRequestVar('forceView', 0, 'int');
     $module = $this->getModule();
     $action = $this->getAction();
     $apiProxy = Proxy::getInstance();
     if (!$apiProxy->isExistingApiAction($module, $action)) {
         throw new Exception("Invalid action name '{$module}' for '{$action}' plugin.");
     }
     $apiAction = $apiProxy->buildApiActionName($module, $action);
     $view = ViewDataTableFactory::build($viewDataTable, $apiAction, $module . '.' . $action, $fixed);
     return $view->render();
 }
Example #7
0
 /**
  * Convenience method that creates and renders a ViewDataTable for a API method.
  *
  * @param string $apiAction The name of the API action (eg, `'getResolution'`).
  * @param bool $controllerAction The name of the Controller action name  that is rendering the report. Defaults
  *                               to the `$apiAction`.
  * @param bool $fetch If `true`, the rendered string is returned, if `false` it is `echo`'d.
  * @throws \Exception if `$pluginName` is not an existing plugin or if `$apiAction` is not an
  *                    existing method of the plugin's API.
  * @return string|void See `$fetch`.
  * @api
  */
 protected function renderReport($apiAction, $controllerAction = false)
 {
     $pluginName = $this->pluginName;
     /** @var Proxy $apiProxy */
     $apiProxy = Proxy::getInstance();
     if (!$apiProxy->isExistingApiAction($pluginName, $apiAction)) {
         throw new \Exception("Invalid action name '{$apiAction}' for '{$pluginName}' plugin.");
     }
     $apiAction = $apiProxy->buildApiActionName($pluginName, $apiAction);
     if ($controllerAction !== false) {
         $controllerAction = $pluginName . '.' . $controllerAction;
     }
     $view = ViewDataTableFactory::build(null, $apiAction, $controllerAction);
     $rendered = $view->render();
     return $rendered;
 }
Example #8
0
 private function getAllApiMethods()
 {
     $result = array();
     foreach (Proxy::getInstance()->getMetadata() as $class => $info) {
         $moduleName = Proxy::getInstance()->getModuleNameFromClassName($class);
         foreach ($info as $methodName => $infoMethod) {
             if ($this->shouldSkipApiMethod($moduleName, $methodName)) {
                 continue;
             }
             $result[] = array($class, $moduleName, $methodName);
         }
     }
     return $result;
 }
Example #9
0
 /**
  * Get a combined report of the *.get API methods.
  */
 public function get($idSite, $period, $date, $segment = false, $columns = false)
 {
     Piwik::checkUserHasViewAccess($idSite);
     $columns = Piwik::getArrayFromApiParameter($columns);
     // build columns map for faster checks later on
     $columnsMap = array();
     foreach ($columns as $column) {
         $columnsMap[$column] = true;
     }
     // find out which columns belong to which plugin
     $columnsByPlugin = array();
     $meta = \Piwik\Plugins\API\API::getInstance()->getReportMetadata($idSite, $period, $date);
     foreach ($meta as $reportMeta) {
         // scan all *.get reports
         if ($reportMeta['action'] == 'get' && !isset($reportMeta['parameters']) && $reportMeta['module'] != 'API' && !empty($reportMeta['metrics'])) {
             $plugin = $reportMeta['module'];
             $allMetrics = array_merge($reportMeta['metrics'], @$reportMeta['processedMetrics'] ?: array());
             foreach ($allMetrics as $column => $columnTranslation) {
                 // a metric from this report has been requested
                 if (isset($columnsMap[$column]) || empty($columnsMap)) {
                     $columnsByPlugin[$plugin][] = $column;
                 }
             }
         }
     }
     krsort($columnsByPlugin);
     $mergedDataTable = false;
     $params = compact('idSite', 'period', 'date', 'segment', 'idGoal');
     foreach ($columnsByPlugin as $plugin => $columns) {
         // load the data
         $className = Request::getClassNameAPI($plugin);
         $params['columns'] = implode(',', $columns);
         $dataTable = Proxy::getInstance()->call($className, 'get', $params);
         $dataTable->filter(function (DataTable $table) {
             $table->clearQueuedFilters();
         });
         // merge reports
         if ($mergedDataTable === false) {
             $mergedDataTable = $dataTable;
         } else {
             $merger = new MergeDataTables();
             $merger->mergeDataTables($mergedDataTable, $dataTable);
         }
     }
     if (!empty($columnsMap) && !empty($mergedDataTable)) {
         $mergedDataTable->queueFilter('ColumnDelete', array(false, array_keys($columnsMap)));
     }
     return $mergedDataTable;
 }
Example #10
0
 /**
  * Get a combined report of the *.get API methods.
  */
 public function get($idSite, $period, $date, $segment = false, $columns = false)
 {
     $columns = Piwik::getArrayFromApiParameter($columns);
     // build columns map for faster checks later on
     $columnsMap = array();
     foreach ($columns as $column) {
         $columnsMap[$column] = true;
     }
     // find out which columns belong to which plugin
     $columnsByPlugin = array();
     $meta = \Piwik\Plugins\API\API::getInstance()->getReportMetadata($idSite, $period, $date);
     foreach ($meta as $reportMeta) {
         // scan all *.get reports
         if ($reportMeta['action'] == 'get' && !isset($reportMeta['parameters']) && $reportMeta['module'] != 'API' && !empty($reportMeta['metrics'])) {
             $plugin = $reportMeta['module'];
             foreach ($reportMeta['metrics'] as $column => $columnTranslation) {
                 // a metric from this report has been requested
                 if (isset($columnsMap[$column]) || empty($columnsMap)) {
                     $columnsByPlugin[$plugin][] = $column;
                 }
             }
         }
     }
     krsort($columnsByPlugin);
     $mergedDataTable = false;
     $params = compact('idSite', 'period', 'date', 'segment', 'idGoal');
     foreach ($columnsByPlugin as $plugin => $columns) {
         // load the data
         $className = Request::getClassNameAPI($plugin);
         $params['columns'] = implode(',', $columns);
         $dataTable = Proxy::getInstance()->call($className, 'get', $params);
         // make sure the table has all columns
         $array = $dataTable instanceof DataTable\Map ? $dataTable->getDataTables() : array($dataTable);
         foreach ($array as $table) {
             // we don't support idSites=all&date=DATE1,DATE2
             if ($table instanceof DataTable) {
                 $firstRow = $table->getFirstRow();
                 if (!$firstRow) {
                     $firstRow = new Row();
                     $table->addRow($firstRow);
                 }
                 foreach ($columns as $column) {
                     if ($firstRow->getColumn($column) === false) {
                         $firstRow->setColumn($column, 0);
                     }
                 }
             }
         }
         // merge reports
         if ($mergedDataTable === false) {
             $mergedDataTable = $dataTable;
         } else {
             $this->mergeDataTables($mergedDataTable, $dataTable);
         }
     }
     return $mergedDataTable;
 }
 public function tearDown()
 {
     parent::tearDown();
     // reset that value after the test
     Proxy::getInstance()->setHideIgnoredFunctions(true);
 }
Example #12
0
 /**
  * Dispatches the API request to the appropriate API method and returns the result
  * after post-processing.
  *
  * Post-processing includes:
  *
  * - flattening if **flat** is 0
  * - running generic filters unless **disable_generic_filters** is set to 1
  * - URL decoding label column values
  * - running queued filters unless **disable_queued_filters** is set to 1
  * - removing columns based on the values of the **hideColumns** and **showColumns** query parameters
  * - filtering rows if the **label** query parameter is set
  * - converting the result to the appropriate format (ie, XML, JSON, etc.)
  *
  * If `'original'` is supplied for the output format, the result is returned as a PHP
  * object.
  *
  * @throws PluginDeactivatedException if the module plugin is not activated.
  * @throws Exception if the requested API method cannot be called, if required parameters for the
  *                   API method are missing or if the API method throws an exception and the **format**
  *                   query parameter is **original**.
  * @return DataTable|Map|string The data resulting from the API call.
  */
 public function process()
 {
     // read the format requested for the output data
     $outputFormat = strtolower(Common::getRequestVar('format', 'xml', 'string', $this->request));
     // create the response
     $response = new ResponseBuilder($outputFormat, $this->request);
     $corsHandler = new CORSHandler();
     $corsHandler->handle();
     $tokenAuth = Common::getRequestVar('token_auth', '', 'string', $this->request);
     $shouldReloadAuth = false;
     try {
         // read parameters
         $moduleMethod = Common::getRequestVar('method', null, 'string', $this->request);
         list($module, $method) = $this->extractModuleAndMethod($moduleMethod);
         list($module, $method) = self::getRenamedModuleAndAction($module, $method);
         PluginManager::getInstance()->checkIsPluginActivated($module);
         $apiClassName = self::getClassNameAPI($module);
         if ($shouldReloadAuth = self::shouldReloadAuthUsingTokenAuth($this->request)) {
             $access = Access::getInstance();
             $tokenAuthToRestore = $access->getTokenAuth();
             $hadSuperUserAccess = $access->hasSuperUserAccess();
             self::forceReloadAuthUsingTokenAuth($tokenAuth);
         }
         // call the method
         $returnedValue = Proxy::getInstance()->call($apiClassName, $method, $this->request);
         $toReturn = $response->getResponse($returnedValue, $module, $method);
     } catch (Exception $e) {
         Log::debug($e);
         $toReturn = $response->getResponseException($e);
     }
     if ($shouldReloadAuth) {
         $this->restoreAuthUsingTokenAuth($tokenAuthToRestore, $hadSuperUserAccess);
     }
     return $toReturn;
 }
Example #13
0
 public function tearDown()
 {
     PluginManager::unsetInstance();
     Proxy::unsetInstance();
 }
Example #14
0
 /**
  * Given a list of default parameters to set, returns the URLs of APIs to call
  * If any API was specified in $this->apiNotToCall we ensure only these are tested.
  * If any API is set as excluded (see list below) then it will be ignored.
  *
  * @param array $parametersToSet Parameters to set in api call
  * @param array $formats         Array of 'format' to fetch from API
  * @param array $periods         Array of 'period' to query API
  * @param bool  $supertableApi
  * @param bool  $setDateLastN    If set to true, the 'date' parameter will be rewritten to query instead a range of dates, rather than one period only.
  * @param bool|string $language        2 letter language code, defaults to default piwik language
  * @param bool|string $fileExtension
  *
  * @throws Exception
  *
  * @return array of API URLs query strings
  */
 protected function generateUrlsApi($parametersToSet, $formats, $periods, $supertableApi = false, $setDateLastN = false, $language = false, $fileExtension = false)
 {
     // Get the URLs to query against the API for all functions starting with get*
     $skipped = $requestUrls = array();
     $apiMetadata = new DocumentationGenerator();
     foreach (Proxy::getInstance()->getMetadata() as $class => $info) {
         $moduleName = Proxy::getInstance()->getModuleNameFromClassName($class);
         foreach ($info as $methodName => $infoMethod) {
             $apiId = $moduleName . '.' . $methodName;
             // If Api to test were set, we only test these
             if (!empty($this->apiToCall) && in_array($moduleName, $this->apiToCall) === false && in_array($apiId, $this->apiToCall) === false) {
                 $skipped[] = $apiId;
                 continue;
             } elseif (strpos($methodName, 'get') !== 0 && $methodName != 'generateReport' || in_array($moduleName, $this->apiNotToCall) === true || in_array($apiId, $this->apiNotToCall) === true || $methodName == 'getLogoUrl' || $methodName == 'getSVGLogoUrl' || $methodName == 'hasSVGLogo' || $methodName == 'getHeaderLogoUrl') {
                 // Excluded modules from test
                 $skipped[] = $apiId;
                 continue;
             }
             foreach ($periods as $period) {
                 $parametersToSet['period'] = $period;
                 // If date must be a date range, we process this date range by adding 6 periods to it
                 if ($setDateLastN) {
                     if (!isset($parametersToSet['dateRewriteBackup'])) {
                         $parametersToSet['dateRewriteBackup'] = $parametersToSet['date'];
                     }
                     $lastCount = (int) $setDateLastN;
                     if ($setDateLastN === true) {
                         $lastCount = 6;
                     }
                     $firstDate = $parametersToSet['dateRewriteBackup'];
                     $secondDate = date('Y-m-d', strtotime("+{$lastCount} " . $period . "s", strtotime($firstDate)));
                     $parametersToSet['date'] = $firstDate . ',' . $secondDate;
                 }
                 // Set response language
                 if ($language !== false) {
                     $parametersToSet['language'] = $language;
                 }
                 // set idSubtable if subtable API is set
                 if ($supertableApi !== false) {
                     $request = new Request(array('module' => 'API', 'method' => $supertableApi, 'idSite' => $parametersToSet['idSite'], 'period' => $parametersToSet['period'], 'date' => $parametersToSet['date'], 'format' => 'php', 'serialize' => 0));
                     // find first row w/ subtable
                     $content = $request->process();
                     $this->checkRequestResponse($content);
                     foreach ($content as $row) {
                         if (isset($row['idsubdatatable'])) {
                             $parametersToSet['idSubtable'] = $row['idsubdatatable'];
                             break;
                         }
                     }
                     // if no subtable found, throw
                     if (!isset($parametersToSet['idSubtable'])) {
                         throw new Exception("Cannot find subtable to load for {$apiId} in {$supertableApi}.");
                     }
                 }
                 // Generate for each specified format
                 foreach ($formats as $format) {
                     $parametersToSet['format'] = $format;
                     $parametersToSet['hideIdSubDatable'] = 1;
                     $parametersToSet['serialize'] = 1;
                     $exampleUrl = $apiMetadata->getExampleUrl($class, $methodName, $parametersToSet);
                     if ($exampleUrl === false) {
                         $skipped[] = $apiId;
                         continue;
                     }
                     // Remove the first ? in the query string
                     $exampleUrl = substr($exampleUrl, 1);
                     $apiRequestId = $apiId;
                     if (strpos($exampleUrl, 'period=') !== false) {
                         $apiRequestId .= '_' . $period;
                     }
                     $apiRequestId .= '.' . $format;
                     if ($fileExtension) {
                         $apiRequestId .= '.' . $fileExtension;
                     }
                     $requestUrls[$apiRequestId] = $exampleUrl;
                 }
             }
         }
     }
     return $requestUrls;
 }
Example #15
0
 /**
  * Convenience method that creates and renders a ViewDataTable for a API method.
  *
  * @param string|\Piwik\Plugin\Report $apiAction The name of the API action (eg, `'getResolution'`) or
  *                                      an instance of an report.
  * @param bool $controllerAction The name of the Controller action name  that is rendering the report. Defaults
  *                               to the `$apiAction`.
  * @param bool $fetch If `true`, the rendered string is returned, if `false` it is `echo`'d.
  * @throws \Exception if `$pluginName` is not an existing plugin or if `$apiAction` is not an
  *                    existing method of the plugin's API.
  * @return string|void See `$fetch`.
  * @api
  */
 protected function renderReport($apiAction, $controllerAction = false)
 {
     if (empty($controllerAction) && is_string($apiAction)) {
         $report = Report::factory($this->pluginName, $apiAction);
         if (!empty($report)) {
             $apiAction = $report;
         }
     }
     if ($apiAction instanceof Report) {
         $this->checkSitePermission();
         $apiAction->checkIsEnabled();
         return $apiAction->render();
     }
     $pluginName = $this->pluginName;
     /** @var Proxy $apiProxy */
     $apiProxy = Proxy::getInstance();
     if (!$apiProxy->isExistingApiAction($pluginName, $apiAction)) {
         throw new \Exception("Invalid action name '{$apiAction}' for '{$pluginName}' plugin.");
     }
     $apiAction = $apiProxy->buildApiActionName($pluginName, $apiAction);
     if ($controllerAction !== false) {
         $controllerAction = $pluginName . '.' . $controllerAction;
     }
     $view = ViewDataTableFactory::build(null, $apiAction, $controllerAction);
     $rendered = $view->render();
     return $rendered;
 }
Example #16
0
 public function test_fetchSubtable_ShouldUseCorrectApiUrl()
 {
     PluginManager::getInstance()->loadPlugins(array('API', 'Referrers'));
     $proxyMock = $this->getMock('stdClass', array('call', '__construct'));
     $proxyMock->expects($this->once())->method('call')->with('\\Piwik\\Plugins\\Referrers\\API', 'getSearchEnginesFromKeywordId', array('idSubtable' => 23, 'idSite' => 1, 'date' => '2012-01-02', 'format' => 'original', 'module' => 'API', 'method' => 'Referrers.getSearchEnginesFromKeywordId'))->willReturn("result");
     Proxy::setSingletonInstance($proxyMock);
     $report = new \Piwik\Plugins\Referrers\Reports\GetKeywords();
     $result = $report->fetchSubtable(23, array('idSite' => 1, 'date' => '2012-01-02'));
     $this->assertEquals("result", $result);
 }
 public function tearDown()
 {
     Proxy::getInstance()->setHideIgnoredFunctions(true);
 }
Example #18
0
 /**
  * Renders a report depending on the configured ViewDataTable see {@link configureView()} and
  * {@link getDefaultTypeViewDataTable()}. If you want to customize the render process or just render any custom view
  * you can overwrite this method.
  *
  * @return string
  * @throws \Exception In case the given API action does not exist yet.
  * @api
  */
 public function render()
 {
     $apiProxy = Proxy::getInstance();
     if (!$apiProxy->isExistingApiAction($this->module, $this->action)) {
         throw new Exception("Invalid action name '{$this->action}' for '{$this->module}' plugin.");
     }
     $apiAction = $apiProxy->buildApiActionName($this->module, $this->action);
     $view = ViewDataTableFactory::build(null, $apiAction, $this->module . '.' . $this->action);
     $rendered = $view->render();
     return $rendered;
 }
 /**
  * Returns the methods $class.$name parameters (and default value if provided) as a string.
  *
  * @param string $class The class name
  * @param string $name The method name
  * @return string  For example "(idSite, period, date = 'today')"
  */
 public function getParametersString($class, $name)
 {
     $aParameters = Proxy::getInstance()->getParametersList($class, $name);
     $asParameters = array();
     foreach ($aParameters as $nameVariable => $defaultValue) {
         // Do not show API parameters starting with _
         // They are supposed to be used only in internal API calls
         if (strpos($nameVariable, '_') === 0) {
             continue;
         }
         $str = $nameVariable;
         if (!$defaultValue instanceof NoDefaultValue) {
             if (is_array($defaultValue)) {
                 $str .= " = 'Array'";
             } else {
                 $str .= " = '{$defaultValue}'";
             }
         }
         $asParameters[] = $str;
     }
     $sParameters = implode(", ", $asParameters);
     return "({$sParameters})";
 }
 protected function callApiAndReturnDataTable($apiModule, $method, $request)
 {
     $class = Request::getClassNameAPI($apiModule);
     $request = $this->manipulateSubtableRequest($request);
     $request['serialize'] = 0;
     $request['expanded'] = 0;
     // don't want to run recursive filters on the subtables as they are loaded,
     // otherwise the result will be empty in places (or everywhere). instead we
     // run it on the flattened table.
     unset($request['filter_pattern_recursive']);
     $dataTable = Proxy::getInstance()->call($class, $method, $request);
     $response = new ResponseBuilder($format = 'original', $request);
     $response->disableSendHeader();
     $dataTable = $response->getResponse($dataTable);
     if (Common::getRequestVar('disable_queued_filters', 0, 'int', $request) == 0) {
         if (method_exists($dataTable, 'applyQueuedFilters')) {
             $dataTable->applyQueuedFilters();
         }
     }
     return $dataTable;
 }
 public function tearDown()
 {
     Proxy::unsetInstance();
     parent::tearDown();
 }