@js4cytoscape/ndex-client 0.5.0-alpha.8 → 0.6.0-alpha.1

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@js4cytoscape/ndex-client",
3
- "version": "0.5.0-alpha.8",
4
- "description": "NDEx client library",
3
+ "version": "0.6.0-alpha.1",
4
+ "description": "NDEx client library - Modern TypeScript implementation",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/cytoscape/js4cytoscape.git"
@@ -10,36 +10,94 @@
10
10
  "url": "https://github.com/cytoscape/js4cytoscape/issues"
11
11
  },
12
12
  "homepage": "https://github.com/cytoscape/js4cytoscape",
13
- "main": "./dist/ndexClient.common.js",
14
- "module": "src/index.js",
13
+ "main": "./dist/index.js",
14
+ "module": "./dist/index.mjs",
15
+ "umd": "./dist/index.umd.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.mjs",
21
+ "require": "./dist/index.js"
22
+ }
23
+ },
15
24
  "files": [
16
25
  "dist",
17
- "src"
26
+ "src",
27
+ "README.md",
28
+ "LICENSE"
18
29
  ],
19
30
  "scripts": {
20
- "watch:js": "webpack --watch",
21
- "build": "webpack --mode=development",
22
- "build-prod": "webpack --mode=production",
23
- "test": "nyc mocha --require @babel/register"
31
+ "dev": "tsup --watch",
32
+ "build": "tsup",
33
+ "build:docs": "typedoc",
34
+ "docs:api": "npm run build:docs",
35
+ "docs:site": "cd docs-site && npm install && npm run build",
36
+ "docs:serve": "cd docs-site && npm run start",
37
+ "docs:build": "npm run docs:api && npm run docs:site",
38
+ "docs:clean": "rimraf docs docs-site/build docs-site/docs/api",
39
+ "test": "npm run test:unit",
40
+ "test:unit": "jest --config jest.unit.config.js",
41
+ "test:integration": "jest --config jest.integration.config.js",
42
+ "test:all": "npm run test:unit && npm run test:integration",
43
+ "test:watch": "npm run test:unit -- --watch",
44
+ "test:coverage": "npm run test:unit -- --coverage",
45
+ "test:ci": "npm run test:all -- --coverage",
46
+ "debug:integration": "node --inspect-brk ./node_modules/.bin/jest --config jest.integration.config.js --runInBand --no-cache",
47
+ "debug:unit": "node --inspect-brk ./node_modules/.bin/jest --config jest.unit.config.js --runInBand --no-cache",
48
+ "debug:client": "node --inspect-brk debug-response.js",
49
+ "lint": "eslint src __tests__ --ext .ts,.js --fix",
50
+ "format": "prettier --write \"src/**/*.{ts,js}\" \"__tests__/**/*.{ts,js}\"",
51
+ "type-check": "tsc --noEmit",
52
+ "clean": "rimraf dist coverage",
53
+ "prepare": "npm run build",
54
+ "changeset": "changeset",
55
+ "release": "changeset publish"
24
56
  },
25
57
  "author": "Jing Chen",
26
58
  "license": "MIT",
27
59
  "engines": {
28
- "node": ">=12.16.1"
60
+ "node": ">=16.0.0"
29
61
  },
62
+ "keywords": [
63
+ "ndex",
64
+ "network",
65
+ "cytoscape",
66
+ "bioinformatics",
67
+ "graph",
68
+ "cx2",
69
+ "typescript"
70
+ ],
30
71
  "dependencies": {
31
- "axios": "^1.6.8"
72
+ "axios": "^1.11.0"
32
73
  },
