Skip to content

Commit 53fc508

Browse files
authored
fix: add error parsing and response models (#23)
1 parent a74de75 commit 53fc508

14 files changed

Lines changed: 288 additions & 747 deletions

File tree

.github/workflows/documentation-preview.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ on:
55
push:
66
branches-ignore:
77
- "master"
8-
# un-comment this after testing will be done !!
9-
# paths:
10-
# - "apps/wapijs.co/**"
8+
paths:
9+
- "apps/wapijs.co/**"
1110

1211
concurrency:
1312
group: ${{ github.workflow }}-${{ github.ref }}

apps/wapijs.co/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
"name": "wapijs.co",
33
"private": true,
44
"scripts": {
5-
"dev": "next dev",
6-
"build": "next build",
7-
"start": "next start",
5+
"dev": "NODE_ENV=development next dev",
6+
"build": "NODE_ENV=production next build",
7+
"start": "NODE_ENV=production next start",
88
"lint": "next lint",
99
"pretty": "pnpm prettier --write \"src/**/*.{ts,tsx,css}\""
1010
},

packages/example-chat-bot/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
},
4040
"packageManager": "pnpm@8.7.0",
4141
"dependencies": {
42-
"@wapijs/wapi.js": "0.0.2",
42+
"@wapijs/wapi.js": "workspace:*",
4343
"dotenv": "^16.3.1"
4444
},
4545
"publishConfig": {

packages/example-chat-bot/src/index.ts

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,63 @@
11
import { whatsappClient } from './utils/client'
2-
import { AudioMessage, ListInteractionMessage, TextMessage } from '@wapijs/wapi.js'
2+
import { ListInteractionMessage, TextMessage } from '@wapijs/wapi.js'
33

44
function init() {
55
whatsappClient.on('Ready', () => {
66
console.log('Client is ready')
77
})
88

99
const faq = {
10-
"What is wapijs": [
10+
'What is wapijs': [
1111
{
12-
question: "What is wapi.js?",
13-
answer: "wapi.js is a Tyepscript library for building WhatsApp chatbots."
12+
question: 'What is wapi.js?',
13+
answer: 'wapi.js is a Tyepscript library for building WhatsApp chatbots.'
1414
},
1515
{
16-
question: "Main features of wapi.js?",
17-
answer: "Object-oriented design, single client, easy messaging, event handling, media upload."
16+
question: 'Main features of wapi.js?',
17+
answer: 'Object-oriented design, single client, easy messaging, event handling, media upload.'
1818
},
1919
{
20-
question: "Can I build AI chatbots with wapi.js? ewfjbesjkfbewjfgbjwekfknwekjnfwefwefwef",
20+
question: 'Can I build AI chatbots with wapi.js?',
2121
answer: "wapi.js itself doesn't have AI, but you can integrate with NLU services."
22-
},
22+
}
2323
],
24-
"Getting Started": [
24+
'Getting Started': [
2525
{
26-
question: "How do I get started with wapi.js?",
27-
answer: "Check out the docs at https://wapijs.co/docs and use the \"create-wapi-bot\" template."
26+
question: 'How do I get started with wapi.js?',
27+
answer: 'Check out the docs at https://wapijs.co/docs and use the "create-wapi-bot" template.'
2828
},
2929
{
30-
question: "Is wapi.js easy to learn?",
31-
answer: "Yes, designed for all levels. Docs and examples help you get started quickly."
32-
},
30+
question: 'Is wapi.js easy to learn?',
31+
answer: 'Yes, designed for all levels. Docs and examples help you get started quickly.'
32+
}
3333
],
34-
"Capabilities": [
34+
Capabilities: [
3535
{
36-
question: "What kind of chatbots can I build?",
37-
answer: "Customer support, marketing, notifications, and more! Leverage WhatsApp Business API."
36+
question: 'What kind of chatbots can I build?',
37+
answer: 'Customer support, marketing, notifications, and more! Leverage WhatsApp Business API.'
3838
},
3939
{
40-
question: "Can I integrate wapi.js with other systems?",
41-
answer: "Absolutely! Integrate with existing backend system."
40+
question: 'Can I integrate wapi.js with other systems?',
41+
answer: 'Absolutely! Integrate with existing backend system.'
4242
},
4343
{
44-
question: "Are there examples of chatbots built with wapi.js?",
44+
question: 'Are there examples of chatbots built with wapi.js?',
4545
answer: "It's in beta, so not many yet. Be among the first to build and share yours!"
4646
}
4747
],
48-
"Help & Support": [
48+
'Help & Support': [
4949
{
50-
question: "Is wapi.js free and open-source?",
50+
question:
51+
'Is wapi.js free and open-source? qwerrqwrf3r3r32r23r23r23r23r2rqrqrerere23re23r23re',
5152
answer: "Yes, it's completely free and open-source under the Apache 2.0 License."
5253
},
5354
{
54-
question: "Where can I get help or support for wapi.js?",
55-
answer: "Create an issue on our GitHub repository: https://github.com/sarthakjdev/wapi.js/issues."
56-
},
55+
question: 'Where can I get help or support for wapi.js?',
56+
answer: 'Create an issue on our GitHub repository: https://github.com/sarthakjdev/wapi.js/issues.'
57+
}
5758
]
5859
}
5960

60-
61-
62-
63-
6461
const listMessage = new ListInteractionMessage({
6562
bodyText: 'Welcome to Wapi.js',
6663
buttonText: 'Ask questions',
@@ -83,11 +80,10 @@ function init() {
8380
whatsappClient.on('TextMessage', async message => {
8481
console.log('Text Message')
8582
if (message.text.data.text.toLowerCase() === 'hello') {
86-
const response = await message.client.message.send({
83+
await message.client.message.send({
8784
message: listMessage,
8885
phoneNumber: message.context.from
8986
})
90-
console.log({ response: JSON.stringify(response) })
9187
} else {
9288
await message.reply({
9389
message: new TextMessage({
@@ -97,7 +93,7 @@ function init() {
9793
}
9894
})
9995

100-
whatsappClient.on('ListInteraction', async (message) => {
96+
whatsappClient.on('ListInteraction', async message => {
10197
console.log('List Interaction', message)
10298

10399
// it would be something like : section-1-question-1
@@ -106,8 +102,6 @@ function init() {
106102
const sectionIndex = parseInt(messageListId.split('-')[1]) - 1
107103
const questionIndex = parseInt(messageListId.split('-')[3]) - 1
108104

109-
console.log({ messageListId, sectionIndex, questionIndex })
110-
111105
// @ts-ignore
112106
const answerToReply = faq[Object.keys(faq)[sectionIndex]][questionIndex].answer
113107

@@ -116,7 +110,6 @@ function init() {
116110
text: answerToReply
117111
})
118112
})
119-
120113
})
121114

122115
whatsappClient.on('Error', error => {
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"extends": "../../api-extractor.json",
3-
"mainEntryPointFilePath": "<projectFolder>/dist/esm/index.d.ts",
4-
"docModel": {
5-
"projectFolderUrl": "https://github.com/wapi.js/wapi.js/tree/main/packages/wapi.js"
6-
}
2+
"extends": "../../api-extractor.json",
3+
"mainEntryPointFilePath": "<projectFolder>/dist/esm/index.d.ts",
4+
"docModel": {
5+
"projectFolderUrl": "https://github.com/sarthakjdev/wapi.js/tree/master/packages/wapi.js"
6+
}
77
}

packages/wapi.js/src/api-request-payload-schema.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,12 @@ export const ListInteractiveMessageSection = z.object({
343343
z.object({
344344
id: z.string(),
345345
title: z.string(),
346-
description: z.string().max(72, "Max length of a row description in list interaction message, can be 72 only"),
346+
description: z
347+
.string()
348+
.max(
349+
72,
350+
"Max length of a row description in list interaction message, can be 72 only",
351+
),
347352
}),
348353
),
349354
});

packages/wapi.js/src/client/interface.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { type Webhook } from "../webhook";
55
import { type MessageManager } from "../manager/message";
66
import { type Client } from "./index";
77
import { type RequestClient } from "./request-client";
8+
import { type z } from "zod";
9+
import { type WapiMessageResponseSchemaType } from "./schema";
810

911
/**
1012
* Represents the interface for the Wapi client.
@@ -115,7 +117,7 @@ export interface RequestClientInterface {
115117
path: string;
116118
body: string;
117119
method: "GET" | "POST" | "DELETE";
118-
}): Promise<void>;
120+
}): Promise<z.infer<typeof WapiMessageResponseSchemaType>>;
119121
}
120122

121123
/**

packages/wapi.js/src/client/request-client.ts

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
import { type z } from "zod";
12
import { type Client } from "./index";
23
import {
34
type RequestClientInterface,
45
type RequestClientConfigOptions,
56
} from "./interface";
7+
import {
8+
CloudApiResponseSchemaType,
9+
type WapiMessageResponseSchemaType,
10+
} from "./schema";
11+
import { MessageStatusEnum } from "../webhook/type";
612

713
/**
814
* Request client used to communicate with WhatsApp Cloud API using HTTP requests.
@@ -61,10 +67,9 @@ export class RequestClient implements RequestClientInterface {
6167
path: string;
6268
body?: string;
6369
method?: "GET" | "POST" | "DELETE";
64-
}): Promise<any> {
70+
}): Promise<z.infer<typeof WapiMessageResponseSchemaType>> {
6571
try {
6672
const requestUrl = this.getRequestUrl();
67-
6873
const response = await fetch(`${requestUrl}${path}`, {
6974
method: method,
7075
body,
@@ -76,13 +81,51 @@ export class RequestClient implements RequestClientInterface {
7681
});
7782

7883
const responseBody = await response.json();
79-
80-
// !TODO: fix types here
81-
return responseBody;
84+
const parsedResponse = CloudApiResponseSchemaType.safeParse(responseBody);
85+
if (parsedResponse.success) {
86+
const responseData = parsedResponse.data;
87+
if (
88+
responseData &&
89+
typeof responseData === "object" &&
90+
"error" in responseData
91+
) {
92+
// api returned errored response
93+
return {
94+
status: "error",
95+
error: {
96+
description: responseData.error.error_data.details,
97+
title: responseData.error.message,
98+
errorCode: responseData.error.code,
99+
errorSubCode: responseData.error.error_subcode,
100+
},
101+
};
102+
} else {
103+
return {
104+
status: "success",
105+
data: {
106+
messageId: responseData.messages[0].id,
107+
receiverPhoneNumber: responseData.contacts[0].input,
108+
senderPhoneNumber: responseData.contacts[0].wa_id,
109+
status: MessageStatusEnum.Sent,
110+
},
111+
};
112+
}
113+
} else {
114+
throw new Error(
115+
"Failed to parse response, Report to sarthak@softlancer.co urgently. or raise an issue on github.",
116+
);
117+
}
82118
} catch (error) {
83-
console.log({ error });
84119
if (error instanceof Error) this.client.emit("Error", error);
85-
return null;
120+
return {
121+
status: "error",
122+
error: {
123+
title: "Request Error",
124+
description: error instanceof Error ? error.message : "",
125+
errorCode: 0,
126+
errorSubCode: "",
127+
},
128+
};
86129
}
87130
}
88131
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { z } from "zod";
2+
import { MessageStatusEnum } from "../webhook/type";
3+
4+
export const CloudApiSuccessResponseSchemaType = z.object({
5+
messages: z.array(
6+
z.object({
7+
id: z.string(),
8+
}),
9+
),
10+
messaging_product: z.literal("whatsapp"),
11+
contacts: z.array(
12+
z.object({
13+
input: z.string(),
14+
wa_id: z.string(),
15+
}),
16+
),
17+
});
18+
19+
export const CloudApiErrorResponseSchemaType = z.object({
20+
error: z.object({
21+
message: z.string(),
22+
type: z.string(),
23+
code: z.number(),
24+
error_data: z.object({
25+
messaging_product: z.string(),
26+
details: z.string(),
27+
}),
28+
error_subcode: z.string().optional(),
29+
fbtrace_id: z.string().optional(),
30+
}),
31+
});
32+
33+
export const CloudApiResponseSchemaType = CloudApiSuccessResponseSchemaType.or(
34+
CloudApiErrorResponseSchemaType,
35+
);
36+
37+
export const WapiMessageErrorResponseSchemaType = z.object({
38+
title: z.string(),
39+
description: z.string(),
40+
errorCode: z.number(),
41+
errorSubCode: z.string().optional(),
42+
});
43+
44+
export const WapiMessageSuccessResponseSchemaType = z.object({
45+
messageId: z.string(),
46+
receiverPhoneNumber: z.string(),
47+
senderPhoneNumber: z.string(),
48+
status: z.nativeEnum(MessageStatusEnum),
49+
});
50+
51+
export const WapiMessageResponseSchemaType = z
52+
.object({
53+
status: z.literal("success"),
54+
data: WapiMessageSuccessResponseSchemaType,
55+
})
56+
.or(
57+
z.object({
58+
status: z.literal("error"),
59+
error: WapiMessageErrorResponseSchemaType,
60+
}),
61+
);

0 commit comments

Comments
 (0)