Skip to content

Commit 708cb42

Browse files
authored
Merge pull request #29 from tourepedia/test
Sembark API Integration
2 parents 60b4441 + 8f2b3b8 commit 708cb42

File tree

11 files changed

+52147
-5847
lines changed

11 files changed

+52147
-5847
lines changed

.env renamed to .build.env.dev

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ BASE_PATH=
33
BOOKINGS_PHONE_NUMBER=+917062386860
44
BOOKINGS_EMAIL=bookings@tourepedia.com
55
SUPPORT_EMAIL=support@tourepedia.com
6-
API_URL=https://test.app.sembark.com/api
76
GOOGLE_SITE_VERIFICATION=random
87
GOOGLE_ANALYTICS_TRACKING_ID=invalid
8+
GOOGLE_RECAPTCHA_SITE_KEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI

.build.env.test

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
PUBLIC_URL=https://test.tourepedia.com
2+
BASE_PATH=
3+
BOOKINGS_PHONE_NUMBER=+917062386860
4+
BOOKINGS_EMAIL=bookings@tourepedia.com
5+
SUPPORT_EMAIL=support@tourepedia.com
6+
GOOGLE_SITE_VERIFICATION=random
7+
GOOGLE_ANALYTICS_TRACKING_ID=invalid
8+
GOOGLE_RECAPTCHA_SITE_KEY=6LegwVgfAAAAADIaK37oopzn2i-vd653SfxS34O-

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SEMBARK_API_BASE_URL=http://localhost:8000/v1
2+
SEMBARK_API_ACCESS_TOKEN=access_token_here
3+
GOOGLE_RECAPTCHA_SECRET=6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ yarn-error.log
66
# Build directory
77
.DS_Store
88
/build
9+
/.netlify
10+
.env

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ Marketing website for tourepedia: https://tourepedia.com
44

55
## Getting started
66

7-
**Prerequisite** - `node^8.12.0`
7+
**Prerequisite** - `node^14.0.0`
88

99
```bash
1010
git clone git@github.com:tourepedia/www www && cd www
11+
cp .env.example .env #update the environment variables accordingly
1112
npm install
1213
npm run dev
1314
```
@@ -16,6 +17,17 @@ npm run dev
1617

