• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CenterForOpenScience / ember-osf-web / 4048712895

pending completion
4048712895

push

github

Yuhuai Liu
Merge branch 'release/23.02.0'

2490 of 3712 branches covered (67.08%)

Branch coverage included in aggregate %.

212 of 212 new or added lines in 14 files covered. (100.0%)

5695 of 7754 relevant lines covered (73.45%)

232.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.21
/lib/osf-components/addon/components/node-metadata-manager/component.ts
1
import Store from '@ember-data/store';
2
import { assert } from '@ember/debug';
3
import { action, notifyPropertyChange } from '@ember/object';
4
import { alias, or } from '@ember/object/computed';
5
import { inject as service } from '@ember/service';
6
import { waitFor } from '@ember/test-waiters';
7
import Component from '@glimmer/component';
8
import Intl from 'ember-intl/services/intl';
9
import Toast from 'ember-toastr/services/toast';
10
import { BufferedChangeset } from 'ember-changeset/types';
11
import { restartableTask, task } from 'ember-concurrency';
12
import { taskFor } from 'ember-concurrency-ts';
13

14
import NodeModel from 'ember-osf-web/models/node';
15
import CustomItemMetadataRecordModel from 'ember-osf-web/models/custom-item-metadata-record';
16
import { resourceTypeGeneralOptions } from 'ember-osf-web/models/custom-metadata';
17
import { Permission } from 'ember-osf-web/models/osf-model';
18
import { getApiErrorMessage } from 'ember-osf-web/utils/capture-exception';
19
import { languageCodes, LanguageCode } from 'ember-osf-web/utils/languages';
20
import buildChangeset from 'ember-osf-web/utils/build-changeset';
21
import { tracked } from '@glimmer/tracking';
22

23
import { languageFromLanguageCode } from 'osf-components/components/file-metadata-manager/component';
24
import InstitutionModel from 'ember-osf-web/models/institution';
25
import LicenseModel from 'ember-osf-web/models/license';
26

27
interface Args {
28
    node: (NodeModel);
29
}
30

31
export interface NodeMetadataManager {
32
    edit: () => void;
33
    save: () => void;
34
    cancel: () => void;
35
    metadata: CustomItemMetadataRecordModel;
36
    node: (NodeModel);
37
    changeset: BufferedChangeset;
38
    inEditMode: boolean;
39
    isSaving: boolean;
40
    userCanEdit: boolean;
41
    isDirty: boolean;
42
    isGatheringData: boolean;
43
    institutions: InstitutionModel[];
44
    license: LicenseModel;
45
}
46

