Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use web component attribute versus environment variable to set base URL for API #1034

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/components/Editor/ImageUploadButton/ImageUploadButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { updateImages, setNameError } from "../../../redux/EditorSlice";
import Button from "../../Button/Button";
import NameErrorMessage from "../ErrorMessage/NameErrorMessage";
import store from "../../../app/store";
import { uploadImages } from "../../../utils/apiCallHandler";
import { Api } from "../../../utils/apiCallHandler";

const allowedExtensions = {
python: ["jpg", "jpeg", "png", "gif"],
Expand All @@ -31,6 +31,7 @@ const allowedExtensionsString = (projectType) => {
};

const ImageUploadButton = () => {
const api = new Api();
const [modalIsOpen, setIsOpen] = useState(false);
const [files, setFiles] = useState([]);
const dispatch = useDispatch();
Expand Down Expand Up @@ -75,7 +76,7 @@ const ImageUploadButton = () => {
}
});
if (store.getState().editor.nameError === "") {
const response = await uploadImages(
const response = await api.uploadImages(
projectIdentifier,
user.access_token,
files,
Expand Down
5 changes: 3 additions & 2 deletions src/components/Editor/Runners/PyodideRunner/PyodideRunner.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import { useMediaQuery } from "react-responsive";
import { MOBILE_MEDIA_QUERY } from "../../../../utils/mediaQueryBreakpoints";
import ErrorMessage from "../../ErrorMessage/ErrorMessage";
import { createError } from "../../../../utils/apiCallHandler";
import { Api } from "../../../../utils/apiCallHandler";
import VisualOutputPane from "./VisualOutputPane";
import OutputViewToggle from "../PythonRunner/OutputViewToggle";
import { SettingsContext } from "../../../../utils/settings";
Expand All @@ -25,6 +25,7 @@
() => new Worker("./PyodideWorker.js", { type: "module" }),
[],
);
const api = new Api();

Check warning on line 28 in src/components/Editor/Runners/PyodideRunner/PyodideRunner.jsx

View workflow job for this annotation

GitHub Actions / lint

'api' is assigned a value but never used
const interruptBuffer = useRef();
const stdinBuffer = useRef();
const stdinClosed = useRef();
Expand Down Expand Up @@ -139,7 +140,7 @@
node.scrollTop = node.scrollHeight;
};

const handleError = (file, line, mistake, type, info) => {
const handleError = async (file, line, mistake, type, info) => {
let errorMessage;

if (type === "KeyboardInterrupt") {
Expand All @@ -152,7 +153,7 @@
errorMessage += `:\n${mistake}`;
}

createError(projectIdentifier, userId, { errorType: type, errorMessage });

Check failure on line 156 in src/components/Editor/Runners/PyodideRunner/PyodideRunner.jsx

View workflow job for this annotation

GitHub Actions / lint

'createError' is not defined
}

dispatch(setError(errorMessage));
Expand Down
5 changes: 3 additions & 2 deletions src/components/Editor/Runners/PythonRunner/PythonRunner.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
triggerDraw,
} from "../../../../redux/EditorSlice";
import ErrorMessage from "../../ErrorMessage/ErrorMessage";
import { createError } from "../../../../utils/apiCallHandler";
import { Api } from "../../../../utils/apiCallHandler";
import store from "../../../../app/store";
import VisualOutputPane from "./VisualOutputPane";
import OutputViewToggle from "./OutputViewToggle";
Expand Down Expand Up @@ -55,6 +55,7 @@ const externalLibraries = {
};

const PythonRunner = ({ outputPanels = ["text", "visual"] }) => {
const api = new Api();
const projectCode = useSelector((state) => state.editor.project.components);
const projectIdentifier = useSelector(
(state) => state.editor.project.identifier,
Expand Down Expand Up @@ -301,7 +302,7 @@ const PythonRunner = ({ outputPanels = ["text", "visual"] }) => {
}

errorMessage = `${errorType}: ${errorDescription} on line ${lineNumber} of ${fileName}`;
createError(projectIdentifier, userId, {
api.createError(projectIdentifier, userId, {
errorType,
errorMessage,
});
Expand Down
5 changes: 3 additions & 2 deletions src/components/Modals/NewProjectModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import { closeNewProjectModal } from "../../redux/EditorSlice";
import { useDispatch, useSelector } from "react-redux";
import { useTranslation } from "react-i18next";
import InputModal from "./InputModal";
import { createOrUpdateProject } from "../../utils/apiCallHandler";
import { Api } from "../../utils/apiCallHandler";
import { useNavigate } from "react-router-dom";
import { DEFAULT_PROJECTS } from "../../utils/defaultProjects";
import HTMLIcon from "../../assets/icons/html.svg";
import PythonIcon from "../../assets/icons/python.svg";

const NewProjectModal = () => {
const api = new Api();
const { t, i18n } = useTranslation();
const dispatch = useDispatch();
const user = useSelector((state) => state.auth.user);
Expand All @@ -29,7 +30,7 @@ const NewProjectModal = () => {
const navigate = useNavigate();

const createProject = async () => {
const response = await createOrUpdateProject(
const response = await api.createOrUpdateProject(
{ ...DEFAULT_PROJECTS[projectType], name: projectName },
user.access_token,
);
Expand Down
236 changes: 121 additions & 115 deletions src/utils/apiCallHandler.js
Original file line number Diff line number Diff line change
@@ -1,124 +1,130 @@
import axios from "axios";
import omit from "lodash/omit";

const host = process.env.REACT_APP_API_ENDPOINT;

const get = async (url, headers) => {
return await axios.get(url, headers);
};

const post = async (url, body, headers) => {
return await axios.post(url, body, headers);
};

const put = async (url, body, headers) => {
return await axios.put(url, body, headers);
};

const headers = (accessToken) => {
let headersHash;
if (accessToken) {
headersHash = { Accept: "application/json", Authorization: accessToken };
} else {
headersHash = { Accept: "application/json" };
}
return { headers: headersHash };
};

export const createOrUpdateProject = async (projectWithUserId, accessToken) => {
const project = omit(projectWithUserId, ["user_id"]);
if (!project.identifier) {
export class Api {
api;

constructor() {
this.api = axios.create({ baseURL: process.env.REACT_APP_API_ENDPOINT });
};

Check failure on line 9 in src/utils/apiCallHandler.js

View workflow job for this annotation

GitHub Actions / lint

Delete `;`

async createOrUpdateProject (projectWithUserId, accessToken) {

Check failure on line 11 in src/utils/apiCallHandler.js

View workflow job for this annotation

GitHub Actions / lint

Delete `·`
const project = omit(projectWithUserId, ["user_id"]);
if (!project.identifier) {
return await post(

Check failure on line 14 in src/utils/apiCallHandler.js

View workflow job for this annotation

GitHub Actions / lint

'post' is not defined
`${host}/api/projects`,

Check failure on line 15 in src/utils/apiCallHandler.js

View workflow job for this annotation

GitHub Actions / lint

'host' is not defined
{ project },
headers(accessToken),

Check failure on line 17 in src/utils/apiCallHandler.js

View workflow job for this annotation

GitHub Actions / lint

'headers' is not defined
);
} else {
return await put(

Check failure on line 20 in src/utils/apiCallHandler.js

View workflow job for this annotation

GitHub Actions / lint

'put' is not defined
`${host}/api/projects/${project.identifier}`,

Check failure on line 21 in src/utils/apiCallHandler.js

View workflow job for this annotation

GitHub Actions / lint

'host' is not defined
{ project },
headers(accessToken),

Check failure on line 23 in src/utils/apiCallHandler.js

View workflow job for this annotation

GitHub Actions / lint

'headers' is not defined
);
}
};

Check failure on line 26 in src/utils/apiCallHandler.js

View workflow job for this annotation

GitHub Actions / lint

Delete `;`

async deleteProject(identifier, accessToken) {
return await axios.delete(
`${host}/api/projects/${identifier}`,
headers(accessToken),
);
};

async getImage(url) {
return await get(url, headers());
};

async loadRemix(projectIdentifier, accessToken) {
return await get(
`${host}/api/projects/${projectIdentifier}/remix`,
headers(accessToken),
);
};

async createRemix(project, accessToken) {
return await post(
`${host}/api/projects`,
`${host}/api/projects/${project.identifier}/remix`,
{ project },
headers(accessToken),
);
} else {
return await put(
`${host}/api/projects/${project.identifier}`,
{ project },
};

async readProject(projectIdentifier, locale, accessToken) {
const queryString = locale ? `?locale=${locale}` : "";
return await get(
`${host}/api/projects/${projectIdentifier}${queryString}`,
headers(accessToken),
);
}
};

export const deleteProject = async (identifier, accessToken) => {
return await axios.delete(
`${host}/api/projects/${identifier}`,
headers(accessToken),
);
};

export const getImage = async (url) => {
return await get(url, headers());
};

export const loadRemix = async (projectIdentifier, accessToken) => {
return await get(
`${host}/api/projects/${projectIdentifier}/remix`,
headers(accessToken),
);
};

export const createRemix = async (project, accessToken) => {
return await post(
`${host}/api/projects/${project.identifier}/remix`,
{ project },
headers(accessToken),
);
};

export const readProject = async (projectIdentifier, locale, accessToken) => {
const queryString = locale ? `?locale=${locale}` : "";
return await get(
`${host}/api/projects/${projectIdentifier}${queryString}`,
headers(accessToken),
);
};

export const loadAssets = async (assetsIdentifier, locale, accessToken) => {
const queryString = locale ? `?locale=${locale}` : "";
return await get(
`${host}/api/projects/${assetsIdentifier}/images${queryString}`,
headers(accessToken),
);
};

export const readProjectList = async (page, accessToken) => {
return await get(`${host}/api/projects`, {
params: { page },
...headers(accessToken),
});
};

export const uploadImages = async (projectIdentifier, accessToken, images) => {
var formData = new FormData();

images.forEach((image) => {
formData.append("images[]", image, image.name);
});

return await post(
`${host}/api/projects/${projectIdentifier}/images`,
formData,
{ ...headers(accessToken), "Content-Type": "multipart/form-data" },
);
};

export const createError = async (
projectIdentifier,
userId,
error,
sendError = false,
) => {
if (!sendError) {
return;
}
const { errorMessage, errorType } = error;
return await post(`${host}/api/project_errors`, {
error: errorMessage,
error_type: errorType,
project_id: projectIdentifier,
user_id: userId,
});
};
};

async loadAssets(assetsIdentifier, locale, accessToken) {
const queryString = locale ? `?locale=${locale}` : "";
return await get(
`${host}/api/projects/${assetsIdentifier}/images${queryString}`,
headers(accessToken),
);
};

async readProjectList(page, accessToken) {
return await get(`${host}/api/projects`, {
params: { page },
...headers(accessToken),
});
};

async uploadImages(projectIdentifier, accessToken, images) {
var formData = new FormData();

images.forEach((image) => {
formData.append("images[]", image, image.name);
});

return await post(
`${host}/api/projects/${projectIdentifier}/images`,
formData,
{ ...headers(accessToken), "Content-Type": "multipart/form-data" },
);
};

async createError(
projectIdentifier,
userId,
error,
sendError = false,
) {
if (!sendError) {
return;
}
const { errorMessage, errorType } = error;
return await post(`${host}/api/project_errors`, {
error: errorMessage,
error_type: errorType,
project_id: projectIdentifier,
user_id: userId,
});
};

async #get(url, headers) {
return await axios.get(url, headers);
};

async #post(url, body, headers) {
return await axios.post(url, body, headers);
};

async #put(url, body, headers) {
return await axios.put(url, body, headers);
};

#headers(accessToken) {
let headersHash;
if (accessToken) {
headersHash = { Accept: "application/json", Authorization: accessToken };
} else {
headersHash = { Accept: "application/json" };
}
return { headers: headersHash };
};
}
Loading