From 96586673dfb7af14ebda1d19fc697e06f9eaa865 Mon Sep 17 00:00:00 2001 From: _sam <3804518+aahung@users.noreply.github.com> Date: Mon, 27 May 2024 17:44:08 -0700 Subject: [PATCH] Support A2p brands and campaigns --- manifest.json | 2 +- src/components/InspectResourceButton.tsx | 10 ++++- src/twilio-request.ts | 11 +++++ src/twilio-resource-loaders.ts | 9 +++++ src/twilio-resources.ts | 51 +++++++++++++++++++++++- 5 files changed, 79 insertions(+), 4 deletions(-) diff --git a/manifest.json b/manifest.json index 8c01ca5..52588f8 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Twilio Console Plus", - "version": "0.1.4", + "version": "0.1.5", "description": "Add some secret saurce to Twilio console", "icons": { "48": "icons/icon-48.png" diff --git a/src/components/InspectResourceButton.tsx b/src/components/InspectResourceButton.tsx index ab1cc4e..afaad3c 100644 --- a/src/components/InspectResourceButton.tsx +++ b/src/components/InspectResourceButton.tsx @@ -1,9 +1,12 @@ +import "react-tooltip/dist/react-tooltip.css"; + import React, { useState } from "react"; import Popup from "reactjs-popup"; import { CopyBlock, dracula } from "react-code-blocks"; import { useQuery } from "@tanstack/react-query"; import { TwilioResource } from "../twilio-resources"; import styled from "styled-components"; +import { Tooltip } from "react-tooltip"; const PopupContainer = styled.div` .close { @@ -31,7 +34,12 @@ const InspectResourceButton: React.FC = ({ const openModal = () => setOpen(true); return ( <> - + + + Click to inspect {resource.getName()} + { }, }).then((res) => res.json()); }; + +export async function* twilioGetPaged( + apiUrl: string, +): AsyncGenerator { + const response = await twilioGet(apiUrl); + yield response; + const next_url = response.meta?.next_page_url; + if (next_url) { + yield* await twilioGetPaged(next_url); + } +} diff --git a/src/twilio-resource-loaders.ts b/src/twilio-resource-loaders.ts index bad2e17..4e645c6 100644 --- a/src/twilio-resource-loaders.ts +++ b/src/twilio-resource-loaders.ts @@ -1,4 +1,5 @@ import { + TwilioA2pBrand, TwilioAddress, TwilioIncomingNumber, TwilioMessagingService, @@ -29,6 +30,14 @@ export const LOADERS: Loader[] = [ resourceUrl: /.+\/sms\/(\w+)\/messaging-service-properties(\?.*)$/, resourceCreator: (match: string[]) => new TwilioMessagingService(match[1]), }, + { + resourceUrl: /.+\/sms\/services\/(\w+)\/[^/]+(\?.*)$/, + resourceCreator: (match: string[]) => new TwilioMessagingService(match[1]), + }, + { + resourceUrl: /.+\/sms\/regulatory-compliance\/brands\/(\w+)$/, + resourceCreator: (match: string[]) => new TwilioA2pBrand(match[1]), + }, ]; export const createTwilioResourceFromUrl = ( diff --git a/src/twilio-resources.ts b/src/twilio-resources.ts index f4a4a72..d1d96a3 100644 --- a/src/twilio-resources.ts +++ b/src/twilio-resources.ts @@ -1,4 +1,4 @@ -import { twilioGet } from "./twilio-request"; +import { twilioGet, twilioGetPaged } from "./twilio-request"; interface Link { label: string; @@ -94,6 +94,10 @@ export class TwilioRegulatorySupportingDocument extends TwilioResource { export class TwilioIncomingNumber extends TwilioResource { getName = () => `Incoming number: ${this.sid}`; + getFullName = async () => { + const object = await this.getObject(); + return `${this.getName()} (${object.friendly_name}; ${object.phone_number})`; + }; getRelatedResources = () => Promise.resolve([]); getApiUrl = () => `https://api.twilio.com/2010-04-01/Accounts/${this._getAccountSid()}/IncomingPhoneNumbers/${this.sid}.json`; @@ -101,6 +105,49 @@ export class TwilioIncomingNumber extends TwilioResource { export class TwilioMessagingService extends TwilioResource { getName = () => `Messaging service: ${this.sid}`; - getRelatedResources = () => Promise.resolve([]); + getRelatedResources = async () => { + const response = await twilioGet( + `https://messaging.twilio.com/v1/Services/${this.sid}/Compliance/Usa2p`, + ); + const results = response.compliance.map( + (campaign: Record) => + new TwllioA2pCampaign(campaign.sid, this.sid), + ); + for await (const response of twilioGetPaged( + `https://messaging.twilio.com/v1/Services/${this.sid}/PhoneNumbers`, + )) { + const phones = ( + response as Record[]> + ).phone_numbers.map( + (phone: Record) => new TwilioIncomingNumber(phone.sid), + ); + results.push(...phones); + } + + return results; + }; getApiUrl = () => `https://messaging.twilio.com/v1/Services/${this.sid}`; } + +export class TwllioA2pCampaign extends TwilioResource { + messagingServiceSid: string; + constructor(sid: string, messagingServiceSid: string) { + super(sid); + this.sid = sid; + this.messagingServiceSid = messagingServiceSid; + } + getName = () => `A2p campaign: ${this.sid}`; + getRelatedResources = async () => { + const object = await this.getObject(); + return [new TwilioA2pBrand(object.brand_registration_sid as string)]; + }; + getApiUrl = () => + `https://messaging.twilio.com/v1/Services/${this.messagingServiceSid}/Compliance/Usa2p/${this.sid}`; +} + +export class TwilioA2pBrand extends TwilioResource { + getName = () => `A2p brand: ${this.sid}`; + getRelatedResources = () => Promise.resolve([]); + getApiUrl = () => + `https://messaging.twilio.com/v1/a2p/BrandRegistrations/${this.sid}`; +}