/** * Test subscribing and then receiving data: */ public function testSubscription() { // 1: Request to subscribe: $hookName = 'ContactsCreate'; $hook = array('event' => 'RecordCreateTrigger', 'target_url' => TEST_WEBROOT_URL . '/api2HooksTest.php?name=' . $hookName); $ch = $this->getCurlHandle('POST', array('{suffix}' => ''), 'admin', $hook, array(CURLOPT_HEADER => 1)); $response = curl_exec($ch); $this->assertResponseCodeIs(201, $ch, VERBOSE_MODE ? $response : ''); $trigger = ApiHook::model()->findByAttributes($hook); // 2. Create a contact $contact = array('firstName' => 'Walter', 'lastName' => 'White', 'email' => '*****@*****.**', 'visibility' => 1); $ch = curl_init(TEST_BASE_URL . 'api2/Contacts'); $options = array(CURLOPT_POSTFIELDS => json_encode($contact), CURLOPT_HTTPHEADER => array('Content-Type: application/json'), CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true); foreach ($this->authCurlOpts() as $opt => $optVal) { $options[$opt] = $optVal; } curl_setopt_array($ch, $options); $response = curl_exec($ch); $c = Contacts::model()->findByAttributes($contact); $this->assertResponseCodeIs(201, $ch); $this->assertNotEmpty($c); // 3. Test that the receiving end got the payload $outputDir = implode(DIRECTORY_SEPARATOR, array(Yii::app()->basePath, 'tests', 'data', 'output')); $this->assertFileExists($outputDir . DIRECTORY_SEPARATOR . "hook_{$hookName}.json"); $contactPulled = json_decode(file_get_contents($outputDir . DIRECTORY_SEPARATOR . "hook_pulled_{$hookName}.json"), 1); foreach ($contact as $field => $value) { $this->assertEquals($value, $contactPulled[$field]); } }
/** * Looks up and runs automation actions that match the provided trigger name and parameters. * * @param string $trigger the name of the trigger to fire * @param array $params an associative array of params, usually including 'model'=>$model, * the primary X2Model to which this trigger applies. * @staticvar int $triggerDepth the current depth of the call stack * @return mixed Null or return value of extractRetValFromTrace */ public static function trigger($triggerName, $params = array()) { if (self::$_triggerDepth > self::MAX_TRIGGER_DEPTH) { // ...have we delved too deep? return; } $triggeredAt = time(); if (isset($params['model']) && (!is_object($params['model']) || !$params['model'] instanceof X2Model)) { // Invalid model provided return false; } // Communicate the event to third-party systems, if any ApiHook::runAll($triggerName, $params); // increment stack depth before doing anything that might call X2Flow::trigger() self::$_triggerDepth++; $flowAttributes = array('triggerType' => $triggerName, 'active' => 1); if (isset($params['model'])) { $flowAttributes['modelClass'] = get_class($params['model']); $params['modelClass'] = get_class($params['model']); } // if flow id is specified, only execute flow with specified id if (isset($params['flowId'])) { $flowAttributes['id'] = $params['flowId']; } $flows = CActiveRecord::model('X2Flow')->findAllByAttributes($flowAttributes); // collect information about trigger for the trigger log. $triggerInfo = array('triggerName' => Yii::t('studio', X2FlowItem::getTitle($triggerName))); if (isset($params['model']) && is_subclass_of($params['model'], 'X2Model') && $params['model']->asa('X2LinkableBehavior')) { $triggerInfo['modelLink'] = Yii::t('studio', 'View record: ') . $params['model']->getLink(); } // find all flows matching this trigger and modelClass $triggerLog; $flowTrace; $flowRetVal = null; foreach ($flows as &$flow) { $triggerLog = new TriggerLog(); $triggerLog->triggeredAt = $triggeredAt; $triggerLog->flowId = $flow->id; $triggerLog->save(); $flowRetArr = self::_executeFlow($flow, $params, null, $triggerLog->id); $flowTrace = $flowRetArr['trace']; $flowRetVal = isset($flowRetArr['retVal']) ? $flowRetArr['retVal'] : null; $flowRetVal = self::extractRetValFromTrace($flowTrace); // save log for triggered flow $triggerLog->triggerLog = CJSON::encode(array_merge(array($triggerInfo), array($flowTrace))); $triggerLog->save(); } self::$_triggerDepth--; // this trigger call is done; decrement the stack depth return $flowRetVal; }
/** * Action for the creation of "hooks" (see {@link ApiHook}) * * @param integer $_id ID of the hook. * @param string $_class Subclass of {@link X2Model} to which the hook pertains */ public function actionHooks($_id = null, $_class = null) { $method = Yii::app()->request->getRequestType(); if ($method == 'DELETE') { $hook = ApiHook::model()->findByPk($_id); if (!$hook instanceof ApiHook) { $this->send(404, '"Hook not found." -Smee'); } elseif (!$hook->userId != Yii::app()->getSuId()) { $this->send(403, 'You cannot delete other API users\' hooks in X2Engine.'); } $hook->setScenario('delete.remote'); if ($hook->delete()) { $this->sendEmpty("Successfully unsubscribed from hook with " . "return URL {$hook->target_url}."); } } else { // POST (will respond to all other methods with 405) if ($_id !== null) { $this->send(405, 'Cannot manipulate preexisting hooks with POST.'); } $hook = new ApiHook(); $hook->attributes = $this->getJpost(); $hook->userId = Yii::app()->getSuId(); if (!empty($_class)) { $hook->modelName = get_class($this->staticModel); } if (!$hook->validate('event')) { $this->send(429, "The maximum number of hooks ({$maximum}) has " . "been reached for events of this type."); } if (!$hook->validate()) { $this->response['errors'] = $hook->errors; $this->send(422); } if ($hook->save()) { $this->response->httpHeader['Location'] = $this->createAbsoluteUrl('/api2/hooks', array('_id' => $hook->id)); $this->responseBody = $hook; $this->send(201); } else { $this->send(500, 'Could not save hook due to unexpected ' . 'internal server error.'); } } }