1718
```bash
1819
npm run dev # start the development server
20+
npm run build_test # create the test build
1921
npm run build # create the production build
20-
npm run start # serve the production build locally
22+
npx netilfy dev # to run the netlify functions
2123
```
24+
25+
## Contributions
26+
27+
### Sembark API Keys
28+
29+
Get the api keys from Sembark Dashboard and update your .env file to interact with Sembark APIs.
30+
31+
### Recaptcha
32+
33+
For testing purpose, we are using [Google Recaptcha Keys for Testing Environment](https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do)
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
import { Handler } from '@netlify/functions'
2+
import fetch, { Response } from 'node-fetch'
3+
4+
const SEMBARK_API_BASE_URL = process.env.SEMBARK_API_BASE_URL
5+
const SEMBARK_API_ACCESS_TOKEN = process.env.SEMBARK_API_ACCESS_TOKEN
6+
const GOOGLE_RECAPTCHA_SECRET = process.env.GOOGLE_RECAPTCHA_SECRET
7+
8+
export const handler: Handler = async (event, context) => {
9+
if (event.httpMethod.toLowerCase() !== 'post') {
10+
return {
11+
statusCode: 405,
12+
body: JSON.stringify({
13+
message: 'Method not allowed',
14+
}),
15+
headers: {
16+
allow: 'post',
17+
},
18+
}
19+
}
20+
if (
21+
!SEMBARK_API_BASE_URL ||
22+
!SEMBARK_API_ACCESS_TOKEN ||
23+
!GOOGLE_RECAPTCHA_SECRET
24+
) {
25+
const errors: Record<string, Array<string>> = {}
26+
if (!SEMBARK_API_BASE_URL) {
27+
errors['SEMBARK_API_BASE_URL'] = [
28+
'Missing `SEMBARK_API_BASE_URL environment variable',
29+
]
30+
}
31+
if (!SEMBARK_API_ACCESS_TOKEN) {
32+
errors['SEMBARK_API_ACCESS_TOKEN'] = [
33+
'Missing `SEMBARK_API_ACCESS_TOKEN` environment variable',
34+
]
35+
}
36+
if (!GOOGLE_RECAPTCHA_SECRET) {
37+
errors['GOOGLE_RECAPTCHA_SECRET'] = [
38+
'Missing `GOOGLE_RECAPTCHA_SECRET` environment variable',
39+
]
40+
}
41+
return {
42+
statusCode: 500,
43+
body: JSON.stringify({
44+
message: 'Missing environment variables',
45+
errors: errors,
46+
}),
47+
}
48+
}
49+
let body: Record<string, string | number> = {}
50+
try {
51+
body = JSON.parse(event.body)
52+
} catch (e) {
53+
return {
54+
statusCode: 422,
55+
body: JSON.stringify({
56+
message: 'Malformed request body',
57+
}),
58+
}
59+
}
60+
const {
61+
name,
62+
phone_number,
63+
email,
64+
destination,
65+
start_date,
66+
no_of_days,
67+
no_of_adults,
68+
no_of_children,
69+
hotel_preference,
70+
comments,
71+
trip_source,
72+
grecaptcha_token,
73+
} = body
74+
75+
let failedCaptchaResponse = await ensureNoRobots(grecaptcha_token)
76+
// check for non-zero status code
77+
if (failedCaptchaResponse !== 0) {
78+
// failed
79+
return failedCaptchaResponse
80+
}
81+
82+
const data = {
83+
name,
84+
phone_number,
85+
email,
86+
destination,
87+
start_date,
88+
no_of_days,
89+
no_of_adults,
90+
no_of_children,
91+
hotel_preference,
92+
comments,
93+
trip_source: trip_source || 'Website',
94+
}
95+
96+
let json: Record<string, unknown> = {}
97+
let response: Response
98+
99+
try {
100+
response = await fetch(`${SEMBARK_API_BASE_URL}/trip-plan-requests`, {
101+
method: 'POST',
102+
body: JSON.stringify(data),
103+
headers: {
104+
'content-type': 'application/json',
105+
accept: 'application/json',
106+
authorization: `Bearer ${SEMBARK_API_ACCESS_TOKEN}`,
107+
},
108+
})
109+
const status_code = response.status
110+
json = (await response.json()) as typeof json
111+
if (status_code !== 201) {
112+
throw json
113+
}
114+
} catch (err) {
115+
if (err.code === 'ECONNREFUSED') {
116+
return {
117+
statusCode: 503,
118+
body: JSON.stringify({
119+
message:
120+
'Unable to connect to our servers. Please try after sometime',
121+
}),
122+
}
123+
}
124+
return {
125+
statusCode: err.status_code || err.statusCode || 500,
126+
body: JSON.stringify(err),
127+
}
128+
}
129+
130+
return {
131+
statusCode: response.status,
132+
body: JSON.stringify(json),
133+
}
134+
}
135+
136+
async function ensureNoRobots(grecaptcha_token: unknown) {
137+
if (!grecaptcha_token) {
138+
return {
139+
statusCode: 422,
140+
body: JSON.stringify({
141+
message:
142+
'Missing captcha verification. Please refresh the page and try again.',
143+
}),
144+
}
145+
}
146+
147+
let json: Record<string, unknown> = {}
148+
let response: Response
149+
150+
try {
151+
response = await fetch(
152+
`https://www.google.com/recaptcha/api/siteverify?secret=${GOOGLE_RECAPTCHA_SECRET}&response=${grecaptcha_token}`,
153+
{
154+
method: 'POST',
155+
headers: {
156+
accept: 'application/json',
157+
},
158+
},
159+
)
160+
json = (await response.json()) as typeof json
161+
if (json && !json.success) {
162+
throw {
163+
status_code: 422,
164+
message: 'Captcha verification failed',
165+
errors: {
166+
grecaptcha_token: getCaptchaDescriptiveError(json['error-codes']),
167+
},
168+
}
169+
}
170+
} catch (err) {
171+
if (err.code === 'ECONNREFUSED') {
172+
return {
173+
statusCode: 503,
174+
body: JSON.stringify({
175+
message:
176+
'Unable to connect to our captcha verification service. Please try after sometime',
177+
}),
178+
}
179+
}
180+
return {
181+
statusCode: err.status_code || err.statusCode || 422,
182+
body: JSON.stringify(err),
183+
}
184+
}
185+
// all is well here
186+
return 0 as const
187+
}
188+
189+
function getCaptchaDescriptiveError(codes?: unknown) {
190+
if (codes && Array.isArray(codes)) {
191+
return codes
192+
.map((code: unknown) => {
193+
if (typeof code === 'string') {
194+
return CAPTCHA_ERROR_CODES[code]
195+
}
196+
})
197+
.filter((x): x is string => Boolean(x))
198+
}
199+
return []
200+
}
201+
202+
const CAPTCHA_ERROR_CODES = {
203+
'missing-input-secret': 'The secret parameter is missing.',
204+
'invalid-input-secret': 'The secret parameter is invalid or malformed.',
205+
'missing-input-response': 'The response parameter is missing',
206+
'invalid-input-response': 'The response parameter is invalid or malformed',
207+
'bad-request': 'The request is invalid or malformed',
208+
'timeout-or-duplicate':
209+
'The response is no longer valid: either is too old or has been used previously',
210+
}

netlify.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[functions]
2+
directory = "apis"
3+
4+
[build]
5+
command = "npm run build"
6+
publish = "build"
7+
8+
[context.test]
9+
command = "npm run build_test"
10+

0 commit comments

Comments
 (0)