forceios 9.2.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/README.md +110 -0
- package/forceios.js +36 -0
- package/package.json +30 -0
- package/shared/commandLineUtils.js +245 -0
- package/shared/configHelper.js +206 -0
- package/shared/constants.js +329 -0
- package/shared/createHelper.js +368 -0
- package/shared/example.userstore.json +24 -0
- package/shared/example.usersyncs.json +190 -0
- package/shared/jsonChecker.js +75 -0
- package/shared/oclifAdapter.js +199 -0
- package/shared/outputColors.js +9 -0
- package/shared/store.schema.json +38 -0
- package/shared/syncs.schema.json +295 -0
- package/shared/templateHelper.js +104 -0
- package/shared/utils.js +453 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2016-present, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* Redistribution and use of this software in source and binary forms, with or
|
|
5
|
+
* without modification, are permitted provided that the following conditions
|
|
6
|
+
* are met:
|
|
7
|
+
* - Redistributions of source code must retain the above copyright notice, this
|
|
8
|
+
* list of conditions and the following disclaimer.
|
|
9
|
+
* - Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
* this list of conditions and the following disclaimer in the documentation
|
|
11
|
+
* and/or other materials provided with the distribution.
|
|
12
|
+
* - Neither the name of salesforce.com, inc. nor the names of its contributors
|
|
13
|
+
* may be used to endorse or promote products derived from this software without
|
|
14
|
+
* specific prior written permission of salesforce.com, inc.
|
|
15
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
16
|
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
17
|
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
18
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
19
|
+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
20
|
+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
21
|
+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
22
|
+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
23
|
+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
24
|
+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
25
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// Dependencies
|
|
29
|
+
var path = require('path'),
|
|
30
|
+
SDK = require('./constants'),
|
|
31
|
+
utils = require('./utils'),
|
|
32
|
+
configHelper = require('./configHelper'),
|
|
33
|
+
prepareTemplate = require('./templateHelper').prepareTemplate,
|
|
34
|
+
getSDKTemplateURI = require('./templateHelper').getSDKTemplateURI,
|
|
35
|
+
fs = require('fs');
|
|
36
|
+
|
|
37
|
+
// Constant
|
|
38
|
+
var SERVER_PROJECT_DIR = 'server';
|
|
39
|
+
|
|
40
|
+
//
|
|
41
|
+
// Helper for native application creation
|
|
42
|
+
//
|
|
43
|
+
function createNativeApp(config) {
|
|
44
|
+
|
|
45
|
+
// Copying template to projectDir
|
|
46
|
+
utils.copyFile(config.templateLocalPath, config.projectDir);
|
|
47
|
+
|
|
48
|
+
// Run prepare function of template
|
|
49
|
+
var prepareResult = prepareTemplate(config, config.projectDir);
|
|
50
|
+
|
|
51
|
+
if (config.platform === 'ios' && config.apptype === 'react_native') {
|
|
52
|
+
// Use legacy build
|
|
53
|
+
useLegacyBuild(config, 'ios');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Cleanup
|
|
57
|
+
utils.removeFile(path.join(config.projectDir, 'template.js'));
|
|
58
|
+
|
|
59
|
+
// Done
|
|
60
|
+
return prepareResult;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//
|
|
64
|
+
// Helper for hybrid application creation
|
|
65
|
+
//
|
|
66
|
+
function createHybridApp(config) {
|
|
67
|
+
|
|
68
|
+
// Create app with cordova
|
|
69
|
+
utils.runProcessThrowError('cordova create "' + config.projectDir + '" ' + config.packagename + ' ' + config.appname);
|
|
70
|
+
utils.runProcessThrowError('npm install shelljs@0.7.0', config.projectDir);
|
|
71
|
+
|
|
72
|
+
for (var platform of config.platform.split(',')) {
|
|
73
|
+
utils.runProcessThrowError('cordova platform add ' + platform + '@' + SDK.tools.cordova.platformVersion[platform], config.projectDir);
|
|
74
|
+
}
|
|
75
|
+
utils.runProcessThrowError('cordova plugin add ' + config.cordovaPluginRepoUri + ' --force', config.projectDir);
|
|
76
|
+
|
|
77
|
+
// Web directory - the home for the template
|
|
78
|
+
var webDir = path.join(config.projectDir, 'www');
|
|
79
|
+
|
|
80
|
+
// Remove the default Cordova app.
|
|
81
|
+
utils.removeFile(webDir);
|
|
82
|
+
|
|
83
|
+
// Copying template to www
|
|
84
|
+
utils.copyFile(config.templateLocalPath, webDir);
|
|
85
|
+
|
|
86
|
+
// Run prepare function of template
|
|
87
|
+
var prepareResult = prepareTemplate(config, webDir);
|
|
88
|
+
|
|
89
|
+
// Cleanup
|
|
90
|
+
utils.removeFile(path.join(webDir, 'template.js'));
|
|
91
|
+
|
|
92
|
+
// If template includes server side files
|
|
93
|
+
// Create a fresh sfdx project
|
|
94
|
+
// Add cordova js and plugins at static resources
|
|
95
|
+
// Merge files from template into it
|
|
96
|
+
if (utils.dirExists(path.join(webDir, SERVER_PROJECT_DIR))) {
|
|
97
|
+
config.serverDir = path.join(config.projectDir, SERVER_PROJECT_DIR)
|
|
98
|
+
utils.runProcessThrowError('sfdx force:project:create -n ' + SERVER_PROJECT_DIR, config.projectDir);
|
|
99
|
+
|
|
100
|
+
// Copy cordova js to static resources
|
|
101
|
+
for (var platform of config.platform.split(',')) {
|
|
102
|
+
var cordovaStaticResourcesDir = path.join(config.serverDir, 'force-app', 'main', 'default', 'staticresources', 'cordova' + platform);
|
|
103
|
+
utils.mkDirIfNeeded(cordovaStaticResourcesDir);
|
|
104
|
+
utils.copyFile(path.join(config.projectDir, 'platforms', platform, 'platform_www', '*'), cordovaStaticResourcesDir);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Merge server files from templates
|
|
108
|
+
utils.mergeFile(path.join(webDir, SERVER_PROJECT_DIR), config.serverDir);
|
|
109
|
+
|
|
110
|
+
// Remove server files from www
|
|
111
|
+
utils.removeFile(path.join(webDir, SERVER_PROJECT_DIR));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Run cordova prepare
|
|
115
|
+
utils.runProcessThrowError('cordova prepare', config.projectDir);
|
|
116
|
+
|
|
117
|
+
if (config.platform === 'ios') {
|
|
118
|
+
// Use legacy build
|
|
119
|
+
useLegacyBuild(config, path.join('platforms', 'ios'));
|
|
120
|
+
|
|
121
|
+
// Removing libCordova.a from build (it causes issues e.g. CDVWKWebViewEngine won't register as plugin because it won't be recognized as a kind of CDVPlugin)
|
|
122
|
+
utils.logInfo('Updating xcode project file');
|
|
123
|
+
var xcodeProjectFile = path.join(config.projectDir,'platforms', 'ios', config.appname + '.xcodeproj', 'project.pbxproj')
|
|
124
|
+
var xcodeProjectFileContent = fs.readFileSync(xcodeProjectFile, 'utf8');
|
|
125
|
+
var newXcodeProjectFileContent = xcodeProjectFileContent.split('\n').filter(line => line.indexOf('libCordova.a in Frameworks') == -1).join('\n');
|
|
126
|
+
fs.writeFileSync(xcodeProjectFile, newXcodeProjectFileContent);
|
|
127
|
+
utils.logInfo('Updated xcode project file');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Done
|
|
131
|
+
return prepareResult;
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
//
|
|
136
|
+
// Use legacy build system in XCode
|
|
137
|
+
//
|
|
138
|
+
function useLegacyBuild(config, iosSubDir) {
|
|
139
|
+
var xcSettingsDir = path.join(config.projectDir, iosSubDir, config.appname + '.xcworkspace', 'xcshareddata')
|
|
140
|
+
var xcSettingsFile = path.join(xcSettingsDir, 'WorkspaceSettings.xcsettings');
|
|
141
|
+
var plistFileContent = '<?xml version="1.0" encoding="UTF-8"?>\n' +
|
|
142
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n' +
|
|
143
|
+
'<plist version="1.0">\n' +
|
|
144
|
+
'<dict>\n' +
|
|
145
|
+
'<key>BuildSystemType</key>\n' +
|
|
146
|
+
'<string>Original</string>\n' +
|
|
147
|
+
'<key>DisableBuildSystemDeprecationDiagnostic</key>\n' +
|
|
148
|
+
'<true/>\n' +
|
|
149
|
+
'</dict>\n' +
|
|
150
|
+
'</plist>\n';
|
|
151
|
+
utils.logInfo('Creating WorkspaceSettings.xcsettings for project. Setting the BuildSystemType to original in ' + xcSettingsFile);
|
|
152
|
+
utils.mkDirIfNeeded(xcSettingsDir)
|
|
153
|
+
fs.writeFileSync(xcSettingsFile,plistFileContent,'utf8');
|
|
154
|
+
utils.logInfo('Created WorkspaceSettings.xcsettings for project ' + config.appname);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
//
|
|
158
|
+
// Print details
|
|
159
|
+
//
|
|
160
|
+
function printDetails(config) {
|
|
161
|
+
// Printing out details
|
|
162
|
+
var details = ['Creating ' + config.platform.replace(',', ' and ') + ' ' + config.apptype + ' application using Salesforce Mobile SDK',
|
|
163
|
+
' with app name: ' + config.appname,
|
|
164
|
+
' package name: ' + config.packagename,
|
|
165
|
+
' organization: ' + config.organization,
|
|
166
|
+
'',
|
|
167
|
+
' in: ' + config.projectPath,
|
|
168
|
+
'',
|
|
169
|
+
' from template repo: ' + config.templaterepouri
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
if (config.templatepath) {
|
|
173
|
+
details = details.concat([' template path: ' + config.templatepath]);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
// Hybrid extra details
|
|
178
|
+
if (config.apptype.indexOf('hybrid') >= 0) {
|
|
179
|
+
if (config.apptype === 'hybrid_remote') {
|
|
180
|
+
details = details.concat([' start page: ' + config.startpage]);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
details = details.concat([' plugin repo: ' + config.cordovaPluginRepoUri]);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
utils.logParagraph(details);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
//
|
|
190
|
+
// Print next steps
|
|
191
|
+
//
|
|
192
|
+
function printNextSteps(ide, projectPath, result) {
|
|
193
|
+
var workspacePath = path.join(projectPath, result.workspacePath);
|
|
194
|
+
var bootconfigFile = path.join(projectPath, result.bootconfigFile);
|
|
195
|
+
|
|
196
|
+
// Printing out next steps
|
|
197
|
+
utils.logParagraph(['Next steps' + (result.platform ? ' for ' + result.platform : '') + ':',
|
|
198
|
+
'',
|
|
199
|
+
'Your application project is ready in ' + projectPath + '.',
|
|
200
|
+
'To use your new application in ' + ide + ', do the following:',
|
|
201
|
+
' - open ' + workspacePath + ' in ' + ide,
|
|
202
|
+
' - build and run',
|
|
203
|
+
'Before you ship, make sure to plug your OAuth Client ID and Callback URI,',
|
|
204
|
+
'and OAuth Scopes into ' + bootconfigFile,
|
|
205
|
+
]);
|
|
206
|
+
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
//
|
|
210
|
+
// Print next steps for server project if present
|
|
211
|
+
//
|
|
212
|
+
function printNextStepsForServerProjectIfNeeded(projectPath) {
|
|
213
|
+
var serverProjectPath = path.join(projectPath, SERVER_PROJECT_DIR);
|
|
214
|
+
var hasServerProject = utils.dirExists(serverProjectPath);
|
|
215
|
+
// Extra steps if there is a server project
|
|
216
|
+
if (hasServerProject) {
|
|
217
|
+
utils.logParagraph(['Your application also has a server project in ' + serverProjectPath + '.',
|
|
218
|
+
'Make sure to deploy it to your org before running your application.',
|
|
219
|
+
'',
|
|
220
|
+
'From ' + projectPath + ' do the following to setup a scratch org, push the server code:',
|
|
221
|
+
' - sfdx force:org:create -f server/config/project-scratch-def.json -a MyOrg',
|
|
222
|
+
' - cd server',
|
|
223
|
+
' - sfdx force:source:push -u MyOrg',
|
|
224
|
+
'You also need a password to login to the scratch org from the mobile app:',
|
|
225
|
+
' - sfdx force:user:password:generate -u MyOrg'
|
|
226
|
+
]);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
//
|
|
233
|
+
// Check tools
|
|
234
|
+
//
|
|
235
|
+
function checkTools(toolNames) {
|
|
236
|
+
try {
|
|
237
|
+
utils.log("Checking tools");
|
|
238
|
+
for (var toolName of toolNames) {
|
|
239
|
+
utils.checkToolVersion(SDK.tools[toolName].checkCmd, SDK.tools[toolName].minVersion, SDK.tools[toolName].maxVersion);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
utils.logError('Missing tools\n', error);
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
//
|
|
249
|
+
// Create app - check tools, read config then actually create app
|
|
250
|
+
//
|
|
251
|
+
function createApp(forcecli, config) {
|
|
252
|
+
|
|
253
|
+
// Can't target ios or run pod if not on a mac
|
|
254
|
+
if (process.platform != 'darwin') {
|
|
255
|
+
forcecli.platforms = forcecli.platforms.filter(p=>p!='ios');
|
|
256
|
+
forcecli.toolNames = forcecli.toolNames.filter(t=>t!='pod');
|
|
257
|
+
|
|
258
|
+
if (forcecli.platforms.length == 0) {
|
|
259
|
+
utils.logError('You can only run ' + forcecli.name + ' on a Mac');
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Check tools
|
|
265
|
+
checkTools(forcecli.toolNames);
|
|
266
|
+
|
|
267
|
+
if (config === undefined) {
|
|
268
|
+
// Read parameters from command line
|
|
269
|
+
configHelper.readConfig(process.argv, forcecli, function(config) { actuallyCreateApp(forcecli, config); });
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
// Use parameters passed through
|
|
273
|
+
actuallyCreateApp(forcecli, config);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
//
|
|
278
|
+
// Actually create app
|
|
279
|
+
//
|
|
280
|
+
function actuallyCreateApp(forcecli, config) {
|
|
281
|
+
try {
|
|
282
|
+
// Adding platform
|
|
283
|
+
if (forcecli.platforms.length == 1) {
|
|
284
|
+
config.platform = forcecli.platforms[0];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Adding app type
|
|
288
|
+
if (forcecli.appTypes.length == 1 || config.apptype === undefined || config.apptype === '') {
|
|
289
|
+
config.apptype = forcecli.appTypes[0];
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Setting log level
|
|
293
|
+
if (config.verbose) {
|
|
294
|
+
utils.setLogLevel(utils.LOG_LEVELS.DEBUG);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
utils.setLogLevel(utils.LOG_LEVELS.INFO);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Computing projectDir
|
|
301
|
+
config.projectDir = config.outputdir ? path.resolve(config.outputdir) : path.join(process.cwd(),config.appname)
|
|
302
|
+
config.projectPath = path.relative(process.cwd(), config.projectDir);
|
|
303
|
+
|
|
304
|
+
// Adding version
|
|
305
|
+
config.version = SDK.version;
|
|
306
|
+
|
|
307
|
+
// Figuring out template repo uri and path
|
|
308
|
+
if (config.templaterepouri) {
|
|
309
|
+
if (!config.templaterepouri.startsWith("https://")) {
|
|
310
|
+
// Given a Mobile SDK template name
|
|
311
|
+
config.templatepath = config.templaterepouri;
|
|
312
|
+
config.templaterepouri = SDK.templatesRepoUri;
|
|
313
|
+
} else {
|
|
314
|
+
// Given a full URI
|
|
315
|
+
var templateUriParsed = utils.separateRepoUrlPathBranch(config.templaterepouri);
|
|
316
|
+
config.templaterepouri = templateUriParsed.repo + '#' + templateUriParsed.branch;
|
|
317
|
+
config.templatepath = templateUriParsed.path;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
config.templaterepouri = SDK.templatesRepoUri;
|
|
322
|
+
config.templatepath = forcecli.appTypesToPath[config.apptype];
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Creating tmp dir for template clone
|
|
326
|
+
var tmpDir = utils.mkTmpDir();
|
|
327
|
+
|
|
328
|
+
// Cloning template repo
|
|
329
|
+
var repoDir = utils.cloneRepo(tmpDir, config.templaterepouri);
|
|
330
|
+
config.templateLocalPath = path.join(repoDir, config.templatepath);
|
|
331
|
+
|
|
332
|
+
// Getting apptype from template
|
|
333
|
+
config.apptype = require(path.join(config.templateLocalPath, 'template.js')).appType;
|
|
334
|
+
|
|
335
|
+
var isNative = config.apptype.indexOf('native') >= 0;
|
|
336
|
+
|
|
337
|
+
// Adding hybrid only config
|
|
338
|
+
if (!isNative) {
|
|
339
|
+
config.cordovaPluginRepoUri = config.pluginrepouri || SDK.tools.cordova.pluginRepoUri;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Print details
|
|
343
|
+
printDetails(config);
|
|
344
|
+
|
|
345
|
+
// Creating application
|
|
346
|
+
var results = isNative ? createNativeApp(config) : createHybridApp(config);
|
|
347
|
+
|
|
348
|
+
// Cleanup
|
|
349
|
+
utils.removeFile(tmpDir);
|
|
350
|
+
|
|
351
|
+
// Printing next steps
|
|
352
|
+
if (!(results instanceof Array)) { results = [results] };
|
|
353
|
+
for (var result of results) {
|
|
354
|
+
var ide = SDK.ides[result.platform || config.platform.split(',')[0]];
|
|
355
|
+
printNextSteps(ide, config.projectPath, result);
|
|
356
|
+
}
|
|
357
|
+
printNextStepsForServerProjectIfNeeded(config.projectPath);
|
|
358
|
+
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
utils.logError(forcecli.name + ' failed\n', error);
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
module.exports = {
|
|
367
|
+
createApp
|
|
368
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"soups": [
|
|
3
|
+
{
|
|
4
|
+
"soupName": "userSoup1",
|
|
5
|
+
"indexes": [
|
|
6
|
+
{ "path": "stringField1", "type": "string"},
|
|
7
|
+
{ "path": "integerField1", "type": "integer"},
|
|
8
|
+
{ "path": "floatingField1", "type": "floating"},
|
|
9
|
+
{ "path": "json1Field1", "type": "json1"},
|
|
10
|
+
{ "path": "ftsField1", "type": "full_text"}
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"soupName": "userSoup2",
|
|
15
|
+
"indexes": [
|
|
16
|
+
{ "path": "stringField2", "type": "string"},
|
|
17
|
+
{ "path": "integerField2", "type": "integer"},
|
|
18
|
+
{ "path": "floatingField2", "type": "floating"},
|
|
19
|
+
{ "path": "json1Field2", "type": "json1"},
|
|
20
|
+
{ "path": "ftsField2", "type": "full_text"}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
{
|
|
2
|
+
"syncs": [
|
|
3
|
+
{
|
|
4
|
+
"syncName": "soqlSyncDown",
|
|
5
|
+
"syncType": "syncDown",
|
|
6
|
+
"soupName": "accounts",
|
|
7
|
+
"target": {
|
|
8
|
+
"type":"soql",
|
|
9
|
+
"query":"SELECT Id, Name, LastModifiedDate FROM Account"
|
|
10
|
+
},
|
|
11
|
+
"options": {
|
|
12
|
+
"mergeMode":"OVERWRITE"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"syncName": "soqlSyncDownWithBatchSize",
|
|
17
|
+
"syncType": "syncDown",
|
|
18
|
+
"soupName": "accounts",
|
|
19
|
+
"target": {
|
|
20
|
+
"type":"soql",
|
|
21
|
+
"query":"SELECT Id, Name, LastModifiedDate FROM Account",
|
|
22
|
+
"maxBatchSize": 200
|
|
23
|
+
},
|
|
24
|
+
"options": {
|
|
25
|
+
"mergeMode":"OVERWRITE"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"syncName": "soslSyncDown",
|
|
30
|
+
"syncType": "syncDown",
|
|
31
|
+
"soupName": "accounts",
|
|
32
|
+
"target": {
|
|
33
|
+
"type":"sosl",
|
|
34
|
+
"query":"FIND {Joe} IN NAME FIELDS RETURNING Account"
|
|
35
|
+
},
|
|
36
|
+
"options": {
|
|
37
|
+
"mergeMode":"LEAVE_IF_CHANGED"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"syncName": "mruSyncDown",
|
|
42
|
+
"syncType": "syncDown",
|
|
43
|
+
"soupName": "accounts",
|
|
44
|
+
"target": {
|
|
45
|
+
"type":"mru",
|
|
46
|
+
"sobjectType": "Account",
|
|
47
|
+
"fieldlist": ["Name", "Description"]
|
|
48
|
+
},
|
|
49
|
+
"options": {
|
|
50
|
+
"mergeMode":"OVERWRITE"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"syncName": "refreshSyncDown",
|
|
55
|
+
"syncType": "syncDown",
|
|
56
|
+
"soupName": "accounts",
|
|
57
|
+
"target": {
|
|
58
|
+
"type":"refresh",
|
|
59
|
+
"sobjectType": "Account",
|
|
60
|
+
"fieldlist": ["Name", "Description"],
|
|
61
|
+
"soupName": "accounts"
|
|
62
|
+
},
|
|
63
|
+
"options": {
|
|
64
|
+
"mergeMode":"LEAVE_IF_CHANGED"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"syncName": "layoutSyncDown",
|
|
69
|
+
"syncType": "syncDown",
|
|
70
|
+
"soupName": "accounts",
|
|
71
|
+
"target": {
|
|
72
|
+
"type":"layout",
|
|
73
|
+
"sobjectType": "Account",
|
|
74
|
+
"formFactor": "Medium",
|
|
75
|
+
"layoutType": "Compact",
|
|
76
|
+
"mode": "Edit"
|
|
77
|
+
},
|
|
78
|
+
"options": {
|
|
79
|
+
"mergeMode":"OVERWRITE"
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"syncName": "metadataSyncDown",
|
|
84
|
+
"syncType": "syncDown",
|
|
85
|
+
"soupName": "accounts",
|
|
86
|
+
"target": {
|
|
87
|
+
"type":"metadata",
|
|
88
|
+
"sobjectType": "Account"
|
|
89
|
+
},
|
|
90
|
+
"options": {
|
|
91
|
+
"mergeMode":"LEAVE_IF_CHANGED"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"syncName": "parentChildrenSyncDown",
|
|
96
|
+
"syncType": "syncDown",
|
|
97
|
+
"soupName": "accounts",
|
|
98
|
+
"target": {
|
|
99
|
+
"parent" : {
|
|
100
|
+
"idFieldName" : "IdX",
|
|
101
|
+
"sobjectType" : "Account",
|
|
102
|
+
"modificationDateFieldName" : "LastModifiedDateX",
|
|
103
|
+
"soupName" : "accounts"
|
|
104
|
+
},
|
|
105
|
+
"parentFieldlist" : ["IdX", "Name", "Description"],
|
|
106
|
+
"children" : {
|
|
107
|
+
"parentIdFieldName" : "AccountId",
|
|
108
|
+
"idFieldName" : "IdY",
|
|
109
|
+
"sobjectType" : "Contact",
|
|
110
|
+
"modificationDateFieldName" : "LastModifiedDateY",
|
|
111
|
+
"soupName" : "contacts",
|
|
112
|
+
"sobjectTypePlural" : "Contacts"
|
|
113
|
+
},
|
|
114
|
+
"childrenFieldlist" : [
|
|
115
|
+
"LastName",
|
|
116
|
+
"AccountId"
|
|
117
|
+
],
|
|
118
|
+
"relationshipType" : "MASTER_DETAIL",
|
|
119
|
+
"parentSoqlFilter" : "NameX like 'James%'",
|
|
120
|
+
"type" : "parent_children"
|
|
121
|
+
},
|
|
122
|
+
"options": {
|
|
123
|
+
"mergeMode": "OVERWRITE"
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"syncName": "noBatchSyncUp",
|
|
128
|
+
"syncType": "syncUp",
|
|
129
|
+
"soupName": "accounts",
|
|
130
|
+
"target": {
|
|
131
|
+
"iOSImpl": "SFSyncUpTarget",
|
|
132
|
+
"androidImpl": "com.salesforce.androidsdk.mobilesync.target.SyncUpTarget",
|
|
133
|
+
"createFieldlist": ["Name"],
|
|
134
|
+
"updateFieldlist": ["Description"]
|
|
135
|
+
},
|
|
136
|
+
"options": {
|
|
137
|
+
"fieldlist": [],
|
|
138
|
+
"mergeMode":"LEAVE_IF_CHANGED"
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"syncName": "batchSyncUp",
|
|
143
|
+
"syncType": "syncUp",
|
|
144
|
+
"soupName": "accounts",
|
|
145
|
+
"target": {
|
|
146
|
+
"idFieldName": "IdX",
|
|
147
|
+
"modificationDateFieldName": "LastModifiedDateX",
|
|
148
|
+
"externalIdFieldName": "ExternalIdX"
|
|
149
|
+
},
|
|
150
|
+
"options": {
|
|
151
|
+
"fieldlist": ["Name", "Description"],
|
|
152
|
+
"mergeMode":"OVERWRITE"
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"syncName": "parentChildrenSyncUp",
|
|
157
|
+
"syncType": "syncUp",
|
|
158
|
+
"soupName": "accounts",
|
|
159
|
+
"target": {
|
|
160
|
+
"iOSImpl" : "SFParentChildrenSyncUpTarget",
|
|
161
|
+
"androidImpl": "com.salesforce.androidsdk.mobilesync.target.ParentChildrenSyncUpTarget",
|
|
162
|
+
"parent" : {
|
|
163
|
+
"idFieldName" : "IdX",
|
|
164
|
+
"externalIdFieldName": "ExternalIdX",
|
|
165
|
+
"sobjectType" : "Account",
|
|
166
|
+
"modificationDateFieldName" : "LastModifiedDateX",
|
|
167
|
+
"soupName" : "accounts"
|
|
168
|
+
},
|
|
169
|
+
"createFieldlist" : ["IdX", "Name", "Description"],
|
|
170
|
+
"updateFieldlist" : ["Name", "Description"],
|
|
171
|
+
"children" : {
|
|
172
|
+
"parentIdFieldName" : "AccountId",
|
|
173
|
+
"idFieldName" : "IdY",
|
|
174
|
+
"externalIdFieldName": "ExternalIdY",
|
|
175
|
+
"sobjectType" : "Contact",
|
|
176
|
+
"modificationDateFieldName" : "LastModifiedDateY",
|
|
177
|
+
"soupName" : "contacts",
|
|
178
|
+
"sobjectTypePlural" : "Contacts"
|
|
179
|
+
},
|
|
180
|
+
"childrenCreateFieldlist" : ["LastName", "AccountId"],
|
|
181
|
+
"childrenUpdateFieldlist" : ["FirstName", "AccountId"],
|
|
182
|
+
"relationshipType" : "MASTER_DETAIL"
|
|
183
|
+
},
|
|
184
|
+
"options": {
|
|
185
|
+
"fieldlist":[],
|
|
186
|
+
"mergeMode":"LEAVE_IF_CHANGED"
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2019-present, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* Redistribution and use of this software in source and binary forms, with or
|
|
5
|
+
* without modification, are permitted provided that the following conditions
|
|
6
|
+
* are met:
|
|
7
|
+
* - Redistributions of source code must retain the above copyright notice, this
|
|
8
|
+
* list of conditions and the following disclaimer.
|
|
9
|
+
* - Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
* this list of conditions and the following disclaimer in the documentation
|
|
11
|
+
* and/or other materials provided with the distribution.
|
|
12
|
+
* - Neither the name of salesforce.com, inc. nor the names of its contributors
|
|
13
|
+
* may be used to endorse or promote products derived from this software without
|
|
14
|
+
* specific prior written permission of salesforce.com, inc.
|
|
15
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
16
|
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
17
|
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
18
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
19
|
+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
20
|
+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
21
|
+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
22
|
+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
23
|
+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
24
|
+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
25
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// Dependencies
|
|
29
|
+
var fs = require('fs'),
|
|
30
|
+
path = require('path'),
|
|
31
|
+
COLOR = require('./outputColors'),
|
|
32
|
+
utils = require('./utils'),
|
|
33
|
+
Ajv = require('ajv'),
|
|
34
|
+
jsonlint = require('jsonlint')
|
|
35
|
+
;
|
|
36
|
+
|
|
37
|
+
// Config type to schema map
|
|
38
|
+
var SCHEMA = {
|
|
39
|
+
store: path.resolve(__dirname, 'store.schema.json'),
|
|
40
|
+
syncs: path.resolve(__dirname, 'syncs.schema.json')
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
//
|
|
45
|
+
// Validate config against schema
|
|
46
|
+
//
|
|
47
|
+
function validateJson(configPath, configType) {
|
|
48
|
+
var config = readJsonFile(configPath)
|
|
49
|
+
var schema = readJsonFile(SCHEMA[configType])
|
|
50
|
+
var ajv = new Ajv({allErrors: true});
|
|
51
|
+
var valid = ajv.validate(schema, config);
|
|
52
|
+
if (!valid) {
|
|
53
|
+
utils.logError(JSON.stringify(ajv.errors, null, " "))
|
|
54
|
+
} else {
|
|
55
|
+
utils.logInfo(`${configPath} conforms to ${configType} schema\n`, COLOR.green)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
//
|
|
60
|
+
// Read json from file and validates that is valid json
|
|
61
|
+
//
|
|
62
|
+
function readJsonFile(filePath) {
|
|
63
|
+
try {
|
|
64
|
+
var content = fs.readFileSync(filePath, "UTF-8");
|
|
65
|
+
var json = jsonlint.parse(content);
|
|
66
|
+
return json;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
utils.logError(`Error parsing ${filePath}: ${error}\n`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = {
|
|
74
|
+
validateJson
|
|
75
|
+
};
|