@salesforce/source-tracking 7.1.25 → 7.2.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/lib/shared/functions.d.ts +5 -0
- package/lib/shared/functions.js +14 -2
- package/lib/shared/metadataKeys.js +2 -2
- package/lib/shared/{expectedSourceMembers.d.ts → remote/expectedSourceMembers.d.ts} +1 -1
- package/lib/shared/{expectedSourceMembers.js → remote/expectedSourceMembers.js} +12 -12
- package/lib/shared/remote/fileOperations.d.ts +13 -0
- package/lib/shared/remote/fileOperations.js +94 -0
- package/lib/shared/remote/orgQueries.d.ts +18 -0
- package/lib/shared/remote/orgQueries.js +80 -0
- package/lib/shared/{remoteSourceTrackingService.d.ts → remote/remoteSourceTrackingService.d.ts} +24 -38
- package/lib/shared/{remoteSourceTrackingService.js → remote/remoteSourceTrackingService.js} +84 -148
- package/lib/shared/remote/types.d.ts +39 -0
- package/lib/shared/remote/types.js +19 -0
- package/lib/shared/types.d.ts +4 -13
- package/lib/sourceTracking.js +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { FileResponseSuccess, ForceIgnore, MetadataComponent, MetadataMember, RegistryAccess, SourceComponent } from '@salesforce/source-deploy-retrieve';
|
|
2
2
|
import { RemoteChangeElement, ChangeResult, ChangeResultWithNameAndType, RemoteSyncInput } from './types';
|
|
3
3
|
export declare const getMetadataKey: (metadataType: string, metadataName: string) => string;
|
|
4
|
+
export declare const getLegacyMetadataKey: (metadataType: string, metadataName: string) => string;
|
|
5
|
+
export declare const getMetadataTypeFromKey: (key: string) => string;
|
|
6
|
+
export declare const getMetadataNameFromKey: (key: string) => string;
|
|
7
|
+
export declare const getMetadataTypeFromLegacyKey: (key: string) => string;
|
|
8
|
+
export declare const getMetadataNameFromLegacyKey: (key: string) => string;
|
|
4
9
|
export declare const getKeyFromObject: (element: RemoteChangeElement | ChangeResult) => string;
|
|
5
10
|
export declare const supportsPartialDelete: (cmp: SourceComponent) => boolean;
|
|
6
11
|
export declare const excludeLwcLocalOnlyTest: (filePath: string) => boolean;
|
package/lib/shared/functions.js
CHANGED
|
@@ -9,7 +9,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
9
9
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.uniqueArrayConcat = exports.changeResultToMetadataComponent = exports.FileResponseSuccessToRemoteSyncInput = exports.remoteChangeToMetadataMember = exports.getAllFiles = exports.sourceComponentHasFullNameAndType = exports.sourceComponentIsCustomLabel = exports.forceIgnoreDenies = exports.deleteCustomLabels = exports.ensureRelative = exports.chunkArray = exports.folderContainsPath = exports.pathIsInFolder = exports.excludeLwcLocalOnlyTest = exports.supportsPartialDelete = exports.getKeyFromObject = exports.getMetadataKey = void 0;
|
|
12
|
+
exports.uniqueArrayConcat = exports.changeResultToMetadataComponent = exports.FileResponseSuccessToRemoteSyncInput = exports.remoteChangeToMetadataMember = exports.getAllFiles = exports.sourceComponentHasFullNameAndType = exports.sourceComponentIsCustomLabel = exports.forceIgnoreDenies = exports.deleteCustomLabels = exports.ensureRelative = exports.chunkArray = exports.folderContainsPath = exports.pathIsInFolder = exports.excludeLwcLocalOnlyTest = exports.supportsPartialDelete = exports.getKeyFromObject = exports.getMetadataNameFromLegacyKey = exports.getMetadataTypeFromLegacyKey = exports.getMetadataNameFromKey = exports.getMetadataTypeFromKey = exports.getLegacyMetadataKey = exports.getMetadataKey = void 0;
|
|
13
13
|
const node_path_1 = require("node:path");
|
|
14
14
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
15
15
|
const ts_types_1 = require("@salesforce/ts-types");
|
|
@@ -17,8 +17,20 @@ const source_deploy_retrieve_1 = require("@salesforce/source-deploy-retrieve");
|
|
|
17
17
|
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
18
18
|
const kit_1 = require("@salesforce/kit");
|
|
19
19
|
const remoteChangeIgnoring_1 = require("./remoteChangeIgnoring");
|
|
20
|
-
const
|
|
20
|
+
const keySplit = '###';
|
|
21
|
+
const legacyKeySplit = '__';
|
|
22
|
+
const getMetadataKey = (metadataType, metadataName) => `${metadataType}${keySplit}${metadataName}`;
|
|
21
23
|
exports.getMetadataKey = getMetadataKey;
|
|
24
|
+
const getLegacyMetadataKey = (metadataType, metadataName) => `${metadataType}${legacyKeySplit}${metadataName}`;
|
|
25
|
+
exports.getLegacyMetadataKey = getLegacyMetadataKey;
|
|
26
|
+
const getMetadataTypeFromKey = (key) => decodeURIComponent(key.split(keySplit)[0]);
|
|
27
|
+
exports.getMetadataTypeFromKey = getMetadataTypeFromKey;
|
|
28
|
+
const getMetadataNameFromKey = (key) => decodeURIComponent(key.split(keySplit)[1]);
|
|
29
|
+
exports.getMetadataNameFromKey = getMetadataNameFromKey;
|
|
30
|
+
const getMetadataTypeFromLegacyKey = (key) => key.split(legacyKeySplit)[0];
|
|
31
|
+
exports.getMetadataTypeFromLegacyKey = getMetadataTypeFromLegacyKey;
|
|
32
|
+
const getMetadataNameFromLegacyKey = (key) => decodeURIComponent(key.split(legacyKeySplit).slice(1).join(legacyKeySplit));
|
|
33
|
+
exports.getMetadataNameFromLegacyKey = getMetadataNameFromLegacyKey;
|
|
22
34
|
const getKeyFromObject = (element) => {
|
|
23
35
|
if (element.type && element.name) {
|
|
24
36
|
return (0, exports.getMetadataKey)(element.type, element.name);
|
|
@@ -38,13 +38,13 @@ const getMetadataKeyFromFileResponse = (fileResponse) => {
|
|
|
38
38
|
// Aura/LWC need to have both the bundle level and file level keys
|
|
39
39
|
if (fileResponse.type === 'LightningComponentBundle' && fileResponse.filePath) {
|
|
40
40
|
return [
|
|
41
|
-
|
|
41
|
+
(0, functions_1.getMetadataKey)('LightningComponentResource', pathAfterFullName(fileResponse)),
|
|
42
42
|
(0, functions_1.getMetadataKey)(fileResponse.type, fileResponse.fullName),
|
|
43
43
|
];
|
|
44
44
|
}
|
|
45
45
|
if (fileResponse.type === 'AuraDefinitionBundle' && fileResponse.filePath) {
|
|
46
46
|
return [
|
|
47
|
-
|
|
47
|
+
(0, functions_1.getMetadataKey)('AuraDefinition', pathAfterFullName(fileResponse)),
|
|
48
48
|
(0, functions_1.getMetadataKey)(fileResponse.type, fileResponse.fullName),
|
|
49
49
|
];
|
|
50
50
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { RemoteSyncInput } from '
|
|
1
|
+
import { RemoteSyncInput } from '../types';
|
|
2
2
|
export declare const calculateExpectedSourceMembers: (expectedMembers: RemoteSyncInput[]) => Map<string, RemoteSyncInput>;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.calculateExpectedSourceMembers = void 0;
|
|
10
10
|
const source_deploy_retrieve_1 = require("@salesforce/source-deploy-retrieve");
|
|
11
|
-
const metadataKeys_1 = require("
|
|
11
|
+
const metadataKeys_1 = require("../metadataKeys");
|
|
12
12
|
const typesToNoPollFor = [
|
|
13
13
|
'CustomObject',
|
|
14
14
|
'EmailFolder',
|
|
@@ -38,16 +38,16 @@ const isSpecialAuraXml = (filePath) => Boolean(filePath &&
|
|
|
38
38
|
filePath.endsWith('.intf-meta.xml')));
|
|
39
39
|
// things that never have SourceMembers
|
|
40
40
|
const excludedKeys = [
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
41
|
+
'AppMenu###Salesforce1',
|
|
42
|
+
'Profile###Standard',
|
|
43
|
+
'Profile###Guest License User',
|
|
44
|
+
'CustomTab###standard-home',
|
|
45
|
+
'Profile###Minimum Access - Salesforce',
|
|
46
|
+
'Profile###Salesforce API Only System Integrations',
|
|
47
|
+
'AssignmentRules###Case',
|
|
48
|
+
'ListView###CollaborationGroup.All_ChatterGroups',
|
|
49
|
+
'CustomTab###standard-mailapp',
|
|
50
|
+
'ApexEmailNotifications###apexEmailNotifications',
|
|
51
51
|
];
|
|
52
52
|
const calculateExpectedSourceMembers = (expectedMembers) => {
|
|
53
53
|
const outstandingSourceMembers = new Map();
|
|
@@ -79,7 +79,7 @@ const calculateExpectedSourceMembers = (expectedMembers) => {
|
|
|
79
79
|
// remove some individual members known to not work with tracking even when their type does
|
|
80
80
|
.filter((key) =>
|
|
81
81
|
// CustomObject could have been re-added by the key generator from one of its fields
|
|
82
|
-
!key.startsWith('
|
|
82
|
+
!key.startsWith('CustomObject###') && !excludedKeys.includes(key))
|
|
83
83
|
.map((key) => outstandingSourceMembers.set(key, member));
|
|
84
84
|
});
|
|
85
85
|
return outstandingSourceMembers;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { RemoteChangeElement } from '../types';
|
|
2
|
+
import { ContentsV0, ContentsV1, MemberRevision, MemberRevisionLegacy } from './types';
|
|
3
|
+
export declare const FILENAME = "maxRevision.json";
|
|
4
|
+
export declare const getFilePath: (orgId: string) => string;
|
|
5
|
+
export declare const readFileContents: (filePath: string) => Promise<ContentsV1 | Record<string, never>>;
|
|
6
|
+
export declare const revisionToRemoteChangeElement: (memberRevision: MemberRevision) => RemoteChangeElement;
|
|
7
|
+
export declare const upgradeFileContents: (contents: ContentsV0) => ContentsV1;
|
|
8
|
+
export declare const writeTrackingFile: ({ filePath, maxCounter, members, }: {
|
|
9
|
+
filePath: string;
|
|
10
|
+
maxCounter: number;
|
|
11
|
+
members: Map<string, MemberRevision>;
|
|
12
|
+
}) => Promise<void>;
|
|
13
|
+
export declare const toLegacyMemberRevision: ([, member]: [string, MemberRevision]) => [key: string, MemberRevisionLegacy];
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.toLegacyMemberRevision = exports.writeTrackingFile = exports.upgradeFileContents = exports.revisionToRemoteChangeElement = exports.readFileContents = exports.getFilePath = exports.FILENAME = void 0;
|
|
7
|
+
/*
|
|
8
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
9
|
+
* All rights reserved.
|
|
10
|
+
* Licensed under the BSD 3-Clause license.
|
|
11
|
+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
12
|
+
*/
|
|
13
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
+
const kit_1 = require("@salesforce/kit");
|
|
16
|
+
const core_1 = require("@salesforce/core");
|
|
17
|
+
const functions_1 = require("../functions");
|
|
18
|
+
exports.FILENAME = 'maxRevision.json';
|
|
19
|
+
const getFilePath = (orgId) => node_path_1.default.join('.sf', 'orgs', orgId, exports.FILENAME);
|
|
20
|
+
exports.getFilePath = getFilePath;
|
|
21
|
+
const readFileContents = async (filePath) => {
|
|
22
|
+
try {
|
|
23
|
+
const contents = await node_fs_1.default.promises.readFile(filePath, 'utf8');
|
|
24
|
+
const parsedContents = (0, kit_1.parseJsonMap)(contents, filePath);
|
|
25
|
+
if (parsedContents.fileVersion === 1) {
|
|
26
|
+
return parsedContents;
|
|
27
|
+
}
|
|
28
|
+
core_1.Logger.childFromRoot('remoteSourceTrackingService:readFileContents').debug(`older tracking file version, found ${parsedContents.fileVersion ?? 'undefined'}. Upgrading file contents. Some expected data may be missing`);
|
|
29
|
+
return (0, exports.upgradeFileContents)(parsedContents);
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
core_1.Logger.childFromRoot('remoteSourceTrackingService:readFileContents').debug(`Error reading or parsing file file at ${filePath}. Will treat as an empty file.`, e);
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
exports.readFileContents = readFileContents;
|
|
37
|
+
const revisionToRemoteChangeElement = (memberRevision) => ({
|
|
38
|
+
type: memberRevision.MemberType,
|
|
39
|
+
name: memberRevision.MemberName,
|
|
40
|
+
deleted: memberRevision.IsNameObsolete,
|
|
41
|
+
modified: memberRevision.IsNewMember === false,
|
|
42
|
+
revisionCounter: memberRevision.RevisionCounter,
|
|
43
|
+
changedBy: memberRevision.ChangedBy,
|
|
44
|
+
memberIdOrName: memberRevision.MemberIdOrName,
|
|
45
|
+
});
|
|
46
|
+
exports.revisionToRemoteChangeElement = revisionToRemoteChangeElement;
|
|
47
|
+
const upgradeFileContents = (contents) => ({
|
|
48
|
+
fileVersion: 1,
|
|
49
|
+
serverMaxRevisionCounter: contents.serverMaxRevisionCounter,
|
|
50
|
+
// @ts-expect-error the old file didn't store the IsNewMember field or any indication of whether the member was add/modified
|
|
51
|
+
sourceMembers: Object.fromEntries(
|
|
52
|
+
// it's the old version
|
|
53
|
+
Object.entries(contents.sourceMembers).map(([key, value]) => [
|
|
54
|
+
(0, functions_1.getMetadataKey)((0, functions_1.getMetadataTypeFromLegacyKey)(key), (0, functions_1.getMetadataNameFromLegacyKey)(key)),
|
|
55
|
+
{
|
|
56
|
+
MemberName: (0, functions_1.getMetadataNameFromLegacyKey)(key),
|
|
57
|
+
MemberType: value.memberType,
|
|
58
|
+
IsNameObsolete: value.isNameObsolete,
|
|
59
|
+
RevisionCounter: value.serverRevisionCounter,
|
|
60
|
+
lastRetrievedFromServer: value.lastRetrievedFromServer ?? undefined,
|
|
61
|
+
ChangedBy: 'unknown',
|
|
62
|
+
MemberIdOrName: 'unknown',
|
|
63
|
+
},
|
|
64
|
+
])),
|
|
65
|
+
});
|
|
66
|
+
exports.upgradeFileContents = upgradeFileContents;
|
|
67
|
+
const writeTrackingFile = async ({ filePath, maxCounter, members, }) => {
|
|
68
|
+
const lockResult = await (0, core_1.lockInit)(filePath);
|
|
69
|
+
const CURRENT_FILE_VERSION_ENV = core_1.envVars.getNumber('SF_SOURCE_TRACKING_FILE_VERSION') ?? 0;
|
|
70
|
+
const contents = CURRENT_FILE_VERSION_ENV === 1
|
|
71
|
+
? {
|
|
72
|
+
fileVersion: 1,
|
|
73
|
+
serverMaxRevisionCounter: maxCounter,
|
|
74
|
+
sourceMembers: Object.fromEntries(members),
|
|
75
|
+
}
|
|
76
|
+
: {
|
|
77
|
+
fileVersion: 0,
|
|
78
|
+
serverMaxRevisionCounter: maxCounter,
|
|
79
|
+
sourceMembers: Object.fromEntries(Array.from(members.entries()).map(exports.toLegacyMemberRevision)),
|
|
80
|
+
};
|
|
81
|
+
await lockResult.writeAndUnlock(JSON.stringify(contents, null, 4));
|
|
82
|
+
};
|
|
83
|
+
exports.writeTrackingFile = writeTrackingFile;
|
|
84
|
+
const toLegacyMemberRevision = ([, member]) => [
|
|
85
|
+
(0, functions_1.getLegacyMetadataKey)(member.MemberType, member.MemberName),
|
|
86
|
+
{
|
|
87
|
+
memberType: member.MemberType,
|
|
88
|
+
serverRevisionCounter: member.RevisionCounter,
|
|
89
|
+
lastRetrievedFromServer: member.lastRetrievedFromServer ?? null,
|
|
90
|
+
isNameObsolete: member.IsNameObsolete,
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
exports.toLegacyMemberRevision = toLegacyMemberRevision;
|
|
94
|
+
//# sourceMappingURL=fileOperations.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Connection } from '@salesforce/core';
|
|
2
|
+
import { Duration } from '@salesforce/kit';
|
|
3
|
+
import { PinoLogger } from './remoteSourceTrackingService';
|
|
4
|
+
import { SourceMember } from './types';
|
|
5
|
+
export declare const calculateTimeout: (logger: PinoLogger) => (memberCount: number) => Duration;
|
|
6
|
+
/** exported only for spy/mock */
|
|
7
|
+
export declare const querySourceMembersTo: (conn: Connection, toRevision: number) => Promise<SourceMember[]>;
|
|
8
|
+
export declare const querySourceMembersFrom: ({ conn, fromRevision, queryCache, userQueryCache, logger, }: {
|
|
9
|
+
conn: Connection;
|
|
10
|
+
fromRevision: number;
|
|
11
|
+
/** optional cache, used if present. Side effect: cache will be mutated */
|
|
12
|
+
queryCache?: Map<number, SourceMember[]>;
|
|
13
|
+
/** optional cache, used if present. Side effect: cache will be mutated */
|
|
14
|
+
userQueryCache?: Map<string, string>;
|
|
15
|
+
/** if you don't pass in a logger, you get no log output */
|
|
16
|
+
logger?: PinoLogger;
|
|
17
|
+
}) => Promise<SourceMember[]>;
|
|
18
|
+
export declare const queryFn: (conn: Connection, query: string) => Promise<SourceMember[]>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.queryFn = exports.querySourceMembersFrom = exports.querySourceMembersTo = exports.calculateTimeout = void 0;
|
|
4
|
+
/*
|
|
5
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
6
|
+
* All rights reserved.
|
|
7
|
+
* Licensed under the BSD 3-Clause license.
|
|
8
|
+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
9
|
+
*/
|
|
10
|
+
const core_1 = require("@salesforce/core");
|
|
11
|
+
const kit_1 = require("@salesforce/kit");
|
|
12
|
+
const types_1 = require("./types");
|
|
13
|
+
const calculateTimeout = (logger) => (memberCount) => {
|
|
14
|
+
const overriddenTimeout = core_1.envVars.getNumber('SF_SOURCE_MEMBER_POLLING_TIMEOUT', 0);
|
|
15
|
+
if (overriddenTimeout > 0) {
|
|
16
|
+
logger.debug(`Overriding SourceMember polling timeout to ${overriddenTimeout}`);
|
|
17
|
+
return kit_1.Duration.seconds(overriddenTimeout);
|
|
18
|
+
}
|
|
19
|
+
// Calculate a polling timeout for SourceMembers based on the number of
|
|
20
|
+
// member names being polled plus a buffer of 5 seconds. This will
|
|
21
|
+
// wait 50s for each 1000 components, plus 5s.
|
|
22
|
+
const pollingTimeout = Math.ceil(memberCount * 0.05) + 5;
|
|
23
|
+
logger.debug(`Computed SourceMember polling timeout of ${pollingTimeout}s`);
|
|
24
|
+
return kit_1.Duration.seconds(pollingTimeout);
|
|
25
|
+
};
|
|
26
|
+
exports.calculateTimeout = calculateTimeout;
|
|
27
|
+
/** exported only for spy/mock */
|
|
28
|
+
const querySourceMembersTo = async (conn, toRevision) => {
|
|
29
|
+
const query = `SELECT ${types_1.SOURCE_MEMBER_FIELDS.join(', ')} FROM SourceMember WHERE RevisionCounter <= ${toRevision}`;
|
|
30
|
+
return (0, exports.queryFn)(conn, query);
|
|
31
|
+
};
|
|
32
|
+
exports.querySourceMembersTo = querySourceMembersTo;
|
|
33
|
+
const querySourceMembersFrom = async ({ conn, fromRevision, queryCache, userQueryCache, logger, }) => {
|
|
34
|
+
if (queryCache) {
|
|
35
|
+
// Check cache first and return if found.
|
|
36
|
+
const cachedQueryResult = queryCache.get(fromRevision);
|
|
37
|
+
if (cachedQueryResult) {
|
|
38
|
+
logger?.debug(`Using cache for SourceMember query for revision ${fromRevision}`);
|
|
39
|
+
return cachedQueryResult;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// because `serverMaxRevisionCounter` is always updated, we need to select > to catch the most recent change
|
|
43
|
+
const query = `SELECT ${types_1.SOURCE_MEMBER_FIELDS.join(', ')} FROM SourceMember WHERE RevisionCounter > ${fromRevision}`;
|
|
44
|
+
logger?.debug(`Query: ${query}`);
|
|
45
|
+
const queryResult = await (0, exports.queryFn)(conn, query);
|
|
46
|
+
if (userQueryCache) {
|
|
47
|
+
await updateCacheWithUnknownUsers(conn, queryResult, userQueryCache);
|
|
48
|
+
}
|
|
49
|
+
const queryResultWithResolvedUsers = queryResult.map((member) => ({
|
|
50
|
+
...member,
|
|
51
|
+
ChangedBy: userQueryCache?.get(member.ChangedBy) ?? member.ChangedBy,
|
|
52
|
+
}));
|
|
53
|
+
queryCache?.set(fromRevision, queryResultWithResolvedUsers);
|
|
54
|
+
return queryResultWithResolvedUsers;
|
|
55
|
+
};
|
|
56
|
+
exports.querySourceMembersFrom = querySourceMembersFrom;
|
|
57
|
+
const queryFn = async (conn, query) => {
|
|
58
|
+
try {
|
|
59
|
+
return (await conn.tooling.query(query, { autoFetch: true, maxFetch: 50_000 })).records.map(sourceMemberCorrections);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
throw core_1.SfError.wrap(error);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
exports.queryFn = queryFn;
|
|
66
|
+
/** A series of workarounds for server-side bugs. Each bug should be filed against a team, with a WI, so we know when these are fixed and can be removed */
|
|
67
|
+
const sourceMemberCorrections = (sourceMember) => {
|
|
68
|
+
if (sourceMember.MemberType === 'QuickActionDefinition') {
|
|
69
|
+
return { ...sourceMember, MemberType: 'QuickAction' }; // W-15837125
|
|
70
|
+
}
|
|
71
|
+
return sourceMember;
|
|
72
|
+
};
|
|
73
|
+
const updateCacheWithUnknownUsers = async (conn, queryResult, userCache) => {
|
|
74
|
+
const unknownUsers = new Set(queryResult.map((member) => member.ChangedBy).filter((u) => !userCache.has(u)));
|
|
75
|
+
if (unknownUsers.size > 0) {
|
|
76
|
+
const userQuery = `SELECT Id, Name FROM User WHERE Id IN ('${Array.from(unknownUsers).join("','")}')`;
|
|
77
|
+
(await conn.query(userQuery, { autoFetch: true, maxFetch: 50_000 })).records.map((u) => userCache.set((0, core_1.trimTo15)(u.Id), u.Name));
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=orgQueries.js.map
|
package/lib/shared/{remoteSourceTrackingService.d.ts → remote/remoteSourceTrackingService.d.ts}
RENAMED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import { Logger, Org
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
/** represents the contents of the config file stored in 'maxRevision.json' */
|
|
5
|
-
export type Contents = {
|
|
6
|
-
serverMaxRevisionCounter: number;
|
|
7
|
-
sourceMembers: Record<string, MemberRevision>;
|
|
8
|
-
};
|
|
9
|
-
type PinoLogger = ReturnType<(typeof Logger)['getRawRootLogger']>;
|
|
1
|
+
import { Logger, Org } from '@salesforce/core';
|
|
2
|
+
import { ChangeResult, RemoteChangeElement, RemoteSyncInput } from '../types';
|
|
3
|
+
export type PinoLogger = ReturnType<(typeof Logger)['getRawRootLogger']>;
|
|
10
4
|
/** Options for RemoteSourceTrackingService.getInstance */
|
|
11
5
|
export type RemoteSourceTrackingServiceOptions = {
|
|
12
6
|
org: Org;
|
|
@@ -16,37 +10,37 @@ export type RemoteSourceTrackingServiceOptions = {
|
|
|
16
10
|
* This service handles source tracking of metadata between a local project and an org.
|
|
17
11
|
* Source tracking state is persisted to .sfdx/orgs/<orgId>/maxRevision.json.
|
|
18
12
|
* This JSON file keeps track of `SourceMember` objects and the `serverMaxRevisionCounter`,
|
|
19
|
-
* which is the highest `
|
|
20
|
-
*
|
|
21
|
-
* Each SourceMember object has 4 fields:
|
|
22
|
-
* * serverRevisionCounter: the current RevisionCounter on the server for this object
|
|
23
|
-
* * lastRetrievedFromServer: the RevisionCounter last retrieved from the server for this object
|
|
24
|
-
* * memberType: the metadata name of the SourceMember
|
|
25
|
-
* * isNameObsolete: `true` if this object has been deleted in the org
|
|
13
|
+
* which is the highest `RevisionCounter` value of all the tracked elements.
|
|
26
14
|
*
|
|
27
|
-
*
|
|
15
|
+
* See @MemberRevision for the structure of the `MemberRevision` object. It's SourceMember (the tooling sobject) with the additional lastRetrievedFromServer field
|
|
28
16
|
```
|
|
29
17
|
{
|
|
18
|
+
fileVersion: 1,
|
|
30
19
|
serverMaxRevisionCounter: 3,
|
|
31
20
|
sourceMembers: {
|
|
32
|
-
|
|
33
|
-
|
|
21
|
+
ApexClass###MyClass: {
|
|
22
|
+
RevisionCounter: 3,
|
|
23
|
+
MemberType: ApexClass,
|
|
24
|
+
...,
|
|
34
25
|
lastRetrievedFromServer: 2,
|
|
35
|
-
memberType: ApexClass,
|
|
36
|
-
isNameObsolete: false
|
|
37
26
|
},
|
|
38
|
-
|
|
39
|
-
|
|
27
|
+
CustomObject###Student__c: {
|
|
28
|
+
RevisionCounter: 1,
|
|
29
|
+
MemberType: CustomObject,
|
|
30
|
+
...,
|
|
40
31
|
lastRetrievedFromServer: 1,
|
|
41
|
-
memberType: CustomObject,
|
|
42
|
-
isNameObsolete: false
|
|
43
32
|
}
|
|
44
33
|
}
|
|
45
34
|
}
|
|
46
35
|
```
|
|
47
|
-
* In this example, `
|
|
36
|
+
* In this example, `ApexClass###MyClass` has been changed in the org because the `serverRevisionCounter` is different
|
|
48
37
|
* from the `lastRetrievedFromServer`. When a pull is performed, all of the pulled members will have their counters set
|
|
49
38
|
* to the corresponding `RevisionCounter` from the `SourceMember` of the org.
|
|
39
|
+
*
|
|
40
|
+
* Tracking files are written to the older format described in `MemberRevisionLegacy`
|
|
41
|
+
* if the environment variable CURRENT_FILE_VERSION_ENV is not set to 1
|
|
42
|
+
*
|
|
43
|
+
* The "in memorgy" storage is in MemberRevision format.
|
|
50
44
|
*/
|
|
51
45
|
export declare class RemoteSourceTrackingService {
|
|
52
46
|
/** map of constructed, init'ed instances; key is orgId. It's like a singleton at the org level */
|
|
@@ -57,6 +51,7 @@ export declare class RemoteSourceTrackingService {
|
|
|
57
51
|
private sourceMembers;
|
|
58
52
|
private org;
|
|
59
53
|
private queryCache;
|
|
54
|
+
private userQueryCache;
|
|
60
55
|
/**
|
|
61
56
|
* Initializes the service with existing remote source tracking data, or sets
|
|
62
57
|
* the state to begin source tracking of metadata changes in the org.
|
|
@@ -77,7 +72,7 @@ export declare class RemoteSourceTrackingService {
|
|
|
77
72
|
*/
|
|
78
73
|
static delete(orgId: string): Promise<string>;
|
|
79
74
|
/**
|
|
80
|
-
* pass in a
|
|
75
|
+
* pass in a series of SDR FilResponses .\
|
|
81
76
|
* it sets their last retrieved revision to the current revision counter from the server.
|
|
82
77
|
*/
|
|
83
78
|
syncSpecifiedElements(elements: RemoteSyncInput[]): Promise<void>;
|
|
@@ -98,10 +93,7 @@ export declare class RemoteSourceTrackingService {
|
|
|
98
93
|
* source tracking state. All `ChangeElements` not in sync with the org
|
|
99
94
|
* are returned.
|
|
100
95
|
*/
|
|
101
|
-
retrieveUpdates(
|
|
102
|
-
sync?: boolean | undefined;
|
|
103
|
-
cache?: boolean | undefined;
|
|
104
|
-
}): Promise<RemoteChangeElement[]>;
|
|
96
|
+
retrieveUpdates(): Promise<RemoteChangeElement[]>;
|
|
105
97
|
/**
|
|
106
98
|
* Polls the org for SourceMember objects matching the provided metadata member names,
|
|
107
99
|
* stopping when all members have been matched or the polling timeout is met or exceeded.
|
|
@@ -112,7 +104,7 @@ export declare class RemoteSourceTrackingService {
|
|
|
112
104
|
*/
|
|
113
105
|
pollForSourceTracking(expectedMembers: RemoteSyncInput[]): Promise<void>;
|
|
114
106
|
/**
|
|
115
|
-
* Adds the given SourceMembers to the list of tracked MemberRevisions,
|
|
107
|
+
* Adds the given SourceMembers to the list of tracked MemberRevisions, optionally updating
|
|
116
108
|
* the lastRetrievedFromServer field (sync), and persists the changes to maxRevision.json.
|
|
117
109
|
*/
|
|
118
110
|
private trackSourceMembers;
|
|
@@ -121,15 +113,9 @@ export declare class RemoteSourceTrackingService {
|
|
|
121
113
|
/** Return a tracked element as MemberRevision data.*/
|
|
122
114
|
private getSourceMember;
|
|
123
115
|
private setMemberRevision;
|
|
124
|
-
private querySourceMembersFrom;
|
|
125
|
-
private write;
|
|
126
116
|
}
|
|
127
117
|
/**
|
|
128
118
|
* pass in an RCE, and this will return a pullable ChangeResult.
|
|
129
119
|
* Useful for correcing bundle types where the files show change results with types but aren't resolvable
|
|
130
120
|
*/
|
|
131
121
|
export declare const remoteChangeElementToChangeResult: (rce: RemoteChangeElement) => ChangeResult;
|
|
132
|
-
export declare const calculateTimeout: (logger: PinoLogger) => (memberCount: number) => Duration;
|
|
133
|
-
/** exported only for spy/mock */
|
|
134
|
-
export declare const querySourceMembersTo: (conn: Connection, toRevision: number) => Promise<SourceMember[]>;
|
|
135
|
-
export {};
|