@vtex/api 6.45.14-beta.1 → 6.45.15
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/CHANGELOG.md +24 -0
- package/lib/clients/infra/VBase.js +29 -4
- package/lib/service/master.js +0 -4
- package/lib/service/worker/listeners.js +0 -4
- package/lib/utils/MineWinsConflictsResolver.d.ts +2 -2
- package/lib/utils/MineWinsConflictsResolver.js +17 -22
- package/lib/utils/conflicts.mock.d.ts +17 -0
- package/lib/utils/conflicts.mock.js +27 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,23 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [6.45.15] - 2023-02-01
|
|
11
|
+
### Changed
|
|
12
|
+
- Removed unnecessary worker signal logs
|
|
13
|
+
|
|
14
|
+
## [6.45.14] - 2023-02-01
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- New tests for `MineWinsConflictsResolver`.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Merged/Rebased content not being returned causing inconsistencies.
|
|
23
|
+
- Do not return empty content if no conflict is found.
|
|
24
|
+
|
|
25
|
+
## [6.45.13] - 2022-12-12
|
|
9
26
|
### Changed
|
|
10
27
|
- Deterministic cache for getWithBody
|
|
11
28
|
- Remove socket metrics per origin
|
|
@@ -13,6 +30,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
|
13
30
|
## [6.45.12] - 2022-04-14
|
|
14
31
|
|
|
15
32
|
### Fixed
|
|
33
|
+
|
|
16
34
|
- Update sensitive fields list.
|
|
17
35
|
- Remove "error" field.
|
|
18
36
|
- Add missing fields.
|
|
@@ -1737,3 +1755,9 @@ instead
|
|
|
1737
1755
|
### Changed
|
|
1738
1756
|
|
|
1739
1757
|
- `HttpClient` now adds `'Accept-Encoding': 'gzip'` header by default.
|
|
1758
|
+
|
|
1759
|
+
|
|
1760
|
+
[Unreleased]: https://github.com/vtex/node-vtex-api/compare/v6.45.15...HEAD
|
|
1761
|
+
[6.45.15]: https://github.com/vtex/node-vtex-api/compare/v6.45.14...v6.45.15
|
|
1762
|
+
[6.45.14]: https://github.com/vtex/node-vtex-api/compare/v6.45.13...v6.45.14
|
|
1763
|
+
[6.45.13]: https://github.com/vtex/node-vtex-api/compare/v6.45.12...v6.45.13
|
|
@@ -68,15 +68,40 @@ class VBase extends InfraClient_1.InfraClient {
|
|
|
68
68
|
const headers = conflictsResolver ? { 'X-Vtex-Detect-Conflicts': true } : {};
|
|
69
69
|
const inflightKey = HttpClient_1.inflightURL;
|
|
70
70
|
const metric = 'vbase-get-json';
|
|
71
|
-
return this.http
|
|
71
|
+
return this.http
|
|
72
|
+
.getRaw(routes.File(bucket, path), {
|
|
73
|
+
headers,
|
|
74
|
+
inflightKey,
|
|
75
|
+
metric,
|
|
76
|
+
nullIfNotFound,
|
|
77
|
+
tracing: {
|
|
72
78
|
requestSpanNameSuffix: metric,
|
|
73
79
|
...tracingConfig === null || tracingConfig === void 0 ? void 0 : tracingConfig.tracing,
|
|
74
|
-
}
|
|
80
|
+
},
|
|
81
|
+
})
|
|
75
82
|
.catch(async (error) => {
|
|
76
83
|
const { response } = error;
|
|
77
84
|
if (response && response.status === 409 && conflictsResolver) {
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
try {
|
|
86
|
+
const conflictsMergedData = await conflictsResolver.resolve(this.context.logger);
|
|
87
|
+
return { ...response, data: conflictsMergedData };
|
|
88
|
+
}
|
|
89
|
+
catch (resolverError) {
|
|
90
|
+
const typedResolverError = resolverError;
|
|
91
|
+
if ((typedResolverError === null || typedResolverError === void 0 ? void 0 : typedResolverError.status) === 404) {
|
|
92
|
+
return this.http.getRaw(routes.File(bucket, path), {
|
|
93
|
+
'X-Vtex-Detect-Conflicts': false,
|
|
94
|
+
inflightKey,
|
|
95
|
+
metric,
|
|
96
|
+
nullIfNotFound,
|
|
97
|
+
tracing: {
|
|
98
|
+
requestSpanNameSuffix: metric,
|
|
99
|
+
...tracingConfig === null || tracingConfig === void 0 ? void 0 : tracingConfig.tracing,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
throw resolverError;
|
|
104
|
+
}
|
|
80
105
|
}
|
|
81
106
|
throw error;
|
|
82
107
|
});
|
package/lib/service/master.js
CHANGED
|
@@ -52,10 +52,6 @@ const onOnline = (worker) => {
|
|
|
52
52
|
const GRACEFULLY_SHUTDOWN_TIMEOUT_S = 30;
|
|
53
53
|
const SIGINT_TIMEOUT_S = 1;
|
|
54
54
|
const handleSignal = (timeout) => (signal) => {
|
|
55
|
-
// Log the Master Process received a signal
|
|
56
|
-
const message = `Master process ${process.pid} received signal ${signal}`;
|
|
57
|
-
console.warn(message);
|
|
58
|
-
listeners_1.logger.warn({ message, signal });
|
|
59
55
|
// For each worker, let's try to kill it gracefully
|
|
60
56
|
Object.values(cluster_1.default.workers).forEach((worker) => worker === null || worker === void 0 ? void 0 : worker.kill(signal));
|
|
61
57
|
// Let's raise the flag to kill the master process after all workers have died
|
|
@@ -5,11 +5,7 @@ const RequestCancelledError_1 = require("../../errors/RequestCancelledError");
|
|
|
5
5
|
const logger_1 = require("../logger");
|
|
6
6
|
exports.logger = new logger_1.Logger({ account: 'unhandled', workspace: 'unhandled', requestId: 'unhandled', operationId: 'unhandled', production: process.env.VTEX_PRODUCTION === 'true' });
|
|
7
7
|
let watched;
|
|
8
|
-
// Remove the any typings once we move to nodejs 10.x
|
|
9
8
|
const handleSignal = signal => {
|
|
10
|
-
const message = `Worker ${process.pid} received signal ${signal}`;
|
|
11
|
-
console.warn(message);
|
|
12
|
-
exports.logger.warn({ message, signal });
|
|
13
9
|
process.exit(os_1.constants.signals[signal]);
|
|
14
10
|
};
|
|
15
11
|
exports.addProcessListeners = () => {
|
|
@@ -12,8 +12,8 @@ export declare class MineWinsConflictsResolver<T> implements ConflictsResolver<T
|
|
|
12
12
|
private filePath;
|
|
13
13
|
private comparableKeys;
|
|
14
14
|
constructor(client: VBase, bucket: string, filePath: string, comparableKeys?: string[]);
|
|
15
|
-
resolve(logger?: Logger): Promise<
|
|
16
|
-
protected mergeMineWins(base: Configuration, master: Configuration, mine: Configuration):
|
|
15
|
+
resolve(logger?: Logger): Promise<any>;
|
|
16
|
+
protected mergeMineWins(base: Configuration, master: Configuration, mine: Configuration): Record<string, object | Record<string, object> | Record<string, object>[]> | Record<string, object>[];
|
|
17
17
|
private parseConflict;
|
|
18
18
|
private serializeContentByMimetype;
|
|
19
19
|
private parseContentByMimetype;
|
|
@@ -11,27 +11,19 @@ class MineWinsConflictsResolver {
|
|
|
11
11
|
this.comparableKeys = comparableKeys;
|
|
12
12
|
}
|
|
13
13
|
async resolve(logger) {
|
|
14
|
-
return await this.client.getConflicts(this.bucket).then(data => {
|
|
14
|
+
return await this.client.getConflicts(this.bucket).then((data) => {
|
|
15
15
|
const { data: conflicts } = data;
|
|
16
|
-
const selectedConflict = conflicts.find(conflict => conflict.path === this.filePath);
|
|
16
|
+
const selectedConflict = conflicts.find((conflict) => conflict.path === this.filePath);
|
|
17
17
|
if (!selectedConflict) {
|
|
18
|
-
|
|
18
|
+
throw {
|
|
19
|
+
message: 'No conflict could be found.',
|
|
20
|
+
status: 404,
|
|
21
|
+
};
|
|
19
22
|
}
|
|
20
23
|
selectedConflict.base.parsedContent = this.parseConflict(selectedConflict.base);
|
|
21
24
|
selectedConflict.master.parsedContent = this.parseConflict(selectedConflict.master);
|
|
22
25
|
selectedConflict.mine.parsedContent = this.parseConflict(selectedConflict.mine);
|
|
23
26
|
const resolved = this.resolveConflictMineWins(selectedConflict);
|
|
24
|
-
if (logger) {
|
|
25
|
-
logger.info({
|
|
26
|
-
base: selectedConflict.base.parsedContent,
|
|
27
|
-
bucket: this.bucket,
|
|
28
|
-
conflictsResolution: MineWinsConflictsResolver.constructor.name,
|
|
29
|
-
filePath: this.filePath,
|
|
30
|
-
master: selectedConflict.master.parsedContent,
|
|
31
|
-
mine: selectedConflict.mine.parsedContent,
|
|
32
|
-
resolved,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
27
|
return resolved;
|
|
36
28
|
});
|
|
37
29
|
}
|
|
@@ -64,16 +56,19 @@ class MineWinsConflictsResolver {
|
|
|
64
56
|
}
|
|
65
57
|
resolveConflictMineWins(conflict) {
|
|
66
58
|
if (!conflict) {
|
|
67
|
-
|
|
59
|
+
throw {
|
|
60
|
+
message: 'No conflict could be found.',
|
|
61
|
+
status: 404,
|
|
62
|
+
};
|
|
68
63
|
}
|
|
69
64
|
const { base, master, mine, path } = conflict;
|
|
70
|
-
this.mergeMineWins(base.parsedContent, master.parsedContent, mine.parsedContent);
|
|
65
|
+
const merged = this.mergeMineWins(base.parsedContent, master.parsedContent, mine.parsedContent);
|
|
71
66
|
const mergedContent = {
|
|
72
|
-
content: js_base64_1.Base64.encode(this.serializeContentByMimetype(
|
|
67
|
+
content: js_base64_1.Base64.encode(this.serializeContentByMimetype(merged, conflict.mine.mimeType)),
|
|
73
68
|
mimeType: conflict.mine.mimeType,
|
|
74
69
|
};
|
|
75
70
|
this.client.resolveConflict(this.bucket, path, mergedContent);
|
|
76
|
-
return
|
|
71
|
+
return merged;
|
|
77
72
|
}
|
|
78
73
|
mergeMineWinsObject(base, master, mine) {
|
|
79
74
|
const merged = { ...master, ...mine };
|
|
@@ -99,10 +94,10 @@ class MineWinsConflictsResolver {
|
|
|
99
94
|
return mine;
|
|
100
95
|
}
|
|
101
96
|
removeMasterDeletedElements(base, master, mine) {
|
|
102
|
-
base.forEach(baseItem => {
|
|
97
|
+
base.forEach((baseItem) => {
|
|
103
98
|
const foundInMaster = this.isObjectInArray(baseItem, master);
|
|
104
99
|
if (!foundInMaster) {
|
|
105
|
-
const foundInMine = mine.findIndex(mineItem => ramda_1.equals(mineItem, baseItem));
|
|
100
|
+
const foundInMine = mine.findIndex((mineItem) => ramda_1.equals(mineItem, baseItem));
|
|
106
101
|
if (foundInMine > -1) {
|
|
107
102
|
mine.splice(foundInMine, 1);
|
|
108
103
|
}
|
|
@@ -110,10 +105,10 @@ class MineWinsConflictsResolver {
|
|
|
110
105
|
});
|
|
111
106
|
}
|
|
112
107
|
isObjectInArray(obj, array) {
|
|
113
|
-
return array.find(item => ramda_1.equals(item, obj));
|
|
108
|
+
return array.find((item) => ramda_1.equals(item, obj));
|
|
114
109
|
}
|
|
115
110
|
appendMasterAddedElements(base, master, mine) {
|
|
116
|
-
master.forEach(item => {
|
|
111
|
+
master.forEach((item) => {
|
|
117
112
|
if (this.shouldAddToMine(item, base, mine)) {
|
|
118
113
|
mine.push(item);
|
|
119
114
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare function getMockConflicts(path: string, base: object, mine: object, master: object): {
|
|
2
|
+
data: {
|
|
3
|
+
base: {
|
|
4
|
+
content: string;
|
|
5
|
+
mimeType: string;
|
|
6
|
+
};
|
|
7
|
+
master: {
|
|
8
|
+
content: string;
|
|
9
|
+
mimeType: string;
|
|
10
|
+
};
|
|
11
|
+
mine: {
|
|
12
|
+
content: string;
|
|
13
|
+
mimeType: string;
|
|
14
|
+
};
|
|
15
|
+
path: string;
|
|
16
|
+
}[];
|
|
17
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
function jsonToBase64(json) {
|
|
4
|
+
return Buffer.from(JSON.stringify(json)).toString('base64');
|
|
5
|
+
}
|
|
6
|
+
function getMockConflicts(path, base, mine, master) {
|
|
7
|
+
return {
|
|
8
|
+
data: [
|
|
9
|
+
{
|
|
10
|
+
base: {
|
|
11
|
+
content: jsonToBase64(base),
|
|
12
|
+
mimeType: 'application/json',
|
|
13
|
+
},
|
|
14
|
+
master: {
|
|
15
|
+
content: jsonToBase64(master),
|
|
16
|
+
mimeType: 'application/json',
|
|
17
|
+
},
|
|
18
|
+
mine: {
|
|
19
|
+
content: jsonToBase64(mine),
|
|
20
|
+
mimeType: 'application/json',
|
|
21
|
+
},
|
|
22
|
+
path,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
exports.getMockConflicts = getMockConflicts;
|