@rinse-dental/open-dental 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npm-publish.yml +34 -0
- package/README.md +1 -0
- package/__tests__/example.test.ts +6 -0
- package/dist/api/appointments.js +30 -0
- package/dist/api/index.js +12 -0
- package/dist/api/patients.js +18 -0
- package/dist/index.js +5 -0
- package/dist/openDental.js +42 -0
- package/dist/utils/errorHandler.js +1 -0
- package/dist/utils/httpClient.js +111 -0
- package/dist/utils/types.js +2 -0
- package/package.json +33 -0
- package/src/api/appointments.ts +40 -0
- package/src/api/index.ts +4 -0
- package/src/api/patients.ts +26 -0
- package/src/index.ts +1 -0
- package/src/openDental.ts +43 -0
- package/src/utils/errorHandler.ts +0 -0
- package/src/utils/httpClient.ts +109 -0
- package/src/utils/types.ts +134 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
|
3
|
+
|
|
4
|
+
name: Node.js Package
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
release:
|
|
8
|
+
types: [created]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-node@v4
|
|
16
|
+
with:
|
|
17
|
+
node-version: 20
|
|
18
|
+
- run: npm ci
|
|
19
|
+
- run: npm test
|
|
20
|
+
|
|
21
|
+
publish-npm:
|
|
22
|
+
needs: build
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
- uses: actions/setup-node@v4
|
|
27
|
+
with:
|
|
28
|
+
node-version: 20
|
|
29
|
+
registry-url: https://registry.npmjs.org/
|
|
30
|
+
scope: '@rinse-dental'
|
|
31
|
+
- run: npm ci
|
|
32
|
+
- run: npm publish
|
|
33
|
+
env:
|
|
34
|
+
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#Hello World
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class Appointments {
|
|
4
|
+
httpClient;
|
|
5
|
+
constructor(httpClient) {
|
|
6
|
+
this.httpClient = httpClient;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Fetch a specific appointment by its ID.
|
|
10
|
+
* @param {number} AptNum - The ID of the appointment.
|
|
11
|
+
* @returns {Promise<Appointment>} - The appointment data.
|
|
12
|
+
* @throws {Error} - If `AptNum` is not provided.
|
|
13
|
+
*/
|
|
14
|
+
async getAppointment(AptNum) {
|
|
15
|
+
if (!AptNum) {
|
|
16
|
+
throw new Error("AptNum is required.");
|
|
17
|
+
}
|
|
18
|
+
return this.httpClient.get(`/appointments/${AptNum}`);
|
|
19
|
+
}
|
|
20
|
+
async getAppointments(params) {
|
|
21
|
+
return this.httpClient.get("/appointments", params);
|
|
22
|
+
}
|
|
23
|
+
async createAppointment(data) {
|
|
24
|
+
if (!data) {
|
|
25
|
+
throw new Error("Appointment data is required.");
|
|
26
|
+
}
|
|
27
|
+
return this.httpClient.post("/appointments", data);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.default = Appointments;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Appointments = exports.Patients = void 0;
|
|
7
|
+
var patients_1 = require("./patients");
|
|
8
|
+
Object.defineProperty(exports, "Patients", { enumerable: true, get: function () { return __importDefault(patients_1).default; } });
|
|
9
|
+
var appointments_1 = require("./appointments");
|
|
10
|
+
Object.defineProperty(exports, "Appointments", { enumerable: true, get: function () { return __importDefault(appointments_1).default; } });
|
|
11
|
+
//export { default as ProcedureLogs } from "./procedureLogs";
|
|
12
|
+
// Add other APIs as needed
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class Patients {
|
|
4
|
+
httpClient;
|
|
5
|
+
constructor(httpClient) {
|
|
6
|
+
this.httpClient = httpClient;
|
|
7
|
+
}
|
|
8
|
+
async getPatient(params) {
|
|
9
|
+
if (!params.PatNum) {
|
|
10
|
+
throw new Error("PatNum is required.");
|
|
11
|
+
}
|
|
12
|
+
return this.httpClient.get(`/patients/${params.PatNum}`);
|
|
13
|
+
}
|
|
14
|
+
async getPatients() {
|
|
15
|
+
return this.httpClient.get("/patients");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.default = Patients;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpenDental = void 0;
|
|
4
|
+
var openDental_1 = require("./openDental");
|
|
5
|
+
Object.defineProperty(exports, "OpenDental", { enumerable: true, get: function () { return openDental_1.OpenDental; } });
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.OpenDental = void 0;
|
|
7
|
+
const httpClient_1 = __importDefault(require("./utils/httpClient"));
|
|
8
|
+
const patients_1 = __importDefault(require("./api/patients"));
|
|
9
|
+
const appointments_1 = __importDefault(require("./api/appointments"));
|
|
10
|
+
class OpenDental {
|
|
11
|
+
static httpClient;
|
|
12
|
+
/**
|
|
13
|
+
* Initialize the OpenDental library with the base URL and auth token.
|
|
14
|
+
*/
|
|
15
|
+
static initialize(baseURL, authToken) {
|
|
16
|
+
if (!baseURL || !authToken) {
|
|
17
|
+
throw new Error("Both baseURL and authToken are required.");
|
|
18
|
+
}
|
|
19
|
+
//to manage future versions of the API
|
|
20
|
+
baseURL = baseURL + "/api/v1";
|
|
21
|
+
this.httpClient = new httpClient_1.default(baseURL, authToken);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Create a new instance of the Patients API.
|
|
25
|
+
*/
|
|
26
|
+
static Patients() {
|
|
27
|
+
if (!this.httpClient) {
|
|
28
|
+
throw new Error("OpenDental not initialized. Call OpenDental.initialize() first.");
|
|
29
|
+
}
|
|
30
|
+
return new patients_1.default(this.httpClient);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create a new instance of the Appointments API.
|
|
34
|
+
*/
|
|
35
|
+
static Appointments() {
|
|
36
|
+
if (!this.httpClient) {
|
|
37
|
+
throw new Error("OpenDental not initialized. Call OpenDental.initialize() first.");
|
|
38
|
+
}
|
|
39
|
+
return new appointments_1.default(this.httpClient);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.OpenDental = OpenDental;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const axios_1 = __importDefault(require("axios"));
|
|
7
|
+
class HttpClient {
|
|
8
|
+
client;
|
|
9
|
+
constructor(baseURL, authToken) {
|
|
10
|
+
this.client = axios_1.default.create({
|
|
11
|
+
baseURL,
|
|
12
|
+
headers: {
|
|
13
|
+
Authorization: authToken,
|
|
14
|
+
"Content-Type": "application/json",
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* GET request
|
|
20
|
+
* @param {string} url - The endpoint URL
|
|
21
|
+
* @param {object} params - Optional query parameters
|
|
22
|
+
* @returns {Promise<T>} - The API response
|
|
23
|
+
*/
|
|
24
|
+
async get(url, params) {
|
|
25
|
+
try {
|
|
26
|
+
const response = await this.client.get(url, { params });
|
|
27
|
+
return response.data;
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
this.handleError(error);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* POST request
|
|
35
|
+
* @param {string} url - The endpoint URL
|
|
36
|
+
* @param {object} data - The payload to send
|
|
37
|
+
* @returns {Promise<T>} - The API response
|
|
38
|
+
*/
|
|
39
|
+
async post(url, data) {
|
|
40
|
+
try {
|
|
41
|
+
const response = await this.client.post(url, data);
|
|
42
|
+
return response.data;
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
this.handleError(error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* PUT request
|
|
50
|
+
* @param {string} url - The endpoint URL
|
|
51
|
+
* @param {object} data - The payload to update
|
|
52
|
+
* @returns {Promise<T>} - The API response
|
|
53
|
+
*/
|
|
54
|
+
async put(url, data) {
|
|
55
|
+
try {
|
|
56
|
+
const response = await this.client.put(url, data);
|
|
57
|
+
return response.data;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
this.handleError(error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* DELETE request
|
|
65
|
+
* @param {string} url - The endpoint URL
|
|
66
|
+
* @returns {Promise<T>} - The API response
|
|
67
|
+
*/
|
|
68
|
+
async delete(url) {
|
|
69
|
+
try {
|
|
70
|
+
const response = await this.client.delete(url);
|
|
71
|
+
return response.data;
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
this.handleError(error);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Error handler to provide detailed error information
|
|
79
|
+
* @param {AxiosError} error - The error object from Axios
|
|
80
|
+
*/
|
|
81
|
+
handleError(error) {
|
|
82
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
83
|
+
// Handle Axios-specific error
|
|
84
|
+
if (error.response) {
|
|
85
|
+
// The request was made, and the server responded with a status code
|
|
86
|
+
console.error("API Response Error:", {
|
|
87
|
+
status: error.response.status,
|
|
88
|
+
data: error.response.data,
|
|
89
|
+
headers: error.response.headers,
|
|
90
|
+
});
|
|
91
|
+
throw new Error(`Request failed with status ${error.response.status}: ${JSON.stringify(error.response.data)}`);
|
|
92
|
+
}
|
|
93
|
+
else if (error.request) {
|
|
94
|
+
// The request was made, but no response was received
|
|
95
|
+
console.error("No Response Error:", error.request);
|
|
96
|
+
throw new Error("No response received from the server.");
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Something happened in setting up the request
|
|
100
|
+
console.error("Request Setup Error:", error.message);
|
|
101
|
+
throw new Error(`Request setup failed: ${error.message}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Handle non-Axios errors
|
|
106
|
+
console.error("Unexpected Error:", error);
|
|
107
|
+
throw new Error("An unexpected error occurred.");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.default = HttpClient;
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rinse-dental/open-dental",
|
|
3
|
+
"version": "0.0.6",
|
|
4
|
+
"description": "A TypeScript library for easily accessing Open Dental APIs.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"author": "Rinse Dental Inc.",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"registry": "https://registry.npmjs.org/",
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/getrinsed/open-dental.git"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"axios": "^1.7.7",
|
|
19
|
+
"moment-timezone": "^0.5.46"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/jest": "^29.5.14",
|
|
23
|
+
"@types/node": "^22.9.3",
|
|
24
|
+
"jest": "^29.7.0",
|
|
25
|
+
"prettier": "^3.3.3",
|
|
26
|
+
"typescript": "^5.7.2"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc",
|
|
30
|
+
"prepare": "npm run build",
|
|
31
|
+
"test": "jest"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import HttpClient from "../utils/httpClient";
|
|
2
|
+
import { Appointment } from "../utils/types";
|
|
3
|
+
|
|
4
|
+
export interface IAppointments {
|
|
5
|
+
getAppointment(AptNum: number): Promise<Appointment>;
|
|
6
|
+
getAppointments(params?: object): Promise<Appointment[]>;
|
|
7
|
+
createAppointment(data: Partial<Appointment>): Promise<Appointment>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default class Appointments implements IAppointments {
|
|
11
|
+
private httpClient: HttpClient;
|
|
12
|
+
|
|
13
|
+
constructor(httpClient: HttpClient) {
|
|
14
|
+
this.httpClient = httpClient;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Fetch a specific appointment by its ID.
|
|
19
|
+
* @param {number} AptNum - The ID of the appointment.
|
|
20
|
+
* @returns {Promise<Appointment>} - The appointment data.
|
|
21
|
+
* @throws {Error} - If `AptNum` is not provided.
|
|
22
|
+
*/
|
|
23
|
+
public async getAppointment(AptNum: number): Promise<Appointment> {
|
|
24
|
+
if (!AptNum) {
|
|
25
|
+
throw new Error("AptNum is required.");
|
|
26
|
+
}
|
|
27
|
+
return this.httpClient.get<Appointment>(`/appointments/${AptNum}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public async getAppointments(params?: object): Promise<Appointment[]> {
|
|
31
|
+
return this.httpClient.get<Appointment[]>("/appointments", params);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public async createAppointment(data: Partial<Appointment>): Promise<Appointment> {
|
|
35
|
+
if (!data) {
|
|
36
|
+
throw new Error("Appointment data is required.");
|
|
37
|
+
}
|
|
38
|
+
return this.httpClient.post<Appointment>("/appointments", data);
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/api/index.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import HttpClient from "../utils/httpClient";
|
|
2
|
+
import { Patient } from "../utils/types";
|
|
3
|
+
|
|
4
|
+
export interface IPatients {
|
|
5
|
+
getPatient(params: { PatNum: number }): Promise<Patient>;
|
|
6
|
+
getPatients(): Promise<Patient[]>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default class Patients implements IPatients {
|
|
10
|
+
private httpClient: HttpClient;
|
|
11
|
+
|
|
12
|
+
constructor(httpClient: HttpClient) {
|
|
13
|
+
this.httpClient = httpClient;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public async getPatient(params: { PatNum: number }): Promise<Patient> {
|
|
17
|
+
if (!params.PatNum) {
|
|
18
|
+
throw new Error("PatNum is required.");
|
|
19
|
+
}
|
|
20
|
+
return this.httpClient.get<Patient>(`/patients/${params.PatNum}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public async getPatients(): Promise<Patient[]> {
|
|
24
|
+
return this.httpClient.get<Patient[]>("/patients");
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { OpenDental } from "./openDental";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import HttpClient from "./utils/httpClient";
|
|
2
|
+
import Patients, { IPatients } from "./api/patients";
|
|
3
|
+
import Appointments, { IAppointments } from "./api/appointments";
|
|
4
|
+
|
|
5
|
+
class OpenDental {
|
|
6
|
+
private static httpClient: HttpClient;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Initialize the OpenDental library with the base URL and auth token.
|
|
10
|
+
*/
|
|
11
|
+
public static initialize(baseURL: string, authToken: string): void {
|
|
12
|
+
if (!baseURL || !authToken) {
|
|
13
|
+
throw new Error("Both baseURL and authToken are required.");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//to manage future versions of the API
|
|
17
|
+
baseURL = baseURL + "/api/v1";
|
|
18
|
+
|
|
19
|
+
this.httpClient = new HttpClient(baseURL, authToken);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create a new instance of the Patients API.
|
|
24
|
+
*/
|
|
25
|
+
public static Patients(): IPatients {
|
|
26
|
+
if (!this.httpClient) {
|
|
27
|
+
throw new Error("OpenDental not initialized. Call OpenDental.initialize() first.");
|
|
28
|
+
}
|
|
29
|
+
return new Patients(this.httpClient);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Create a new instance of the Appointments API.
|
|
34
|
+
*/
|
|
35
|
+
public static Appointments(): IAppointments {
|
|
36
|
+
if (!this.httpClient) {
|
|
37
|
+
throw new Error("OpenDental not initialized. Call OpenDental.initialize() first.");
|
|
38
|
+
}
|
|
39
|
+
return new Appointments(this.httpClient);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { OpenDental };
|
|
File without changes
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import axios, { AxiosInstance, AxiosResponse, AxiosError } from "axios";
|
|
2
|
+
|
|
3
|
+
class HttpClient {
|
|
4
|
+
private client: AxiosInstance;
|
|
5
|
+
|
|
6
|
+
constructor(baseURL: string, authToken: string) {
|
|
7
|
+
this.client = axios.create({
|
|
8
|
+
baseURL,
|
|
9
|
+
headers: {
|
|
10
|
+
Authorization: authToken,
|
|
11
|
+
"Content-Type": "application/json",
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* GET request
|
|
18
|
+
* @param {string} url - The endpoint URL
|
|
19
|
+
* @param {object} params - Optional query parameters
|
|
20
|
+
* @returns {Promise<T>} - The API response
|
|
21
|
+
*/
|
|
22
|
+
public async get<T>(url: string, params?: object): Promise<T> {
|
|
23
|
+
try {
|
|
24
|
+
const response: AxiosResponse<T> = await this.client.get<T>(url, { params });
|
|
25
|
+
return response.data;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
this.handleError(error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* POST request
|
|
33
|
+
* @param {string} url - The endpoint URL
|
|
34
|
+
* @param {object} data - The payload to send
|
|
35
|
+
* @returns {Promise<T>} - The API response
|
|
36
|
+
*/
|
|
37
|
+
public async post<T>(url: string, data: object): Promise<T> {
|
|
38
|
+
try {
|
|
39
|
+
const response: AxiosResponse<T> = await this.client.post<T>(url, data);
|
|
40
|
+
return response.data;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
this.handleError(error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* PUT request
|
|
48
|
+
* @param {string} url - The endpoint URL
|
|
49
|
+
* @param {object} data - The payload to update
|
|
50
|
+
* @returns {Promise<T>} - The API response
|
|
51
|
+
*/
|
|
52
|
+
public async put<T>(url: string, data: object): Promise<T> {
|
|
53
|
+
try {
|
|
54
|
+
const response: AxiosResponse<T> = await this.client.put<T>(url, data);
|
|
55
|
+
return response.data;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
this.handleError(error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* DELETE request
|
|
63
|
+
* @param {string} url - The endpoint URL
|
|
64
|
+
* @returns {Promise<T>} - The API response
|
|
65
|
+
*/
|
|
66
|
+
public async delete<T>(url: string): Promise<T> {
|
|
67
|
+
try {
|
|
68
|
+
const response: AxiosResponse<T> = await this.client.delete<T>(url);
|
|
69
|
+
return response.data;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
this.handleError(error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Error handler to provide detailed error information
|
|
77
|
+
* @param {AxiosError} error - The error object from Axios
|
|
78
|
+
*/
|
|
79
|
+
private handleError(error: unknown): never {
|
|
80
|
+
if (axios.isAxiosError(error)) {
|
|
81
|
+
// Handle Axios-specific error
|
|
82
|
+
if (error.response) {
|
|
83
|
+
// The request was made, and the server responded with a status code
|
|
84
|
+
console.error("API Response Error:", {
|
|
85
|
+
status: error.response.status,
|
|
86
|
+
data: error.response.data,
|
|
87
|
+
headers: error.response.headers,
|
|
88
|
+
});
|
|
89
|
+
throw new Error(
|
|
90
|
+
`Request failed with status ${error.response.status}: ${JSON.stringify(error.response.data)}`
|
|
91
|
+
);
|
|
92
|
+
} else if (error.request) {
|
|
93
|
+
// The request was made, but no response was received
|
|
94
|
+
console.error("No Response Error:", error.request);
|
|
95
|
+
throw new Error("No response received from the server.");
|
|
96
|
+
} else {
|
|
97
|
+
// Something happened in setting up the request
|
|
98
|
+
console.error("Request Setup Error:", error.message);
|
|
99
|
+
throw new Error(`Request setup failed: ${error.message}`);
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
// Handle non-Axios errors
|
|
103
|
+
console.error("Unexpected Error:", error);
|
|
104
|
+
throw new Error("An unexpected error occurred.");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default HttpClient;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a patient in the Open Dental system.
|
|
3
|
+
* @see https://www.opendental.com/site/apipatients.html
|
|
4
|
+
*/
|
|
5
|
+
export interface Patient {
|
|
6
|
+
PatNum: number; // Unique patient identifier
|
|
7
|
+
LName: string; // Last name
|
|
8
|
+
FName: string; // First name
|
|
9
|
+
MiddleI?: string; // Middle initial
|
|
10
|
+
Preferred?: string; // Preferred name
|
|
11
|
+
PatStatus: 'Patient' | 'NonPatient' | 'Inactive' | 'Archived' | 'Deceased' | 'Prospective'; // Patient status
|
|
12
|
+
Gender: 'Male' | 'Female' | 'Unknown' | 'Other'; // Gender
|
|
13
|
+
Position: 'Single' | 'Married' | 'Child' | 'Widowed' | 'Divorced'; // Marital status
|
|
14
|
+
Birthdate: string; // Date of birth in 'yyyy-MM-dd' format
|
|
15
|
+
SSN?: string; // Social Security Number
|
|
16
|
+
Address?: string; // Primary address
|
|
17
|
+
Address2?: string; // Secondary address
|
|
18
|
+
City?: string; // City
|
|
19
|
+
State?: string; // State
|
|
20
|
+
Zip?: string; // ZIP code
|
|
21
|
+
HmPhone?: string; // Home phone number
|
|
22
|
+
WkPhone?: string; // Work phone number
|
|
23
|
+
WirelessPhone?: string; // Mobile phone number
|
|
24
|
+
Guarantor: number; // Guarantor's PatNum
|
|
25
|
+
Email?: string; // Email address
|
|
26
|
+
EstBalance: number; // Estimated balance
|
|
27
|
+
PriProv: number; // Primary provider number
|
|
28
|
+
priProvAbbr?: string; // Primary provider abbreviation
|
|
29
|
+
SecProv?: number; // Secondary provider number
|
|
30
|
+
secProvAbbr?: string; // Secondary provider abbreviation
|
|
31
|
+
BillingType?: string; // Billing type description
|
|
32
|
+
ImageFolder?: string; // Image folder name
|
|
33
|
+
FamFinUrgNote?: string; // Family financial urgent note
|
|
34
|
+
ChartNumber?: string; // Chart number
|
|
35
|
+
MedicaidID?: string; // Medicaid ID
|
|
36
|
+
BalTotal: number; // Total balance
|
|
37
|
+
DateFirstVisit?: string; // Date of first visit in 'yyyy-MM-dd' format
|
|
38
|
+
ClinicNum?: number; // Clinic number
|
|
39
|
+
clinicAbbr?: string; // Clinic abbreviation
|
|
40
|
+
Ward?: string; // Ward
|
|
41
|
+
PreferConfirmMethod?: 'None' | 'Email' | 'TextMessage' | 'PhoneCall'; // Preferred confirmation method
|
|
42
|
+
PreferContactMethod?: 'None' | 'Email' | 'TextMessage' | 'PhoneCall' | 'Mail'; // Preferred contact method
|
|
43
|
+
PreferRecallMethod?: 'None' | 'Email' | 'TextMessage' | 'PhoneCall' | 'Mail'; // Preferred recall method
|
|
44
|
+
Language?: string; // Language code (ISO 639-2 format)
|
|
45
|
+
AdmitDate?: string; // Admission date in 'yyyy-MM-dd' format
|
|
46
|
+
siteDesc?: string; // Site description
|
|
47
|
+
DateTStamp: string; // Timestamp of the last update in 'yyyy-MM-dd HH:mm:ss' format
|
|
48
|
+
SuperFamily?: number; // Super family number
|
|
49
|
+
TxtMsgOk?: 'Unknown' | 'Yes' | 'No'; // Text message consent
|
|
50
|
+
SecDateEntry?: string; // Secondary date entry in 'yyyy-MM-dd' format
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Represents an appointment in the Open Dental system.
|
|
55
|
+
* @see https://www.opendental.com/site/apiappointments.html
|
|
56
|
+
*/
|
|
57
|
+
export interface Appointment {
|
|
58
|
+
AptNum: number; // Unique appointment identifier
|
|
59
|
+
PatNum: number; // Patient's PatNum
|
|
60
|
+
AptStatus: 'Scheduled' | 'Complete' | 'UnschedList' | 'ASAP' | 'Broken' | 'Planned' | 'PtNote' | 'PtNoteCompleted'; // Appointment status
|
|
61
|
+
Pattern: string; // Time pattern
|
|
62
|
+
Confirmed: number; // Confirmation status code
|
|
63
|
+
confirmed?: string; // Confirmation status description
|
|
64
|
+
TimeLocked: boolean; // Indicates if the time is locked
|
|
65
|
+
Op: number; // Operatory number
|
|
66
|
+
Note?: string; // Appointment note
|
|
67
|
+
ProvNum: number; // Provider number
|
|
68
|
+
provAbbr?: string; // Provider abbreviation
|
|
69
|
+
ProvHyg?: number; // Hygienist provider number
|
|
70
|
+
AptDateTime: string; // Appointment date and time in 'yyyy-MM-dd HH:mm:ss' format
|
|
71
|
+
NextAptNum?: number; // Next appointment number
|
|
72
|
+
UnschedStatus?: number; // Unscheduled status code
|
|
73
|
+
unschedStatus?: string; // Unscheduled status description
|
|
74
|
+
IsNewPatient: boolean; // Indicates if the patient is new
|
|
75
|
+
ProcDescript?: string; // Procedure description
|
|
76
|
+
ClinicNum?: number; // Clinic number
|
|
77
|
+
IsHygiene: boolean; // Indicates if it's a hygiene appointment
|
|
78
|
+
DateTStamp: string; // Timestamp of the last update in 'yyyy-MM-dd HH:mm:ss' format
|
|
79
|
+
DateTimeArrived?: string; // Arrival time in 'yyyy-MM-dd HH:mm:ss' format
|
|
80
|
+
DateTimeSeated?: string; // Seated time in 'yyyy-MM-dd HH:mm:ss' format
|
|
81
|
+
DateTimeDismissed?: string; // Dismissed time in 'yyyy-MM-dd HH:mm:ss' format
|
|
82
|
+
InsPlan1?: number; // Primary insurance plan number
|
|
83
|
+
InsPlan2?: number; // Secondary insurance plan number
|
|
84
|
+
DateTimeAskedToArrive?: string; // Requested arrival time in 'yyyy-MM-dd HH:mm:ss' format
|
|
85
|
+
colorOverride?: string; // Color override in 'R,G,B' format
|
|
86
|
+
AppointmentTypeNum?: number; // Appointment type number
|
|
87
|
+
SecDateTEntry?: string; // Secondary date entry in 'yyyy-MM-dd' format
|
|
88
|
+
Priority?: 'Normal' | 'ASAP'; // Appointment priority
|
|
89
|
+
PatternSecondary?: string; // Secondary time pattern
|
|
90
|
+
ItemOrderPlanned?: number; // Planned item order
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Represents a procedure log entry in the Open Dental system.
|
|
95
|
+
* @see https://www.opendental.com/site/apiprocedurelogs.html
|
|
96
|
+
*/
|
|
97
|
+
export interface ProcedureLog {
|
|
98
|
+
ProcNum: number; // Unique procedure identifier
|
|
99
|
+
PatNum: number; // Patient's PatNum
|
|
100
|
+
AptNum?: number; // Associated appointment number
|
|
101
|
+
ProcDate: string; // Procedure date in 'yyyy-MM-dd' format
|
|
102
|
+
ProcFee: number; // Procedure fee
|
|
103
|
+
Surf?: string; // Tooth surface
|
|
104
|
+
ToothNum?: string; // Tooth number
|
|
105
|
+
ToothRange?: string; // Tooth range
|
|
106
|
+
Priority?: number; // Priority code
|
|
107
|
+
priority?: string; // Priority description
|
|
108
|
+
ProcStatus: 'TP' | 'C' | 'EO' | 'R' | 'D' | 'E'; // Procedure status: Treatment Planned (TP), Complete (C), Existing Other Provider (EO), Referred (R), Deleted (D), or Existing Current Provider (E)
|
|
109
|
+
ProvNum: number; // Provider number
|
|
110
|
+
provAbbr?: string; // Provider abbreviation
|
|
111
|
+
Dx?: string; // Diagnosis code
|
|
112
|
+
dxName?: string; // Diagnosis name
|
|
113
|
+
PlannedAptNum?: number; // Planned appointment number
|
|
114
|
+
DateEntryC?: string; // Date the procedure was created in 'yyyy-MM-dd HH:mm:ss' format
|
|
115
|
+
ClinicNum?: number; // Clinic number
|
|
116
|
+
MedicalCode?: string; // Medical procedure code
|
|
117
|
+
DiagnosticCode?: string; // Diagnostic code
|
|
118
|
+
CodeNum: number; // Procedure code number
|
|
119
|
+
codeText?: string; // Procedure code description
|
|
120
|
+
ProcNote?: string; // Procedure note
|
|
121
|
+
ToothColor?: string; // Tooth color (if applicable)
|
|
122
|
+
LabFee?: number; // Lab fee
|
|
123
|
+
IsProsth?: boolean; // Indicates if it's a prosthodontic procedure
|
|
124
|
+
ProcTime?: string; // Time taken for the procedure
|
|
125
|
+
PriorityNum?: number; // Priority number
|
|
126
|
+
Prosthesis?: 'None' | 'Crown' | 'Denture' | 'Bridge'; // Type of prosthesis
|
|
127
|
+
ProsthesisDate?: string; // Prosthesis delivery date in 'yyyy-MM-dd' format
|
|
128
|
+
UnitQty?: number; // Quantity of units
|
|
129
|
+
DateTStamp: string; // Timestamp of the last update in 'yyyy-MM-dd HH:mm:ss' format
|
|
130
|
+
SecDateTEntry?: string; // Secondary date entry in 'yyyy-MM-dd HH:mm:ss' format
|
|
131
|
+
MedicalOrderNum?: number; // Medical order number
|
|
132
|
+
PlaceService?: 'Office' | 'InpatientHospital' | 'OutpatientHospital'; // Place of service
|
|
133
|
+
}
|
|
134
|
+
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"outDir": "./dist",
|
|
6
|
+
"rootDir": "./src",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true
|
|
10
|
+
},
|
|
11
|
+
"include": ["src/**/*"],
|
|
12
|
+
"exclude": ["node_modules", "dist"]
|
|
13
|
+
}
|