import * as CONFIG from '../config';
import {
SESSION,
BUDGET,
EXPENSES,
INCOMES,
Transaction
} from './Session';
/** URL of the server which delivers the API. */
//const SERVER_URL = "http://group10.web-tek.ninja:8090"; // For deployment
const SERVER_URL = "http://localhost:8090"; // For localhost
/**
* __HTTP Interface__
*
* Acts as an interface which provides all communications
* which are forwarded to the server. Rather than having
* different components be responsible for their own requests
* through the API, the interface is designed with the intention
* to acts as an intermediary between the client and the server.
*
* @since 17.04.2023
* @version 17.04.2023
* @author Group 19
*/
export const HttpInterface = {
/**
* Fetches the budget instance which belongs
* to a given user.
*
* @param {Number} bid The ID of the Budget.
*/
fetchBudget: async function (bid) {
await fetch(SERVER_URL + "/api/budgets/" + bid,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + SESSION.getJwt()
}
})
.then(response => {
if (response.ok) return response.text();
else if (CONFIG.SHOW_BUDGET_FAILURE_ALERT) window.alert("Incomes request FAILED.");
})
.then(function (data) {
const responseBody = JSON.parse(data);
BUDGET.setBudgetId(bid);
BUDGET.setStartDate(responseBody.startDate);
BUDGET.setEndDate(responseBody.endDate);
BUDGET.setBoundary(responseBody.boundary);
if (CONFIG.SHOW_AUTHENTICATION_BUDGET_ALERT) {
window.alert(
"Budget ID: " + BUDGET.getBudgetId() +
"\nStart: " + BUDGET.getStartDate() +
"\nEnd: " + BUDGET.getEndDate() +
"\nBoundary " + BUDGET.getBoundary()
);
}
})
.catch(err => console.error(err));
this.fetchAllExpenses();
this.fetchAllIncomes();
},
/**
* Fetches all transactions labeled as an expense to the given budget.
* The /GET request is forwarded to {@link SERVER_URL}, which is where
* the RESTful services are implemented in the back-end.
*/
fetchAllExpenses: function () {
fetch(SERVER_URL + "/transactions/expensesById/" + BUDGET.getBudgetId(),
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + SESSION.getJwt()
}
})
.then(response => {
if (response.ok) return response.text();
else if (CONFIG.SHOW_TRANSACTION_FAILURE_ALERT) window.alert("Expenses request FAILED.");
})
.then(function (data) {
const body = JSON.parse(data);
for (let element of body) {
let ex = new Transaction(
element.tid,
element.tname,
element.value,
element.description,
element.date,
Number(BUDGET.getBudgetId())
);
EXPENSES.push(ex)
}
if (CONFIG.SHOW_TRANSACTION_FETCHING_ALERT) {
alert("Number of expenses: " + EXPENSES.length);
}
})
.catch(err => console.error(err));
},
/**
* Fetches all expenses associated to the budget in
* ascending order by date. The /GET request is forwarded
* to the /api endpoint, which will return each expense
* along with the href attribute for each transaction.
*/
fetchAllExpenses2: async function () {
const link = SERVER_URL + "/api/transactions/search/getExpensesByBudgetIdOrderByDateAsc?budgetId=";
const result = await fetch(link + BUDGET.getBudgetId(),
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + SESSION.getJwt()
}
})
.then(response => response.json())
.then(data => {return data._embedded.transactions})
.catch(err => console.error(err));
return result;
},
/**
* Fetches all income associated to the budget in
* ascending order by date. The /GET request is forwarded
* to the /api endpoint, which will return each expense
* along with the href attribute for each transaction.
*/
fetchAllIncomes2: async function () {
const link = SERVER_URL + "/api/transactions/search/getIncomesByBudgetIdOrderByDateAsc?budgetId=";
const result = await fetch(link + BUDGET.getBudgetId(),
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + SESSION.getJwt()
}
})
.then(response => response.json())
.then(data => {return data._embedded.transactions})
.catch(err => console.error(err));
return result;
},
/**
* Fetches all transactions labeled as an income to the given budget.
* The /GET request is forwarded to {@link SERVER_URL}, which is where
* the RESTful services are implemented in the back-end.
*/
fetchAllIncomes: function () {
fetch(SERVER_URL + "/transactions/incomesById/" + BUDGET.getBudgetId(),
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + SESSION.getJwt()
}
})
.then(response => {
if (response.ok) return response.text();
else if (CONFIG.SHOW_TRANSACTION_FAILURE_ALERT) window.alert("Incomes request FAILED.");
})
.then(function (data) {
const body = JSON.parse(data);
for (let element of body) {
let ex = new Transaction(
element.tid,
element.tname,
element.value,
element.description,
element.date,
Number(BUDGET.getBudgetId())
);
INCOMES.push(ex)
}
if (CONFIG.SHOW_TRANSACTION_FETCHING_ALERT) {
alert("Number of incomes: " + INCOMES.length);
}
})
.catch(err => console.error(err));
},
/**
* Fetches the transaction dates from the back-end implementation. The response
* is a list which contains string representations of all the dates which a
* transaction, either income or expense, has been made in the given budget in ascending order.
*/
fetchTransactionDates: async function() {
const result = await fetch(SERVER_URL + "/transactions/budgetTransactionDates/" + BUDGET.getBudgetId(),
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + SESSION.getJwt()
}
})
.then(response => response.json())
.then(data => {return data; })
.catch(err => console.error(err));
return result;
},
/**
* Adds a new transaction categorized to the given budget.
* The /POST request is forwarded to the server, which uploads
* the new transaction to the database.
*/
addTransaction: async function (trans) {
const result = await fetch(SERVER_URL + "/transactions",
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + SESSION.getJwt()
},
body: JSON.stringify(trans)
})
.then(response => {
if (!response.ok) {
alert("Something went wrong!");
}
})
.catch(err => window.alert(err));
return result;
},
/**
* Updates an existing transaction to the given budget.
* The /PUT request is forwarded to the server, which updates the
* existing transaction in the database.
*/
updateTransaction: async function (id, newTransaction) {
const result = await fetch(SERVER_URL + "/transactions/" + id,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + SESSION.getJwt()
},
body: JSON.stringify(newTransaction)
})
.then(response => {
if (!response.ok) {
alert("Something went wrong!");
}
})
.catch(err => window.alert(err));
return result;
},
/**
* Deletes an existing transaction to the given budget.
* The /DELETE request is forwarded to the server, which deletes the
* existing transaction in the database.
*/
deleteTransaction: async function (id) {
const result = await fetch(SERVER_URL + "/transactions/" + id,
{
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + SESSION.getJwt()
}
})
.then(response => {
if (!response.ok) {
alert("Something went wrong!");
}
})
.catch(err => window.alert(err));
return result;
},
/**
* Updates the budget which the user has been provided with.
* The /PUT request is forwarded to the server, which updates the
* existing budget in the database.
*/
updateBudget: async function () {
const newBudget = {
startDate: BUDGET.startDate,
endDate: BUDGET.endDate,
boundary: Number(BUDGET.boundary)
}
const result = await fetch(SERVER_URL + "/budgets/" + BUDGET.getBudgetId(),
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + SESSION.getJwt()
},
body: JSON.stringify(newBudget)
})
.then(response => {
if (response.ok) {
window.alert(
"Budget has been updated.\nMargin: "
+ BUDGET.getBoundary() + "\nStart date: "
+ BUDGET.getStartDate() + "\nEnd date: "
+ BUDGET.getEndDate()
);
} else {
alert("Something went wrong!");
}
})
.catch(err => window.alert(err));
return result;
},
/**
* Authenticates a session and validates the login-
* credentials provided by the user. Upon a succesful
* login, the server will respond with a JSON Web Token
* for authentication, and the ID of the budget which
* is assigned to the given user.
*/
authenticateLogin: async function (credentials) {
/** True if the user is authenticated. */
let authenticated = false;
const response = await fetch(SERVER_URL + '/login',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
})
.then(response => {
if (response.ok) {
const auth = response.headers.get("Authorization").split(' ');
const jwt = auth[1].split(',')[0];
const bid = Number(auth[3]);
SESSION.setJwt(jwt);
SESSION.setAuth(true);
this.fetchBudget(bid);
authenticated = true;
} else if (CONFIG.SHOW_AUTHENTICATION_FAILURE_ALERT) {
window.alert("Bad credentials");
console.log(credentials);
}
})
.then(authenticated => {return authenticated;})
.catch(err => console.error(err));
if (CONFIG.SHOW_AUTHENTICATION_FAILURE_ALERT && response !== undefined) {
console.log(response);
}
return authenticated;
},
/**
* Registers a new user with the credentials
* provided by the user. The /POST request is
* forwarded to the server, which assign a new
* Budget instance to the user and upload both
* the user and the budget to the database.
*
* The password provided is encrypted with a
* BCrypt password encoder.
*
* If the username is taken, the server will
* respond with an error which will be returned
* to the user.
*/
signUp: async function (credentials) {
const username = credentials.username;
const password = credentials.password;
const result = await fetch(SERVER_URL + "/users",
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(credentials)
})
.then(async response => {
if (!response.ok) {
alert("Username already exists, please try a different one.");
} else {
const loginInfo = {
username: username,
password: password
};
const login = await HttpInterface.authenticateLogin(loginInfo);
return login;
}
})
.catch(err => window.alert(err));
return result;
},
};