Skip to content

Commit 212741b

Browse files
merge
2 parents 16eb666 + 068449a commit 212741b

File tree

17 files changed

+2056
-108
lines changed

17 files changed

+2056
-108
lines changed

packages/app/src/app/overmind/effects/analytics.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import track, {
22
identify,
3+
logError,
34
setAnonymousId,
45
setUserId,
56
} from '@codesandbox/common/lib/utils/analytics';
@@ -16,6 +17,7 @@ export default (() => {
1617
trackedEvents[event] = true;
1718
track(event, data);
1819
},
20+
logError,
1921
identify,
2022
setAnonymousId,
2123
setUserId,

packages/app/src/app/overmind/effects/api/apiFactory.ts

Lines changed: 30 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/* eslint-disable camelcase */
2-
import axios, { AxiosResponse, AxiosError, AxiosRequestConfig } from 'axios';
3-
import { logError } from '@codesandbox/common/lib/utils/analytics';
4-
import { values } from 'lodash-es';
2+
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
53
import { camelizeKeys, decamelizeKeys } from 'humps';
64

75
export const API_ROOT = '/api/v1';
86

9-
type RecursivePartial<T> = { [P in keyof T]?: RecursivePartial<T[P]> };
7+
export type ApiError = AxiosError<
8+
{ errors: string[] } | { error: string } | any
9+
>;
1010

1111
export type Params = {
1212
[key: string]: string;
@@ -28,36 +28,17 @@ export type Api = {
2828
export type ApiConfig = {
2929
provideJwtToken: () => string;
3030
getParsedConfigurations: () => any;
31-
onError: (error: string) => void;
31+
onError: (error: ApiError) => void;
3232
};
3333

34-
export default (config: {
35-
provideJwtToken: () => string;
36-
onError: (error: string) => void;
37-
}) => {
34+
export default (config: ApiConfig) => {
3835
const createHeaders = (jwt: string) =>
3936
jwt
4037
? {
4138
Authorization: `Bearer ${jwt}`,
4239
}
4340
: {};
4441

45-
const showError = error => {
46-
config.onError(error.message);
47-
error.apiMessage = error.message; // eslint-disable-line no-param-reassign
48-
};
49-
50-
const handleError = error => {
51-
const newError = convertError(error);
52-
try {
53-
showError(newError);
54-
} catch (e) {
55-
console.error(e);
56-
}
57-
58-
throw newError;
59-
};
60-
6142
const api: Api = {
6243
get(path, params, options) {
6344
return axios
@@ -66,31 +47,43 @@ export default (config: {
6647
headers: createHeaders(config.provideJwtToken()),
6748
})
6849
.then(response => handleResponse(response, options))
69-
.catch(e => handleError(e));
50+
.catch(e => {
51+
config.onError(e);
52+
return Promise.reject(e);
53+
});
7054
},
7155
post(path, body, options) {
7256
return axios
7357
.post(API_ROOT + path, decamelizeKeys(body), {
7458
headers: createHeaders(config.provideJwtToken()),
7559
})
7660
.then(response => handleResponse(response, options))
77-
.catch(e => handleError(e));
61+
.catch(e => {
62+
config.onError(e);
63+
return Promise.reject(e);
64+
});
7865
},
7966
patch(path, body, options) {
8067
return axios
8168
.patch(API_ROOT + path, decamelizeKeys(body), {
8269
headers: createHeaders(config.provideJwtToken()),
8370
})
8471
.then(response => handleResponse(response, options))
85-
.catch(e => handleError(e));
72+
.catch(e => {
73+
config.onError(e);
74+
return Promise.reject(e);
75+
});
8676
},
8777
put(path, body, options) {
8878
return axios
8979
.put(API_ROOT + path, decamelizeKeys(body), {
9080
headers: createHeaders(config.provideJwtToken()),
9181
})
9282
.then(response => handleResponse(response, options))
93-
.catch(e => handleError(e));
83+
.catch(e => {
84+
config.onError(e);
85+
return Promise.reject(e);
86+
});
9487
},
9588
delete(path, params, options) {
9689
return axios
@@ -99,7 +92,10 @@ export default (config: {
9992
headers: createHeaders(config.provideJwtToken()),
10093
})
10194
.then(response => handleResponse(response, options))
102-
.catch(e => handleError(e));
95+
.catch(e => {
96+
config.onError(e);
97+
return Promise.reject(e);
98+
});
10399
},
104100
request(requestConfig, options) {
105101
return axios
@@ -111,49 +107,16 @@ export default (config: {
111107
})
112108
)
113109
.then(response => handleResponse(response, options))
114-
.catch(e => handleError(e));
110+
.catch(e => {
111+
config.onError(e);
112+
return Promise.reject(e);
113+
});
115114
},
116115
};
117116

118117
return api;
119118
};
120119

121-
function convertError(error: AxiosError) {
122-
const { response } = error;
123-
124-
if (!response || response.status >= 500) {
125-
logError(error);
126-
}
127-
128-
if (response && response.data) {
129-
if (response.data.errors) {
130-
const errors = values(response.data.errors)[0];
131-
if (Array.isArray(errors)) {
132-
if (errors[0]) {
133-
error.message = errors[0]; // eslint-disable-line no-param-reassign,prefer-destructuring
134-
}
135-
} else {
136-
error.message = errors; // eslint-disable-line no-param-reassign
137-
}
138-
} else if (response.data.error) {
139-
const { error_code, message, ...data } = response.data.error as {
140-
message: string;
141-
error_code: string;
142-
[k: string]: any;
143-
};
144-
// @ts-ignore
145-
error.error_code = error_code; // eslint-disable-line no-param-reassign
146-
error.message = message; // eslint-disable-line no-param-reassign
147-
// @ts-ignore
148-
error.data = data; // eslint-disable-line no-param-reassign
149-
} else if (response.status === 413) {
150-
return 'File too large, upload limit is 5MB.';
151-
}
152-
}
153-
154-
return error;
155-
}
156-
157120
export function handleResponse(
158121
response: AxiosResponse,
159122
{ shouldCamelize = true } = {}

packages/app/src/app/overmind/factories.ts

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import { Contributor } from '@codesandbox/common/lib/types';
2+
import { notificationState } from '@codesandbox/common/lib/utils/notifications';
3+
import { NotificationStatus } from '@codesandbox/notifications';
4+
import { AxiosError } from 'axios';
25
import { IDerive, IState, json } from 'overmind';
36

47
import { AsyncAction } from '.';
@@ -38,11 +41,31 @@ export const withLoadApp = <T>(
3841
actions.userNotifications.internal.initialize();
3942
effects.api.preloadTemplates();
4043
} catch (error) {
41-
effects.notificationToast.error(
42-
'Your session seems to be expired, please log in again...'
43-
);
44-
effects.jwt.reset();
45-
effects.analytics.setAnonymousId();
44+
if (error.isAxiosError && (error as AxiosError).response.status === 401) {
45+
// Reset existing sign in info
46+
effects.jwt.reset();
47+
effects.analytics.setAnonymousId();
48+
49+
// Allow user to sign in again in notification
50+
notificationState.addNotification({
51+
message: 'Your session seems to be expired, please log in again...',
52+
status: NotificationStatus.ERROR,
53+
actions: {
54+
primary: [
55+
{
56+
label: 'Sign in',
57+
run: () => {
58+
actions.signInClicked({ useExtraScopes: false });
59+
},
60+
},
61+
],
62+
},
63+
});
64+
} else {
65+
effects.notificationToast.error(
66+
"We weren't able to sign you in, this could be due to a flaky connection or something on our server. Please try again in a minute."
67+
);
68+
}
4669
}
4770
} else {
4871
effects.jwt.reset();
@@ -77,28 +100,30 @@ export const withOwnedSandbox = <T>(
77100
): AsyncAction<T> => async (context, payload) => {
78101
const { state, actions } = context;
79102

80-
if (!state.editor.currentSandbox.owned) {
81-
if (state.editor.isForkingSandbox) {
82-
return cancelAction(context, payload);
83-
}
84-
85-
await actions.editor.internal.forkSandbox({
86-
sandboxId: state.editor.currentSandbox.id,
87-
});
88-
} else if (
89-
state.editor.currentSandbox.isFrozen &&
90-
state.editor.sessionFrozen
91-
) {
92-
const modalResponse = await actions.modals.forkFrozenModal.open();
103+
if (state.editor.currentSandbox) {
104+
if (!state.editor.currentSandbox.owned) {
105+
if (state.editor.isForkingSandbox) {
106+
return cancelAction(context, payload);
107+
}
93108

94-
if (modalResponse === 'fork') {
95109
await actions.editor.internal.forkSandbox({
96110
sandboxId: state.editor.currentSandbox.id,
97111
});
98-
} else if (modalResponse === 'unfreeze') {
99-
state.editor.sessionFrozen = false;
100-
} else if (modalResponse === 'cancel') {
101-
return cancelAction(context, payload);
112+
} else if (
113+
state.editor.currentSandbox.isFrozen &&
114+
state.editor.sessionFrozen
115+
) {
116+
const modalResponse = await actions.modals.forkFrozenModal.open();
117+
118+
if (modalResponse === 'fork') {
119+
await actions.editor.internal.forkSandbox({
120+
sandboxId: state.editor.currentId,
121+
});
122+
} else if (modalResponse === 'unfreeze') {
123+
state.editor.sessionFrozen = false;
124+
} else if (modalResponse === 'cancel') {
125+
return cancelAction(context, payload);
126+
}
102127
}
103128
}
104129

0 commit comments

Comments
 (0)