33
74
  "devDependencies": {
34
- "@microsoft/tsdoc": "^0.14.2",
35
- "@babel/core": "^7.14.6",
36
- "@babel/preset-env": "^7.14.5",
37
- "@babel/register": "^7.14.5",
38
- "babel-plugin-istanbul": "^6.0.0"
39
- },
40
- "babel": {
41
- "presets": [
42
- "@babel/preset-env"
43
- ]
75
+ "@changesets/cli": "^2.27.1",
76
+ "@types/jest": "^29.5.12",
77
+ "@types/node": "^20.11.25",
78
+ "@typescript-eslint/eslint-plugin": "^7.1.1",
79
+ "@typescript-eslint/parser": "^7.1.1",
80
+ "eslint": "^8.57.0",
81
+ "eslint-config-prettier": "^9.1.0",
82
+ "eslint-plugin-prettier": "^5.1.3",
83
+ "glob": "^7.2.0",
84
+ "jest": "^29.7.0",
85
+ "jest-environment-jsdom": "^29.7.0",
86
+ "nock": "^13.5.4",
87
+ "prettier": "^3.2.5",
88
+ "rimraf": "^5.0.5",
89
+ "ts-jest": "^29.1.2",
90
+ "tsup": "^8.0.2",
91
+ "typedoc": "^0.25.12",
92
+ "typedoc-plugin-markdown": "^3.17.1",
93
+ "typescript": "^5.4.2"
94
+ },
95
+ "peerDependencies": {
96
+ "cytoscape": "^3.0.0"
97
+ },
98
+ "peerDependenciesMeta": {
99
+ "cytoscape": {
100
+ "optional": true
101
+ }
44
102
  }
45
103
  }
