Skip to content

Commit b1fd830

Browse files
committed
* 2019-10-24 (**Version 4.3.0**)
* New features/support to help with downloading the **entire** (!) instance setup * added new search option templates and helpful notes for day-2-day search/resync workflows * added more system type tables like choice, props, dictionary etc. to extract more config * allow searching without the sys_class_name restriction to include all records from child tables or when that table doesn't have the field * Provide `"noClassName": true` in folder config per desired folder * See also the readme section for "Folder definitions" * Support for mass API calls to an instance to avoid HTTP 427 responses (better `--resync` support)
1 parent 6162859 commit b1fd830

File tree

9 files changed

+255
-16
lines changed

9 files changed

+255
-16
lines changed

CHANGES.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# FileSync Changes
22

3+
* 2019-10-24 (**Version 4.3.0**)
4+
* New features/support to help with downloading the **entire** (!) instance setup
5+
* added new search option templates and helpful notes for day-2-day search/resync workflows
6+
* added more system type tables like choice, props, dictionary etc. to extract more config
7+
* allow searching without the sys_class_name restriction to include all records from child tables or when that table doesn't have the field
8+
* Provide `"noClassName": true` in folder config per desired folder
9+
* See also the readme section for "Folder definitions"
10+
* Support for mass API calls to an instance to avoid HTTP 427 responses (better `--resync` support)
11+
12+
313
* 2017-03-26 (**Version 4.2.4**)
414
* Add debug option via the command line additional to config setting. (`--debug`)
515
* Fix export not using subdir format

README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
sn-filesync -- ServiceNow FileSync (v4.2.4)
1+
sn-filesync -- ServiceNow FileSync
22
=================
33

