Skip to content

Commit

Permalink
refactor(web): use axios instead of fetch (#1530)
Browse files Browse the repository at this point in the history
* Use [axios](https://axios-http.com/) instead of `fetch` as it
implements a promise-based API which is better for our use case.
* Move the logic related to interacting with the API to an `API` module.
* Handle the `ServiceStatusChanged` signal properly.

## To do

- [x] software
- [x] l10n
- [x] users
- [x] issues
- [x] progress
- [x] status
  • Loading branch information
imobachgs authored Aug 7, 2024
2 parents 1b2c08a + d3c9540 commit 15c0d56
Show file tree
Hide file tree
Showing 21 changed files with 556 additions and 183 deletions.
27 changes: 19 additions & 8 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
"@patternfly/react-core": "^5.1.1",
"@patternfly/react-table": "^5.1.1",
"@tanstack/react-query": "^5.49.2",
"axios": "^1.7.3",
"fast-sort": "^3.4.0",
"ipaddr.js": "^2.1.0",
"react": "^18.2.0",
Expand Down
68 changes: 68 additions & 0 deletions web/src/api/http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (c) [2024] SUSE LLC
*
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, contact SUSE LLC.
*
* To contact SUSE LLC about this file by physical or electronic mail, you may
* find current contact information at www.suse.com.
*/

import axios from "axios";

const http = axios.create({
responseType: "json",
});

/**
* Retrieves the object from given URL
*
* @param url - HTTP URL
* @return data from the response body
*/
const get = (url: string) => http.get(url).then(({ data }) => data);

/**
* Performs a PATCH request with the given URL and data
*
* @param url - endpoint URL
* @param data - Request payload
*/
const patch = (url: string, data: object) => http.patch(url, data);

/**
* Performs a PUT request with the given URL and data
*
* @param url - endpoint URL
* @param data - request payload
*/
const put = (url: string, data: object) => http.put(url, data);

/**
* Performs a POST request with the given URL and data
*
* @param url - endpoint URL
* @param data - request payload
*/
const post = (url: string, data: object) => http.post(url, data);

/**
* Performs a DELETE request on the given URL
*
* @param url - endpoint URL
* @param data - request payload
*/
const del = (url: string) => http.delete(url);

export { get, patch, post, put, del };
37 changes: 37 additions & 0 deletions web/src/api/issues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) [2024] SUSE LLC
*
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, contact SUSE LLC.
*
* To contact SUSE LLC about this file by physical or electronic mail, you may
* find current contact information at www.suse.com.
*/

import { get } from "~/api/http";
import { Issue, IssuesScope } from "~/types/issues";

const URLS = {
product: "software/issues/product",
software: "software/issues/software",
users: "users/issues",
storage: "storage/issues",
};

/**
* Return the issues of the given scope.
*/
const fetchIssues = (scope: IssuesScope): Promise<Issue[]> => get(`/api/${URLS[scope]}`);

export { fetchIssues };
70 changes: 70 additions & 0 deletions web/src/api/l10n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) [2024] SUSE LLC
*
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, contact SUSE LLC.
*
* To contact SUSE LLC about this file by physical or electronic mail, you may
* find current contact information at www.suse.com.
*/

import { get, patch } from "~/api/http";
import { Keymap, Locale, LocaleConfig, Timezone } from "~/types/l10n";
import { timezoneUTCOffset } from "~/utils";

/**
* Returns the l10n configuration
*/
const fetchConfig = (): Promise<LocaleConfig> => get("/api/l10n/config");

/**
* Returns the list of known locales for installation
*/
const fetchLocales = async (): Promise<Locale[]> => {
const json = await get("/api/l10n/locales");
return json.map(({ id, language, territory }): Locale => {
return { id, name: language, territory };
});
};

/**
* Returns the list of known timezones
*/
const fetchTimezones = async (): Promise<Timezone[]> => {
const json = await get("/api/l10n/timezones");
return json.map(({ code, parts, country }): Timezone => {
const offset = timezoneUTCOffset(code);
return { id: code, parts, country, utcOffset: offset };
});
};

/**
* Returns the list of known keymaps
*/
const fetchKeymaps = async (): Promise<Keymap[]> => {
const json = await get("/api/l10n/keymaps");
const keymaps: Keymap[] = json.map(({ id, description }): Keymap => {
return { id, name: description };
});
return keymaps.sort((a, b) => (a.name < b.name ? -1 : 1));
};

/**
* Updates the l10n configuration for the system to install
*
* @param config - Localization configuration
*/
const updateConfig = (config: LocaleConfig) => patch("/api/l10n/config", config);

export { fetchConfig, fetchKeymaps, fetchLocales, fetchTimezones, updateConfig };
38 changes: 38 additions & 0 deletions web/src/api/progress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) [2024] SUSE LLC
*
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, contact SUSE LLC.
*
* To contact SUSE LLC about this file by physical or electronic mail, you may
* find current contact information at www.suse.com.
*/

import { get } from "~/api/http";
import { APIProgress, Progress } from "~/types/progress";

/**
* Returns the progress information for a given service
*
* At this point, the services that implement the progress API are
* "manager", "software" and "storage".
*
* @param service - Service to retrieve the progress from (e.g., "manager")
*/
const fetchProgress = async (service: string): Promise<Progress> => {
const progress: APIProgress = await get(`/api/${service}/progress`);
return Progress.fromApi(progress);
};

export { fetchProgress };
74 changes: 74 additions & 0 deletions web/src/api/questions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) [2024] SUSE LLC
*
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, contact SUSE LLC.
*
* To contact SUSE LLC about this file by physical or electronic mail, you may
* find current contact information at www.suse.com.
*/

import { get, put } from "~/api/http";
import { Answer, Question, QuestionType } from "~/types/questions";

type APIQuestion = {
generic?: Question;
withPassword?: Pick<Question, "password">;
};

/**
* Internal method to build proper question objects
*
* TODO: improve/simplify it once the backend API is improved.
*/
function buildQuestion(httpQuestion: APIQuestion) {
const question: Question = { ...httpQuestion.generic };

if (httpQuestion.generic) {
question.type = QuestionType.generic;
question.answer = httpQuestion.generic.answer;
}

if (httpQuestion.withPassword) {
question.type = QuestionType.withPassword;
question.password = httpQuestion.withPassword.password;
}

return question;
}

/**
* Returns the list of questions
*/
const fetchQuestions = async (): Promise<Question[]> => {
const apiQuestions: APIQuestion[] = await get("/api/questions");
return apiQuestions.map(buildQuestion);
};

/**
* Update a questions' answer
*
* The answer is part of the Question object.
*/
const updateAnswer = async (question: Question): Promise<void> => {
const answer: Answer = { generic: { answer: question.answer } };

if (question.type === QuestionType.withPassword) {
answer.withPassword = { password: question.password };
}

await put(`/api/questions/${question.id}/answer`, answer);
};

export { fetchQuestions, updateAnswer };
Loading

0 comments on commit 15c0d56

Please sign in to comment.