@@ -0,0 +1,178 @@
1
+ const CY_REST_BASE_URL = 'http://127.0.0.1';
2
+
3
+ import { default as axios } from 'axios';
4
+
5
+ class CyNDEx {
6
+
7
+ constructor(port = 1234) {
8
+ this._port = port;
9
+ }
10
+ get port() { return this._port; }
11
+
12
+ static get cyRestBaseURL() {
13
+ return CY_REST_BASE_URL;
14
+ }
15
+
16
+ setNDExServer(ndexServer) {
17
+ if (ndexServer !== undefined && ndexServer != null && ndexServer !== '') {
18
+ this._ndexServer = ndexServer;
19
+ }
20
+ }
21
+
22
+ getNDExServer() {
23
+ return this._ndexServer ? this._ndexServer : 'https://www.ndexbio.org';
24
+ }
25
+
26
+ setGoogleAuth(googleAuthObj) {
27
+ if (googleAuthObj !== undefined) {
28
+ this._googleAuth = googleAuthObj;
29
+ this._authType = 'g'; // valid values are 'g','b' or undefined
30
+ }
31
+ }
32
+
33
+ setAuthToken(authToken) {
34
+ if (authToken !== undefined ) {
35
+ this._authToken = authToken;
36
+ this._authType = 'g'; // valid values are 'g','b' or undefined
37
+ }
38
+ }
39
+
40
+ setBasicAuth(username, password) {
41
+ if (username !== undefined && username != null && username !== '') {
42
+ this._username = username;
43
+ this._password = password;
44
+ this._authType = 'b';
45
+ }
46
+ }
47
+
48
+ cyRestURL() {
49
+ return CY_REST_BASE_URL + ':' + this._port;
50
+ }
51
+
52
+ _getIdToken() {
53
+ return this._authToken ? this._authToken : this._googleAuth.getAuthInstance().currentUser.get().getAuthResponse().id_token;
54
+ }
55
+
56
+ _getAuthorizationFields() {
57
+ switch (this._authType) {
58
+ case 'b' : return {
59
+ username : this._username,
60
+ password : this._password
61
+ };
62
+ case 'g' : return {
63
+ idToken : this._getIdToken()
64
+ };
65
+ default : return {};
66
+ }
67
+ }
68
+
69
+ _httpGet(url) {
70
+
71
+ const baseURL = this.cyRestURL();
72
+
73
+ return new Promise(function (resolve, reject) {
74
+ axios({
75
+ method: 'get',
76
+ url: url,
77
+ baseURL: baseURL
78
+ }).then((response) => {
79
+ if (response.status === 200) {
80
+ return resolve(response.data);
81
+ }
82
+ return reject(response);
83
+ },
84
+ (response) => { return reject(response); }
85
+ );
86
+ });
87
+ }
88
+
89
+ _httpPut(url, parameters, data) {
90
+ return this._http('put', url, parameters, data);
91
+ }
92
+
93
+ _httpPost(url, parameters, data) {
94
+ return this._http('post', url, parameters, data);
95
+ }
96
+
97
+ _http(method, url, parameters, data) {
98
+
99
+ const baseURL = this.cyRestURL();
100
+
101
+ let config = {
102
+ method: method,
103
+ url: url,
104
+ baseURL: baseURL
105
+ };
106
+
107
+ if (parameters !== undefined) {
108
+ config['params'] = parameters;
109
+ }
110
+
111
+ config.data = data;
112
+ return new Promise(function (resolve, reject) {
113
+ axios(config).then((response) => {
114
+ if (response.status >= 200 && response.status <= 299) {
115
+ return resolve(response.data);
116
+ }
117
+ return reject(response);
118
+ },
119
+ (response) => {
120
+ return reject(response);
121
+ }
122
+ );
123
+ });
124
+
125
+ }
126
+
127
+ getCyNDExStatus() {
128
+ return this._httpGet('/cyndex2/v1');
129
+ }
130
+
131
+ getCytoscapeNetworkSummary(suid = 'current') {
132
+ return this._httpGet('/cyndex2/v1/networks/' + suid);
133
+ }
134
+
135
+ postNDExNetworkToCytoscape(uuid, accessKey, createView = undefined) {
136
+ const importParams = {
137
+ serverUrl: this.getNDExServer() + '/v2',
138
+ uuid: uuid,
139
+ accessKey: accessKey,
140
+ createView: createView
141
+ };
142
+
143
+ const authorizationFields = this._getAuthorizationFields();
144
+
145
+ return this._httpPost('/cyndex2/v1/networks', undefined, Object.assign(importParams, authorizationFields));
146
+ }
147
+
148
+ postCXNetworkToCytoscape(cx) {
149
+ return this._httpPost('/cyndex2/v1/networks/cx', undefined, cx);
150
+ }
151
+
152
+ postCX2NetworkToCytoscape(cx2_string, title, collection_name) {
153
+ return this._httpPost('/v1/networks',{ 'format': 'cx2', 'collection': collection_name, 'title':title}, cx2_string);
154
+ }
155
+
156
+ postCytoscapeNetworkToNDEx(suid = 'current') {
157
+ const saveParams = {
158
+ serverUrl: this.getNDExServer() + '/v2',
159
+ };
160
+
161
+ const authorizationFields = this._getAuthorizationFields();
162
+
163
+ return this._httpPost('/cyndex2/v1/networks/' + suid, undefined, Object.assign(saveParams, authorizationFields));
164
+ }
165
+
166
+ putCytoscapeNetworkInNDEx(suid = 'current', uuid) {
167
+ const saveParams = {
168
+ serverUrl: this.getNDExServer() + '/v2',
169
+ uuid: uuid
170
+ };
171
+
172
+ const authorizationFields = this._getAuthorizationFields();
173
+
174
+ return this._httpPut('/cyndex2/v1/networks/' + suid, undefined, Object.assign(saveParams, authorizationFields));
175
+ }
176
+ }
177
+
178
+ export default CyNDEx ;
package/src/NDEx.js CHANGED
@@ -394,6 +394,10 @@ class NDEx {
394
394
  return this._httpGetV3ProtectedObj('networks/' + uuid + '/summary', parameters);
395
395
  }
396
396
 
397
+ getNetworkDOI(uuid, key, email){
398
+ return this._httpGetV3ProtectedObj('networks/' + uuid + '/DOI', {key: key, email: email});
399
+ }
400
+
397
401
  getAttributesOfSelectedNodes(uuid, {ids: nodeIds, attributeNames: names},accessKey) {
398
402
  let parameters = {
399
403
  };
@@ -425,6 +429,10 @@ class NDEx {
425
429
  });
