@opencloning/ui 1.5.5 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/package.json +10 -3
  3. package/src/components/assembler/Assembler.jsx +1 -0
  4. package/src/components/dummy/index.js +1 -0
  5. package/src/components/sources/FinishedSource.jsx +46 -1
  6. package/src/components/sources/RecombinaseList.cy.jsx +103 -0
  7. package/src/components/sources/RecombinaseList.jsx +131 -0
  8. package/src/components/sources/Source.jsx +2 -0
  9. package/src/components/sources/SourceAssembly.jsx +42 -17
  10. package/src/components/sources/SourceTypeSelector.jsx +1 -0
  11. package/src/hooks/useDatabase.js +2 -17
  12. package/src/providers/DatabaseContext.jsx +15 -0
  13. package/src/version.js +1 -1
  14. package/src/components/eLabFTW/ELabFTWCategorySelect.cy.jsx +0 -86
  15. package/src/components/eLabFTW/ELabFTWCategorySelect.jsx +0 -43
  16. package/src/components/eLabFTW/ELabFTWFileSelect.cy.jsx +0 -43
  17. package/src/components/eLabFTW/ELabFTWFileSelect.jsx +0 -29
  18. package/src/components/eLabFTW/ELabFTWResourceSelect.cy.jsx +0 -107
  19. package/src/components/eLabFTW/ELabFTWResourceSelect.jsx +0 -23
  20. package/src/components/eLabFTW/GetPrimerComponent.cy.jsx +0 -261
  21. package/src/components/eLabFTW/GetPrimerComponent.jsx +0 -55
  22. package/src/components/eLabFTW/GetSequenceFileAndDatabaseIdComponent.cy.jsx +0 -184
  23. package/src/components/eLabFTW/GetSequenceFileAndDatabaseIdComponent.jsx +0 -62
  24. package/src/components/eLabFTW/LoadHistoryComponent.cy.jsx +0 -235
  25. package/src/components/eLabFTW/LoadHistoryComponent.jsx +0 -51
  26. package/src/components/eLabFTW/PrimersNotInDatabaseComponent.cy.jsx +0 -159
  27. package/src/components/eLabFTW/PrimersNotInDatabaseComponent.jsx +0 -54
  28. package/src/components/eLabFTW/SubmitToDatabaseComponent.cy.jsx +0 -185
  29. package/src/components/eLabFTW/SubmitToDatabaseComponent.jsx +0 -51
  30. package/src/components/eLabFTW/common.js +0 -26
  31. package/src/components/eLabFTW/eLabFTWInterface.js +0 -293
  32. package/src/components/eLabFTW/eLabFTWInterface.test.js +0 -839
  33. package/src/components/eLabFTW/envValues.js +0 -7
  34. package/src/components/eLabFTW/utils.js +0 -30
