Repository: Yoctol/bottender
Pull request #451 (2019-09-26 07:16:17)

feat: add pageId config for Subscribing app
Add `pageId` field to `bottender.config.js`, so it can automatically subscribe app for the page when using `bottender messenger webhook set`.

codecov-io: # [Codecov](https://codecov.io/gh/Yoctol/bottender/pull/451?src=pr&el=h1) Report
> Merging [#451](https://codecov.io/gh/Yoctol/bottender/pull/451?src=pr&el=desc) into [master](https://codecov.io/gh/Yoctol/bottender/commit/47488309f7c42dd4d5197830358afc7194655b4a?src=pr&el=desc) will **decrease** coverage by `0.04%`.
> The diff coverage is `33.33%`.

[![Impacted file tree graph](https://codecov.io/gh/Yoctol/bottender/pull/451/graphs/tree.svg?width=650&token=JX3EGtxYH3&height=150&src=pr)](https://codecov.io/gh/Yoctol/bottender/pull/451?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #451      +/-   ##
==========================================
- Coverage   85.21%   85.17%   -0.05%     
==========================================
  Files          97       97              
  Lines        3524     3527       +3     
  Branches      858      848      -10     
==========================================
+ Hits         3003     3004       +1     
- Misses        484      490       +6     
+ Partials       37       33       -4
```


| [Impacted Files](https://codecov.io/gh/Yoctol/bottender/pull/451?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...s/create-bottender-app/src/utils/generateConfig.ts](https://codecov.io/gh/Yoctol/bottender/pull/451/diff?src=pr&el=tree#diff-cGFja2FnZXMvY3JlYXRlLWJvdHRlbmRlci1hcHAvc3JjL3V0aWxzL2dlbmVyYXRlQ29uZmlnLnRz) | `0% <ø> (ø)` | :arrow_up: |
| [...s/bottender/src/cli/providers/messenger/webhook.ts](https://codecov.io/gh/Yoctol/bottender/pull/451/diff?src=pr&el=tree#diff-cGFja2FnZXMvYm90dGVuZGVyL3NyYy9jbGkvcHJvdmlkZXJzL21lc3Nlbmdlci93ZWJob29rLnRz) | `76.71% <33.33%> (-1.86%)` | :arrow_down: |
| [packages/create-bottender-app/src/shared/log.ts](https://codecov.io/gh/Yoctol/bottender/pull/451/diff?src=pr&el=tree#diff-cGFja2FnZXMvY3JlYXRlLWJvdHRlbmRlci1hcHAvc3JjL3NoYXJlZC9sb2cudHM=) | `0% <0%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Yoctol/bottender/pull/451?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Yoctol/bottender/pull/451?src=pr&el=footer). Last update [4748830...42a7015](https://codecov.io/gh/Yoctol/bottender/pull/451?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/packages/bottender/src/cli/providers/messenger/webhook.ts b/packages/bottender/src/cli/providers/messenger/webhook.ts
index 83bb63c3..d4f1187b 100644
--- a/packages/bottender/src/cli/providers/messenger/webhook.ts
+++ b/packages/bottender/src/cli/providers/messenger/webhook.ts
@@ -53,6 +53,7 @@ export async function setWebhook(ctx: CliContext): Promise<void> {
       appId,
       appSecret,
       verifyToken,
+      pageId,
       path = '/webhooks/messenger',
     } = config;
 
@@ -160,6 +161,18 @@ export async function setWebhook(ctx: CliContext): Promise<void> {
     invariant(success, 'Setting for webhook is failed');
 
     print('Successfully set Messenger webhook callback URL');
+
+    if (pageId) {
+      const { data } = await client.axios.post(
+        `/${pageId}/subscribed_apps?access_token=${accessToken}`,
+        {
+          subscribed_fields: fields.join(','),
+        }
+      );
+
+      invariant(data.success, 'Subscribing app for page is failed');
+    }
+
     print(
       `Check callback URL on: https://developers.facebook.com/apps/${appId}/webhooks/`
     );
diff --git a/packages/create-bottender-app/src/utils/generateConfig.ts b/packages/create-bottender-app/src/utils/generateConfig.ts
index 8c0f0c03..bfadbce7 100644
--- a/packages/create-bottender-app/src/utils/generateConfig.ts
+++ b/packages/create-bottender-app/src/utils/generateConfig.ts
@@ -30,10 +30,11 @@ const generateConfig = (
     messenger: {
       enabled: ${platforms.includes('messenger')},
       path: '/webhooks/messenger',
+      pageId: process.env.MESSENGER_PAGE_ID,
       accessToken: process.env.MESSENGER_ACCESS_TOKEN,
-      verifyToken: process.env.MESSENGER_VERIFY_TOKEN,
       appId: process.env.MESSENGER_APP_ID,
       appSecret: process.env.MESSENGER_APP_SECRET,
+      verifyToken: process.env.MESSENGER_VERIFY_TOKEN,
     },
     line: {
       enabled: ${platforms.includes('line')},
diff --git a/packages/create-bottender-app/template/.env.example b/packages/create-bottender-app/template/.env.example
index 45214c95..6f0706fd 100644
--- a/packages/create-bottender-app/template/.env.example
+++ b/packages/create-bottender-app/template/.env.example
@@ -1,7 +1,8 @@
+MESSENGER_PAGE_ID=
 MESSENGER_ACCESS_TOKEN=
-MESSENGER_VERIFY_TOKEN=
 MESSENGER_APP_ID=
 MESSENGER_APP_SECRET=
+MESSENGER_VERIFY_TOKEN=
 
 LINE_ACCESS_TOKEN=
 LINE_CHANNEL_SECRET=


Test patch:
diff --git a/packages/bottender/src/cli/providers/messenger/__tests__/setWebhook.spec.ts b/packages/bottender/src/cli/providers/messenger/__tests__/setWebhook.spec.ts
index 8198ad9e..17335cc9 100644
--- a/packages/bottender/src/cli/providers/messenger/__tests__/setWebhook.spec.ts
+++ b/packages/bottender/src/cli/providers/messenger/__tests__/setWebhook.spec.ts
@@ -13,6 +13,7 @@ jest.mock('../../../shared/getWebhookFromNgrok');
 jest.mock('../../../shared/log');
 jest.mock('../../../shared/getConfig');
 
+const PAGE_ID = '__PAGE_ID__';
 const ACCESS_TOKEN = '__ACCESS_TOKEN__';
 const APP_ID = '__APP_ID__';
 const APP_SECRET = '__APP_SECRET__';
@@ -53,6 +54,11 @@ function setup({
   });
 
   client.getPageInfo.mockResolvedValue({ id: '123456789', name: 'Page Name' });
+  client.axios = {
+    post: jest.fn(),
+  };
+
+  client.axios.post.mockResolvedValue({ data: { success: true } });
 
   MessengerClient.connect = jest.fn(() => client);
 
@@ -108,6 +114,10 @@ describe('resolve', () => {
   it('get ngrok webhook to setup', async () => {
     const { client } = setup();
 
+    client.axios.post = jest
+      .fn()
+      .mockResolvedValue({ data: { success: true } });
+
     const ctx = {
       argv: {
         _: ['messenger', 'webhook', 'set'],
@@ -146,6 +156,50 @@ describe('resolve', () => {
 
     expect(getWebhookFromNgrok).toBeCalledWith('5555');
   });
+
+  it('should subscribe app for the page if pageId is provided', async () => {
+    const { client } = setup({
+      config: {
+        pageId: PAGE_ID,
+        accessToken: ACCESS_TOKEN,
+        appId: APP_ID,
+        appSecret: APP_SECRET,
+        verifyToken: VERIFY_TOKEN,
+      },
+    });
+
+    const ctx = {
+      argv: {
+        _: ['messenger', 'webhook', 'set'],
+      },
+    };
+
+    await setWebhook(ctx);
+
+    expect(getWebhookFromNgrok).toBeCalledWith('4040');
+    expect(client.createSubscription).toBeCalledWith({
+      access_token: '__APP_ID__|__APP_SECRET__',
+      callback_url: 'https://fakeDomain.ngrok.io/webhooks/messenger',
+      fields: [
+        'messages',
+        'messaging_postbacks',
+        'messaging_optins',
+        'messaging_referrals',
+        'messaging_handovers',
+        'messaging_policy_enforcement',
+      ],
+      object: 'page',
+      verify_token: '__VERIFY_TOKEN__',
+    });
+
+    expect(client.axios.post).toBeCalledWith(
+      '/__PAGE_ID__/subscribed_apps?access_token=__ACCESS_TOKEN__',
+      {
+        subscribed_fields:
+          'messages,messaging_postbacks,messaging_optins,messaging_referrals,messaging_handovers,messaging_policy_enforcement',
+      }
+    );
+  });
 });
 
 describe('reject', () => {