Migrated from Redmine #1317 | Author: Ilya Radu
Status: New | Priority: Immediate, there is BUG! | Created: 2024-11-27
Hello earlier when sending a booking request I got the error “Client email value is wrong” I thought it was related to adding a client and so I decided to do it differently, specifically not to add a new client but to get through the api a list of clients that already exist and by their id to make a booking request, but for some reason I still get the error “Client email value is wrong” even when I take an existing client.
this my backend file:
// server.js
const express = require("express");
const axios = require("axios");
const dotenv = require("dotenv");
const cors = require("cors");
const bodyParser = require("body-parser");
// Load environment variables from .env file
dotenv.config();
const app = express();
// Middleware to capture raw request body
const rawBodySaver = (req, res, buf, encoding) => {
if (buf && buf.length) {
req.rawBody = buf.toString(encoding || "utf8");
}
};
// Configure express.json with rawBodySaver
app.use(express.json({ verify: rawBodySaver }));
app.use(cors());
// Middleware to capture response body
const captureResponseBody = (req, res, next) => {
let oldWrite = res.write;
let oldEnd = res.end;
const chunks = [];
res.write = function (chunk, ...args) {
chunks.push(Buffer.from(chunk));
oldWrite.apply(res, [chunk, ...args]);
};
res.end = function (chunk, ...args) {
if (chunk) {
chunks.push(Buffer.from(chunk));
}
const body = Buffer.concat(chunks).toString("utf8");
res.locals.body = body;
oldEnd.apply(res, [chunk, ...args]);
};
next();
};
app.use(captureResponseBody);
// Middleware for logging incoming HTTP requests
app.use((req, res, next) => {
console.log("=== Incoming HTTP Request ===");
console.log(`${req.method} ${req.originalUrl}`);
console.log("Headers:", req.headers);
console.log("Body:", req.rawBody);
// Override res.send method to log the response
const originalSend = res.send;
res.send = function (body) {
console.log("=== Outgoing HTTP Response ===");
console.log(`Status: ${res.statusCode}`);
console.log("Headers:", res.getHeaders());
console.log("Body:", body);
originalSend.apply(res, arguments);
};
next();
});
// Load environment variables
const API_KEY = process.env.SIMPLYBOOK_API_KEY;
const COMPANY_LOGIN = process.env.COMPANY_LOGIN;
const LOGIN_URL = "https://user-api.simplybook.me/login";
const API_URL = "https://user-api.simplybook.me";
// Function to get authorization token
const getAuthToken = async () => {
try {
const response = await axios.post(
LOGIN_URL,
{
method: "getToken",
params: {
companyLogin: COMPANY_LOGIN,
apiKey: API_KEY,
},
id: 1,
},
{
headers: {
"Content-Type": "application/json",
},
},
);
console.log("API response when obtaining token:", response.data);
if (response.data && response.data.result) {
const token = response.data.result;
console.log("Authorization token obtained:", token);
return token;
} else {
throw new Error("Failed to obtain authorization token from API response");
}
} catch (error) {
console.error(
"Error obtaining authorization token:",
error.response ? error.response.data : error.message,
);
throw error;
}
};
const getUserAuthToken = async () => {
try {
const response = await axios.post(
LOGIN_URL,
{
method: "getUserToken",
params: {
companyLogin: COMPANY_LOGIN,
userLogin: "ilya@1-tn.com",
userPassword: "pk7m7ohocq46fs",
},
id: 1,
},
{
headers: {
"Content-Type": "application/json",
},
},
);
if (response.data && response.data.result) {
const userToken = response.data.result;
console.log("User token obtained:", userToken);
return userToken;
} else {
throw new Error("Failed to obtain user token from API response");
}
} catch (error) {
console.error(
"Error obtaining user token:",
error.response ? error.response.data : error.message,
);
throw error;
}
};
// Route to get the list of services
app.get("/api/services", async (req, res) => {
try {
const token = await getAuthToken();
const response = await axios.post(
API_URL,
{
method: "getEventList",
params: {},
id: 1,
},
{
headers: {
"Content-Type": "application/json",
"X-Company-Login": COMPANY_LOGIN,
"X-Token": token,
},
},
);
if (response.data.error) {
console.error("Error fetching services list:", response.data.error);
return res.status(500).json({ message: "Error fetching services" });
}
res.status(200).json(response.data.result);
} catch (error) {
console.error("Error fetching services:", error.message);
res.status(500).json({ message: "Error fetching services" });
}
});
// Route to get the list of performers
app.get("/api/performers", async (req, res) => {
try {
const token = await getAuthToken();
const response = await axios.post(
API_URL,
{
method: "getUnitList",
params: {},
id: 1,
},
{
headers: {
"Content-Type": "application/json",
"X-Company-Login": COMPANY_LOGIN,
"X-Token": token,
},
},
);
if (response.data.error) {
console.error("Error fetching performers list:", response.data.error);
return res.status(500).json({ message: "Error fetching performers" });
}
res.status(200).json(response.data.result);
} catch (error) {
console.error("Error fetching performers:", error.message);
res.status(500).json({ message: "Error fetching performers" });
}
});
// Route to get the nearest available date
app.get("/api/available-dates", async (req, res) => {
const { performerId } = req.query; // Get performerId from the request
try {
const token = await getAuthToken();
const response = await axios.post(
API_URL,
{
method: "getFirstWorkingDay",
params: [performerId || null], // If performerId is not specified, pass null
id: 1,
},
{
headers: {
"Content-Type": "application/json",
"X-Company-Login": COMPANY_LOGIN,
"X-Token": token,
},
},
);
if (response.data.error) {
console.error("Error fetching available dates:", response.data.error);
return res.status(500).json({ message: "Error fetching available dates" });
}
res.status(200).json({ firstAvailableDate: response.data.result });
} catch (error) {
console.error("Error fetching available dates:", error.message);
res.status(500).json({ message: "Error fetching available dates" });
}
});
// Route to get available time slots for a selected date
app.get("/api/available-times", async (req, res) => {
const { date, serviceId, performerId } = req.query;
if (!date || !serviceId) {
return res.status(400).json({ message: "Parameters date and serviceId are required" });
}
try {
const token = await getAuthToken();
const response = await axios.post(
API_URL,
{
method: "getStartTimeMatrix",
params: [date, date, serviceId, performerId || null, 1],
id: 1,
},
{
headers: {
"Content-Type": "application/json",
"X-Company-Login": COMPANY_LOGIN,
"X-Token": token,
},
},
);
if (response.data.error) {
console.error("Error fetching available times:", response.data.error);
return res.status(500).json({ message: "Error fetching available times" });
}
const times = response.data.result[date] || [];
res.status(200).json({ availableTimes: times });
} catch (error) {
console.error("Error fetching available times:", error.message);
res.status(500).json({ message: "Error fetching available times" });
}
});
// Function to add a client
const addClient = async (client, clientData) => {
const response = await client.post("/", {
method: "addClient",
params: {
name: clientData.name,
email: clientData.email,
phone: clientData.phone,
address1: clientData.address1 || "",
address2: clientData.address2 || "",
city: clientData.city || "",
zip: clientData.zip || "",
country_id: clientData.country_id || null,
},
id: 1,
});
if (response.data.error) {
throw new Error(response.data.error.message);
}
return response.data.result;
};
// Update getClients function to return the list of clients
const getClients = async () => {
try {
const token = await getAuthToken();
const response = await axios.post(
API_URL,
{
method: "getClientList",
params: {},
id: 1,
},
{
headers: {
"Content-Type": "application/json",
"X-Company-Login": COMPANY_LOGIN,
"X-Token": token,
},
},
);
if (response.data.error) {
console.error("Error fetching clients list:", response.data.error);
return null; // Return null in case of error
}
const clients = response.data.result;
console.log("Clients list:", clients);
return clients; // Return the list of clients
} catch (error) {
console.error("Error fetching clients list:", error.message);
return null;
}
};
// Modify route for test client addition
app.post("/api/test-add-client", async (req, res) => {
console.log("=== Test Client Addition ===");
// Fixed client data for testing
const fixedClientData = {
name: "Test Client",
email: "radui2004@gmail.com",
phone: "+71234567890", // Changed: added '+' and country code
address1: "Example Street, 1",
address2: "Apt. 101",
city: "Moscow",
zip: "123456",
country_id: 1,
};
console.log("Client data used:", fixedClientData);
try {
// Obtain authorization token
const token = await getAuthToken();
console.log("Authorization token obtained:", token);
// Create client for JSON-RPC requests
const client = axios.create({
baseURL: API_URL,
headers: {
"Content-Type": "application/json",
"X-Company-Login": COMPANY_LOGIN,
"X-Token": token,
},
});
// Fetch and log existing clients
console.log("Fetching existing clients...");
const existingClients = await getClients(); // Fixed: removed parameter
console.log("Existing clients:", existingClients);
// Log the data being sent
const addClientPayload = {
method: "addClient",
params: {
name: fixedClientData.name,
email: fixedClientData.email,
phone: fixedClientData.phone,
address1: fixedClientData.address1,
address2: fixedClientData.address2,
city: fixedClientData.city,
zip: fixedClientData.zip,
country_id: fixedClientData.country_id,
},
id: 1,
};
console.log("Data sent to addClient:", JSON.stringify(addClientPayload, null, 2));
// Send request to add client
const clientResult = await addClient(client, fixedClientData);
console.log("Client successfully added:", clientResult);
res.status(200).json({
message: "Test client addition successful",
data: clientResult,
});
} catch (error) {
console.error(
"Error during test client addition:",
error.response ? error.response.data : error.message,
);
res.status(500).json({
message: "Error during test client addition",
error: error.message,
});
}
});
// Route to add a client with fixed data
app.post("/api/add-client", async (req, res) => {
try {
// Obtain user token
const userToken = await getUserAuthToken();
// Client data
const clientData = {
name: "Vasco",
email: "radui2004@gmail.com",
phone: "+351938901782",
address1: "Example Street, 1",
address2: "Apt. 101",
city: "Moscow",
zip: "123456",
country_id: 1, // Country ID (e.g., 1 for Russia)
};
// Request to add client
const response = await axios.post(
`${API_URL}/admin/`,
{
method: "addClient",
params: clientData,
id: 1,
},
{
headers: {
"Content-Type": "application/json",
"X-Company-Login": COMPANY_LOGIN,
"X-User-Token": userToken,
},
},
);
if (response.data.error) {
console.error("Error adding client:", response.data.error);
return res.status(500).json({ message: "Error adding client" });
}
console.log("Client successfully added:", response.data.result);
res.status(200).json({
message: "Client successfully added",
clientId: response.data.result,
});
} catch (error) {
console.error("Error adding client:", error.message);
res.status(500).json({ message: "Error adding client" });
}
});
app.get("/api/clients", async (req, res) => {
try {
const userToken = await getUserAuthToken();
const response = await axios.post(
`${API_URL}/admin/`,
{
method: "getClientList",
params: {},
id: 1,
},
{
headers: {
"Content-Type": "application/json",
"X-Company-Login": COMPANY_LOGIN,
"X-User-Token": userToken,
},
},
);
if (response.data.error) {
console.error("Error fetching clients list:", response.data.error);
return res.status(500).json({ message: "Error fetching clients list" });
}
console.log("Clients list:", response.data.result);
res.status(200).json(response.data.result);
} catch (error) {
console.error("Error fetching clients list:", error.message);
res.status(500).json({ message: "Error fetching clients list" });
}
});
app.post("/api/book-with-client", async (req, res) => {
const { clientId, serviceId, performerId, date, time } = req.body;
if (!clientId || !serviceId || !date || !time) {
return res
.status(400)
.json({ message: "Parameters clientId, serviceId, date, and time are required" });
}
try {
const token = await getAuthToken();
const bookingData = {
method: "book",
params: [serviceId, performerId || null, `${date} ${time}`, clientId, {}, 1],
id: 1,
};
const response = await axios.post(API_URL, bookingData, {
headers: {
"Content-Type": "application/json",
"X-Company-Login": COMPANY_LOGIN,
"X-Token": token,
},
});
if (response.data.error) {
console.error("Error booking:", response.data.error);
return res.status(500).json({ message: "Error booking" });
}
res.status(200).json({
message: "Booking successfully completed",
data: response.data.result,
});
} catch (error) {
console.error("Error booking:", error.message);
res.status(500).json({ message: "Error booking" });
}
});
// Start the server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
this is my frontend file:
import React, { useState, useEffect } from "react";
import axios from "axios";
import {
Container,
TextField,
Select,
MenuItem,
FormControl,
InputLabel,
Checkbox,
FormControlLabel,
FormGroup,
Button,
Typography,
Grid,
Box,
Alert,
} from "@mui/material";
const BookingForm = () => {
const [services, setServices] = useState({});
const [performers, setPerformers] = useState({});
const [selectedService, setSelectedService] = useState("");
const [selectedPerformer, setSelectedPerformer] = useState("");
const [selectedDate, setSelectedDate] = useState("");
const [selectedTime, setSelectedTime] = useState("");
const [availableTimes, setAvailableTimes] = useState([]);
const [additionalFields, setAdditionalFields] = useState({});
const [clients, setClients] = useState([]);
const [selectedClientId, setSelectedClientId] = useState("");
const [totalPrice, setTotalPrice] = useState(0);
const [message, setMessage] = useState("");
useEffect(() => {
// Fetch the list of services
const fetchServices = async () => {
try {
const response = await axios.get("http://localhost:5000/api/services");
console.log("Services fetched:", response.data);
setServices(response.data);
} catch (error) {
console.error("Error fetching services:", error);
}
};
fetchServices();
}, []);
useEffect(() => {
// Fetch the list of performers
const fetchPerformers = async () => {
try {
const response = await axios.get("http://localhost:5000/api/performers");
console.log("Performers fetched:", response.data);
setPerformers(response.data);
} catch (error) {
console.error("Error fetching performers:", error);
}
};
fetchPerformers();
}, []);
useEffect(() => {
// Fetch the list of clients
const fetchClients = async () => {
try {
const response = await axios.get("http://localhost:5000/api/clients");
console.log("Clients fetched:", response.data);
setClients(response.data);
if (response.data.length > 0) {
setSelectedClientId(response.data[0].id);
}
} catch (error) {
console.error("Error fetching clients:", error);
}
};
fetchClients();
}, []);
useEffect(() => {
// Update available dates when a performer is selected
if (selectedPerformer) {
const fetchAvailableDate = async () => {
try {
const response = await axios.get("http://localhost:5000/api/available-dates", {
params: {
performerId: selectedPerformer,
},
});
console.log("First available date:", response.data.firstAvailableDate);
setSelectedDate(response.data.firstAvailableDate);
} catch (error) {
console.error("Error fetching available dates:", error);
}
};
fetchAvailableDate();
}
}, [selectedPerformer]);
useEffect(() => {
// Fetch available times for the selected date
if (selectedDate && selectedService) {
const fetchAvailableTimes = async () => {
try {
const response = await axios.get("http://localhost:5000/api/available-times", {
params: {
date: selectedDate,
serviceId: selectedService,
performerId: selectedPerformer,
},
});
console.log("Available times:", response.data.availableTimes);
setAvailableTimes(response.data.availableTimes);
} catch (error) {
console.error("Error fetching available times:", error);
}
};
fetchAvailableTimes();
}
}, [selectedDate, selectedService, selectedPerformer]);
useEffect(() => {
// Calculate the total price
let basePrice = services[selectedService]?.price || 0;
setTotalPrice(basePrice);
}, [selectedService, services]);
const handleSubmit = async (e) => {
e.preventDefault();
console.log("Submitting booking form with data:", {
clientId: selectedClientId,
serviceId: selectedService,
performerId: selectedPerformer,
date: selectedDate,
time: selectedTime,
additionalFields,
});
const bookingPayload = {
clientId: selectedClientId,
serviceId: selectedService,
performerId: selectedPerformer,
date: selectedDate,
time: selectedTime,
additionalFields: additionalFields,
};
try {
const response = await axios.post(
"http://localhost:5000/api/book-with-client",
bookingPayload,
);
console.log("Booking successful:", response.data);
setMessage(response.data.message);
} catch (error) {
console.error("Error during booking:", error.response?.data || error.message);
setMessage(error.response?.data?.message || "An error occurred during booking.");
}
};
const handleServiceChange = (e) => {
setSelectedService(e.target.value);
setSelectedDate("");
setSelectedTime("");
setAvailableTimes([]);
};
const handlePerformerChange = (e) => {
setSelectedPerformer(e.target.value);
setSelectedDate("");
setSelectedTime("");
setAvailableTimes([]);
};
return (
Booking Form
{message && (
{message}
)}
{/* Client Selection */}
{/* If you need to select a client from the list, you can add a dropdown */}
{/*
Select Client
*/}
{/* Service Selection */}
Select Service
{/* Performer Selection */}
Select Performer
{/* Date Selection */}
{
setSelectedDate(e.target.value);
setSelectedTime("");
}}
required
/>
{/* Time Selection */}
Select Time
{/* Display Total Price */}
Total Price: {totalPrice} EUR
{/* Submit Button */}
Book
);
};
export default BookingForm;
The http request and response I got:
=== Incoming HTTP Request ===
POST /api/book-with-client
Headers: {
host: ‘localhost:5000’,
connection: ‘keep-alive’,
‘content-length’: ‘110’,
‘sec-ch-ua’: ‘“Chromium”;v=“128”, “Not;A=Brand”;v=“24”, “Opera GX”;v=“114”’,
accept: ‘application/json, text/plain, /’,
‘sec-ch-ua-platform’: ‘“Windows”’,
‘sec-ch-ua-mobile’: ‘?0’,
‘user-agent’: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 OPR/114.0.0.0’,
‘content-type’: ‘application/json’,
origin: ‘http://localhost:3000’,
‘sec-fetch-site’: ‘same-site’,
‘sec-fetch-mode’: ‘cors’,
‘sec-fetch-dest’: ‘empty’,
referer: ‘http://localhost:3000/’,
‘accept-encoding’: ‘gzip, deflate, br, zstd’,
‘accept-language’: ‘ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7’
}
Body: {“clientId”:“2”,“serviceId”:“7”,“performerId”:“8”,“date”:“2024-11-28”,“time”:“13:20:00”,“additionalFields”:{}}
API response when obtaining token: {
result: ‘5ee6a030f6c852ab2b9a6053c9994efa624aa4d8cba722664c56516b7ccc0748’,
id: ‘1’
}
Authorization token obtained: 5ee6a030f6c852ab2b9a6053c9994efa624aa4d8cba722664c56516b7ccc0748
Error booking: {
code: -32061,
message: ‘Client name value is wrong’,
data: { field: ‘name’ }
}
=== Outgoing HTTP Response ===
Status: 500
Headers: [Object: null prototype] {
‘x-powered-by’: ‘Express’,
‘access-control-allow-origin’: ‘*’,
‘content-type’: ‘application/json; charset=utf-8’
}
Body: {“message”:“Error booking”}