426
430
  }
427
431
 
432
+ copyNetwork(uuid){
433
+ return this._httpPostV3ProtectedObj('networks/'+uuid+'/copy', undefined, {});
434
+ }
435
+
428
436
  updateNetworkFromRawCX(uuid, rawcx) {
429
437
  return this._httpPutObj('network/' + uuid, undefined, rawcx);
430
438
  }
@@ -585,11 +593,19 @@ class NDEx {
585
593
  }
586
594
 
587
595
  moveNetworks(networkIds, folderId){
588
- return this._httpPostV3ProtectedObj('batch/networks/move', undefined, {targetFolder:folderId, networks: networkIds});
596
+ if(Object.isArray(networkIds)){
597
+ return this._httpPostV3ProtectedObj('batch/networks/move', undefined, {targetFolder:folderId, networks: networkIds});
598
+ }else{
599
+ throw new Error('Invalid networkIds');
600
+ }
589
601
  }
590
602
 
591
603
  setNetworksVisibility(files, visibility){
592
- return this._httpPostV3ProtectedObj('batch/networks/visibility', undefined, {files: files, visibility: visibility});
604
+ if(this._validateShareData(files)){
605
+ return this._httpPostV3ProtectedObj('batch/networks/visibility', undefined, {items: files, visibility: visibility});
606
+ }else{
607
+ throw new Error('Invalid share data');
608
+ }
593
609
  }
594
610
 
595
611
  /* network set functions */
@@ -869,6 +885,18 @@ class NDEx {
869
885
  return this._httpPostV3ProtectedObj('users/signin', undefined, {idToken: idToken});
870
886
  }
871
887
 
888
+ // Permission and AccessKey APIs
889
+
890
+ //Todo:permission APIs
891
+
892
+ getAccessKey(uuid){
893
+ return this._httpGetProtectedObj('network/'+uuid+'/accesskey', {});
894
+ }
895
+
896
+ updateAccessKey(uuid, action){
897
+ return this._httpPutObj('network/'+uuid+'/accesskey', {action: action});
898
+ }
899
+
872
900
  // V3-Files APIs
873
901
  copyFile(fromUuid, toPath, type, accessKey) {
874
902
  let parameters = {};
@@ -891,29 +919,77 @@ class NDEx {
891
919
  return this._httpDeleteV3Obj('files/trash', undefined);
892
920
  }
893
921
 
922
+ permanentlyDeleteFile(fileId) {
923
+ return this._httpDeleteV3Obj('files/trash/'+fileId, undefined);
924
+ }
925
+
894
926
  restoreFile(networkIds, folderIds, shortcutIds) {
895
927
  return this._httpPostV3ProtectedObj('files/trash/restore', undefined, {networks: networkIds, folders: folderIds, shortcuts: shortcutIds});
896
928
  }
897
929
  // Sharing
898
930
  //add, remove, update member
931
+ _validateShareData(data) {
932
+ // Check if data is an object and has files property
933
+ if (typeof data !== 'object' || data === null || data.files === undefined) {
934
+ throw new Error('Data must be an object with a "files" property');
935
+ }
936
+
937
+ // Check if files is an object
938
+ if (typeof data.files !== 'object' || data.files === null) {
939
+ throw new Error('The "files" property must be an object');
940
+ }
941
+
942
+ // Check each key-value pair in files
943
+ const validValues = ['NETWORK', 'FOLDER', 'SHORTCUT'];
944
+
945
+ for (const [uuid, fileType] of Object.entries(data.files)) {
946
+ // Validate UUID format (basic validation)
947
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(uuid)) {
948
+ throw new Error(`Invalid UUID format: ${uuid}`);
949
+ }
950
+
951
+ // Validate file type
952
+ if (!validValues.includes(fileType)) {
953
+ throw new Error(`Invalid file type for ${uuid}: ${fileType}. Must be one of: ${validValues.join(', ')}`);
954
+ }
955
+ }
956
+ }
957
+ _validateMemberData(data){
958
+ if(typeof data !== 'object' || data === null || data.members === undefined){
959
+ throw new Error('Data must be an object with a "members" property');
960
+ }
961
+ const validValues = ['READ', 'WRITE'];
962
+ for (const [uuid, permission] of Object.entries(data.members)) {
963
+ if (!validValues.includes(permission)) {
964
+ throw new Error(`Invalid permission for ${uuid}: ${permission}. Must be one of: ${validValues.join(', ')}`);
965
+ }
966
+ if(typeof uuid !== 'string' || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(uuid)){
967
+ throw new Error(`Invalid UUID format: ${uuid}`);
968
+ }
969
+ }
970
+
971
+ }
972
+
899
973
  updateMember(files, members){
900
- return this._httpPostV3ProtectedObj('files/sharing/members', undefined, {files: files, members: members});
974
+ if(this._validateShareData(files) && this._validateMemberData(members)){
975
+ return this._httpPostV3ProtectedObj('files/sharing/members', undefined, {files: files, members: members});
976
+ }else{
977
+ throw new Error('Invalid share data');
978
+ }
901
979
  }
