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 fetch helpers instead of fetch #27026

Merged
merged 15 commits into from
Sep 19, 2023
Merged
5 changes: 4 additions & 1 deletion .eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ overrides:
- files: ["*.config.*"]
rules:
import/no-unused-modules: [0]
- files: ["web_src/js/modules/fetch.js", "web_src/js/standalone/**/*"]
rules:
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression]

rules:
"@eslint-community/eslint-comments/disable-enable-pair": [2]
Expand Down Expand Up @@ -420,7 +423,7 @@ rules:
no-restricted-exports: [0]
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, location, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, self, status, statusbar, stop, toolbar, top, __dirname, __filename]
no-restricted-imports: [0]
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression]
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression, {selector: "CallExpression[callee.name='fetch']", message: "use modules/fetch.js instead"}]
no-return-assign: [0]
no-script-url: [2]
no-self-assign: [2, {props: true}]
Expand Down
5 changes: 3 additions & 2 deletions web_src/js/components/DashboardRepoList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import {createApp, nextTick} from 'vue';
import $ from 'jquery';
import {SvgIcon} from '../svg.js';
import {GET} from '../modules/fetch.js';

const {appSubUrl, assetUrlPrefix, pageData} = window.config;

Expand Down Expand Up @@ -233,11 +234,11 @@ const sfc = {
try {
if (!this.reposTotalCount) {
const totalCountSearchURL = `${this.subUrl}/repo/search?count_only=1&uid=${this.uid}&team_id=${this.teamId}&q=&page=1&mode=`;
response = await fetch(totalCountSearchURL);
response = await GET(totalCountSearchURL);
this.reposTotalCount = response.headers.get('X-Total-Count');
}

response = await fetch(searchedURL);
response = await GET(searchedURL);
json = await response.json();
} catch {
if (searchedURL === this.searchURL) {
Expand Down
3 changes: 2 additions & 1 deletion web_src/js/components/DiffCommitSelector.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script>
import {SvgIcon} from '../svg.js';
import {GET} from '../modules/fetch.js';

export default {
components: {SvgIcon},
Expand Down Expand Up @@ -123,7 +124,7 @@ export default {
},
/** Load the commits to show in this dropdown */
async fetchCommits() {
const resp = await fetch(`${this.issueLink}/commits/list`);
const resp = await GET(`${this.issueLink}/commits/list`);
const results = await resp.json();
this.commits.push(...results.commits.map((x) => {
x.hovered = false;
Expand Down
4 changes: 2 additions & 2 deletions web_src/js/components/RepoBranchTagSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import $ from 'jquery';
import {SvgIcon} from '../svg.js';
import {pathEscapeSegments} from '../utils/url.js';
import {showErrorToast} from '../modules/toast.js';
import {GET} from '../modules/fetch.js';

const sfc = {
components: {SvgIcon},
Expand Down Expand Up @@ -191,8 +192,7 @@ const sfc = {
this.isLoading = true;
try {
// the "data.defaultBranch" is ambiguous, it could be "branch name" or "tag name"
const reqUrl = `${this.repoLink}/${this.mode}/list`;
const resp = await fetch(reqUrl);
const resp = await GET(`${this.repoLink}/${this.mode}/list`);
const {results} = await resp.json();
for (const result of results) {
let selected = false;
Expand Down
3 changes: 2 additions & 1 deletion web_src/js/features/common-global.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {htmlEscape} from 'escape-goat';
import {showTemporaryTooltip} from '../modules/tippy.js';
import {confirmModal} from './comp/ConfirmModal.js';
import {showErrorToast} from '../modules/toast.js';
import {request} from '../modules/fetch.js';

const {appUrl, appSubUrl, csrfToken, i18n} = window.config;

Expand Down Expand Up @@ -81,7 +82,7 @@ function fetchActionDoRedirect(redirect) {

async function fetchActionDoRequest(actionElem, url, opt) {
try {
const resp = await fetch(url, opt);
const resp = await request(url, opt);
if (resp.status === 200) {
let {redirect} = await resp.json();
redirect = redirect || actionElem.getAttribute('data-redirect');
Expand Down
5 changes: 3 additions & 2 deletions web_src/js/features/common-issue-list.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import $ from 'jquery';
import {isElemHidden, onInputDebounce, toggleElem} from '../utils/dom.js';
const {appSubUrl} = window.config;
import {GET} from '../modules/fetch.js';

const {appSubUrl} = window.config;
const reIssueIndex = /^(\d+)$/; // eg: "123"
const reIssueSharpIndex = /^#(\d+)$/; // eg: "#123"
const reIssueOwnerRepoIndex = /^([-.\w]+)\/([-.\w]+)#(\d+)$/; // eg: "{owner}/{repo}#{index}"
Expand Down Expand Up @@ -54,7 +55,7 @@ export function initCommonIssueListQuickGoto() {
// try to check whether the parsed goto link is valid
let targetUrl = parseIssueListQuickGotoLink(repoLink, searchText);
if (targetUrl) {
const res = await fetch(`${targetUrl}/info`);
const res = await GET(`${targetUrl}/info`);
if (res.status !== 200) targetUrl = '';
}

Expand Down
9 changes: 2 additions & 7 deletions web_src/js/features/comp/ImagePaste.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import $ from 'jquery';

const {csrfToken} = window.config;
import {POST} from '../../modules/fetch.js';

async function uploadFile(file, uploadUrl) {
const formData = new FormData();
formData.append('file', file, file.name);

const res = await fetch(uploadUrl, {
method: 'POST',
headers: {'X-Csrf-Token': csrfToken},
body: formData,
});
const res = await POST(uploadUrl, {data: formData});
return await res.json();
}

Expand Down
14 changes: 3 additions & 11 deletions web_src/js/features/comp/ReactionSelector.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import $ from 'jquery';

const {csrfToken} = window.config;
import {POST} from '../../modules/fetch.js';

export function initCompReactionSelector($parent) {
$parent.find(`.select-reaction .item.reaction, .comment-reaction-button`).on('click', async function (e) {
Expand All @@ -12,15 +11,8 @@ export function initCompReactionSelector($parent) {
const reactionContent = $(this).attr('data-reaction-content');
const hasReacted = $(this).closest('.ui.segment.reactions').find(`a[data-reaction-content="${reactionContent}"]`).attr('data-has-reacted') === 'true';

const res = await fetch(`${actionUrl}/${hasReacted ? 'unreact' : 'react'}`, {
method: 'POST',
headers: {
'content-type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
_csrf: csrfToken,
content: reactionContent,
}),
const res = await POST(`${actionUrl}/${hasReacted ? 'unreact' : 'react'}`, {
data: new URLSearchParams({content: reactionContent}),
});

const data = await res.json();
Expand Down
3 changes: 2 additions & 1 deletion web_src/js/features/copycontent.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {clippie} from 'clippie';
import {showTemporaryTooltip} from '../modules/tippy.js';
import {convertImage} from '../utils.js';
import {GET} from '../modules/fetch.js';

const {i18n} = window.config;

Expand All @@ -20,7 +21,7 @@ export function initCopyContent() {
if (link) {
btn.classList.add('is-loading', 'small-loading-icon');
try {
const res = await fetch(link, {credentials: 'include', redirect: 'follow'});
const res = await GET(link, {credentials: 'include', redirect: 'follow'});
const contentType = res.headers.get('content-type');

if (contentType.startsWith('image/') && !contentType.startsWith('image/svg')) {
Expand Down
3 changes: 2 additions & 1 deletion web_src/js/features/install.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import $ from 'jquery';
import {hideElem, showElem} from '../utils/dom.js';
import {GET} from '../modules/fetch.js';

export function initInstall() {
const $page = $('.page-content.install');
Expand Down Expand Up @@ -111,7 +112,7 @@ function initPostInstall() {
const targetUrl = el.getAttribute('href');
let tid = setInterval(async () => {
try {
const resp = await fetch(targetUrl);
const resp = await GET(targetUrl);
if (tid && resp.status === 200) {
clearInterval(tid);
tid = null;
Expand Down
9 changes: 3 additions & 6 deletions web_src/js/features/pull-view-file.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {diffTreeStore} from '../modules/stores.js';
import {setFileFolding} from './file-fold.js';
import {POST} from '../modules/fetch.js';

const {csrfToken, pageData} = window.config;
const {pageData} = window.config;
const prReview = pageData.prReview || {};
const viewedStyleClass = 'viewed-file-checked-form';
const viewedCheckboxSelector = '.viewed-file-form'; // Selector under which all "Viewed" checkbox forms can be found
Expand Down Expand Up @@ -68,11 +69,7 @@ export function initViewedCheckboxListenerFor() {
const data = {files};
const headCommitSHA = form.getAttribute('data-headcommit');
if (headCommitSHA) data.headCommitSHA = headCommitSHA;
fetch(form.getAttribute('data-link'), {
method: 'POST',
headers: {'X-Csrf-Token': csrfToken},
body: JSON.stringify(data),
});
POST(form.getAttribute('data-link'), {data});

// Fold the file accordingly
const parentBox = form.closest('.diff-file-header');
Expand Down
3 changes: 2 additions & 1 deletion web_src/js/features/repo-diff-commit.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {hideElem, showElem, toggleElem} from '../utils/dom.js';
import {GET} from '../modules/fetch.js';

async function loadBranchesAndTags(area, loadingButton) {
loadingButton.classList.add('disabled');
try {
const res = await fetch(loadingButton.getAttribute('data-fetch-url'));
const res = await GET(loadingButton.getAttribute('data-fetch-url'));
const data = await res.json();
hideElem(loadingButton);
addTags(area, data.tags);
Expand Down
14 changes: 3 additions & 11 deletions web_src/js/features/repo-issue-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {htmlEscape} from 'escape-goat';
import {confirmModal} from './comp/ConfirmModal.js';
import {showErrorToast} from '../modules/toast.js';
import {createSortable} from '../modules/sortable.js';
import {DELETE, POST} from '../modules/fetch.js';

function initRepoIssueListCheckboxes() {
const $issueSelectAll = $('.issue-checkbox-all');
Expand Down Expand Up @@ -146,10 +147,8 @@ function initPinRemoveButton() {
const id = Number(el.getAttribute('data-issue-id'));

// Send the unpin request
const response = await fetch(el.getAttribute('data-unpin-url'), {
method: 'delete',
const response = await DELETE(el.getAttribute('data-unpin-url'), {
headers: {
'X-Csrf-Token': window.config.csrfToken,
'Content-Type': 'application/json',
},
});
Expand All @@ -166,14 +165,7 @@ function initPinRemoveButton() {
async function pinMoveEnd(e) {
const url = e.item.getAttribute('data-move-url');
const id = Number(e.item.getAttribute('data-issue-id'));
await fetch(url, {
method: 'post',
body: JSON.stringify({id, position: e.newIndex + 1}),
headers: {
'X-Csrf-Token': window.config.csrfToken,
'Content-Type': 'application/json',
},
});
await POST(url, {data: {id, position: e.newIndex + 1}});
}

async function initIssuePinSort() {
Expand Down
9 changes: 4 additions & 5 deletions web_src/js/features/repo-migrate.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import $ from 'jquery';
import {hideElem, showElem} from '../utils/dom.js';
import {GET, POST} from '../modules/fetch.js';

const {appSubUrl, csrfToken} = window.config;
const {appSubUrl} = window.config;

export function initRepoMigrationStatusChecker() {
const $repoMigrating = $('#repo_migrating');
Expand All @@ -13,7 +14,7 @@ export function initRepoMigrationStatusChecker() {

// returns true if the refresh still need to be called after a while
const refresh = async () => {
const res = await fetch(`${appSubUrl}/user/task/${task}`);
const res = await GET(`${appSubUrl}/user/task/${task}`);
if (res.status !== 200) return true; // continue to refresh if network error occurs

const data = await res.json();
Expand Down Expand Up @@ -58,10 +59,8 @@ export function initRepoMigrationStatusChecker() {
}

async function doMigrationRetry(e) {
await fetch($(e.target).attr('data-migrating-task-retry-url'), {
method: 'post',
await POST($(e.target).attr('data-migrating-task-retry-url'), {
headers: {
'X-Csrf-Token': csrfToken,
'Content-Type': 'application/json',
},
});
Expand Down
38 changes: 13 additions & 25 deletions web_src/js/features/user-auth-webauthn.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {encodeURLEncodedBase64, decodeURLEncodedBase64} from '../utils.js';
import {showElem} from '../utils/dom.js';
import {GET, POST} from '../modules/fetch.js';

const {appSubUrl, csrfToken} = window.config;
const {appSubUrl} = window.config;

export async function initUserAuthWebAuthn() {
const elPrompt = document.querySelector('.user.signin.webauthn-prompt');
Expand All @@ -13,7 +14,7 @@ export async function initUserAuthWebAuthn() {
return;
}

const res = await fetch(`${appSubUrl}/user/webauthn/assertion`);
const res = await GET(`${appSubUrl}/user/webauthn/assertion`);
if (res.status !== 200) {
webAuthnError('unknown');
return;
Expand Down Expand Up @@ -53,12 +54,8 @@ async function verifyAssertion(assertedCredential) {
const sig = new Uint8Array(assertedCredential.response.signature);
const userHandle = new Uint8Array(assertedCredential.response.userHandle);

const res = await fetch(`${appSubUrl}/user/webauthn/assertion`, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: JSON.stringify({
const res = await POST(`${appSubUrl}/user/webauthn/assertion`, {
data: {
id: assertedCredential.id,
rawId: encodeURLEncodedBase64(rawId),
type: assertedCredential.type,
Expand All @@ -69,7 +66,7 @@ async function verifyAssertion(assertedCredential) {
signature: encodeURLEncodedBase64(sig),
userHandle: encodeURLEncodedBase64(userHandle),
},
}),
},
});
if (res.status === 500) {
webAuthnError('unknown');
Expand All @@ -88,21 +85,16 @@ async function webauthnRegistered(newCredential) {
const clientDataJSON = new Uint8Array(newCredential.response.clientDataJSON);
const rawId = new Uint8Array(newCredential.rawId);

const res = await fetch(`${appSubUrl}/user/settings/security/webauthn/register`, {
method: 'POST',
headers: {
'X-Csrf-Token': csrfToken,
'Content-Type': 'application/json; charset=utf-8',
},
body: JSON.stringify({
const res = await POST(`${appSubUrl}/user/settings/security/webauthn/register`, {
data: {
id: newCredential.id,
rawId: encodeURLEncodedBase64(rawId),
type: newCredential.type,
response: {
attestationObject: encodeURLEncodedBase64(attestationObject),
clientDataJSON: encodeURLEncodedBase64(clientDataJSON),
},
}),
},
});

if (res.status === 409) {
Expand Down Expand Up @@ -165,15 +157,11 @@ export function initUserAuthWebAuthnRegister() {
async function webAuthnRegisterRequest() {
const elNickname = document.getElementById('nickname');

const body = new FormData();
body.append('name', elNickname.value);
const formData = new FormData();
formData.append('name', elNickname.value);

const res = await fetch(`${appSubUrl}/user/settings/security/webauthn/request_register`, {
method: 'POST',
headers: {
'X-Csrf-Token': csrfToken,
},
body,
const res = await POST(`${appSubUrl}/user/settings/security/webauthn/request_register`, {
data: formData,
});

if (res.status === 409) {
Expand Down
3 changes: 2 additions & 1 deletion web_src/js/modules/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const {csrfToken} = window.config;
// fetch wrapper, use below method name functions and the `data` option to pass in data
// which will automatically set an appropriate content-type header. For json content,
// only object and array types are currently supported.
function request(url, {headers, data, body, ...other} = {}) {
export function request(url, {headers, data, body, ...other} = {}) {
let contentType;
if (!body) {
if (data instanceof FormData) {
Expand All @@ -14,6 +14,7 @@ function request(url, {headers, data, body, ...other} = {}) {
} else if (data instanceof URLSearchParams) {
contentType = 'application/x-www-form-urlencoded';
body = data;
body.set('_csrf', csrfToken);
} else if (isObject(data) || Array.isArray(data)) {
contentType = 'application/json';
body = JSON.stringify(data);
Expand Down