47
export default class NodeMetadataManagerComponent extends Component<Args> {
48
    @service store!: Store;
49
    @service intl!: Intl;
50
    @service toast!: Toast;
51

52
    @tracked metadata!: CustomItemMetadataRecordModel;
53
    node: (NodeModel) = this.args.node;
8✔
54
    @tracked changeset!: BufferedChangeset;
55
    @tracked nodeChangeset!: BufferedChangeset;
56
    @or(
57
        'isEditingDescription',
58
        'isEditingFunding',
59
        'isEditingResources',
60
    ) inEditMode!: boolean;
61
    @tracked isEditingDescription = false;
8✔
62
    @tracked isEditingFunding = false;
8✔
63
    @tracked isEditingResources = false;
8✔
64
    @tracked userCanEdit!: boolean;
65
    @or(
66
        'getGuidMetadata.isRunning',
67
        'cancelMetadata.isRunning',
68
        'cancelNode.isRunning',
69
    ) isGatheringData!: boolean;
70
    @or('saveNode.isRunning', 'saveMetadata.isRunning') isSaving!: boolean;
71
    @alias('changeset.isDirty') isDirty!: boolean;
72
    @alias('node.id') nodeId!: string;
73
    @tracked institutions!: InstitutionModel[];
74
    @tracked license!: LicenseModel;
75
    @tracked guidType!: string | undefined;
76
    resourceTypeGeneralOptions: string[] = resourceTypeGeneralOptions;
8✔
77
    languageCodes: LanguageCode[] = languageCodes;
8✔
78
    saveErrored = false;
8✔
79
    saveNodeErrored = false;
8✔
80

81
    constructor(owner: unknown, args: Args) {
82
        super(owner, args);
8✔
83
        assert(
8✔
84
            'You will need pass in a node or registration object to the NodeMetadataManager component to get metadata',
85
            Boolean(args.node),
86
        );
87
        try {
8✔
88
            taskFor(this.getGuidMetadata).perform();
8✔
89
            this.userCanEdit = this.node.currentUserPermissions.includes(Permission.Write);
8✔
90
        } catch (e) {
91
            const errorTitle = this.intl.t('osf-components.item-metadata-manager.error-getting-metadata');
×
92
            this.toast.error(getApiErrorMessage(e), errorTitle);
×
93
        }
94
    }
95

96
    @task
97
    @waitFor
98
    async getGuidMetadata() {
99
        if (this.node) {
8!
100
            const guidRecord = await this.store.findRecord('guid', this.node.id, {
8✔
101
                include: 'custom_metadata',
102
                resolve: false,
103
            });
104
            this.guidType = guidRecord.referentType;
8✔
105
            this.metadata = await guidRecord.customMetadata as CustomItemMetadataRecordModel;
8✔
106
            notifyPropertyChange(this, 'metadata');
8✔
107
            const node = this.node as NodeModel;
8✔
108
            this.institutions = await node.queryHasMany(
8✔
109
                'affiliatedInstitutions', {
110
                    pageSize: 100,
111
                },
112
            );
113
            this.license = await node.license;
8✔
114
            this.changeset = buildChangeset(this.metadata, null);
8✔
115
            this.changeset.languageObject = {
8✔
116
                code: this.metadata.language,
117
                name: this.languageFromCode,
118
            };
119
            this.changeset.execute();
8✔
120
            this.nodeChangeset = buildChangeset(this.node, null);
8✔
121
        }
122
    }
123

124
    get languageFromCode(){
125
        const languageCode = this.metadata?.language || '';
30✔
126
        return languageFromLanguageCode(languageCode);
30✔
127
    }
128

129
    @action
130
    editDescription(){
131
        this.isEditingDescription = true;
3✔
132
    }
133

134
    @action
135
    editResources(){
136
        this.isEditingResources = true;
3✔
137
    }
138

139
    @action
140
    editFunding(){
141
        this.isEditingFunding = true;
1✔
142
    }
143

144
    @task
145
    @waitFor
146
    async cancelMetadata(){
147
        if (this.saveErrored) {
3✔
148
            await this.metadata.reload();
1✔
149
            this.metadata.rollbackAttributes();
1✔
150
            this.saveErrored = false;
1✔
151
        }
152
        this.changeset.rollback();
3✔
153
        this.changeset.languageObject = {
3✔
154
            code: this.metadata.language,
155
            name: this.languageFromCode,
156
        };
157
        this.changeset.execute();
3✔
158
        this.isEditingFunding = false;
3✔
159
        this.isEditingResources = false;
3✔
160
    }
161

162
    @task
163
    @waitFor
164
    async cancelNode(){
165
        if (this.saveNodeErrored){
2✔
166
            await this.node.reload();
1✔
167
            this.saveNodeErrored = false;
1✔
168
        }
169
        this.nodeChangeset.rollback();
2✔
170
        this.isEditingDescription = false;
2✔
171
    }
172

173
    @action
174
    changeLanguage(selected: LanguageCode) {
175
        const language = selected ? selected.code : '';
3!
176
        this.changeset.set('language', language);
3✔
177
    }
178

179
    @restartableTask
180
    @waitFor
181
    async saveMetadata(){
182
        try {
2✔
183
            await this.changeset.save();
2✔
184
            this.isEditingFunding = false;
1✔
185
            this.isEditingResources = false;
1✔
186
            this.saveErrored = false;
1✔
187
        } catch (e) {
188
            this.saveErrored = true;
1✔
189
            if (e.errors[0].source.pointer === '/data/attributes/funders/award_uri') {
1!
190
                this.toast.error(this.intl.t('osf-components.funding-metadata.api_uri_validation_error'));
×
191
            } else {
192
                const errorTitle = this.intl.t('osf-components.item-metadata-manager.error-saving-metadata');
1✔
193
                this.toast.error(getApiErrorMessage(e), errorTitle);
1✔
194
            }
195
        }
196
    }
197

198
    @restartableTask
199
    @waitFor
200
    async saveNode(){
201
        try {
2✔
202
            await this.nodeChangeset.save();
2✔
203
            this.isEditingDescription = false;
1✔
204
            this.saveNodeErrored = false;
1✔
205
        } catch (e) {
206
            this.saveNodeErrored = true;
1✔
207
            const errorTitle = this.intl.t('osf-components.item-metadata-manager.error-saving-metadata');
1✔
208
            this.toast.error(getApiErrorMessage(e), errorTitle);
1✔
209
        }
210
    }
211
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc