Skip to content

Commit 4400936

Browse files
authored
Merge pull request #37 from steven-t-h/main
feat: add RESTlet support
2 parents 91c3fce + 2d81df9 commit 4400936

File tree

12 files changed

+125
-28
lines changed

12 files changed

+125
-28
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,8 @@ coverage/
1414
*.tgz
1515

1616
# yarn errors
17-
yarn-error.log
17+
yarn-error.log
18+
19+
# IDE files
20+
.vscode/
21+
.idea/

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"prepublishOnly": "yarn build"
2727
},
2828
"devDependencies": {
29-
"@tsconfig/node18": "^18.2.2",
29+
"@tsconfig/node20": "^20.1.4",
3030
"@types/got": "^9.6.12",
3131
"@vitest/coverage-v8": "^3.0.0",
3232
"dotenv": "^16.3.1",

pnpm-lock.yaml

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

restlet-demo.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* @NAPIVersion 2.0
3+
* @NScriptType Restlet
4+
*/
5+
6+
/*
7+
This file was generated via the
8+
headintheclouds [typings-suitescript-2.0[(https://github.com/headintheclouddev/typings-suitescript-2.0) project.
9+
10+
It can be deployed as a restlet and will return a successful response to any GET request.
11+
*/
12+
13+
var __importDefault = (this && this.__importDefault) || function (mod) {
14+
return (mod && mod.__esModule) ? mod : { "default": mod };
15+
};
16+
define(["N/log"], function (log_1) {
17+
var exports = {};
18+
Object.defineProperty(exports, "__esModule", { value: true });
19+
exports.get = void 0;
20+
log_1 = __importDefault(log_1);
21+
var get = function (requestParams) {
22+
log_1.default.debug({
23+
title: 'GET Request',
24+
details: requestParams,
25+
});
26+
return {
27+
success: true,
28+
};
29+
};
30+
exports.get = get;
31+
return exports;
32+
});

src/client.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
NetsuiteResponse,
1010
} from "./types.js";
1111
import { NetsuiteError } from "./errors.js";
12+
import {removeLeadingSlash, removeTrailingSlash} from "./utils.js";
1213

1314
export default class NetsuiteApiClient {
1415
consumer_key: string;
@@ -28,15 +29,16 @@ export default class NetsuiteApiClient {
2829
this.version = "1.0";
2930
this.algorithm = "HMAC-SHA256";
3031
this.realm = options.realm;
31-
this.base_url = options.base_url;
32+
this.base_url = options.base_url ? removeTrailingSlash(options.base_url) : undefined;
3233
}
3334

3435
/**
3536
* Retrieve the Authorization Header
36-
* @param options
3737
* @returns
38+
* @param url
39+
* @param method
3840
*/
39-
getAuthorizationHeader(url: string, method: string) {
41+
getAuthorizationHeader(url: string, method: string): { [key: string]: string } {
4042
const oauth = new OAuth({
4143
consumer: {
4244
key: this.consumer_key,
@@ -68,16 +70,20 @@ export default class NetsuiteApiClient {
6870
* @returns
6971
*/
7072
public async request(opts: NetsuiteRequestOptions) {
71-
const { path = "*", method = "GET", body = "", heads = {} } = opts;
73+
const { path = "*", method = "GET", body = "", heads = {}, restletUrl } = opts;
74+
const cleanPath = removeLeadingSlash(path);
75+
// Set up the Request URI
7276

73-
// Setup the Request URI
74-
let uri;
75-
if (this.base_url) uri = `${this.base_url}/services/rest/${path}`;
76-
else {
77-
// as suggested by dylbarne in #15: sanitize url to enhance overall usability
78-
uri = `https://${this.realm
77+
// as suggested by dylbarne in #15: sanitize url to enhance overall usability
78+
let uri = `https://${this.realm
7979
.toLowerCase()
80-
.replace("_", "-")}.suitetalk.api.netsuite.com/services/rest/${path}`;
80+
.replace("_", "-")}.suitetalk.api.netsuite.com/services/rest/${cleanPath}`;
81+
if (this.base_url) {
82+
uri = `${this.base_url}/services/rest/${cleanPath}`
83+
}
84+
// if restletUrl is provided, use it instead of the default uri
85+
if (restletUrl) {
86+
uri = restletUrl;
8187
}
8288

8389
const options = {

src/types.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import type {Buffer} from "node:buffer";
2+
import type {Readable} from "node:stream";
3+
14
export type NetsuiteOptions = {
25
consumer_key: string;
36
consumer_secret_key: string;
@@ -7,13 +10,25 @@ export type NetsuiteOptions = {
710
base_url?: string;
811
};
912

10-
export type NetsuiteRequestOptions = {
11-
path?: string;
12-
method?: string;
13-
body?: any;
14-
heads?: any;
13+
type BaseRequestOptions = {
14+
/**
15+
* The HTTP method to use
16+
*/
17+
method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS" | 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head' | 'options';
18+
/**
19+
* The body of the request
20+
*/
21+
body?: string;
22+
/**
23+
* Additional headers to send with the request
24+
*/
25+
heads?: any;
1526
};
1627

28+
export type NetsuiteRequestOptions =
29+
| (BaseRequestOptions & { path?: string; restletUrl?: never })
30+
| (BaseRequestOptions & { path?: never; restletUrl?: string });
31+
1732
export type NetsuiteResponse = {
1833
statusCode: number;
1934
headers: NodeJS.Dict<string | string[]>;

src/utils.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
function removeTrailingSlash(str: string): string {
2+
return str.replace(/\/$/, '');
3+
}
4+
5+
function removeLeadingSlash(str: string): string {
6+
return str.replace(/^\//, '');
7+
}
8+
9+
export { removeLeadingSlash, removeTrailingSlash };

test/basic.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import "dotenv/config";
2-
import NetsuiteApiClient from "../src/client";
2+
import NetsuiteApiClient from "../src/client.js";
33
import { beforeAll, describe, expect, it } from "vitest";
44

55
let client: NetsuiteApiClient;

test/query.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import "dotenv/config";
2-
import NetsuiteApiClient from "../src/client";
2+
import NetsuiteApiClient from "../src/client.js";
33
import { beforeAll, describe, expect, it } from "vitest";
44

55
let client: NetsuiteApiClient;

test/request.test.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import "dotenv/config";
2-
import NetsuiteApiClient from "../src/client";
2+
import NetsuiteApiClient from "../src/client.js";
33
import { describe, expect, test, beforeAll, afterAll, it } from "vitest";
44

55
describe("Test request method", () => {
@@ -14,6 +14,7 @@ describe("Test request method", () => {
1414
process.env.token_secret == undefined ||
1515
process.env.realm == undefined ||
1616
process.env.base_url == undefined
17+
// || process.env.restlet_url == undefined
1718
) {
1819
throw new Error("Please create a `.env` file based on `.env.sample`");
1920
}
@@ -45,6 +46,18 @@ describe("Test request method", () => {
4546
expect(response.statusCode).toEqual(204);
4647
});
4748

49+
// it('should make a RESTlet request', async () => {
50+
// expect.assertions(1);
51+
// const response = await client.request({
52+
// method: 'GET',
53+
// restletUrl: process.env.restlet_url,
54+
// heads: {
55+
// 'Content-Type': 'application/json'
56+
// }
57+
// })
58+
// expect(response.statusCode).toEqual(200);
59+
// })
60+
4861
it("should make GET request - GET Customers", async () => {
4962
expect.assertions(4);
5063
const response = await client.request({

0 commit comments

Comments
 (0)