Skip to content

Commit

Permalink
[webhooks] add CRUD methods
Browse files Browse the repository at this point in the history
  • Loading branch information
capcom6 committed Jul 9, 2024
1 parent dd01ffc commit 15469cf
Show file tree
Hide file tree
Showing 6 changed files with 282 additions and 7 deletions.
27 changes: 23 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
# Android SMS Gateway JS/TS API Client
![npm](https://img.shields.io/npm/v/android-sms-gateway?style=for-the-badge)
![License](https://img.shields.io/badge/license-Apache--2.0-blue?style=for-the-badge)
![Downloads](https://img.shields.io/npm/dw/android-sms-gateway?style=for-the-badge)
![GitHub issues](https://img.shields.io/github/issues/capcom6/android-sms-gateway-ts?style=for-the-badge)
![GitHub stars](https://img.shields.io/github/stars/capcom6/android-sms-gateway-ts?style=for-the-badge)
<!-- ![Build Status](https://img.shields.io/travis/com/capcom6/android-sms-gateway-ts/master) -->
<!-- ![Coverage](https://img.shields.io/codecov/c/github/capcom6/android-sms-gateway-ts) -->
<!-- ![Dependencies](https://img.shields.io/david/capcom6/android-sms-gateway-ts) -->
<!-- ![TypeScript Version](https://img.shields.io/npm/types/android-sms-gateway) -->

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

This is a JavaScript/TypeScript client library for interfacing with the [SMS Gateway for Android API](https://sms.capcom.me).

## Features

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

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

## Installation

To install the SMS Gateway API Client, run this command in your terminal:
To install the SMS Gateway for Android API Client, run this command in your terminal:

```bash
npm install android-sms-gateway
Expand Down Expand Up @@ -52,6 +63,14 @@ const httpFetchClient = {
body: JSON.stringify(body)
});

return response.json();
},
delete: async (url, headers) => {
const response = await fetch(url, {
method: "DELETE",
headers
});

return response.json();
}
};
Expand Down Expand Up @@ -79,7 +98,7 @@ apiClient.getState(messageId)

## API Reference

For more information on the API endpoints and data structures, please consult the [Android SMS Gateway API documentation](https://sms.capcom.me/api/).
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/).

# Contributing

Expand Down
Binary file modified bun.lockb
Binary file not shown.
65 changes: 64 additions & 1 deletion src/client.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BASE_URL, Client } from './client';
import { Message, MessageState, ProcessState } from './domain';
import { Message, MessageState, RegisterWebHookRequest, ProcessState, WebHook, WebHookEventType } from './domain';
import { HttpClient } from './http';

import { beforeEach, describe, expect, it, jest } from "bun:test";
Expand All @@ -12,6 +12,7 @@ describe('Client', () => {
mockHttpClient = {
get: jest.fn(),
post: jest.fn(),
delete: jest.fn(),
};
client = new Client('login', 'password', mockHttpClient);
});
Expand Down Expand Up @@ -74,4 +75,66 @@ describe('Client', () => {
);
expect(result).toBe(expectedState);
});

it('gets webhooks', async () => {
const expectedWebhooks: WebHook[] = [
{ id: '1', url: 'https://example.com/webhook1', event: WebHookEventType.SmsReceived },
{ id: '2', url: 'https://example.com/webhook2', event: WebHookEventType.SystemPing },
];

mockHttpClient.get.mockResolvedValue(expectedWebhooks);

const result = await client.getWebhooks();

expect(mockHttpClient.get).toHaveBeenCalledWith(
`${BASE_URL}/webhooks`,
{
"User-Agent": "android-sms-gateway/1.0 (client; js)",
Authorization: expect.any(String),
},
);
expect(result).toEqual(expectedWebhooks);
});

it('register a webhook', async () => {
const req: RegisterWebHookRequest = {
url: 'https://example.com/webhook',
event: WebHookEventType.SmsReceived,
}
const expectedRes: WebHook = {
id: 'test',
url: 'https://example.com/webhook',
event: WebHookEventType.SmsReceived,
};

mockHttpClient.post.mockResolvedValue(expectedRes);

const result = await client.registerWebhook(req);

expect(mockHttpClient.post).toHaveBeenCalledWith(
`${BASE_URL}/webhooks`,
req,
{
"Content-Type": "application/json",
"User-Agent": "android-sms-gateway/1.0 (client; js)",
Authorization: expect.any(String),
},
);
expect(result).toBe(expectedRes);
});

it('delete a webhook', async () => {
mockHttpClient.post.mockResolvedValue(undefined);

const result = await client.deleteWebhook('test');

expect(mockHttpClient.delete).toHaveBeenCalledWith(
`${BASE_URL}/webhooks/test`,
{
"User-Agent": "android-sms-gateway/1.0 (client; js)",
Authorization: expect.any(String),
},
);
expect(result).toBe(undefined);
});
});
30 changes: 29 additions & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Message, MessageState } from "./domain";
import { Message, MessageState, RegisterWebHookRequest, WebHook } from "./domain";
import { HttpClient } from "./http";

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

return this.httpClient.get<MessageState>(url, headers);
}

async getWebhooks(): Promise<WebHook[]> {
const url = `${this.baseUrl}/webhooks`;
const headers = {
...this.defaultHeaders,
};

return this.httpClient.get<WebHook[]>(url, headers);
}

async registerWebhook(request: RegisterWebHookRequest): Promise<WebHook> {
const url = `${this.baseUrl}/webhooks`;
const headers = {
"Content-Type": "application/json",
...this.defaultHeaders,
};

return this.httpClient.post<WebHook>(url, request, headers);
}

async deleteWebhook(webhookId: string): Promise<void> {
const url = `${this.baseUrl}/webhooks/${webhookId}`;
const headers = {
...this.defaultHeaders,
};

return this.httpClient.delete<void>(url, headers);
}
}
166 changes: 165 additions & 1 deletion src/domain.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@


export enum ProcessState {
Pending = "Pending",
Processed = "Processed",
Expand All @@ -6,23 +8,185 @@ export enum ProcessState {
Failed = "Failed",
}

/**
* Represents the state of a recipient of an SMS message.
*/
export interface RecipientState {
/**
* The phone number of the recipient.
*/
phoneNumber: string;

/**
* The state of the recipient.
*/
state: ProcessState;

/**
* An optional error message, if the recipient failed to receive the message.
*/
error?: string | null;
}

/**
* Represents the state of an SMS message.
*/
export interface MessageState {
/**
* The ID of the message.
*/
id: string;

/**
* The state of the message.
*/
state: ProcessState;

/**
* The list of recipients' states for the message.
*/
recipients: RecipientState[];
}

/**
* Represents an SMS message.
*/
export interface Message {
/**
* The ID of the message, will be generated if not provided.
* @default null
*/
id?: string | null;

/**
* The message content.
*/
message: string;

/**
* The time-to-live (TTL) of the message in seconds.
* If set to null, the message will not expire.
* @default null
*/
ttl?: number | null;

/**
* The phone numbers to send the message to.
*/
phoneNumbers: string[];

/**
* The SIM number to send the message from.
* If not specified, the message will be sent from the default SIM.
* @default null
*/
simNumber?: number | null;

/**
* Whether to include a delivery report for the message.
* @default true
*/
withDeliveryReport?: boolean | null;
}
}

/**
* Represents the type of events that can trigger a webhook.
*/
export enum WebHookEventType {
/**
* Indicates that a new SMS message has been received.
*/
SmsReceived = 'sms:received',

/**
* Indicates that a ping request has been sent.
*/
SystemPing = 'system:ping',
}

/**
* Represents a request to create or update a webhook.
*/
export interface RegisterWebHookRequest {
/**
* The ID of the webhook.
* If not specified, a new ID will be generated.
* @default null
*/
id?: string | null;

/**
* The event type that triggers the webhook.
*/
event: WebHookEventType;

/**
* The URL to send the webhook request to.
*/
url: string;
}

/**
* Represents a webhook configuration.
* @see RegisterWebHookRequest
*/
export type WebHook = Required<RegisterWebHookRequest>;

export type WebHookEvent = {
id: string;
webhookId: string;
deviceId: string;
} & WebHookPayload

/**
* Represents the payload of a webhook event.
*/
export type WebHookPayload =
/**
* Represents the payload of a webhook event of type `SmsReceived`.
*/
{
/**
* The event type.
*/
event: WebHookEventType.SmsReceived;

/**
* The payload of the event.
*/
payload: {
/**
* The received message.
*/
message: string;

/**
* The phone number of the sender.
*/
phoneNumber: string;

/**
* The date and time of when the message was received.
*/
receivedAt: string;
};
} |
/**
* Represents the payload of a webhook event of type `SystemPing`.
*/
{
/**
* The event type.
*/
event: WebHookEventType.SystemPing;

/**
* The payload of the event.
* This is an empty object.
*/
payload: EmptyObject;
};

type EmptyObject = {
[K in any]: never
}
1 change: 1 addition & 0 deletions src/http.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface HttpClient {
get<T>(url: string, headers?: Record<string, string>): Promise<T>;
post<T>(url: string, body: any, headers?: Record<string, string>): Promise<T>;
delete<T>(url: string, headers?: Record<string, string>): Promise<T>;
}

0 comments on commit 15469cf

Please sign in to comment.