@js4cytoscape/ndex-client 0.5.0-alpha.9 → 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.9",
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
@@ -593,11 +593,19 @@ class NDEx {
593
593
  }
594
594
 
595
595
  moveNetworks(networkIds, folderId){
596
- 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
+ }
597
601
  }
598
602
 
599
603
  setNetworksVisibility(files, visibility){
600
- 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
+ }
601
609
  }
602
610
 
603
611
  /* network set functions */
@@ -877,6 +885,18 @@ class NDEx {
877
885
  return this._httpPostV3ProtectedObj('users/signin', undefined, {idToken: idToken});
878
886
  }
879
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
+
880
900
  // V3-Files APIs
881
901
  copyFile(fromUuid, toPath, type, accessKey) {
882
902
  let parameters = {};
@@ -908,14 +928,6 @@ class NDEx {
908
928
  }
909
929
  // Sharing
910
930
  //add, remove, update member
911
- updateMember(files, members){
912
- return this._httpPostV3ProtectedObj('files/sharing/members', undefined, {files: files, members: members});
913
- }
914
-
915
- listMembers(files){
916
- return this._httpGetV3ProtectedObj('files/sharing/members/list', files);
917
- }
918
-
919
931
  _validateShareData(data) {
920
932
  // Check if data is an object and has files property
921
933
  if (typeof data !== 'object' || data === null || data.files === undefined) {
@@ -942,9 +954,42 @@ class NDEx {
942
954
  }
943
955
  }
944
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
+
973
+ updateMember(files, 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
+ }
979
+ }
980
+
981
+ listMembers(files){
982
+ return this._httpGetV3ProtectedObj('files/sharing/members/list', files);
983
+ }
984
+
985
+
945
986
 
946
987
  transferOwnership(files, newOwner){
947
- 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
+ }
948
993
  }
949
994
 
950
995
  listShares(limit){
@@ -956,11 +1001,19 @@ class NDEx {
956
1001
  }
957
1002
 
958
1003
  share(files){
959
- 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
+ }
960
1009
  }
961
1010
 
962
1011
  unshare(files){
963
- 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
+ }
964
1017
  }
965
1018
 
966
1019
  // Folder