@@ -1,54 +0,0 @@
1
- import React from 'react';
2
- import { useSelector } from 'react-redux';
3
- import { Alert } from '@mui/material';
4
- import { getSubState } from '@opencloning/utils/network';
5
- import ELabFTWCategorySelect from './ELabFTWCategorySelect';
6
-
7
- function PrimersNotInDatabaseComponent({ id, submissionData, setSubmissionData }) {
8
- const primerCategoryId = submissionData?.primerCategoryId;
9
- const primers = useSelector((state) => {
10
- const subState = getSubState(state, id, true);
11
- return subState.primers.filter((p) => !p.database_id);
12
- });
13
-
14
- if (primers.length === 0) return null;
15
-
16
- return (
17
- <Alert
18
- severity={primerCategoryId ? 'success' : 'info'}
19
- sx={{
20
- marginTop: 2,
21
- paddingY: 1,
22
- width: '100%',
23
- '& .MuiAlert-message': {
24
- width: '100%',
25
- },
26
- }}
27
- icon={false}
28
- >
29
- {!primerCategoryId && (
30
- <>
31
- <div>Do you want used primers to be saved to the database?</div>
32
- <ul>
33
- {primers.map((primer) => (
34
- <li key={primer.id}>
35
- {primer.name}
36
- </li>
37
- ))}
38
- </ul>
39
- </>
40
- )}
41
-
42
- <ELabFTWCategorySelect
43
- setCategory={(c) => {
44
- setSubmissionData((prev) => ({ ...prev, primerCategoryId: c ? c.id : null }));
45
- }}
46
- label="Save primers as"
47
- fullWidth
48
- />
49
-
50
- </Alert>
51
- );
52
- }
53
-
54
- export default PrimersNotInDatabaseComponent;
@@ -1,185 +0,0 @@
1
- import React from 'react';
2
- import { Provider } from 'react-redux';
3
- import { configureStore } from '@reduxjs/toolkit';
4
- import SubmitToDatabaseComponent from './SubmitToDatabaseComponent';
5
- import { eLabFTWHttpClient } from './common';
6
- import { mockSequences, mockPrimers, mockSources, mockTeselaJsonCache } from '../../../../../tests/mockNetworkData';
7
- import { clearAutocompleteValue } from '../../../../../cypress/e2e/common_functions';
8
-
9
- const PRIMER_CATEGORY_ID = 3;
10
-
11
- // Mock initial state with both primers and sequences
12
- const defaultState = {
13
- sequences: mockSequences,
14
- sources: mockSources,
15
- primers: mockPrimers,
16
- teselaJsonCache: mockTeselaJsonCache,
17
- };
18
-
19
- const createTestStore = (cloningState) => configureStore({
20
- reducer: {
21
- cloning: (state = cloningState) => state,
22
- },
23
- preloadedState: { cloning: cloningState },
24
- });
25
-
26
- describe('<SubmitToDatabaseComponent />', () => {
27
- it('Primers: initializes with primer name and handles updates', () => {
28
- cy.stub(eLabFTWHttpClient, 'get')
29
- .withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: { limit: 9999 } })
30
- .resolves({
31
- data: [
32
- { id: PRIMER_CATEGORY_ID, title: 'Primers' },
33
- { id: 2, title: 'Sequences' },
34
- ],
35
- }).withArgs('/api/v2/info').resolves({
36
- data: {
37
- elabftw_version_int: 50200,
38
- },
39
- });
40
- const store = createTestStore(defaultState);
41
- const setSubmissionDataSpy = cy.spy().as('setSubmissionDataSpy');
42
-
43
- cy.mount(
44
- <Provider store={store}>
45
- <SubmitToDatabaseComponent
46
- id={mockPrimers[0].id}
47
- resourceType="primer"
48
- setSubmissionData={setSubmissionDataSpy}
49
- />
50
- </Provider>,
51
- );
52
-
53
- // Should initialize with primer name
54
- cy.get('input#resource_title').should('have.value', 'Primer1');
55
-
56
- // Change title
57
- cy.get('input#resource_title').clear();
58
- cy.get('input#resource_title').type('Modified Primer');
59
-
60
- // Select category
61
- cy.get('input').last().click();
62
- cy.get('li').contains('Primers').click();
63
-
64
- // Should update submission data with both title and category
65
- cy.get('@setSubmissionDataSpy').should((spy) => {
66
- const updateFn = spy.lastCall.args[0];
67
- const result = updateFn({ existingKey: 'value' });
68
- expect(result).to.deep.equal({
69
- existingKey: 'value',
70
- categoryId: PRIMER_CATEGORY_ID,
71
- title: 'Modified Primer',
72
- });
73
- });
74
- // Clearing the field should set submission data to null
75
- clearAutocompleteValue('Save primer as', 'div');
76
- cy.get('@setSubmissionDataSpy').should((spy) => {
77
- const result = spy.lastCall.args[0];
78
- expect(result).equal(null);
79
- });
80
- });
81
-
82
- it('Sequences: initializes with sequence name and handles updates', () => {
83
- cy.stub(eLabFTWHttpClient, 'get')
84
- .withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: { limit: 9999 } })
85
- .resolves({
86
- data: [
87
- { id: PRIMER_CATEGORY_ID, title: 'Primers' },
88
- { id: 2, title: 'Sequences' },
89
- ],
90
- }).withArgs('/api/v2/info').resolves({
91
- data: {
92
- elabftw_version_int: 50200,
93
- },
94
- });
95
- const store = createTestStore(defaultState);
96
- const setSubmissionDataSpy = cy.spy().as('setSubmissionDataSpy');
97
-
98
- cy.mount(
99
- <Provider store={store}>
100
- <SubmitToDatabaseComponent
101
- id={mockSequences[0].id}
102
- resourceType="sequence"
103
- setSubmissionData={setSubmissionDataSpy}
104
- />
105
- </Provider>,
106
- );
107
-
108
- // Should initialize with primer name
109
- cy.get('input#resource_title').should('have.value', 'Seq1');
110
-
111
- // Change title
112
- cy.get('input#resource_title').clear();
113
- cy.get('input#resource_title').type('Modified Sequence');
114
-
115
- // Select category
116
- cy.get('input').last().click();
117
- cy.get('li').contains('Sequences').click();
118
-
119
- // Should update submission data with both title and category
120
- cy.get('@setSubmissionDataSpy').should((spy) => {
121
- const updateFn = spy.lastCall.args[0];
122
- const result = updateFn({ existingKey: 'value' });
123
- expect(result).to.deep.equal({
124
- existingKey: 'value',
125
- categoryId: 2,
126
- title: 'Modified Sequence',
127
- });
128
- });
129
- // Clearing the field should set submission data to null
130
- clearAutocompleteValue('Save sequence as', 'div');
131
- cy.get('@setSubmissionDataSpy').should((spy) => {
132
- const result = spy.lastCall.args[0];
133
- expect(result).equal(null);
134
- });
135
- });
136
-
137
- it('handles API errors in category loading', () => {
138
- const store = createTestStore(defaultState);
139
- let firstCall = true;
140
-
141
- // Stub API to fail first time
142
- cy.stub(eLabFTWHttpClient, 'get')
143
- .withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: { limit: 9999 } })
144
- .callsFake(() => {
145
- if (firstCall) {
146
- firstCall = false;
147
- const error = new Error('Failed to fetch');
148
- error.response = { status: 500 };
149
- return Promise.reject(error);
150
- }
151
- return Promise.resolve({
152
- data: [
153
- { id: PRIMER_CATEGORY_ID, title: 'Primers' },
154
- { id: 2, title: 'Sequences' },
155
- ],
156
- })
157
- }).withArgs('/api/v2/info', { headers: { Authorization: 'test-read-key' }}).resolves({
158
- data: {
159
- elabftw_version_int: 50200,
160
- },
161
- });
162
-
163
- cy.mount(
164
- <Provider store={store}>
165
- <SubmitToDatabaseComponent
166
- id={mockPrimers[0].id}
167
- resourceType="primer"
168
- setSubmissionData={cy.spy()}
169
- />
170
- </Provider>,
171
- );
172
-
173
- // Should show error state
174
- cy.get('.MuiAlert-colorError').should('exist');
175
- cy.contains('Could not retrieve categories').should('exist');
176
-
177
- // Click retry
178
- cy.contains('Retry').click();
179
-
180
- // Should show categories after retry
181
- cy.get('input').last().click();
182
- cy.get('li').contains('Primers').should('exist');
183
- cy.get('li').contains('Sequences').should('exist');
184
- });
185
- });
@@ -1,51 +0,0 @@
1
- import { FormControl, TextField } from '@mui/material';
2
- import React from 'react';
3
- import { useSelector } from 'react-redux';
4
- import ELabFTWCategorySelect from './ELabFTWCategorySelect';
5
-
6
- function SubmitToDatabaseComponent({ id, setSubmissionData, resourceType }) {
7
- const name = useSelector((state) => {
8
- if (resourceType === 'primer') {
9
- return state.cloning.primers.find((p) => p.id === id).name;
10
- }
11
- return state.cloning.teselaJsonCache[id].name;
12
- });
13
- const [title, setTitle] = React.useState(name);
14
- const [category, setCategory] = React.useState(null);
15
-
16
- React.useEffect(() => {
17
- setTitle(name);
18
- }, [name]);
19
-
20
- React.useEffect(() => {
21
- if (category && title) {
22
- // We do this not overwrite primerCategoryId, set from a different component
23
- setSubmissionData((prev) => ({ ...prev, categoryId: category.id, title }));
24
- } else {
25
- setSubmissionData(null);
26
- }
27
- }, [category, title]);
28
-
29
- return (
30
- <>
31
- <FormControl fullWidth sx={{ mb: 2 }}>
32
- <TextField
33
- autoFocus
34
- required
35
- id="resource_title"
36
- label="Resource title"
37
- variant="standard"
38
- value={title}
39
- onChange={(e) => setTitle(e.target.value)}
40
- />
41
- </FormControl>
42
- <ELabFTWCategorySelect
43
- fullWidth
44
- label={`Save ${resourceType} as`}
45
- setCategory={setCategory}
46
- />
47
- </>
48
- );
49
- }
50
-
51
- export default SubmitToDatabaseComponent;
@@ -1,26 +0,0 @@
1
- import axios from 'axios';
2
- import { readApiKey, writeApiKey, baseUrl as envBaseUrl } from './envValues';
3
-
4
- export const baseUrl = envBaseUrl;
5
- export const readHeaders = readApiKey ? { Authorization: readApiKey } : {};
6
- export const writeHeaders = writeApiKey ? { Authorization: writeApiKey } : {};
7
-
8
- export const eLabFTWHttpClient = axios.create({
9
- baseURL: baseUrl,
10
- });
11
-
12
- export const makeSequenceMetadata = (sequence) => JSON.stringify({
13
- extra_fields: {
14
- sequence: {
15
- type: 'text',
16
- value: sequence,
17
- group_id: null,
18
- },
19
- },
20
- });
21
-
22
- export const getELabFTWVersion = async () => {
23
- const url = `/api/v2/info`;
24
- const resp = await eLabFTWHttpClient.get(url, { headers: readHeaders });
25
- return resp.data.elabftw_version_int;
26
- };
@@ -1,293 +0,0 @@
1
- import { Save as SaveIcon, Link as LinkIcon } from '@mui/icons-material';
2
- import GetSequenceFileAndDatabaseIdComponent from './GetSequenceFileAndDatabaseIdComponent';
3
- import SubmitToDatabaseComponent from './SubmitToDatabaseComponent';
4
- import PrimersNotInDatabaseComponent from './PrimersNotInDatabaseComponent';
5
- import GetPrimerComponent from './GetPrimerComponent';
6
- import { eLabFTWHttpClient, writeHeaders, readHeaders, baseUrl, getELabFTWVersion } from './common';
7
- import { getFileFromELabFTW, error2String } from './utils';
8
- import LoadHistoryComponent from './LoadHistoryComponent';
9
-
10
- async function deleteResource(resourceId) {
11
- const url = `/api/v2/items/${resourceId}`;
12
- // eLabFTW requires application/json for delete requests, axios seems to require a data field for it to work
13
- const resp = await eLabFTWHttpClient.delete(url, { data: {}, headers: { ...writeHeaders, 'Content-Type': 'application/json' } });
14
- return resp.data;
15
- }
16
-
17
- const linkToParent = async (childId, parentId) => {
18
- await eLabFTWHttpClient.post(
19
- `/api/v2/items/${childId}/items_links/${parentId}`,
20
- {},
21
- { headers: writeHeaders },
22
- );
23
- };
24
-
25
- const createResource = async (categoryId) => {
26
- const eLabFTWVersion = await getELabFTWVersion();
27
- const categoryKey = eLabFTWVersion && eLabFTWVersion >= 50300 ? 'category' : 'category_id';
28
- const createdItemResponse = await eLabFTWHttpClient.post(
29
- '/api/v2/items',
30
- {
31
- [categoryKey]: categoryId,
32
- },
33
- { headers: writeHeaders },
34
- );
35
- return Number(createdItemResponse.headers.location.split('/').pop());
36
- };
37
-
38
- const patchResource = async (resourceId, title, metadata = undefined) => eLabFTWHttpClient.patch(
39
- `/api/v2/items/${resourceId}`,
40
- { title, ...(metadata !== undefined && { metadata }) },
41
- { headers: writeHeaders },
42
- );
43
-
44
- async function submitPrimerToDatabase({ submissionData: { title, categoryId }, primer, linkedSequenceId = null }) {
45
- let resourceId;
46
- try {
47
- resourceId = await createResource(categoryId);
48
- } catch (e) {
49
- console.error(e);
50
- throw new Error(`Error creating primer: ${error2String(e)}`);
51
- }
52
- const metadata = JSON.stringify({ extra_fields: { sequence: { type: 'text', value: primer.sequence, group_id: null } } });
53
- let stage;
54
- try {
55
- stage = 'naming primer';
56
- await patchResource(resourceId, title, metadata);
57
- if (linkedSequenceId) {
58
- stage = 'linking to sequence';
59
- await linkToParent(linkedSequenceId, resourceId);
60
- }
61
- } catch (e) {
62
- console.error(e);
63
- try {
64
- await deleteResource(resourceId);
65
- } catch (e2) {
66
- console.error(e2);
67
- throw new Error(`There was an error (${error2String(e2)}) while trying to delete primer with id ${resourceId} after an error ${stage}.`);
68
- }
69
- throw new Error(`Error ${stage}: ${error2String(e)}`);
70
- }
71
- return resourceId;
72
- }
73
-
74
- async function uploadTextFileToResource(resourceId, fileName, textContent, comment) {
75
- const blob = new Blob([textContent], { type: 'text/plain' });
76
- const formData = new FormData();
77
- formData.append('file', blob, fileName);
78
- formData.append('comment', comment);
79
- const response = await eLabFTWHttpClient.post(`/api/v2/items/${resourceId}/uploads`, formData, { headers: writeHeaders });
80
- return Number(response.headers.location.split('/').pop());
81
- }
82
-
83
- async function submitSequenceToDatabase({ submissionData: { title, categoryId, primerCategoryId }, substate, id }) {
84
- /**
85
- * Submit a sequence to eLabFTW database
86
- * @param {Object} params - The parameters object
87
- * @param {Object} params.submissionData - Data needed for submission
88
- * @param {string} params.submissionData.title - Title of the sequence
89
- * @param {number} params.submissionData.categoryId - Category ID in eLabFTW
90
- * @param {Object} params.substate - The substate containing sequence data
91
- * @param {Array} params.substate.sources - Array of source objects
92
- * @param {Array} params.substate.primers - Array of primer objects
93
- * @param {Array} params.substate.sequences - Array of sequence objects
94
- * @param {string} params.id - ID of the sequence to submit
95
- * @returns {Promise<Object>}
96
- */
97
-
98
- const { sources, primers, sequences, appInfo } = substate;
99
- const { backendVersion, schemaVersion, frontendVersion } = appInfo;
100
-
101
- const sequence2export = sequences.find((e) => e.id === id);
102
- const parentSource = sources.find((s) => s.id === id);
103
- if (parentSource.database_id) {
104
- throw new Error('Sequence already has a database_id');
105
- }
106
- // Get ancestor sources that are database sources to link to the sequence
107
- const parentDatabaseSources = sources.filter((source) => source.database_id);
108
- const parentResourceIds = parentDatabaseSources.map((source) => source.database_id);
109
- const primerIds = primers.map((p) => p.id);
110
-
111
- // Link and/or add used primers
112
- const newPrimersToSave = [];
113
- const existingPrimersToLink = primers.filter((p) => primerIds.includes(p.id) && p.database_id).map((p) => p.database_id);
114
- if (primerCategoryId) {
115
- newPrimersToSave.push(...primers.filter((p) => primerIds.includes(p.id) && !p.database_id));
116
- }
117
-
118
- // Create and name the resource
119
- let resourceId;
120
- try {
121
- resourceId = await createResource(categoryId);
122
- } catch (e) {
123
- console.error(e);
124
- throw new Error(`Error creating resource: ${error2String(e)}`);
125
- }
126
- let stage;
127
- let newPrimerDatabaseIds = [];
128
- try {
129
- // Patch the resource with the title
130
- stage = 'setting resource title';
131
- await patchResource(resourceId, title);
132
-
133
- // Add the links to parent Resources
134
- stage = 'linking to parent resources';
135
- await Promise.all(parentResourceIds.map((parentId) => linkToParent(resourceId, parentId)));
136
-
137
- // Add the links to the existing primers
138
- stage = 'linking to existing primers';
139
- await Promise.all(existingPrimersToLink.map((primerId) => linkToParent(resourceId, primerId)));
140
-
141
- // Add the new primers to the database and link them to the resource
142
- stage = 'submitting new primers';
143
- newPrimerDatabaseIds = await Promise.all(newPrimersToSave.map((primer) => submitPrimerToDatabase({ submissionData: { title: primer.name, categoryId: primerCategoryId }, primer, linkedSequenceId: resourceId })));
144
- const primerMappings = newPrimerDatabaseIds.map((databaseId, index) => ({ databaseId, localId: newPrimersToSave[index].id }));
145
-
146
- // Deep-copy primers and update the primers with the database IDs before storing the history
147
- const primersCopy = primers.map((p) => ({ ...p }));
148
- primerMappings.forEach(({ databaseId: dbId, localId }) => {
149
- primersCopy.find((p) => p.id === localId).database_id = dbId;
150
- });
151
-
152
- const cloningStrategy = {
153
- sources,
154
- primers: primersCopy,
155
- sequences,
156
- backend_version: backendVersion,
157
- schema_version: schemaVersion,
158
- frontend_version: frontendVersion,
159
- };
160
-
161
- // Add the sequence and history files to the resource
162
- stage = 'uploading sequence file';
163
- await uploadTextFileToResource(resourceId, `${title}.gb`, sequence2export.file_content, 'resource sequence - generated by OpenCloning');
164
- stage = 'uploading history file';
165
- await uploadTextFileToResource(resourceId, `${title}_history.json`, JSON.stringify(cloningStrategy), 'history file - generated by OpenCloning');
166
- // Format output values
167
- return { primerMappings, databaseId: resourceId };
168
- } catch (e) {
169
- console.error(e);
170
- let primersDeleted = false;
171
- try {
172
- await Promise.all(newPrimerDatabaseIds.map(deleteResource));
173
- primersDeleted = true;
174
- await deleteResource(resourceId);
175
- } catch (e2) {
176
- console.error(e2);
177
- if (primersDeleted) {
178
- throw new Error(`There was an error (${error2String(e2)}) while trying to delete the sequence with id ${resourceId} after an error ${stage}.`);
179
- }
180
- throw new Error(`There was an error (${error2String(e2)}) while trying to delete newly created primers linked to sequence with id ${resourceId} after an error ${stage}.`);
181
- }
182
- throw new Error(`Error ${stage}: ${error2String(e)}`);
183
- }
184
- }
185
-
186
- function isSubmissionDataValid(submissionData) {
187
- // This function is necessary because you might be setting submissionData from multiple components
188
- return Boolean(submissionData.title && submissionData.categoryId);
189
- }
190
-
191
- async function loadSequenceFromUrlParams(urlParams) {
192
- const { item_id: itemId, file_id: fileId } = urlParams;
193
-
194
- if (itemId && fileId) {
195
- const url = `/api/v2/items/${itemId}/uploads/${fileId}`;
196
- let fileInfo;
197
- try {
198
- const resp = await eLabFTWHttpClient.get(url, { headers: readHeaders });
199
- fileInfo = resp.data;
200
- } catch (e) {
201
- throw new Error(`${error2String(e)}`);
202
- }
203
-
204
- // getFileFromELabFTW already handles errors
205
- const file = await getFileFromELabFTW(itemId, fileInfo);
206
- return { file, databaseId: itemId };
207
- }
208
- return null;
209
- }
210
-
211
- async function getPrimer(databaseId) {
212
- const url = `/api/v2/items/${databaseId}`;
213
- try {
214
- const resp = await eLabFTWHttpClient.get(url, { headers: readHeaders });
215
- resp.data.metadata = JSON.parse(resp.data.metadata);
216
- return { name: resp.data.title, database_id: databaseId, sequence: resp.data.metadata.extra_fields.sequence?.value };
217
- } catch (e) {
218
- console.error(e);
219
- if (e.code === 'ERR_NETWORK') {
220
- throw new Error(`Error getting primer: ${error2String(e)}`);
221
- }
222
- throw new Error(`Error getting primer with id ${databaseId}, it might have been deleted or you can no longer access it`);
223
- }
224
- }
225
-
226
- async function getSequenceName(databaseId) {
227
- const url = `/api/v2/items/${databaseId}`;
228
- try {
229
- const resp = await eLabFTWHttpClient.get(url, { headers: readHeaders });
230
- return resp.data.title;
231
- } catch (e) {
232
- console.error(e);
233
- if (e.code === 'ERR_NETWORK') {
234
- throw new Error(`Error getting sequence name: ${error2String(e)}`);
235
- }
236
- throw new Error(`Error getting name of sequence with id ${databaseId}, it might have been deleted or you can no longer access it`);
237
- }
238
- }
239
-
240
- async function getSequencingFiles(databaseId) {
241
- // This function should return an array of objects:
242
- // name: the name of the file
243
- // getFile: an async function that returns the file content
244
- const url = `/api/v2/items/${databaseId}/uploads`;
245
- try {
246
- const resp = await eLabFTWHttpClient.get(url, { headers: readHeaders });
247
- return resp.data.map((fileInfo) => ({
248
- name: fileInfo.real_name,
249
- getFile: async () => getFileFromELabFTW(databaseId, fileInfo),
250
- }));
251
- } catch (e) {
252
- console.error(e);
253
- throw new Error(`${error2String(e)}`);
254
- }
255
- }
256
-
257
- export default {
258
- // Name of the database interface
259
- name: 'eLabFTW',
260
- // Returns a link to the sequence in the database
261
- getSequenceLink: (databaseId) => `${baseUrl}/database.php?mode=view&id=${databaseId}`,
262
- // Returns a link to the primer in the database
263
- getPrimerLink: (databaseId) => `${baseUrl}/database.php?mode=view&id=${databaseId}`,
264
- // Component for selecting and loading sequence files from the database
265
- GetSequenceFileAndDatabaseIdComponent,
266
- // Component for selecting and loading primers from the database
267
- GetPrimerComponent,
268
- // Component for submitting resources to the database
269
- SubmitToDatabaseComponent,
270
- // Component for handling primers not yet in database
271
- PrimersNotInDatabaseComponent,
272
- // Function to submit a primer to the database
273
- submitPrimerToDatabase,
274
- // Function to submit a sequence and its history to the database
275
- submitSequenceToDatabase,
276
- // Function to validate submission data
277
- isSubmissionDataValid,
278
- // Icon displayed on the node corner to submit
279
- SubmitIcon: SaveIcon,
280
- // Icon displayed on the node corner for sequences in the database
281
- DatabaseIcon: LinkIcon,
282
- // OPTIONAL =======================================================================
283
- // Component for loading history from the database (can be hook-like does not have to render anything)
284
- LoadHistoryComponent,
285
- // Function to load sequences from url parameters
286
- loadSequenceFromUrlParams,
287
- // Function to get the primer ({name, database_id, sequence}) from the database
288
- getPrimer,
289
- // Function to get the name of a sequence from the database
290
- getSequenceName,
291
- // Function to get the sequencing files from the database, see docs for what the return value should be
292
- getSequencingFiles,
293
- };