From 6751eb659192e20dca10855fd5bd9226663bea34 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sat, 7 Jul 2018 15:01:18 +0300 Subject: [PATCH] Implemented webhooks database structure and console command register webhooks --- common/models/WebHook.php | 42 +++++++++++ common/models/WebHookEvent.php | 27 +++++++ console/controllers/WebhooksController.php | 57 +++++++++++++++ console/db/Migration.php | 24 ++----- .../migrations/m180706_230451_webhooks.php | 28 ++++++++ console/models/.gitkeep | 1 - console/models/WebHookForm.php | 70 +++++++++++++++++++ 7 files changed, 228 insertions(+), 21 deletions(-) create mode 100644 common/models/WebHook.php create mode 100644 common/models/WebHookEvent.php create mode 100644 console/controllers/WebhooksController.php create mode 100644 console/migrations/m180706_230451_webhooks.php delete mode 100644 console/models/.gitkeep create mode 100644 console/models/WebHookForm.php diff --git a/common/models/WebHook.php b/common/models/WebHook.php new file mode 100644 index 0000000..586fa9e --- /dev/null +++ b/common/models/WebHook.php @@ -0,0 +1,42 @@ + TimestampBehavior::class, + 'updatedAtAttribute' => false, + ], + ]; + } + + public function getEvents(): ActiveQueryInterface { + return $this->hasMany(WebHookEvent::class, ['webhook_id' => 'id']); + } + +} diff --git a/common/models/WebHookEvent.php b/common/models/WebHookEvent.php new file mode 100644 index 0000000..280d4ab --- /dev/null +++ b/common/models/WebHookEvent.php @@ -0,0 +1,27 @@ +hasOne(WebHook::class, ['id' => 'webhook_id']); + } + +} diff --git a/console/controllers/WebhooksController.php b/console/controllers/WebhooksController.php new file mode 100644 index 0000000..6f77acc --- /dev/null +++ b/console/controllers/WebhooksController.php @@ -0,0 +1,57 @@ + true, + 'validator' => function(string $input, ?string &$error) use ($form): bool { + $form->url = $input; + if (!$form->validate('url')) { + $error = $form->getFirstError('url'); + return false; + } + + return true; + }, + ]); + $secret = Console::prompt('Enter webhook secret (empty to no secret):'); + + $options = $form::getEvents(); + $options[''] = 'Finish input'; // It's needed to allow finish input cycle + $events = []; + + do { + $availableOptions = array_diff($options, $events); + $eventIndex = Console::select('Choose wanted events (submit no input to finish):', $availableOptions); + if ($eventIndex !== '') { + $events[] = $options[$eventIndex]; + } + } while($eventIndex !== '' || empty($events)); // User must choose at least one event + + $form->url = $url; + $form->events = $events; + if ($secret !== '') { + $form->secret = $secret; + } + + if (!$form->save()) { + Console::error('Unable to create new webhook. Check errors list below' . PHP_EOL . Console::errorSummary($form)); + return ExitCode::UNSPECIFIED_ERROR; + } + + return ExitCode::OK; + } + +} diff --git a/console/db/Migration.php b/console/db/Migration.php index e05b6aa..3ec8284 100644 --- a/console/db/Migration.php +++ b/console/db/Migration.php @@ -25,28 +25,12 @@ class Migration extends YiiMigration { parent::createTable($table, $columns, $options); } - protected function primary(...$columns) { - switch (count($columns)) { - case 0: - $key = ''; - break; - case 1: - $key = $columns[0]; - break; - default: - $key = $this->buildKey($columns); + protected function primary(string ...$columns): string { + foreach ($columns as &$column) { + $column = $this->db->quoteColumnName($column); } - return " PRIMARY KEY ($key) "; - } - - private function buildKey(array $columns) { - $key = ''; - foreach ($columns as $i => $column) { - $key .= $i === count($columns) ? $column : "$column,"; - } - - return $key; + return ' PRIMARY KEY (' . implode(', ', $columns) . ') '; } } diff --git a/console/migrations/m180706_230451_webhooks.php b/console/migrations/m180706_230451_webhooks.php new file mode 100644 index 0000000..df6a620 --- /dev/null +++ b/console/migrations/m180706_230451_webhooks.php @@ -0,0 +1,28 @@ +createTable('{{%webhooks}}', [ + 'id' => $this->primaryKey(11)->unsigned(), + 'url' => $this->string()->notNull(), + 'secret' => $this->string(), + 'created_at' => $this->integer(11)->unsigned()->notNull(), + ]); + + $this->createTable('{{%webhooks_events}}', [ + 'webhook_id' => $this->db->getTableSchema('{{%webhooks}}')->getColumn('id')->dbType . ' NOT NULL', + 'event_type' => $this->string()->notNull(), + $this->primary('webhook_id', 'event_type'), + ]); + $this->addForeignKey('FK_webhook_event_to_webhook', '{{%webhooks_events}}', 'webhook_id', 'webhooks', 'id', 'CASCADE', 'CASCADE'); + } + + public function safeDown() { + $this->dropTable('{{%webhooks_events}}'); + $this->dropTable('{{%webhooks}}'); + } + +} diff --git a/console/models/.gitkeep b/console/models/.gitkeep deleted file mode 100644 index 72e8ffc..0000000 --- a/console/models/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/console/models/WebHookForm.php b/console/models/WebHookForm.php new file mode 100644 index 0000000..4f9cfcd --- /dev/null +++ b/console/models/WebHookForm.php @@ -0,0 +1,70 @@ +webHook = $webHook; + } + + public function rules(): array { + return [ + [['url'], 'required'], + [['url'], 'url'], + [['secret'], 'string'], + [['events'], 'in', 'range' => static::getEvents(), 'allowArray' => true], + ]; + } + + public function save(): bool { + if (!$this->validate()) { + return false; + } + + $transaction = Yii::$app->db->beginTransaction(); + + $webHook = $this->webHook; + $webHook->url = $this->url; + $webHook->secret = $this->secret; + if (!$webHook->save()) { + throw new ThisShouldNotHappenException('Cannot save webhook.'); + } + + foreach ($this->events as $event) { + $eventModel = new WebHookEvent(); + $eventModel->webhook_id = $webHook->id; + $eventModel->event_type = $event; + if (!$eventModel->save()) { + throw new ThisShouldNotHappenException('Cannot save webhook event.'); + } + } + + $transaction->commit(); + + return true; + } + + public static function getEvents(): array { + return [ + 'account.edit', + ]; + } + +}