@opencloning/opencloning-elabftw 1.9.2 → 2.0.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.
- package/CHANGELOG.md +57 -0
- package/package.json +4 -4
- package/src/ELabFTWCategorySelect.cy.jsx +3 -22
- package/src/ELabFTWCategorySelect.jsx +9 -3
- package/src/ELabFTWFileSelect.jsx +2 -1
- package/src/GetPrimerComponent.jsx +1 -0
- package/src/GetSequenceFileAndDatabaseIdComponent.jsx +1 -1
- package/src/LoadHistoryComponent.jsx +2 -1
- package/src/PrimersNotInDatabaseComponent.cy.jsx +1 -1
- package/src/PrimersNotInDatabaseComponent.jsx +1 -0
- package/src/SubmitToDatabaseComponent.cy.jsx +3 -3
- package/src/SubmitToDatabaseComponent.jsx +1 -0
- package/src/common.js +2 -0
- package/src/eLabFTWInterface.js +10 -15
- package/src/eLabFTWInterface.test.js +8 -5
- package/src/utils.js +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
# @opencloning/opencloning-elabftw
|
|
2
2
|
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#778](https://github.com/OpenCloning/OpenCloning_frontend/pull/778) [`fe1d81c`](https://github.com/OpenCloning/OpenCloning_frontend/commit/fe1d81c6f4b85f6317a2a9e46cc68c571c79c394) Thanks [@manulera](https://github.com/manulera)! - Only send the auth token to the backend, and rename the http client APIs.
|
|
8
|
+
|
|
9
|
+
- `@opencloning/utils`: `@opencloning/utils/httpClients` exports `getBackendHttpClient(backendUrl, { authenticated })`, a client that can only reach the backend (`allowAbsoluteUrls: false`) and is the only one carrying the auth token, and `getExternalHttpClient()` for whitelisted external urls. Replaces `@opencloning/utils/getHttpClient` (`getHttpClient`, `getAuthenticatedHttpClient`). `attachAuthInterceptors` refuses clients that could send the token elsewhere.
|
|
10
|
+
- `@opencloning/ui` (breaking): `useHttpClient` is renamed `useBackendHttpClient` and takes urls relative to `backendUrl`; `useBackendRoute` is removed; `useBackendAPI` is renamed `useSourceRequest` and `sendPostRequest` is renamed `sendSourceRequest`.
|
|
11
|
+
- `@opencloning/opencloningdb`, `@opencloning/opencloning-elabftw`: clients can no longer be pointed at other hosts.
|
|
12
|
+
|
|
13
|
+
- [#778](https://github.com/OpenCloning/OpenCloning_frontend/pull/778) [`fe1d81c`](https://github.com/OpenCloning/OpenCloning_frontend/commit/fe1d81c6f4b85f6317a2a9e46cc68c571c79c394) Thanks [@manulera](https://github.com/manulera)! - Encode values interpolated into request URL paths to prevent client-side path traversal. A sample UID such as `../workspaces/1/users/5` could make a user's own request hit a different endpoint, e.g. "delete UID" removing a workspace member.
|
|
14
|
+
|
|
15
|
+
- `@opencloning/utils`: new `encodePathSegment` (`@opencloning/utils/urlUtils`), which applies `encodeURIComponent` and rejects `.` and `..`.
|
|
16
|
+
- `@opencloning/opencloningdb`: all path parameters in `endpoints.js` are encoded.
|
|
17
|
+
- `@opencloning/opencloning-elabftw`: eLabFTW item and upload paths are encoded.
|
|
18
|
+
- `@opencloning/ui`: the `example`, `key` and `template` URL parameters are encoded when loading examples and templates.
|
|
19
|
+
|
|
20
|
+
- Updated dependencies [[`fe1d81c`](https://github.com/OpenCloning/OpenCloning_frontend/commit/fe1d81c6f4b85f6317a2a9e46cc68c571c79c394), [`fe1d81c`](https://github.com/OpenCloning/OpenCloning_frontend/commit/fe1d81c6f4b85f6317a2a9e46cc68c571c79c394), [`fe1d81c`](https://github.com/OpenCloning/OpenCloning_frontend/commit/fe1d81c6f4b85f6317a2a9e46cc68c571c79c394)]:
|
|
21
|
+
- @opencloning/utils@2.0.0
|
|
22
|
+
- @opencloning/ui@2.0.0
|
|
23
|
+
|
|
24
|
+
## 1.10.0
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- [#776](https://github.com/OpenCloning/OpenCloning_frontend/pull/776) [`5f84aea`](https://github.com/OpenCloning/OpenCloning_frontend/commit/5f84aea004a24499a5634c17678696925ee780cf) Thanks [@manulera](https://github.com/manulera)! - Hotfix for issue where to load resources the template categories were shown instead
|
|
29
|
+
|
|
30
|
+
- Updated dependencies [[`f7f376e`](https://github.com/OpenCloning/OpenCloning_frontend/commit/f7f376ee351d9b2da52413700836dc55ba54b5ea)]:
|
|
31
|
+
- @opencloning/ui@1.10.0
|
|
32
|
+
- @opencloning/utils@1.10.0
|
|
33
|
+
|
|
34
|
+
## 1.9.5
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- Updated dependencies [[`73363e7`](https://github.com/OpenCloning/OpenCloning_frontend/commit/73363e7a7f7e494e20a54bd87e5dba97f8a9197e)]:
|
|
39
|
+
- @opencloning/utils@1.9.5
|
|
40
|
+
- @opencloning/ui@1.9.5
|
|
41
|
+
|
|
42
|
+
## 1.9.4
|
|
43
|
+
|
|
44
|
+
### Patch Changes
|
|
45
|
+
|
|
46
|
+
- [#769](https://github.com/OpenCloning/OpenCloning_frontend/pull/769) [`105210b`](https://github.com/OpenCloning/OpenCloning_frontend/commit/105210b8149af71101f348967b3096636aeeea09) Thanks [@manulera](https://github.com/manulera)! - Fix request payloads. Drop support for eLabFTW < 5.3. Related to https://github.com/elabftw/elabftw/issues/7218.
|
|
47
|
+
|
|
48
|
+
- Updated dependencies []:
|
|
49
|
+
- @opencloning/ui@1.9.4
|
|
50
|
+
- @opencloning/utils@1.9.4
|
|
51
|
+
|
|
52
|
+
## 1.9.3
|
|
53
|
+
|
|
54
|
+
### Patch Changes
|
|
55
|
+
|
|
56
|
+
- Updated dependencies [[`df69a1b`](https://github.com/OpenCloning/OpenCloning_frontend/commit/df69a1bfb3b3128e93d89eef48a29f81a54ebb82)]:
|
|
57
|
+
- @opencloning/utils@1.9.3
|
|
58
|
+
- @opencloning/ui@1.9.3
|
|
59
|
+
|
|
3
60
|
## 1.9.2
|
|
4
61
|
|
|
5
62
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"license": "MIT",
|
|
3
3
|
"name": "@opencloning/opencloning-elabftw",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.0.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@mui/icons-material": "^5.18.0",
|
|
17
17
|
"@mui/material": "^5.18.0",
|
|
18
|
-
"@opencloning/ui": "
|
|
19
|
-
"@opencloning/utils": "
|
|
20
|
-
"axios": "^1.
|
|
18
|
+
"@opencloning/ui": "2.0.0",
|
|
19
|
+
"@opencloning/utils": "2.0.0",
|
|
20
|
+
"axios": "^1.20.0"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
23
|
"react": "^18.3.1",
|
|
@@ -10,29 +10,10 @@ describe('<ELabFTWCategorySelect />', () => {
|
|
|
10
10
|
cy.get('button').contains('Retry').click();
|
|
11
11
|
cy.get('@eLabFTWHttpClientSpy.all').should('have.callCount', 2);
|
|
12
12
|
});
|
|
13
|
-
it('shows the right options for eLabFTW version 50300', () => {
|
|
14
|
-
const setCategorySpy = cy.spy().as('setCategorySpy');
|
|
15
|
-
cy.stub(eLabFTWHttpClient, 'get').withArgs('/api/v2/info', { headers: { Authorization: 'test-read-key' } }).resolves({
|
|
16
|
-
data: {
|
|
17
|
-
elabftw_version_int: 50300,
|
|
18
|
-
},
|
|
19
|
-
}).withArgs('/api/v2/teams/current/resources_categories', { headers: { Authorization: 'test-read-key' }, params: { limit: 9999 } }).resolves({
|
|
20
|
-
data: [
|
|
21
|
-
{ id: 1, title: 'Category 1' },
|
|
22
|
-
{ id: 2, title: 'Category 2' },
|
|
23
|
-
],
|
|
24
|
-
});
|
|
25
|
-
cy.mount(<ELabFTWCategorySelect fullWidth setCategory={setCategorySpy} />);
|
|
26
|
-
cy.get('.MuiAutocomplete-root').click();
|
|
27
|
-
cy.get('li').contains('Category 1').should('exist');
|
|
28
|
-
cy.get('li').contains('Category 2').should('exist');
|
|
29
|
-
cy.get('li').contains('Category 1').click();
|
|
30
|
-
cy.get('@setCategorySpy').should('have.been.calledWith', { id: 1, title: 'Category 1' });
|
|
31
|
-
});
|
|
32
13
|
it('shows the right options', () => {
|
|
33
14
|
const setCategorySpy = cy.spy().as('setCategorySpy');
|
|
34
15
|
cy.stub(eLabFTWHttpClient, 'get')
|
|
35
|
-
.withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: {
|
|
16
|
+
.withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: {} }).resolves({
|
|
36
17
|
data: [
|
|
37
18
|
{ id: 1, title: 'Category 1' },
|
|
38
19
|
{ id: 2, title: 'Category 2' },
|
|
@@ -54,7 +35,7 @@ describe('<ELabFTWCategorySelect />', () => {
|
|
|
54
35
|
it('shows empty options if no categories are found', () => {
|
|
55
36
|
cy.mount(<ELabFTWCategorySelect fullWidth />);
|
|
56
37
|
cy.stub(eLabFTWHttpClient, 'get')
|
|
57
|
-
.withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: {
|
|
38
|
+
.withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: {} }).resolves({
|
|
58
39
|
data: [],
|
|
59
40
|
})
|
|
60
41
|
.withArgs('/api/v2/info', { headers: { Authorization: 'test-read-key' } }).resolves({
|
|
@@ -75,7 +56,7 @@ describe('<ELabFTWCategorySelect />', () => {
|
|
|
75
56
|
elabftw_version_int: 50300,
|
|
76
57
|
},
|
|
77
58
|
}).as('eLabFTWHttpClientSpyInfo')
|
|
78
|
-
.withArgs('/api/v2/
|
|
59
|
+
.withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: {} })
|
|
79
60
|
.rejects(new Error('Connection error')).as('eLabFTWHttpClientSpy');
|
|
80
61
|
cy.get('.MuiAlert-message').should('contain', 'Could not retrieve categories');
|
|
81
62
|
// Clicking the retry button makes the request again
|
|
@@ -3,7 +3,7 @@ import GetRequestMultiSelect from '@opencloning/ui/components/form/GetRequestMul
|
|
|
3
3
|
import RequestStatusWrapper from '@opencloning/ui/components/form/RequestStatusWrapper';
|
|
4
4
|
import { eLabFTWHttpClient, getELabFTWVersion, readHeaders } from './common';
|
|
5
5
|
|
|
6
|
-
function ELabFTWCategorySelect({ setCategory, label = 'Resource category', ...rest }) {
|
|
6
|
+
function ELabFTWCategorySelect({ setCategory, mode, label = 'Resource category', ...rest }) {
|
|
7
7
|
const [eLabFTWVersion, setELabFTWVersion] = React.useState(null);
|
|
8
8
|
const [requestStatus, setRequestStatus] = React.useState({ status: 'loading' });
|
|
9
9
|
const [retry, setRetry] = React.useState(0);
|
|
@@ -16,7 +16,14 @@ function ELabFTWCategorySelect({ setCategory, label = 'Resource category', ...re
|
|
|
16
16
|
}
|
|
17
17
|
).catch(() => setRequestStatus({ status: 'error', message: 'Could not retrieve eLabFTW version' }));
|
|
18
18
|
}, [retry]);
|
|
19
|
-
|
|
19
|
+
let url;
|
|
20
|
+
if (mode === 'resource_type') {
|
|
21
|
+
url = '/api/v2/teams/current/resources_categories';
|
|
22
|
+
} else if (mode === 'template') {
|
|
23
|
+
url = '/api/v2/items_types';
|
|
24
|
+
} else {
|
|
25
|
+
throw new Error(`Invalid mode: ${mode}`);
|
|
26
|
+
}
|
|
20
27
|
const getOptionsFromResponse = (data) => data;
|
|
21
28
|
const messages = { loadingMessage: 'retrieving categories', errorMessage: 'Could not retrieve categories from eLab' };
|
|
22
29
|
const onChange = (value) => setCategory(value);
|
|
@@ -28,7 +35,6 @@ function ELabFTWCategorySelect({ setCategory, label = 'Resource category', ...re
|
|
|
28
35
|
httpClient={eLabFTWHttpClient}
|
|
29
36
|
requestHeaders={readHeaders}
|
|
30
37
|
url={url}
|
|
31
|
-
requestParams={{ limit: 9999 }}
|
|
32
38
|
label={label}
|
|
33
39
|
messages={messages}
|
|
34
40
|
onChange={onChange}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import GetRequestMultiSelect from '@opencloning/ui/components/form/GetRequestMultiSelect';
|
|
3
3
|
import { eLabFTWHttpClient, readHeaders } from './common';
|
|
4
|
+
import { encodePathSegment } from '@opencloning/utils/urlUtils';
|
|
4
5
|
|
|
5
6
|
function ELabFTWFileSelect({ itemId, setFileInfo, ...rest }) {
|
|
6
|
-
const url = `/api/v2/items/${itemId}`;
|
|
7
|
+
const url = `/api/v2/items/${encodePathSegment(itemId)}`;
|
|
7
8
|
const getOptionsFromResponse = ({ uploads }) => uploads;
|
|
8
9
|
const label = 'File with sequence';
|
|
9
10
|
const messages = { loadingMessage: 'retrieving attachments', errorMessage: 'Could not retrieve attachment from eLab' };
|
|
@@ -51,7 +51,7 @@ function GetSequenceFileAndDatabaseIdComponent({ setFile, setDatabaseId }) {
|
|
|
51
51
|
|
|
52
52
|
return (
|
|
53
53
|
<>
|
|
54
|
-
<ELabFTWCategorySelect fullWidth setCategory={setCategory} className="elabftw-category-select" />
|
|
54
|
+
<ELabFTWCategorySelect mode="resource_type" fullWidth setCategory={setCategory} className="elabftw-category-select" />
|
|
55
55
|
{category && <ELabFTWResourceSelect fullWidth setResource={setResource} categoryId={category.id} className="elabftw-resource-select" />}
|
|
56
56
|
{resource && <ELabFTWFileSelect fullWidth setFileInfo={setFileInfo} itemId={resource.id} className="elabftw-file-select" />}
|
|
57
57
|
{fileLoadError && <RetryAlert severity="error" onRetry={() => setRetry((prev) => prev + 1)}>{fileLoadError}</RetryAlert>}
|
|
@@ -3,9 +3,10 @@ import React from 'react';
|
|
|
3
3
|
import { eLabFTWHttpClient, readHeaders } from './common';
|
|
4
4
|
import RetryAlert from '@opencloning/ui/components/form/RetryAlert';
|
|
5
5
|
import { getFileFromELabFTW } from './utils';
|
|
6
|
+
import { encodePathSegment } from '@opencloning/utils/urlUtils';
|
|
6
7
|
|
|
7
8
|
function LoadHistoryComponent({ databaseId, loadDatabaseFile }) {
|
|
8
|
-
const url = `/api/v2/items/${databaseId}`;
|
|
9
|
+
const url = `/api/v2/items/${encodePathSegment(databaseId)}`;
|
|
9
10
|
const [error, setError] = React.useState(null);
|
|
10
11
|
const [loading, setLoading] = React.useState(false);
|
|
11
12
|
const [retry, setRetry] = React.useState(0);
|
|
@@ -107,7 +107,7 @@ describe('<PrimersNotInDatabaseComponent />', () => {
|
|
|
107
107
|
it('shows error when update fails', () => {
|
|
108
108
|
let firstCall = true;
|
|
109
109
|
cy.stub(eLabFTWHttpClient, 'get')
|
|
110
|
-
.withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: {
|
|
110
|
+
.withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: {} })
|
|
111
111
|
.callsFake((url, config) => {
|
|
112
112
|
if (url !== '/api/v2/items_types' || config.headers?.Authorization !== 'test-read-key') {
|
|
113
113
|
throw new Error('Unexpected call to get method with these parameters');
|
|
@@ -26,7 +26,7 @@ const createTestStore = (cloningState) => configureStore({
|
|
|
26
26
|
describe('<SubmitToDatabaseComponent />', () => {
|
|
27
27
|
it('Primers: initializes with primer name and handles updates', () => {
|
|
28
28
|
cy.stub(eLabFTWHttpClient, 'get')
|
|
29
|
-
.withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: {
|
|
29
|
+
.withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: {} })
|
|
30
30
|
.resolves({
|
|
31
31
|
data: [
|
|
32
32
|
{ id: PRIMER_CATEGORY_ID, title: 'Primers' },
|
|
@@ -81,7 +81,7 @@ describe('<SubmitToDatabaseComponent />', () => {
|
|
|
81
81
|
|
|
82
82
|
it('Sequences: initializes with sequence name and handles updates', () => {
|
|
83
83
|
cy.stub(eLabFTWHttpClient, 'get')
|
|
84
|
-
.withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: {
|
|
84
|
+
.withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: {} })
|
|
85
85
|
.resolves({
|
|
86
86
|
data: [
|
|
87
87
|
{ id: PRIMER_CATEGORY_ID, title: 'Primers' },
|
|
@@ -140,7 +140,7 @@ describe('<SubmitToDatabaseComponent />', () => {
|
|
|
140
140
|
|
|
141
141
|
// Stub API to fail first time
|
|
142
142
|
cy.stub(eLabFTWHttpClient, 'get')
|
|
143
|
-
.withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: {
|
|
143
|
+
.withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: {} })
|
|
144
144
|
.callsFake(() => {
|
|
145
145
|
if (firstCall) {
|
|
146
146
|
firstCall = false;
|
package/src/common.js
CHANGED
|
@@ -7,6 +7,8 @@ export const writeHeaders = writeApiKey ? { Authorization: writeApiKey } : {};
|
|
|
7
7
|
|
|
8
8
|
export const eLabFTWHttpClient = axios.create({
|
|
9
9
|
baseURL: baseUrl,
|
|
10
|
+
// Every url is appended to baseURL, so requests (and the API key) only reach eLabFTW
|
|
11
|
+
allowAbsoluteUrls: false,
|
|
10
12
|
});
|
|
11
13
|
|
|
12
14
|
export const makeSequenceMetadata = (sequence) => JSON.stringify({
|
package/src/eLabFTWInterface.js
CHANGED
|
@@ -6,9 +6,10 @@ import GetPrimerComponent from './GetPrimerComponent';
|
|
|
6
6
|
import { eLabFTWHttpClient, writeHeaders, readHeaders, baseUrl, getELabFTWVersion } from './common';
|
|
7
7
|
import { getFileFromELabFTW, error2String } from './utils';
|
|
8
8
|
import LoadHistoryComponent from './LoadHistoryComponent';
|
|
9
|
+
import { encodePathSegment } from '@opencloning/utils/urlUtils';
|
|
9
10
|
|
|
10
11
|
async function deleteResource(resourceId) {
|
|
11
|
-
const url = `/api/v2/items/${resourceId}`;
|
|
12
|
+
const url = `/api/v2/items/${encodePathSegment(resourceId)}`;
|
|
12
13
|
// eLabFTW requires application/json for delete requests, axios seems to require a data field for it to work
|
|
13
14
|
const resp = await eLabFTWHttpClient.delete(url, { data: {}, headers: { ...writeHeaders, 'Content-Type': 'application/json' } });
|
|
14
15
|
return resp.data;
|
|
@@ -16,7 +17,7 @@ async function deleteResource(resourceId) {
|
|
|
16
17
|
|
|
17
18
|
const linkToParent = async (childId, parentId) => {
|
|
18
19
|
await eLabFTWHttpClient.post(
|
|
19
|
-
`/api/v2/items/${childId}/items_links/${parentId}`,
|
|
20
|
+
`/api/v2/items/${encodePathSegment(childId)}/items_links/${encodePathSegment(parentId)}`,
|
|
20
21
|
{},
|
|
21
22
|
{ headers: writeHeaders },
|
|
22
23
|
);
|
|
@@ -24,13 +25,7 @@ const linkToParent = async (childId, parentId) => {
|
|
|
24
25
|
|
|
25
26
|
const createResource = async (categoryId) => {
|
|
26
27
|
const eLabFTWVersion = await getELabFTWVersion();
|
|
27
|
-
|
|
28
|
-
// 0 is for development (nightly image)
|
|
29
|
-
if (eLabFTWVersion >= 50300 || eLabFTWVersion === 0) {
|
|
30
|
-
categoryKey = 'template';
|
|
31
|
-
} else {
|
|
32
|
-
categoryKey = 'category_id';
|
|
33
|
-
}
|
|
28
|
+
const categoryKey = 'template';
|
|
34
29
|
const createdItemResponse = await eLabFTWHttpClient.post(
|
|
35
30
|
'/api/v2/items',
|
|
36
31
|
{
|
|
@@ -42,7 +37,7 @@ const createResource = async (categoryId) => {
|
|
|
42
37
|
};
|
|
43
38
|
|
|
44
39
|
const patchResource = async (resourceId, title, metadata = undefined) => eLabFTWHttpClient.patch(
|
|
45
|
-
`/api/v2/items/${resourceId}`,
|
|
40
|
+
`/api/v2/items/${encodePathSegment(resourceId)}`,
|
|
46
41
|
{ title, ...(metadata !== undefined && { metadata }) },
|
|
47
42
|
{ headers: writeHeaders },
|
|
48
43
|
);
|
|
@@ -82,7 +77,7 @@ async function uploadTextFileToResource(resourceId, fileName, textContent, comme
|
|
|
82
77
|
const formData = new FormData();
|
|
83
78
|
formData.append('file', blob, fileName);
|
|
84
79
|
formData.append('comment', comment);
|
|
85
|
-
const response = await eLabFTWHttpClient.post(`/api/v2/items/${resourceId}/uploads`, formData, { headers: writeHeaders });
|
|
80
|
+
const response = await eLabFTWHttpClient.post(`/api/v2/items/${encodePathSegment(resourceId)}/uploads`, formData, { headers: writeHeaders });
|
|
86
81
|
return Number(response.headers.location.split('/').pop());
|
|
87
82
|
}
|
|
88
83
|
|
|
@@ -198,7 +193,7 @@ async function loadSequenceFromUrlParams(urlParams) {
|
|
|
198
193
|
const { item_id: itemId, file_id: fileId } = urlParams;
|
|
199
194
|
|
|
200
195
|
if (itemId && fileId) {
|
|
201
|
-
const url = `/api/v2/items/${itemId}/uploads/${fileId}`;
|
|
196
|
+
const url = `/api/v2/items/${encodePathSegment(itemId)}/uploads/${encodePathSegment(fileId)}`;
|
|
202
197
|
let fileInfo;
|
|
203
198
|
try {
|
|
204
199
|
const resp = await eLabFTWHttpClient.get(url, { headers: readHeaders });
|
|
@@ -215,7 +210,7 @@ async function loadSequenceFromUrlParams(urlParams) {
|
|
|
215
210
|
}
|
|
216
211
|
|
|
217
212
|
async function getPrimer(databaseId) {
|
|
218
|
-
const url = `/api/v2/items/${databaseId}`;
|
|
213
|
+
const url = `/api/v2/items/${encodePathSegment(databaseId)}`;
|
|
219
214
|
try {
|
|
220
215
|
const resp = await eLabFTWHttpClient.get(url, { headers: readHeaders });
|
|
221
216
|
resp.data.metadata = JSON.parse(resp.data.metadata);
|
|
@@ -230,7 +225,7 @@ async function getPrimer(databaseId) {
|
|
|
230
225
|
}
|
|
231
226
|
|
|
232
227
|
async function getSequenceName(databaseId) {
|
|
233
|
-
const url = `/api/v2/items/${databaseId}`;
|
|
228
|
+
const url = `/api/v2/items/${encodePathSegment(databaseId)}`;
|
|
234
229
|
try {
|
|
235
230
|
const resp = await eLabFTWHttpClient.get(url, { headers: readHeaders });
|
|
236
231
|
return resp.data.title;
|
|
@@ -247,7 +242,7 @@ async function getSequencingFiles(databaseId) {
|
|
|
247
242
|
// This function should return an array of objects:
|
|
248
243
|
// name: the name of the file
|
|
249
244
|
// getFile: an async function that returns the file content
|
|
250
|
-
const url = `/api/v2/items/${databaseId}/uploads`;
|
|
245
|
+
const url = `/api/v2/items/${encodePathSegment(databaseId)}/uploads`;
|
|
251
246
|
try {
|
|
252
247
|
const resp = await eLabFTWHttpClient.get(url, { headers: readHeaders });
|
|
253
248
|
return resp.data.map((fileInfo) => ({
|
|
@@ -135,7 +135,10 @@ async function getScenario(method, url, data, config) {
|
|
|
135
135
|
expect(nextScenario).toBeDefined();
|
|
136
136
|
expect({ method: nextScenario.method, url: nextScenario.url, config: nextScenario.config }).toEqual({ method, url, config });
|
|
137
137
|
|
|
138
|
-
if (nextScenario.data instanceof FormData
|
|
138
|
+
if (nextScenario.data instanceof FormData || data instanceof FormData) {
|
|
139
|
+
expect(data).toBeInstanceOf(FormData);
|
|
140
|
+
expect(nextScenario.data).toBeInstanceOf(FormData);
|
|
141
|
+
|
|
139
142
|
const scenarioComment = nextScenario.data.get('comment');
|
|
140
143
|
const requestComment = data.get('comment');
|
|
141
144
|
expect(scenarioComment).toBe(requestComment);
|
|
@@ -152,6 +155,8 @@ async function getScenario(method, url, data, config) {
|
|
|
152
155
|
} else if (scenarioFileText !== requestFileText) {
|
|
153
156
|
expect(scenarioFileText).toBe(requestFileText);
|
|
154
157
|
}
|
|
158
|
+
} else {
|
|
159
|
+
expect(data).toEqual(nextScenario.data);
|
|
155
160
|
}
|
|
156
161
|
return nextScenario;
|
|
157
162
|
}
|
|
@@ -212,8 +217,7 @@ const mockCreateResource = {
|
|
|
212
217
|
method: 'post',
|
|
213
218
|
url: '/api/v2/items',
|
|
214
219
|
data: {
|
|
215
|
-
|
|
216
|
-
tags: [],
|
|
220
|
+
template: MAIN_RESOURCE_CATEGORY_ID,
|
|
217
221
|
},
|
|
218
222
|
config: { headers: {} },
|
|
219
223
|
response: { headers: { location: `/api/v2/items/${MAIN_RESOURCE_DATABASE_ID}` } },
|
|
@@ -686,8 +690,7 @@ const submissionPayloadWithAncestors = {
|
|
|
686
690
|
const mockCreateSecondaryResource = {
|
|
687
691
|
...mockCreateResource,
|
|
688
692
|
data: {
|
|
689
|
-
|
|
690
|
-
category_id: SECONDARY_RESOURCE_CATEGORY_ID,
|
|
693
|
+
template: SECONDARY_RESOURCE_CATEGORY_ID,
|
|
691
694
|
},
|
|
692
695
|
response: { headers: { location: `/api/v2/items/${SECONDARY_RESOURCE_DATABASE_ID1}` } },
|
|
693
696
|
};
|
package/src/utils.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// so including them in eLabFTWInterface.js would make circular dependencies.
|
|
3
3
|
// They are not included in common.js to be able to mock eLabFTWHttpClient in tests.
|
|
4
4
|
import { eLabFTWHttpClient, readHeaders } from './common';
|
|
5
|
+
import { encodePathSegment } from '@opencloning/utils/urlUtils';
|
|
5
6
|
|
|
6
7
|
export function error2String(error) {
|
|
7
8
|
if (error.code === 'ERR_NETWORK') { return 'Network error: Cannot connect to eLabFTW'; }
|
|
@@ -18,7 +19,7 @@ export function error2String(error) {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export const getFileFromELabFTW = async (itemId, fileInfo) => {
|
|
21
|
-
const url = `/api/v2/items/${itemId}/uploads/${fileInfo.id}?format=binary`;
|
|
22
|
+
const url = `/api/v2/items/${encodePathSegment(itemId)}/uploads/${encodePathSegment(fileInfo.id)}?format=binary`;
|
|
22
23
|
try {
|
|
23
24
|
const resp = await eLabFTWHttpClient.get(url, { headers: readHeaders, responseType: 'blob' });
|
|
24
25
|
// Convert blob to file
|