Skip to content

Commit bfdf4b9

Browse files
committed
feat(ember)!: dependency sources
- adjust to backend - adjust form to include the ability to set maintainers per source
1 parent 666fac1 commit bfdf4b9

File tree

21 files changed

+278
-142
lines changed

21 files changed

+278
-142
lines changed

ember/app/components/project-compact.gjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function icon(status) {
1919
}
2020

2121
function version(project) {
22-
return project.versionedDependencies[0];
22+
return project.sources.at(0)?.versions.at(0);
2323
}
2424

2525
const Dependency = <template>

ember/app/components/project-detailed.gjs

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export default class ProjectDetailedComponent extends Component {
2424
const project = await this.fetch.fetch(
2525
`/api/projects/${this.args.project.id}/sync?${new URLSearchParams({
2626
include:
27-
'versionedDependencies,versionedDependencies.releaseVersion,versionedDependencies.releaseVersion.dependency',
27+
'sources,sources.versions,sources.versions.release-version,sources.versions.release-version.dependency,sources.maintainers,sources.maintainers.user',
2828
})}`,
2929
{
3030
method: 'POST',
@@ -60,8 +60,8 @@ export default class ProjectDetailedComponent extends Component {
6060
}
6161
});
6262

63-
get dependencyData() {
64-
return this.args.project.versionedDependencies.map((version) => ({
63+
sourceData(source) {
64+
return source.versions.map((version) => ({
6565
component: <template>
6666
<tr class='{{statusToClass version.status}}'>{{yield}}</tr>
6767
</template>,
@@ -76,21 +76,24 @@ export default class ProjectDetailedComponent extends Component {
7676
}));
7777
}
7878

79-
get maintainerData() {
80-
return this.args.project.maintainers.map((m) => ({
81-
values: {
82-
username: <template>
83-
{{#if m.isPrimary}}<UkIcon @icon='star' @ratio={{0.5}} />
84-
{{/if}}{{m.user.username}}
85-
</template>,
86-
email: <template>
87-
<a
88-
class='uk-link-text'
89-
href='mailto:{{m.user.email}}'
90-
>{{m.user.email}}</a>
91-
</template>,
92-
},
93-
}));
79+
maintainerData(source) {
80+
return source.maintainers
81+
.slice()
82+
.sort((a, b) => (a.isPrimary - b.isPrimary) * -1)
83+
.map((m) => ({
84+
values: {
85+
username: <template>
86+
{{#if m.isPrimary}}<UkIcon @icon='star' @ratio={{0.5}} />
87+
{{/if}}{{m.user.username}}
88+
</template>,
89+
email: <template>
90+
<a
91+
class='uk-link-text'
92+
href='mailto:{{m.user.email}}'
93+
>{{m.user.email}}</a>
94+
</template>,
95+
},
96+
}));
9497
}
9598

9699
<template>
@@ -109,19 +112,21 @@ export default class ProjectDetailedComponent extends Component {
109112
</div>
110113

111114
<hr class='seperator' />
112-
<Table @data={{this.maintainerData}} @title='Maintainers' as |t|>
113-
<t.head />
114-
<t.body />
115-
</Table>
116-
<Table
117-
@data={{this.dependencyData}}
118-
@fallback='No dependencies yet'
119-
@title='Dependencies'
120-
as |t|
121-
>
122-
<t.head />
123-
<t.body />
124-
</Table>
115+
{{#each @project.sources as |source|}}
116+
<Table
117+
@data={{this.sourceData source}}
118+
@fallback='No dependencies yet'
119+
@title={{source.path}}
120+
as |t|
121+
>
122+
<t.head />
123+
<t.body />
124+
</Table>
125+
<Table @data={{this.maintainerData source}} as |t|>
126+
<t.head />
127+
<t.body />
128+
</Table>
129+
{{/each}}
125130
<UkButton
126131
@label='Sync'
127132
@loading={{this.syncProject.isRunning}}
Lines changed: 7 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { service } from '@ember/service';
22
import Component from '@glimmer/component';
33
import { dropTask } from 'ember-concurrency';
4-
import { scheduleTask } from 'ember-lifeline';
54
import { tracked } from 'tracked-built-ins';
65

76
import { emptyChangeset } from 'outdated/utils';
@@ -10,7 +9,6 @@ import ProjectValidations from 'outdated/validations/project';
109
export default class ProjectFormComponent extends Component {
1110
@service store;
1211
@service router;
13-
@tracked maintainers;
1412

1513
@service notification;
1614

@@ -19,51 +17,23 @@ export default class ProjectFormComponent extends Component {
1917
this.args.project ?? this.store.createRecord('project'),
2018
);
2119

22-
constructor(...args) {
23-
super(...args);
24-
if (this.args.project) {
25-
scheduleTask(this, 'actions', () => {
26-
this.maintainers = this.project.maintainers;
27-
this.project.users = this.project.maintainers.map((m) => m.user);
28-
this.project.primaryMaintainer = this.maintainers?.find(
29-
(m) => m.isPrimary,
30-
)?.user;
31-
});
32-
}
33-
}
34-
3520
saveProject = dropTask(async () => {
3621
try {
3722
if (this.project.repoType === 'public') {
3823
this.project.accessToken = '';
3924
}
4025

26+
document
27+
.querySelectorAll('[data-source-form-submit-button]')
28+
.forEach((e) => e.click());
29+
4130
const project = await this.project.save({
4231
adapterOptions: {
4332
include:
44-
'versionedDependencies,versionedDependencies.releaseVersion,versionedDependencies.releaseVersion.dependency,maintainers,maintainers.user',
33+
'sources,sources.versions,sources.versions.release-version,sources.versions.release-version.dependency,sources.maintainers,sources.maintainers.user',
4534
},
4635
});
4736

48-
this.maintainers
49-
?.filter((m) => !this.project.users?.includes(m.user))
50-
.forEach((m) => m.destroyRecord());
51-
this.project.users?.forEach((user) => {
52-
const maintainer = this.maintainers?.find((m) => m.user.id === user.id);
53-
if (maintainer) {
54-
maintainer.isPrimary = user.id === this.primaryMaintainer.id;
55-
if (maintainer.hasDirtyAttributes) maintainer.save();
56-
} else {
57-
this.store
58-
.createRecord('maintainer', {
59-
user,
60-
project,
61-
isPrimary: user === this.primaryMaintainer,
62-
})
63-
.save();
64-
}
65-
});
66-
6737
this.router.transitionTo('projects.detailed', project.id);
6838
this.project.accessToken = '';
6939
this.notification.success('Successfully saved!');
@@ -73,19 +43,11 @@ export default class ProjectFormComponent extends Component {
7343
}
7444
});
7545

76-
get primaryMaintainer() {
77-
return (
78-
this.project.users?.find(
79-
(u) => u.id === this.project.primaryMaintainer?.id,
80-
) ?? this.project.users[0]
81-
);
46+
get repoTypes() {
47+
return ['public', 'access-token'];
8248
}
8349

8450
get users() {
8551
return this.store.peekAll('user');
8652
}
87-
88-
get repoTypes() {
89-
return ['public', 'access-token'];
90-
}
9153
}

ember/app/components/project-form/template.hbs

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,8 @@
3030
autocomplete="off"
3131
@hidden={{(eq this.project.repoType "public")}}
3232
/>
33-
34-
<f.input
35-
@name="users"
36-
@type="select"
37-
@label="Maintainers"
38-
@multiple={{true}}
39-
@value={{this.project.users}}
40-
@options={{this.users}}
41-
@searchField="searchField"
42-
@visibleField="fullName"
43-
/>
44-
{{#if (gt this.project.users.length 1)}}
45-
<f.input
46-
@name="primaryMaintainer"
47-
@type="select"
48-
@options={{this.project.users}}
49-
@value={{this.primaryMaintainer}}
50-
@searchField="searchField"
51-
@visibleField="fullName"
52-
/>
53-
{{/if}}
33+
{{#each @project.sources as |source|}}
34+
<SourceForm @source={{source}} />
35+
{{/each}}
5436
<f.button @loading={{this.saveProject.isRunning}} />
5537
</Form>
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { service } from '@ember/service';
2+
import Component from '@glimmer/component';
3+
import { dropTask } from 'ember-concurrency';
4+
import perform from 'ember-concurrency/helpers/perform';
5+
import { scheduleTask } from 'ember-lifeline';
6+
import { lt, or } from 'ember-truth-helpers';
7+
import { tracked } from 'tracked-built-ins';
8+
9+
import Form from './form';
10+
11+
import { emptyChangeset } from 'outdated/utils';
12+
import SourceValidations from 'outdated/validations/source';
13+
14+
export default class SourceForm extends Component {
15+
@service store;
16+
@service notification;
17+
18+
@tracked source = emptyChangeset(SourceValidations, this.args.source);
19+
20+
constructor(...args) {
21+
super(...args);
22+
23+
scheduleTask(this, 'actions', () => {
24+
const source = this.args.source;
25+
source.users = source.maintainers.map((m) => m.user);
26+
source.primaryMaintainer = source.maintainers.find(
27+
(m) => m.isPrimary,
28+
)?.user;
29+
});
30+
}
31+
32+
get primaryMaintainer() {
33+
return (
34+
this.source.users?.find(
35+
(u) => u.id === this.source.primaryMaintainer?.id,
36+
) ?? this.source.users[0]
37+
);
38+
}
39+
40+
get users() {
41+
return this.store.peekAll('user');
42+
}
43+
44+
saveMaintainers = dropTask(async () => {
45+
try {
46+
this.source.maintainers
47+
?.filter(
48+
(m) => !this.source.users?.map((u) => u.id).includes(m.user.id),
49+
)
50+
.forEach((m) => m.destroyRecord());
51+
this.source.users
52+
?.filter(
53+
(u) => !this.source.maintainers?.find((m) => m.user.id === u.id),
54+
)
55+
.forEach((user) => {
56+
this.store.createRecord('maintainer', {
57+
user,
58+
source: this.args.source,
59+
isPrimary: user.id === this.primaryMaintainer.id,
60+
});
61+
});
62+
this.source.maintainers.forEach((m) => {
63+
m.isPrimary = m.user.id === this.primaryMaintainer.id;
64+
if (m.hasDirtyAttributes) m.save();
65+
});
66+
this.store.findRecord('dependency-source', this.source.id, {
67+
include: 'maintainers,maintainers.user',
68+
});
69+
this.notification.success(
70+
`Successfully saved maintainers for ${this.source.path}`,
71+
);
72+
} catch (e) {
73+
this.notification.danger(e);
74+
console.error(e);
75+
}
76+
});
77+
78+
<template>
79+
<Form
80+
@onSubmit={{perform this.saveMaintainers}}
81+
@model={{this.source}}
82+
@name={{@source.path}}
83+
as |f|
84+
>
85+
<f.input
86+
@label='Maintainers'
87+
@name='users'
88+
@type='select'
89+
@options={{this.users}}
90+
@multiple={{true}}
91+
@searchField='searchField'
92+
@visibleField='fullName'
93+
/>
94+
<f.input
95+
@name='primaryMaintainer'
96+
@options={{this.source.users}}
97+
@hidden={{lt (or this.source.users.length 0) 2}}
98+
@value={{this.primaryMaintainer}}
99+
@type='select'
100+
@searchField='searchField'
101+
@visibleField='fullName'
102+
/>
103+
<f.button class='uk-hidden' data-source-form-submit-button />
104+
</Form>
105+
</template>
106+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import Model, { attr, hasMany, belongsTo } from '@ember-data/model';
2+
import { tracked } from 'tracked-built-ins';
3+
4+
export default class DependencySourceModel extends Model {
5+
@attr status;
6+
@attr path;
7+
8+
@hasMany('version', { inverse: null, async: false }) versions;
9+
@hasMany('maintainer', { inverse: 'source', async: false, as: 'source' })
10+
maintainers;
11+
@belongsTo('project', {
12+
inverse: 'sources',
13+
async: true,
14+
as: 'dependency-source',
15+
})
16+
project;
17+
18+
@tracked users;
19+
@tracked primaryMaintainer;
20+
}

ember/app/models/maintainer.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ import Model, { attr, belongsTo } from '@ember-data/model';
22

33
export default class MaintainerModel extends Model {
44
@attr isPrimary;
5-
@belongsTo('project', { inverse: 'maintainers', async: true }) project;
5+
@belongsTo('source', {
6+
inverse: 'maintainers',
7+
async: true,
8+
as: 'maintainer',
9+
polymorphic: true,
10+
})
11+
source;
612
@belongsTo('user', { inverse: null, async: false }) user;
713
}

ember/app/models/project.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import Model, { attr, hasMany } from '@ember-data/model';
2-
import { tracked } from '@glimmer/tracking';
32

43
export default class ProjectModel extends Model {
54
@attr name;
@@ -8,11 +7,13 @@ export default class ProjectModel extends Model {
87
@attr({ defaultValue: 'public' }) repoType;
98
@attr accessToken;
109

11-
@hasMany('version', { inverse: null, async: false }) versionedDependencies;
12-
@hasMany('maintainer', { inverse: 'project', async: false }) maintainers;
13-
14-
@tracked users;
15-
@tracked primaryMaintainer;
10+
@hasMany('dependency-source', {
11+
inverse: 'project',
12+
async: false,
13+
as: 'project',
14+
polymorphic: true,
15+
})
16+
sources;
1617

1718
get repoURL() {
1819
return `https://${this.repo}`;

0 commit comments

Comments
 (0)