902
980
 
903
981
  listMembers(files){
904
982
  return this._httpGetV3ProtectedObj('files/sharing/members/list', files);
905
983
  }
906
984
 
907
- _validateShareData(data){
908
- // must be object
909
- if (typeof data !== 'object') {
910
- throw new Error('Data must be an object');
911
- }
912
-
913
- }
985
+
914
986
 
915
987
  transferOwnership(files, newOwner){
916
- return this._httpPostV3ProtectedObj('files/sharing/transfer_ownership', undefined, {files: files, new_owner: newOwner});
988
+ if(this._validateShareData(files)){
989
+ return this._httpPostV3ProtectedObj('files/sharing/transfer_ownership', undefined, {files: files, new_owner: newOwner});
990
+ } else {
991
+ throw new Error('Invalid share data');
992
+ }
917
993
  }
918
994
 
919
995
  listShares(limit){
@@ -925,11 +1001,19 @@ class NDEx {
925
1001
  }
926
1002
 
927
1003
  share(files){
928
- return this._httpPostV3ProtectedObj('files/sharing/share', undefined, {files: files});
1004
+ if(this._validateShareData(files)){
1005
+ return this._httpPostV3ProtectedObj('files/sharing/share', undefined, {files: files});
1006
+ }else{
1007
+ throw new Error('Invalid share data');
1008
+ }
929
1009
  }
930
1010
 
931
1011
  unshare(files){
932
- return this._httpPostV3ProtectedObj('files/sharing/unshare', undefined, {files: files});
1012
+ if(this._validateShareData(files)){
1013
+ return this._httpPostV3ProtectedObj('files/sharing/unshare', undefined, {files: files});
1014
+ }else{
1015
+ throw new Error('Invalid share data');
1016
+ }
933
1017
  }
934
1018
 
935
1019
  // Folder
@@ -972,7 +1056,7 @@ class NDEx {
972
1056
  return this._httpGetV3ProtectedObj('files/folders/' + folderId + '/count', parameters);
973
1057
  }
974
1058
 
975
- getFolderList(folderId, accessKey, format) {
1059
+ getFolderList(folderId, accessKey, format, type) {
976
1060
  let parameters = {};
977
1061
 
978
1062
  if (accessKey !== undefined) {
@@ -981,6 +1065,9 @@ class NDEx {
981
1065
  if (format !== undefined) {
982
1066
  parameters['format'] = format;
983
1067
  }
1068
+ if (type !== undefined) {
1069
+ parameters['type'] = type;
1070
+ }
984
1071
  return this._httpGetV3ProtectedObj('files/folders/' + folderId + '/list', parameters);
985
1072
  }
986
1073