Skip to content

Commit

Permalink
Merge pull request #4 from junwen-k/request_url_builder
Browse files Browse the repository at this point in the history
Request URL builder / User Agent header
  • Loading branch information
junwen-k authored Jun 1, 2020
2 parents d6e3fb7 + e403ce0 commit 44a47a3
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 99 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/.vscode/
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- `buildRequestURL` function to generate request URL to access OneWaySMS API.
- `deps.ts` to manage dependencies.
- `.gitignore` to ignore vscode settings.

### Updated

- Improve README.md formatting.
- Improve type for request URL params.
- Client to include `User-Agent` header to indicate SDK's request.

## [0.1.1] - 2020-05-29

Expand Down
6 changes: 3 additions & 3 deletions _mock_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import {
listenAndServe,
ServerRequest,
Status,
} from "https://deno.land/std/http/mod.ts";
import { decode } from "https://deno.land/std/node/querystring.ts";
decode,
} from "./deps.ts";
import { HOSTNAME, PORT } from "./_mock_config.ts";

console.log(`OneWay test server running at http://${HOSTNAME}:${PORT}/`);
console.log(`OneWaySMS mock server running at http://${HOSTNAME}:${PORT}/`);
listenAndServe(
{ port: PORT, hostname: HOSTNAME },
(req: ServerRequest): void => {
Expand Down
9 changes: 9 additions & 0 deletions deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright 2020 KwanJunWen. All rights reserved. MIT license.
export {
listenAndServe,
ServerRequest,
Status,
STATUS_TEXT,
} from "https://deno.land/std/http/mod.ts";
export { decode } from "https://deno.land/std/node/querystring.ts";
export { assertEquals, assert } from "https://deno.land/std/testing/asserts.ts";
2 changes: 1 addition & 1 deletion error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import {
Status,
STATUS_TEXT,
} from "https://deno.land/std/http/mod.ts";
} from "./deps.ts";
/**
* OneWay specific error. Switch based on code to handle specific error
* when using OneWayClient.
Expand Down
3 changes: 3 additions & 0 deletions mod.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"version": "0.1.1"
}
213 changes: 121 additions & 92 deletions oneway.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
// Copyright 2020 KwanJunWen. All rights reserved. MIT license.
import {
CheckCreditBalanceOutput,
CheckCreditBalanceURLParams,
CheckTransactionStatusInput,
CheckTransactionStatusOutput,
CheckTransactionStatusURLParams,
LanguageType,
MTTransactionStatus,
OneWayClient,
OneWayClientConfig,
OneWayURLParams,
OneWayURLPath,
SendSMSInput,
SendSMSOutput,
SendSMSURLParams,
} from "./types.ts";
import {
OneWayError,
OneWayErrorType,
} from "./error.ts";
import { version } from "./mod.json";

// Based on specifications found in http://smsd2.onewaysms.sg/api.pdf.
export class OneWay implements OneWayClient {
Expand Down Expand Up @@ -49,31 +55,41 @@ export class OneWay implements OneWayClient {
return "1";
}

/**
* Builds request URL to access OneWaySMS API.
*/
private buildRequestURL<T = OneWayURLParams>(
path: OneWayURLPath,
urlParams: { [P in keyof T]: string },
): string {
const { baseURL } = this.config;
const params = new URLSearchParams();
for (const key in urlParams) {
params.set(key, urlParams[key]);
}
return `${baseURL}/${path}?${params.toString()}`;
}

/**
* Builds request URL to send mobile terminating message based on SMS provided.
*/
private buildSendSMSRequestURL(input: SendSMSInput): string {
const { baseURL, apiUsername, apiPassword, senderID } = this.config;

const urlParams = new URLSearchParams();
const { apiUsername, apiPassword, senderID } = this.config;

if (!input.languageType) {
input.languageType = this.getLanguageType(input.message);
}

urlParams.set("apiusername", apiUsername);
urlParams.set("apipassword", apiPassword);
urlParams.set("senderid", senderID);
urlParams.set("mobileno", input.mobileNo.toString());
urlParams.set("languagetype", input.languageType);
urlParams.set(
"message",
input.languageType === "2"
return this.buildRequestURL<SendSMSURLParams>("api.aspx", {
apiusername: apiUsername,
apipassword: apiPassword,
senderid: input.senderID || senderID,
mobileno: input.mobileNo.toString(),
languagetype: input.languageType,
message: input.languageType === "2"
? this.messageToHex(input.message)
: input.message,
);

return `${baseURL}/api.aspx?${urlParams.toString()}`;
});
}

/**
Expand All @@ -82,28 +98,34 @@ export class OneWay implements OneWayClient {
private buildCheckTransactionStatusRequestURL(
input: CheckTransactionStatusInput,
): string {
const { baseURL } = this.config;

const urlParams = new URLSearchParams();

urlParams.set("mtid", input.mtID.toString());

return `${baseURL}/bulktrx.aspx?${urlParams.toString()}`;
return this.buildRequestURL<CheckTransactionStatusURLParams>(
"bulktrx.aspx",
{ mtid: input.mtID.toString() },
);
}

/**
* Builds request URL to check remaining credit balance based on API Username and API Password
* from client's config.
*/
private buildCheckCreditBalanceRequestURL(): string {
const { baseURL, apiUsername, apiPassword } = this.config;
const { apiUsername, apiPassword } = this.config;

const urlParams = new URLSearchParams();

urlParams.set("apiusername", apiUsername);
urlParams.set("apipassword", apiPassword);
return this.buildRequestURL<CheckCreditBalanceURLParams>(
"bulkcredit.aspx",
{ apiusername: apiUsername, apipassword: apiPassword },
);
}

return `${baseURL}/bulkcredit.aspx?${urlParams.toString()}`;
/**
* Calls "GET" request with User-Agent set to this client library.
*/
private async getRequest(requestURL: string): Promise<Response> {
const headers = new Headers();
headers.append("User-Agent", `onewaysms-sdk-deno/${version}`);
return fetch(requestURL, {
headers,
});
}

/**
Expand Down Expand Up @@ -155,7 +177,7 @@ export class OneWay implements OneWayClient {
const requestURL = this.buildSendSMSRequestURL(input);
return Promise.resolve(
new Promise((res, rej) => {
fetch(requestURL).then((resp) => {
this.getRequest(requestURL).then((resp) => {
if (!resp.ok) {
rej(
new OneWayError(
Expand All @@ -168,69 +190,76 @@ export class OneWay implements OneWayClient {
return resp.text();
}).then((respText: string): void => {
const mtIDs = respText.split(",").map((t: string) => parseInt(t));
if (mtIDs.length > 0) {
if (mtIDs[0] > 0) {
res({ mtIDs });
if (mtIDs.length <= 0) {
rej(
new OneWayError(
"unknown error",
OneWayErrorType.UnknownError,
),
);
return;
}
if (mtIDs[0] > 0) {
res({ mtIDs });
return;
}
switch (mtIDs[0]) {
case -100:
rej(
new OneWayError(
"apiusername or apipassword is invalid",
OneWayErrorType.InvalidCredentials,
),
);
return;
case -200:
rej(
new OneWayError(
"senderid parameter is invalid",
OneWayErrorType.InvalidSenderID,
),
);
return;
case -300:
rej(
new OneWayError(
"mobileno parameter is invalid",
OneWayErrorType.InvalidMobileNo,
),
);
return;
case -400:
rej(
new OneWayError(
"languagetype is invalid",
OneWayErrorType.InvalidLanguageType,
),
);
return;
case -500:
rej(
new OneWayError(
"characters in message are invalid",
OneWayErrorType.InvalidMessageCharacters,
),
);
return;
case -600:
rej(
new OneWayError(
"insufficient credit balance",
OneWayErrorType.InsufficientCreditBalance,
),
);
return;
default:
rej(
new OneWayError(
"unknown error",
OneWayErrorType.UnknownError,
),
);
return;
}
switch (mtIDs[0]) {
case -100:
rej(
new OneWayError(
"apiusername or apipassword is invalid",
OneWayErrorType.InvalidCredentials,
),
);
return;
case -200:
rej(
new OneWayError(
"senderid parameter is invalid",
OneWayErrorType.InvalidSenderID,
),
);
return;
case -300:
rej(
new OneWayError(
"mobileno parameter is invalid",
OneWayErrorType.InvalidMobileNo,
),
);
return;
case -400:
rej(
new OneWayError(
"languagetype is invalid",
OneWayErrorType.InvalidLanguageType,
),
);
return;
case -500:
rej(
new OneWayError(
"characters in message are invalid",
OneWayErrorType.InvalidMessageCharacters,
),
);
return;
case -600:
rej(
new OneWayError(
"insufficient credit balance",
OneWayErrorType.InsufficientCreditBalance,
),
);
return;
default:
rej(
new OneWayError(
"unknown error",
OneWayErrorType.UnknownError,
),
);
return;
}
}
})
.catch(rej);
Expand Down Expand Up @@ -278,7 +307,7 @@ export class OneWay implements OneWayClient {
const requestURL = this.buildCheckTransactionStatusRequestURL(input);
return Promise.resolve(
new Promise((res, rej) => {
fetch(requestURL)
this.getRequest(requestURL)
.then((resp) => resp.text())
.then((respText: string): void => {
const status = parseInt(respText);
Expand Down Expand Up @@ -351,7 +380,7 @@ export class OneWay implements OneWayClient {
const requestURL = this.buildCheckCreditBalanceRequestURL();
return Promise.resolve(
new Promise((res, rej) => {
fetch(requestURL)
this.getRequest(requestURL)
.then((resp) => resp.text())
.then((respText: string): void => {
const creditBalance = parseInt(respText);
Expand Down
3 changes: 1 addition & 2 deletions oneway_test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright 2020 KwanJunWen. All rights reserved. MIT license.
import { Status } from "https://deno.land/std/http/mod.ts";
import { assertEquals, assert } from "https://deno.land/std/testing/asserts.ts";
import { Status, assertEquals, assert } from "./deps.ts";
import { OneWay } from "./oneway.ts";
import {
OneWayError,
Expand Down
Loading

0 comments on commit 44a47a3

Please sign in to comment.