Skip to content

Commit bab6f06

Browse files
christianalfoniSaraVieira
authored andcommitted
Fix deleting sandboxes (codesandbox#2418)
* Fix deleting sandboxes * Zeit implementation reviewed * Fix payload on alias * Fix contributors * Fix review comments * fix close of new sandbox dependencies
1 parent bb72617 commit bab6f06

File tree

9 files changed

+130
-48
lines changed

9 files changed

+130
-48
lines changed
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import axios from 'axios';
1+
import axios, { AxiosResponse } from 'axios';
22

33
export default {
44
get: axios.get,
@@ -7,4 +7,12 @@ export default {
77
delete: axios.delete,
88
put: axios.put,
99
request: axios.request,
10+
async getJson<T>(url: string): Promise<AxiosResponse<T>> {
11+
const response = await axios.get(url);
12+
13+
return {
14+
...response,
15+
data: JSON.parse(response.data),
16+
};
17+
},
1018
};

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

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,22 @@ export default (() => {
2727
};
2828
}
2929

30+
async function deploysByID(id) {
31+
try {
32+
const response = await axios.get(
33+
`https://api.zeit.co/v2/now/deployments/${id}/aliases`,
34+
{
35+
headers: getDefaultHeaders(),
36+
}
37+
);
38+
39+
return response.data;
40+
} catch (e) {
41+
console.error(e);
42+
return null;
43+
}
44+
}
45+
3046
return {
3147
initialize(options: Options) {
3248
_options = options;
@@ -53,11 +69,22 @@ export default (() => {
5369
}
5470
);
5571

56-
return response.data
57-
.filter(deployment => deployment.name === name)
58-
.sort((deploymentA, deploymentB) =>
59-
deploymentA.created < deploymentB.created ? 1 : -1
60-
);
72+
const deploysNoAlias = response.data
73+
.filter(d => d.name === name)
74+
.sort((a, b) => (a.created < b.created ? 1 : -1));
75+
76+
const assignAlias = async d => {
77+
const alias = await deploysByID(d.uid);
78+
if (alias) {
79+
// eslint-disable-next-line
80+
d.alias = alias.aliases;
81+
} else {
82+
d.alias = [];
83+
}
84+
return d;
85+
};
86+
87+
return Promise.all(deploysNoAlias.map(assignAlias));
6188
},
6289
async getUser(): Promise<ZeitUser> {
6390
const response = await axios.get('https://api.zeit.co/www/user', {
@@ -67,32 +94,32 @@ export default (() => {
6794
return response.data;
6895
},
6996
async deleteDeployment(id: string) {
70-
return axios.request({
71-
url: `https://api.zeit.co/v2/now/deployments/${id}`,
72-
method: 'DELETE',
97+
return axios.delete(`https://api.zeit.co/v9/now/deployments/${id}`, {
7398
headers: getDefaultHeaders(),
7499
});
75100
},
76101
async deploy(contents: any, sandbox: Sandbox): Promise<string> {
77102
const apiData = await getApiData(contents, sandbox);
78-
const deploymentVersion = apiData.version === 2 ? 'v6' : 'v3';
103+
const deploymentVersion = apiData.version === 2 ? 'v9' : 'v3';
79104

80-
const response = await axios.request({
81-
url: `https://api.zeit.co/${deploymentVersion}/now/deployments?forceNew=1`,
82-
data: apiData,
83-
method: 'POST',
84-
headers: getDefaultHeaders(),
85-
});
105+
const response = await axios.post(
106+
`https://api.zeit.co/${deploymentVersion}/now/deployments?forceNew=1`,
107+
apiData,
108+
{
109+
headers: getDefaultHeaders(),
110+
}
111+
);
86112

87113
return `https://${response.data.result.url}`;
88114
},
89115
async aliasDeployment(id: string, zeitConfig: ZeitConfig): Promise<string> {
90-
const response = await axios.request({
91-
url: `https://api.zeit.co/v2/now/deployments/${id}/aliases`,
92-
data: { alias: zeitConfig.alias },
93-
method: 'POST',
94-
headers: getDefaultHeaders(),
95-
});
116+
const response = await axios.post(
117+
`https://api.zeit.co/v2/now/deployments/${id}/aliases`,
118+
{ alias: zeitConfig.alias },
119+
{
120+
headers: getDefaultHeaders(),
121+
}
122+
);
96123

97124
return `https://${response.data.result.alias}`;
98125
},
@@ -147,17 +174,23 @@ async function getApiData(contents: any, sandbox: Sandbox) {
147174
apiData.public = nowJSON.public || nowDefaults.public;
148175

149176
// if now v2 we need to tell now the version, builds and routes
150-
if (nowJSON.version === 2) {
177+
if (nowJSON.version === 1) {
178+
apiData.config = omit(nowJSON, [
179+
'public',
180+
'type',
181+
'name',
182+
'files',
183+
'version',
184+
]);
185+
apiData.forceNew = true;
186+
} else {
151187
apiData.version = 2;
152188
apiData.builds = nowJSON.builds;
153189
apiData.routes = nowJSON.routes;
154190
apiData.env = nowJSON.env;
155191
apiData.scope = nowJSON.scope;
156192
apiData['build.env'] = nowJSON['build.env'];
157193
apiData.regions = nowJSON.regions;
158-
} else {
159-
apiData.config = omit(nowJSON, ['public', 'type', 'name', 'files']);
160-
apiData.forceNew = true;
161194
}
162195

163196
if (!nowJSON.files) {
@@ -180,7 +213,7 @@ async function getApiData(contents: any, sandbox: Sandbox) {
180213
// if person added some files but no package.json
181214
if (
182215
filePath === 'package.json' &&
183-
!apiData.files.filter(f => f.name === 'package.json')
216+
!apiData.files.find(f => f.name === 'package.json')
184217
) {
185218
apiData.files.push({
186219
file: 'package.json',

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ export const withLoadApp = <T>(
5050
state.isAuthenticating = false;
5151

5252
try {
53-
const response = await effects.http.get<{ contributors: Contributor[] }>(
53+
const response = await effects.http.getJson<{
54+
contributors: Contributor[];
55+
}>(
5456
'https://raw.githubusercontent.com/codesandbox/codesandbox-client/master/.all-contributorsrc'
5557
);
5658

packages/app/src/app/overmind/namespaces/deployment/actions.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,16 @@ export const deployClicked: AsyncAction = async ({
103103
export const deploySandboxClicked: AsyncAction = async ({ state, effects }) => {
104104
state.currentModal = 'deployment';
105105

106-
if (!state.user.integrations.zeit.email) {
106+
const zeitIntegration = state.user.integrations.zeit;
107+
108+
if (!zeitIntegration || !zeitIntegration.token) {
109+
effects.notificationToast.error(
110+
'You are not authorized with Zeit, please refresh and log in again'
111+
);
112+
return;
113+
}
114+
115+
if (!zeitIntegration.email) {
107116
try {
108117
const user = await effects.zeit.getUser();
109118

@@ -116,7 +125,9 @@ export const deploySandboxClicked: AsyncAction = async ({ state, effects }) => {
116125
state.deployment.url = null;
117126
};
118127

119-
export const setDeploymentToDelete: Action<string> = ({ state }, id) => {
128+
export const setDeploymentToDelete: Action<{
129+
id: string;
130+
}> = ({ state }, { id }) => {
120131
state.deployment.deployToDelete = id;
121132
};
122133

@@ -128,33 +139,35 @@ export const deleteDeployment: AsyncAction = async ({
128139
const id = state.deployment.deployToDelete;
129140

130141
state.currentModal = null;
131-
state.deployment.isDeletingDeployment = true;
142+
state.deployment.deploysBeingDeleted.push(id);
132143

133144
try {
134145
await effects.zeit.deleteDeployment(id);
135146

136147
effects.notificationToast.success('Deployment deleted');
137-
138148
actions.deployment.getDeploys();
139149
} catch (error) {
140150
effects.notificationToast.error(
141151
'An unknown error occurred when deleting your deployment'
142152
);
143153
}
144154

145-
state.deployment.isDeletingDeployment = false;
155+
state.deployment.deploysBeingDeleted.splice(
156+
state.deployment.deploysBeingDeleted.indexOf(id),
157+
1
158+
);
146159
};
147160

148-
export const aliasDeployment: AsyncAction<string> = async (
149-
{ state, effects },
150-
id
151-
) => {
161+
export const aliasDeployment: AsyncAction<{
162+
id: string;
163+
}> = async ({ state, effects, actions }, { id }) => {
152164
const zeitConfig = effects.zeit.getConfig(state.editor.currentSandbox);
153165

154166
try {
155167
const url = await effects.zeit.aliasDeployment(id, zeitConfig);
156168

157169
effects.notificationToast.success(`Deployed to ${url}`);
170+
actions.deployment.getDeploys();
158171
} catch (error) {
159172
effects.notificationToast.error(
160173
'An unknown error occurred when aliasing your deployment'

packages/app/src/app/overmind/namespaces/deployment/state.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ type State = {
66
netlifyClaimUrl: string;
77
netlifyLogs: string;
88
netlifySite: NetlifySite;
9+
deploysBeingDeleted: string[];
910
deployToDelete: string;
1011
deploying: boolean;
1112
url: string;
1213
gettingDeploys: boolean;
1314
sandboxDeploys: ZeitDeployment[];
14-
isDeletingDeployment: boolean;
1515
};
1616

1717
export const state: State = {
1818
hasAlias: false,
19-
isDeletingDeployment: false,
19+
deploysBeingDeleted: [],
2020
deployToDelete: null,
2121
deploying: false,
2222
building: false,

packages/app/src/app/pages/Dashboard/Sidebar/SandboxesItem/folder-drop-target.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ function addSandboxesToCollection(props, item) {
1717
variables: {
1818
collectionPath: path || '/',
1919
teamId,
20-
sandboxIds: selectedSandboxes.toJS(),
20+
sandboxIds: selectedSandboxes.toJS
21+
? selectedSandboxes.toJS()
22+
: selectedSandboxes,
2123
},
2224
optimisticResponse: {
2325
__typename: 'Mutation',

packages/app/src/app/pages/Dashboard/queries.js

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,9 @@ export function makeTemplates(selectedSandboxes, teamId, collections) {
356356
.mutate({
357357
mutation: MAKE_SANDBOXES_TEMPLATE_MUTATION,
358358
variables: {
359-
sandboxIds: selectedSandboxes.toJS(),
359+
sandboxIds: selectedSandboxes.toJS
360+
? selectedSandboxes.toJS()
361+
: selectedSandboxes,
360362
},
361363
refetchQueries: [
362364
'DeletedSandboxes',
@@ -412,7 +414,11 @@ export function makeTemplates(selectedSandboxes, teamId, collections) {
412414
track('Template - Removed', {
413415
source: 'Undo',
414416
});
415-
unmakeTemplates(selectedSandboxes.toJS());
417+
unmakeTemplates(
418+
selectedSandboxes.toJS
419+
? selectedSandboxes.toJS()
420+
: selectedSandboxes
421+
);
416422
},
417423
},
418424
],
@@ -426,7 +432,9 @@ export function undeleteSandboxes(selectedSandboxes) {
426432
client.mutate({
427433
mutation: ADD_SANDBOXES_TO_FOLDER_MUTATION,
428434
variables: {
429-
sandboxIds: selectedSandboxes.toJS(),
435+
sandboxIds: selectedSandboxes.toJS
436+
? selectedSandboxes.toJS()
437+
: selectedSandboxes,
430438
collectionPath: '/',
431439
},
432440
optimisticResponse: {
@@ -447,7 +455,9 @@ export function permanentlyDeleteSandboxes(selectedSandboxes) {
447455
client.mutate({
448456
mutation: PERMANENTLY_DELETE_SANDBOXES_MUTATION,
449457
variables: {
450-
sandboxIds: selectedSandboxes.toJS(),
458+
sandboxIds: selectedSandboxes.toJS
459+
? selectedSandboxes.toJS()
460+
: selectedSandboxes,
451461
},
452462
update: cache => {
453463
try {
@@ -480,7 +490,9 @@ export function deleteSandboxes(selectedSandboxes, collections = []) {
480490
client.mutate({
481491
mutation: DELETE_SANDBOXES_MUTATION,
482492
variables: {
483-
sandboxIds: selectedSandboxes.toJS(),
493+
sandboxIds: selectedSandboxes.toJS
494+
? selectedSandboxes.toJS()
495+
: selectedSandboxes,
484496
},
485497
refetchQueries: [
486498
'DeletedSandboxes',
@@ -527,7 +539,9 @@ export function setSandboxesPrivacy(selectedSandboxes, privacy) {
527539
client.mutate({
528540
mutation: SET_SANDBOXES_PRIVACY_MUTATION,
529541
variables: {
530-
sandboxIds: selectedSandboxes.toJS(),
542+
sandboxIds: selectedSandboxes.toJS
543+
? selectedSandboxes.toJS()
544+
: selectedSandboxes,
531545
privacy,
532546
},
533547
});

packages/app/src/app/pages/Sandbox/Editor/Content/index.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @flow
22
import * as React from 'react';
3+
import { json } from 'overmind';
34
import { ThemeProvider } from 'styled-components';
45
import { Prompt } from 'react-router-dom';
56
import { TextOperation } from 'ot';
@@ -107,7 +108,12 @@ class EditorPreview extends React.Component {
107108
.changeSandbox(
108109
newSandbox,
109110
store.editor.currentModule,
110-
parsed ? parsed.dependencies : newSandbox.npmDependencies.toJS()
111+
// eslint-disable-next-line
112+
parsed
113+
? parsed.dependencies
114+
: newSandbox.npmDependencies.toJS
115+
? newSandbox.npmDependencies.toJS()
116+
: json(newSandbox.npmDependencies)
111117
)
112118
.then(() => {
113119
isChangingSandbox = false;

packages/app/src/app/pages/Sandbox/Editor/Workspace/items/Deployment/Zeit/Deploys/Actions/DeleteDeploymentButton/DeleteDeploymentButton.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ export const DeleteDeploymentButton = inject('store', 'signals')(
2020
store: { deployment },
2121
}: Props) => (
2222
<Action
23-
disabled={deployment[`${id}Deleting`]}
23+
disabled={
24+
deployment.deploysBeingDeleted
25+
? deployment.deploysBeingDeleted.includes(id)
26+
: deployment[`${id}Deleting`]
27+
}
2428
onClick={() => {
2529
setDeploymentToDelete({ id });
2630
modalOpened({ modal: 'deleteDeployment' });

0 commit comments

Comments
 (0)