Skip to content

Commit 15469cf

Browse files
committed
[webhooks] add CRUD methods
1 parent dd01ffc commit 15469cf

File tree

6 files changed

+282
-7
lines changed

6 files changed

+282
-7
lines changed

README.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1-
# Android SMS Gateway JS/TS API Client
1+
![npm](https://img.shields.io/npm/v/android-sms-gateway?style=for-the-badge)
2+
![License](https://img.shields.io/badge/license-Apache--2.0-blue?style=for-the-badge)
3+
![Downloads](https://img.shields.io/npm/dw/android-sms-gateway?style=for-the-badge)
4+
![GitHub issues](https://img.shields.io/github/issues/capcom6/android-sms-gateway-ts?style=for-the-badge)
5+
![GitHub stars](https://img.shields.io/github/stars/capcom6/android-sms-gateway-ts?style=for-the-badge)
6+
<!-- ![Build Status](https://img.shields.io/travis/com/capcom6/android-sms-gateway-ts/master) -->
7+
<!-- ![Coverage](https://img.shields.io/codecov/c/github/capcom6/android-sms-gateway-ts) -->
8+
<!-- ![Dependencies](https://img.shields.io/david/capcom6/android-sms-gateway-ts) -->
9+
<!-- ![TypeScript Version](https://img.shields.io/npm/types/android-sms-gateway) -->
210

3-
This is a JavaScript/TypeScript client library for interfacing with the [Android SMS Gateway API](https://sms.capcom.me).
11+
# SMS Gateway for Android™ JS/TS API Client
12+
13+
This is a JavaScript/TypeScript client library for interfacing with the [SMS Gateway for Android API](https://sms.capcom.me).
414

515
## Features
616

717
- Send SMS messages with a simple method call.
818
- Check the state of sent messages.
19+
- Managing webhooks.
920
- Customizable base URL for use with local or cloud servers.
1021

1122
## Prerequisites
@@ -17,7 +28,7 @@ Before you begin, ensure you have met the following requirements:
1728

1829
## Installation
1930

20-
To install the SMS Gateway API Client, run this command in your terminal:
31+
To install the SMS Gateway for Android API Client, run this command in your terminal:
2132

2233
```bash
2334
npm install android-sms-gateway
@@ -52,6 +63,14 @@ const httpFetchClient = {
5263
body: JSON.stringify(body)
5364
});
5465

66+
return response.json();
67+
},
68+
delete: async (url, headers) => {
69+
const response = await fetch(url, {
70+
method: "DELETE",
71+
headers
72+
});
73+
5574
return response.json();
5675
}
5776
};
@@ -79,7 +98,7 @@ apiClient.getState(messageId)
7998

8099
## API Reference
81100

82-
For more information on the API endpoints and data structures, please consult the [Android SMS Gateway API documentation](https://sms.capcom.me/api/).
101+
For more information on the API endpoints and data structures, please consult the [SMS Gateway for Android API documentation](https://sms.capcom.me/integration/api/).
83102

84103
# Contributing
85104

bun.lockb

0 Bytes
Binary file not shown.

src/client.test.ts

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { BASE_URL, Client } from './client';
2-
import { Message, MessageState, ProcessState } from './domain';
2+
import { Message, MessageState, RegisterWebHookRequest, ProcessState, WebHook, WebHookEventType } from './domain';
33
import { HttpClient } from './http';
44

55
import { beforeEach, describe, expect, it, jest } from "bun:test";
@@ -12,6 +12,7 @@ describe('Client', () => {
1212
mockHttpClient = {
1313
get: jest.fn(),
1414
post: jest.fn(),
15+
delete: jest.fn(),
1516
};
1617
client = new Client('login', 'password', mockHttpClient);
1718
});
@@ -74,4 +75,66 @@ describe('Client', () => {
7475
);
7576
expect(result).toBe(expectedState);
7677
});
78+
79+
it('gets webhooks', async () => {
80+
const expectedWebhooks: WebHook[] = [
81+
{ id: '1', url: 'https://example.com/webhook1', event: WebHookEventType.SmsReceived },
82+
{ id: '2', url: 'https://example.com/webhook2', event: WebHookEventType.SystemPing },
83+
];
84+
85+
mockHttpClient.get.mockResolvedValue(expectedWebhooks);
86+
87+
const result = await client.getWebhooks();
88+
89+
expect(mockHttpClient.get).toHaveBeenCalledWith(
90+
`${BASE_URL}/webhooks`,
91+
{
92+
"User-Agent": "android-sms-gateway/1.0 (client; js)",
93+
Authorization: expect.any(String),
94+
},
95+
);
96+
expect(result).toEqual(expectedWebhooks);
97+
});
98+
99+
it('register a webhook', async () => {
100+
const req: RegisterWebHookRequest = {
101+
url: 'https://example.com/webhook',
102+
event: WebHookEventType.SmsReceived,
103+
}
104+
const expectedRes: WebHook = {
105+
id: 'test',
106+
url: 'https://example.com/webhook',
107+
event: WebHookEventType.SmsReceived,
108+
};
109+
110+
mockHttpClient.post.mockResolvedValue(expectedRes);
111+
112+
const result = await client.registerWebhook(req);
113+
114+
expect(mockHttpClient.post).toHaveBeenCalledWith(
115+
`${BASE_URL}/webhooks`,
116+
req,
117+
{
118+
"Content-Type": "application/json",
119+
"User-Agent": "android-sms-gateway/1.0 (client; js)",
120+
Authorization: expect.any(String),
121+
},
122+
);
123+
expect(result).toBe(expectedRes);
124+
});
125+
126+
it('delete a webhook', async () => {
127+
mockHttpClient.post.mockResolvedValue(undefined);
128+
129+
const result = await client.deleteWebhook('test');
130+
131+
expect(mockHttpClient.delete).toHaveBeenCalledWith(
132+
`${BASE_URL}/webhooks/test`,
133+
{
134+
"User-Agent": "android-sms-gateway/1.0 (client; js)",
135+
Authorization: expect.any(String),
136+
},
137+
);
138+
expect(result).toBe(undefined);
139+
});
77140
});

src/client.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Message, MessageState } from "./domain";
1+
import { Message, MessageState, RegisterWebHookRequest, WebHook } from "./domain";
22
import { HttpClient } from "./http";
33

44
export const BASE_URL = "https://sms.capcom.me/api/3rdparty/v1";
@@ -35,4 +35,32 @@ export class Client {
3535

3636
return this.httpClient.get<MessageState>(url, headers);
3737
}
38+
39+
async getWebhooks(): Promise<WebHook[]> {
40+
const url = `${this.baseUrl}/webhooks`;
41+
const headers = {
42+
...this.defaultHeaders,
43+
};
44+
45+
return this.httpClient.get<WebHook[]>(url, headers);
46+
}
47+
48+
async registerWebhook(request: RegisterWebHookRequest): Promise<WebHook> {
49+
const url = `${this.baseUrl}/webhooks`;
50+
const headers = {
51+
"Content-Type": "application/json",
52+
...this.defaultHeaders,
53+
};
54+
55+
return this.httpClient.post<WebHook>(url, request, headers);
56+
}
57+
58+
async deleteWebhook(webhookId: string): Promise<void> {
59+
const url = `${this.baseUrl}/webhooks/${webhookId}`;
60+
const headers = {
61+
...this.defaultHeaders,
62+
};
63+
64+
return this.httpClient.delete<void>(url, headers);
65+
}
3866
}

src/domain.ts

Lines changed: 165 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
2+
13
export enum ProcessState {
24
Pending = "Pending",
35
Processed = "Processed",
@@ -6,23 +8,185 @@ export enum ProcessState {
68
Failed = "Failed",
79
}
810

11+
/**
12+
* Represents the state of a recipient of an SMS message.
13+
*/
914
export interface RecipientState {
15+
/**
16+
* The phone number of the recipient.
17+
*/
1018
phoneNumber: string;
19+
20+
/**
21+
* The state of the recipient.
22+
*/
1123
state: ProcessState;
24+
25+
/**
26+
* An optional error message, if the recipient failed to receive the message.
27+
*/
1228
error?: string | null;
1329
}
1430

31+
/**
32+
* Represents the state of an SMS message.
33+
*/
1534
export interface MessageState {
35+
/**
36+
* The ID of the message.
37+
*/
1638
id: string;
39+
40+
/**
41+
* The state of the message.
42+
*/
1743
state: ProcessState;
44+
45+
/**
46+
* The list of recipients' states for the message.
47+
*/
1848
recipients: RecipientState[];
1949
}
2050

51+
/**
52+
* Represents an SMS message.
53+
*/
2154
export interface Message {
55+
/**
56+
* The ID of the message, will be generated if not provided.
57+
* @default null
58+
*/
2259
id?: string | null;
60+
61+
/**
62+
* The message content.
63+
*/
2364
message: string;
65+
66+
/**
67+
* The time-to-live (TTL) of the message in seconds.
68+
* If set to null, the message will not expire.
69+
* @default null
70+
*/
2471
ttl?: number | null;
72+
73+
/**
74+
* The phone numbers to send the message to.
75+
*/
2576
phoneNumbers: string[];
77+
78+
/**
79+
* The SIM number to send the message from.
80+
* If not specified, the message will be sent from the default SIM.
81+
* @default null
82+
*/
2683
simNumber?: number | null;
84+
85+
/**
86+
* Whether to include a delivery report for the message.
87+
* @default true
88+
*/
2789
withDeliveryReport?: boolean | null;
28-
}
90+
}
91+
92+
/**
93+
* Represents the type of events that can trigger a webhook.
94+
*/
95+
export enum WebHookEventType {
96+
/**
97+
* Indicates that a new SMS message has been received.
98+
*/
99+
SmsReceived = 'sms:received',
100+
101+
/**
102+
* Indicates that a ping request has been sent.
103+
*/
104+
SystemPing = 'system:ping',
105+
}
106+
107+
/**
108+
* Represents a request to create or update a webhook.
109+
*/
110+
export interface RegisterWebHookRequest {
111+
/**
112+
* The ID of the webhook.
113+
* If not specified, a new ID will be generated.
114+
* @default null
115+
*/
116+
id?: string | null;
117+
118+
/**
119+
* The event type that triggers the webhook.
120+
*/
121+
event: WebHookEventType;
122+
123+
/**
124+
* The URL to send the webhook request to.
125+
*/
126+
url: string;
127+
}
128+
129+
/**
130+
* Represents a webhook configuration.
131+
* @see RegisterWebHookRequest
132+
*/
133+
export type WebHook = Required<RegisterWebHookRequest>;
134+
135+
export type WebHookEvent = {
136+
id: string;
137+
webhookId: string;
138+
deviceId: string;
139+
} & WebHookPayload
140+
141+
/**
142+
* Represents the payload of a webhook event.
143+
*/
144+
export type WebHookPayload =
145+
/**
146+
* Represents the payload of a webhook event of type `SmsReceived`.
147+
*/
148+
{
149+
/**
150+
* The event type.
151+
*/
152+
event: WebHookEventType.SmsReceived;
153+
154+
/**
155+
* The payload of the event.
156+
*/
157+
payload: {
158+
/**
159+
* The received message.
160+
*/
161+
message: string;
162+
163+
/**
164+
* The phone number of the sender.
165+
*/
166+
phoneNumber: string;
167+
168+
/**
169+
* The date and time of when the message was received.
170+
*/
171+
receivedAt: string;
172+
};
173+
} |
174+
/**
175+
* Represents the payload of a webhook event of type `SystemPing`.
176+
*/
177+
{
178+
/**
179+
* The event type.
180+
*/
181+
event: WebHookEventType.SystemPing;
182+
183+
/**
184+
* The payload of the event.
185+
* This is an empty object.
186+
*/
187+
payload: EmptyObject;
188+
};
189+
190+
type EmptyObject = {
191+
[K in any]: never
192+
}

src/http.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export interface HttpClient {
22
get<T>(url: string, headers?: Record<string, string>): Promise<T>;
33
post<T>(url: string, body: any, headers?: Record<string, string>): Promise<T>;
4+
delete<T>(url: string, headers?: Record<string, string>): Promise<T>;
45
}

0 commit comments

Comments
 (0)