Skip to content

Commit 2861c8d

Browse files
committed
Allows yanking a crate from the web UI
1 parent 44773ef commit 2861c8d

File tree

11 files changed

+138
-7
lines changed

11 files changed

+138
-7
lines changed

app/components/yank-button.hbs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{{#if @version.yanked}}
2+
<button
3+
type="button"
4+
local-class="button"
5+
...attributes
6+
data-test-version-unyank-button={{@version.num}}
7+
disabled={{@version.unyankTask.isRunning}}
8+
{{on "click" (perform @version.unyankTask)}}
9+
>
10+
{{#if @version.unyankTask.isRunning }}
11+
Unyanking...
12+
{{else}}
13+
Unyank
14+
{{/if}}
15+
</button>
16+
{{else}}
17+
<button
18+
type="button"
19+
local-class="button"
20+
...attributes
21+
data-test-version-yank-button={{@version.num}}
22+
disabled={{@version.yankTask.isRunning}}
23+
{{on "click" (perform @version.yankTask)}}
24+
>
25+
{{#if @version.yankTask.isRunning }}
26+
Yanking...
27+
{{else}}
28+
Yank
29+
{{/if}}
30+
</button>
31+
{{/if}}

app/components/yank-button.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import Component from '@ember/component';
2+
3+
export default Component.extend({
4+
tagName: '',
5+
});

app/components/yank-button.module.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.button {
2+
composes: yellow-button from '../styles/shared/buttons.module.css';
3+
padding: 6px 12px;
4+
border-radius: 18px;
5+
}

app/controllers/crate/versions.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import Controller from '@ember/controller';
2+
import { computed } from '@ember/object';
3+
import { inject as service } from '@ember/service';
4+
5+
export default Controller.extend({
6+
session: service(),
7+
8+
isOwner: computed('model.owner_user', 'session.currentUser.id', function () {
9+
return this.get('model.owner_user').findBy('id', this.get('session.currentUser.id'));
10+
}),
11+
});

app/models/version.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,26 @@ export default class Version extends Model {
6060
}
6161
}).keepLatest())
6262
loadReadmeTask;
63+
64+
@(task(function* () {
65+
let response = yield fetch(`/api/v1/crates/${this.crate.id}/${this.num}/yank`, { method: 'DELETE' });
66+
if (!response.ok) {
67+
throw new Error(`Yank request for ${this.crateName} v${this.num} failed`);
68+
}
69+
this.set('yanked', true);
70+
71+
return yield response.text();
72+
}).keepLatest())
73+
yankTask;
74+
75+
@(task(function* () {
76+
let response = yield fetch(`/api/v1/crates/${this.crate.id}/${this.num}/unyank`, { method: 'PUT' });
77+
if (!response.ok) {
78+
throw new Error(`Unyank request for ${this.crateName} v${this.num} failed`);
79+
}
80+
this.set('yanked', false);
81+
82+
return yield response.text();
83+
}).keepLatest())
84+
unyankTask;
6385
}

app/styles/crate/version.module.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,7 @@ div.header {
273273
.yanked {
274274
composes: yanked from '../shared/typography.module.css';
275275
}
276+
277+
.version {
278+
padding: 4px 0;
279+
}

app/styles/crate/versions.module.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
.arrow {
3838
display: inline-block;
3939
float: right;
40+
margin-left: 8px;
4041

4142
svg {
4243
background: #EEECDD;

app/templates/crate/version.hbs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,17 @@
163163
<h3>Versions</h3>
164164
<ul>
165165
{{#each this.smallSortedVersions as |version|}}
166-
<li>
166+
<li local-class="version">
167167
<LinkTo @route="crate.version" @model={{version.num}} data-test-version-link={{version.num}}>
168168
{{ version.num }}
169169
</LinkTo>
170170
{{moment-format version.created_at 'll'}}
171-
{{#if version.yanked}}
172-
<span local-class='yanked'>yanked</span>
171+
{{#if this.isOwner}}
172+
<YankButton @version={{version}}/>
173+
{{else}}
174+
{{#if version.yanked}}
175+
<span local-class='yanked'>yanked</span>
176+
{{/if}}
173177
{{/if}}
174178
</li>
175179
{{/each}}

app/templates/crate/versions.hbs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,14 @@
1717
<span local-class='yanked'>yanked</span>
1818
{{/if}}
1919
</div>
20-
<LinkTo @route="crate.version" @model={{version.num}} local-class="arrow">
21-
{{svg-jar "right-arrow"}}
22-
</LinkTo>
20+
<div>
21+
{{#if this.isOwner}}
22+
<YankButton @version={{version}} />
23+
{{/if}}
24+
<LinkTo @route="crate.version" @model={{version.num}} local-class="arrow">
25+
{{svg-jar "right-arrow"}}
26+
</LinkTo>
27+
</div>
2328
</div>
2429
{{/each}}
2530
</div>

mirage/route-handlers/crates.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,28 @@ export function register(server) {
254254

255255
return {};
256256
});
257+
258+
server.delete('/api/v1/crates/:crate_id/:version/yank', (schema, request) => {
259+
const crateId = request.params.crate_id;
260+
const versionNum = request.params.version;
261+
262+
const version = schema.versions.findBy({ crateId, num: versionNum });
263+
if (!version) {
264+
return notFound();
265+
}
266+
267+
return {};
268+
});
269+
270+
server.put('/api/v1/crates/:crate_id/:version/unyank', (schema, request) => {
271+
const crateId = request.params.crate_id;
272+
const versionNum = request.params.version;
273+
274+
const version = schema.versions.findBy({ crateId, num: versionNum });
275+
if (!version) {
276+
return notFound();
277+
}
278+
279+
return {};
280+
});
257281
}

tests/acceptance/crate-test.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { click, fillIn, currentURL, currentRouteName, visit } from '@ember/test-helpers';
1+
import { click, fillIn, currentURL, currentRouteName, visit, waitFor } from '@ember/test-helpers';
22
import { setupApplicationTest } from 'ember-qunit';
33
import { module, test } from 'qunit';
44

@@ -219,6 +219,25 @@ module('Acceptance | crate page', function (hooks) {
219219
assert.dom('[data-test-license]').hasText('MIT/Apache-2.0');
220220
});
221221

222+
test('crates can be yanked by owner', async function (assert) {
223+
this.server.loadFixtures();
224+
225+
let user = this.server.schema.users.findBy({ login: 'thehydroimpulse' });
226+
this.authenticateAs(user);
227+
228+
await visit('/crates/nanomsg');
229+
await click('[data-test-version-yank-button="0.5.0"]');
230+
assert.dom('[data-test-version-yank-button="0.5.0"]').hasText('Yanking...');
231+
assert.dom('[data-test-version-yank-button="0.5.0"]').isDisabled();
232+
233+
await waitFor('[data-test-version-unyank-button="0.5.0"]');
234+
await click('[data-test-version-unyank-button="0.5.0"]');
235+
assert.dom('[data-test-version-unyank-button="0.5.0"]').hasText('Unyanking...');
236+
assert.dom('[data-test-version-unyank-button="0.5.0"]').isDisabled();
237+
238+
await waitFor('[data-test-version-yank-button="0.5.0"]');
239+
});
240+
222241
test('navigating to the owners page when not logged in', async function (assert) {
223242
this.server.loadFixtures();
224243

0 commit comments

Comments
 (0)