4-
[![NPM](https://nodei.co/npm-dl/sn-filesync.png?height=3&months=9)](https://nodei.co/npm-dl/sn-filesync/)
4+
[![NPM](https://nodei.co/npm/sn-filesync.png?downloadRank=true&stars=true)](https://nodei.co/npm-dl/sn-filesync/)
55

66
[![Intro to FileSync](https://raw.githubusercontent.com/dynamicdan/filesync/master/thumbnail.png)](https://www.youtube.com/watch?v=OlVllfPVOrA "Intro to FileSync")
77

@@ -219,7 +219,14 @@ See the **lib/records.config.json** file for sample definitions.
219219
"client.js": "client_script", // for ui pages, you might have three separate files:
220220
"server.js": "processing_script" // mypage.xhtml, mypage.client.js, mypage.server.js
221221
} // to store the all script associated with the page
222-
}
222+
},
223+
"sys_choice": {
224+
"_info": "Choices",
225+
"table": "sys_choice",
226+
"key": "element",
227+
"subDirPattern": "name",
228+
"noClassName": true // allow searching all child tables of sys_choice OR for when sys_class_name isn't present (!) on the table but it's used in a search query
229+
},
223230
...
224231
},
225232
```

app.config.json

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,46 @@
2121
},
2222

2323
"search": {
24+
25+
26+
"__notes_daily_search": {
27+
"readme": "Command line searches to do prior to development start. Full download of all the relevant/new stuff which can be completed as the first step to get the latest version of all config. WARNING: do a sanity check to verify credentials are still valid with limit of 1 first to avoid potential account lock out! (--records_per_search 1)",
28+
29+
"optional_step0_sanity_check": "./bin/app.js --config ../some/folder/app.config.json --search mine --records_per_search 1",
30+
31+
"step1": "./bin/app.js --config ../some/folder/app.config.json --search mine --download",
32+
33+
"step2": "./bin/app.js --config ../some/folder/app.config.json --search team-but-me --download",
34+
35+
"then_start_work": "./bin/app.js --config ../some/folder/app.config.json"
36+
},
37+
38+
39+
40+
"all-from2019": {
41+
"query": "sys_created_on>javascript:gs.dateGenerate('2018-12-31','23:59:59')",
42+
"records_per_search": "10",
43+
"download": false
44+
},
45+
46+
"init": {
47+
"_note": "Query will run across all defined tables. Somewhat experimental in terms of mass download. Once downloaded, will work fine.",
48+
"query": "ORDERBYDESCsys_updated_on",
49+
"records_per_search": "10000",
50+
"download": false
51+
},
52+
2453
"mine": {
25-
"query": "active=true^sys_updated_by=javascript:gs.getUserName()^ORsys_created_by=javascript:gs.getUserName()^ORDERBYDESCsys_updated_on",
26-
"records_per_search": "100"
54+
"query": "sys_updated_by=javascript:gs.getUserName()^ORsys_created_by=javascript:gs.getUserName()^ORDERBYDESCsys_updated_on",
55+
"records_per_search": "300"
2756
},
28-
"team": {
57+
"team-but-me": {
58+
"_note": "change the date to reflect when your team/instance was first setup",
59+
"query": "sys_created_on>javascript:gs.dateGenerate('2019-06-01','00:00:00')^sys_created_by!=javascript:gs.getUserName()^sys_created_by!=admin^ORDERBYDESCsys_updated_on",
60+
"records_per_search": "300"
61+
},
62+
"team_active": {
63+
"_note": "change the date to reflect when your team/instance was first setup",
2964
"query": "active=true^sys_created_on>javascript:gs.dateGenerate('2015-03-25','23:59:59')^sys_created_by!=javascript:gs.getUserName()^sys_updated_by!=javascript:gs.getUserName()^sys_created_by!=admin^ORDERBYDESCsys_updated_on",
3065
"records_per_search": "100"
3166
},
@@ -41,7 +76,71 @@
4176
"records_per_search": "1",
4277
"download": true,
4378
"full_record": true
79+
},
80+
81+
82+
83+
"__notes_instance_download": {
84+
"info": "The below config allows downloading the entier data model including fields and labels. The following ~90K column/dictionary entries are broken down into groups under 10K to avoid exceeding the 10K query limit. The complete cmdb is ignored (~57K records)."
85+
},
86+
"t1": {
87+
"table": "sys_dictionary",
88+
"query": "nameSTARTSWITHa^ORnameSTARTSWITHb^ORnameSTARTSWITHc^ORnameSTARTSWITHd^ORnameSTARTSWITHe^ORnameSTARTSWITHf^ORnameSTARTSWITHg^ORnameSTARTSWITHh^ORnameSTARTSWITHi^ORnameSTARTSWITHj^ORnameSTARTSWITHk^ORnameSTARTSWITHl^ORnameSTARTSWITHm^ORnameSTARTSWITHn^nameNOT LIKEcmdb_",
89+
"records_per_search": "10000",
90+
"download": true,
91+
"full_record": true
92+
},
93+
"t2": {
94+
"table": "sys_dictionary",
95+
"query": "nameSTARTSWITHo^ORnameSTARTSWITHp^ORnameSTARTSWITHq^ORnameSTARTSWITHr^ORnameSTARTSWITHt^ORnameSTARTSWITHu^ORnameSTARTSWITHv^ORnameSTARTSWITHw^ORnameSTARTSWITHx^ORnameSTARTSWITHy^ORnameSTARTSWITHz^nameNOT LIKEcmdb_",
96+
"records_per_search": "10000",
97+
"download": true,
98+
"full_record": true
99+
},
100+
"t3": {
101+
"table": "sys_dictionary",
102+
"query": "nameSTARTSWITHs^nameNOT LIKEcmdb_^nameNOT LIKEsys_",
103+
"records_per_search": "10000",
104+
"download": true,
105+
"full_record": true
106+
},
107+
"t4": {
108+
"table": "sys_dictionary",
109+
"query": "nameSTARTSWITHs^nameNOT LIKEcmdb_^nameLIKEsys_",
110+
"records_per_search": "10000",
111+
"download": true,
112+
"full_record": true
113+
},
114+
"d1": {
115+
"table": "sys_documentation",
116+
"query": "nameSTARTSWITHa^ORnameSTARTSWITHb^ORnameSTARTSWITHc^ORnameSTARTSWITHd^ORnameSTARTSWITHe^ORnameSTARTSWITHf^ORnameSTARTSWITHg^ORnameSTARTSWITHh^ORnameSTARTSWITHi^ORnameSTARTSWITHj^ORnameSTARTSWITHk^ORnameSTARTSWITHl^ORnameSTARTSWITHm^ORnameSTARTSWITHn^nameNOT LIKEcmdb_",
117+
"records_per_search": "10000",
118+
"download": true,
119+
"full_record": true
120+
},
121+
"d2": {
122+
"table": "sys_documentation",
123+
"query": "nameSTARTSWITHo^ORnameSTARTSWITHp^ORnameSTARTSWITHq^ORnameSTARTSWITHr^ORnameSTARTSWITHt^ORnameSTARTSWITHu^ORnameSTARTSWITHv^ORnameSTARTSWITHw^ORnameSTARTSWITHx^ORnameSTARTSWITHy^ORnameSTARTSWITHz^nameNOT LIKEcmdb_",
124+
"records_per_search": "10000",
125+
"download": true,
126+
"full_record": true
127+
},
128+
"d3": {
129+
"table": "sys_documentation",
130+
"query": "nameSTARTSWITHs^nameNOT LIKEcmdb_^nameNOT LIKEsys_",
131+
"records_per_search": "10000",
132+
"download": true,
133+
"full_record": true
134+
},
135+
"d4": {
136+
"table": "sys_documentation",
137+
"query": "nameSTARTSWITHs^nameNOT LIKEcmdb_^nameLIKEsys_",
138+
"records_per_search": "10000",
139+
"download": true,
140+
"full_record": true
44141
}
142+
143+
45144
},
46145

47146
"preLoad": true,

bin/app.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ var chokidar = require('chokidar');
1010
require('colors');
1111
var fs = require('fs-extra');
1212
var path = require('path');
13-
// used to generate a hash of a file
14-
var crypto = require('crypto');
13+
1514
var glob = require("glob");
1615
var winston = require('winston');
1716
var moment = require('moment');
@@ -49,7 +48,10 @@ var constants = {
4948
DOWNLOAD_OK: 1,
5049
DOWNLOAD_FAIL: -1,
5150

52-
SLASH: '/'
51+
SLASH: '/',
52+
53+
// max number of requests allowed to be running at once
54+
instanceAPITolerance: 30
5355

5456
//isMac: /^darwin/.test(process.platform)
5557
//isWin = /^win/.test(process.platform)
@@ -828,6 +830,7 @@ function addToPreLoadList(filePath, options) {
828830
}
829831
}
830832

833+
// TODO: not currently used... is this a bad thing?
831834
function addIfNotPresent(filePath) {
832835
fs.exists(filePath, function (exists) {
833836
if (!exists) {
@@ -1041,6 +1044,17 @@ function receive(file, allDoneCallBack) {
10411044
// this is a combination of sys_id and name. Use sys_id from name when possible and ignore the query
10421045
query = sys_id ? '' : map.key + '=' + map.keyValue;
10431046

1047+
1048+
// avoid overwhleming the instance and getting HTTP 427 responses
1049+
if(filesInQueueToDownload >= constants.instanceAPITolerance) {
1050+
setTimeout(function() {
1051+
receive(file, allDoneCallBack);
1052+
}, 200);
1053+
1054+
logit.debug('Delaying request to avoid HTTP 427 errors.');
1055+
return;
1056+
}
1057+
10441058
logit.debug('Adding:', {
10451059
file: file,
10461060
table: map.table,

lib/file-record.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,18 @@ function makeHash(data) {
2828
return hash1;
2929
}
3030

31-
function getFieldMap(filename, map) {
31+
method.getFieldMap = function(filename, map) {
32+
33+
if(typeof map.fields == 'undefined') {
34+
// if this is the case then the config doesn't specify which fields should be downloaded for the record.
35+
// this means that we are only interested in the full record
36+
// todo: support resyncing the full record
37+
this.logger.debug("No fields defined for this file type");
38+
this.logger.debug('map...');
39+
this.logger.debug(map);
40+
return null;
41+
}
42+
3243
var suffixes = Object.keys(map.fields);
3344

3445
// sort suffixes so most specific suffixes are looped through first...
@@ -51,7 +62,7 @@ function getFieldMap(filename, map) {
5162
}
5263
}
5364
return null;
54-
}
65+
};
5566

5667
// fix windows path issues (windows can handle both '\' and '/' so be *nix friendly)
5768
function normalisePath(p) {
@@ -245,7 +256,7 @@ method.getSyncMap = function () {
245256
}
246257

247258
// validate file suffix is mapped
248-
var fieldMap = getFieldMap(fileName, map);
259+
var fieldMap = this.getFieldMap(fileName, map);
249260
if (!fieldMap) {
250261
// note that full_record download will not have a mapped suffix
251262
this.logger.warn('No field map fileName: %s',fileName);

lib/records.config.json

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"fields": {
5252
"js": "script"
5353
},
54-
"subDirPattern": "client_<client_callable>"
54+
"subDirPattern": "scope_<sys_scope>/client_<client_callable>"
5555
},
5656
"style_sheets": {
5757
"table": "content_css",
@@ -185,6 +185,91 @@
185185
"html": "template"
186186
},
187187
"subDirPattern": "sp_widget"
188+
},
189+
190+
191+
192+
193+
194+
195+
196+
197+
"sys_properties": {
198+
"table": "sys_properties",
199+
"key": "name",
200+
"subDirPattern": "<sys_scope>",
201+
"_custom": true
202+
},
203+
"sys_app_module": {
204+
"_info": "Nav modules",
205+
"table": "sys_app_module",
206+
"key": "title",
207+
"subDirPattern": "application",
208+
"searchConstraint": "titleISNOTEMPTY",
209+
"_info_searchConstraint": "Avoid separators",
210+
"_custom": true
211+
},
212+
"sys_data_policy2": {
213+
"table": "sys_data_policy2",
214+
"key": "model_table",
215+
"subDirPattern": "sys_package",
216+
"_custom": true
217+
},
218+
"sysevent_register": {
219+
"table": "sysevent_register",
220+
"key": "event_name",
221+
"subDirPattern": "sys_scope/table",
222+
"_custom": true
223+
},
224+
"sys_choice": {
225+
"_info": "Choices",
226+
"table": "sys_choice",
227+
"key": "element",
228+
"subDirPattern": "name",
229+
"noClassName": true,
230+
"_custom": true
231+
},
232+
"tables": {
233+
"_info": "Tables",
234+
"table": "sys_db_object",
235+
"key": "name",
236+
"subDirPattern": "sys_package",
237+
"searchConstraint": "sys_nameISNOTEMPTY",
238+
"_custom": true
239+
},
240+
"sys_dictionary": {
241+
"_info": "fields",
242+
"table": "sys_dictionary",
243+
"key": "element",
244+
"subDirPattern": "sys_scope/name",
245+
"_note": "name = table system name. sys_ fields have no scope and we do not want them.",
246+
"searchConstraint": "elementISNOTEMPTY^sys_scopeISNOTEMPTY",
247+
"_custom": true
248+
},
249+
"sys_documentation": {
250+
"_info": "Field labels",
251+
"table": "sys_documentation",
252+
"key": "element",
253+
"subDirPattern": "sys_scope/name",
254+
"searchConstraint": "language=en^sys_scopeISNOTEMPTY",
255+
"_custom": true
256+
},
257+
"sys_user_role": {
258+
"_info": "user roles",
259+
"table": "sys_user_role",
260+
"key": "name",
261+
"subDirPattern": "sys_package",
262+
"_custom": true
263+
},
264+
"sys_security_acl": {
265+
"_info": "ACLs",
266+
"table": "sys_security_acl",
267+
"key": "sys_name",
268+
"fields": {
269+
"js": "script"
270+
},
271+
"subDirPattern": "type/sys_name/operation",
272+
"_custom": true
188273
}
189274
}
190275
}

lib/search.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,15 @@ method.getResults = function (queryObj, callback) {
116116
db.folder = folder;
117117
db.table = folObj.table;
118118
db.subDir = folObj.subDirPattern || false;
119+
120+
// if a table has a search constraint defined then AND it to the existing query
121+
if(folObj.searchConstraint) {
122+
db.query += '^' + folObj.searchConstraint;
123+
}
124+
125+
// overrides the default search logic to restrict search by the table sys_class_name
126+
db.noClassName = folObj.noClassName ? true : false;
127+
119128
// fields are optional
120129
db.fields = queryObj.recordOnly ? false : folObj.fields;
121130

lib/snc-client.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,12 @@ var sncClient = restler.service(function sncClient(config) {
175175
};
176176
if (query.query) {
177177
parms.parmValue = query.query;
178-
// ensures that tables that are extended are still restricted to 1 table
179-
parms.parmValue += "^sys_class_name=" + tableName;
178+
if(query.noClassName) {
179+
// don't add the additional query constraint
180+
} else {
181+
// ensures that tables that are extended are still restricted to 1 table
182+
parms.parmValue += "^sys_class_name=" + tableName;
183+
}
180184
}
181185
if (query.rows) {
182186
parms.rows = query.rows;

lib/string-util.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ method.extractTableFromName = function(str) {
7676
* Example
7777
* sp_widget_c6545050ff223100ba13ffffffffffe8
7878
*/
79-
var sys_id = StringUtil.extractSysIdFromName(str);
79+
var sys_id = method.extractSysIdFromName(str);
8080
table = str.replace('_' + sys_id, '');
8181
}
8282

0 commit comments

Comments
 (0)