Skip to content

Commit 6471790

Browse files
author
Tobias Brennecke
committed
#540 Add a version to the serialized graph
When changing the structure of a PatternGraph, users might still have an old version, so we need to rebuild all patterns and the whole graph.
1 parent 47d7e6c commit 6471790

File tree

4 files changed

+84
-10
lines changed

4 files changed

+84
-10
lines changed

core/lib/pattern_graph.js

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ const CompileState = require('./object_factory').CompileState;
99
const PatternGraphDot = require('./pattern_graph_dot');
1010
const PatternRegistry = require('./pattern_registry');
1111

12+
/**
13+
* The most recent version of the pattern graph. This is used to rebuild the graph when
14+
* the version of a serialized graph does not match the current version.
15+
* @type {number}
16+
*/
17+
const PATTERN_GRAPH_VERSION = 1;
18+
1219
/**
1320
* Wrapper around a graph library to build a dependency graph of patterns.
1421
* Each node in the graph will maintain a {@link CompileState}. This allows finding all
@@ -21,13 +28,14 @@ const PatternRegistry = require('./pattern_registry');
2128
*
2229
* @param {Graph} graph The graphlib graph object
2330
* @param {int} timestamp The unix timestamp
31+
* @param {int} version The graph version.
2432
*
2533
* @returns {{PatternGraph: PatternGraph}}
2634
2735
* @see PatternGraph#fromJson
2836
* @see <a href="https://github.com/pattern-lab/patternlab-node/issues/540">#540</a>
2937
*/
30-
const PatternGraph = function (graph, timestamp) {
38+
const PatternGraph = function (graph, timestamp, version) {
3139

3240
this.graph = graph || new Graph({
3341
directed: true
@@ -38,6 +46,7 @@ const PatternGraph = function (graph, timestamp) {
3846
// The idea here is to make a pattern known to the graph as soon as it exists
3947
this.patterns = new PatternRegistry();
4048
this.timestamp = timestamp || new Date().getTime();
49+
this.version = version || PATTERN_GRAPH_VERSION;
4150
};
4251

4352
// shorthand. Use relPath as it is always unique, even with subPatternType
@@ -53,7 +62,7 @@ PatternGraph.prototype = {
5362
clone: function () {
5463
const json = graphlib.json.write(this.graph);
5564
const graph = graphlib.json.read(json);
56-
return new PatternGraph(graph, this.timestamp);
65+
return new PatternGraph(graph, this.timestamp, this.version);
5766
},
5867

5968
/**
@@ -267,6 +276,7 @@ PatternGraph.prototype = {
267276
*/
268277
toJson: function () {
269278
return {
279+
version: this.version,
270280
timestamp: this.timestamp,
271281
graph: graphlib.json.write(this.graph)
272282
};
@@ -277,26 +287,56 @@ PatternGraph.prototype = {
277287
*/
278288
nodes: function () {
279289
return this.graph.nodes();
290+
},
291+
292+
/**
293+
* Updates the version to the most recent one
294+
*/
295+
upgradeVersion: function () {
296+
this.version = PATTERN_GRAPH_VERSION;
280297
}
281298
};
282299

283300
/**
284301
* Creates an empty graph with a unix timestamp of 0 as last compilation date.
285-
*
302+
* @param {int} [version=PATTERN_GRAPH_VERSION]
286303
* @return {PatternGraph}
287304
*/
288-
PatternGraph.empty = function () {
289-
return new PatternGraph(null, 0);
305+
PatternGraph.empty = function (version) {
306+
return new PatternGraph(null, 0, version || PATTERN_GRAPH_VERSION);
307+
};
308+
309+
/**
310+
* Checks if the version of
311+
* @param {PatternGraph|Object} graphOrJson
312+
* @return {boolean}
313+
*/
314+
PatternGraph.checkVersion = function(graphOrJson) {
315+
return graphOrJson.version === PATTERN_GRAPH_VERSION;
290316
};
291317

318+
/**
319+
* Error that is thrown if the given version does not match the current graph version.
320+
*
321+
* @param oldVersion
322+
* @constructor
323+
*/
324+
function VersionMismatch(oldVersion) {
325+
this.message = `Version of graph on disk ${oldVersion} != current version ${PATTERN_GRAPH_VERSION}. Please clean your patterns output directory.`;
326+
this.name = "VersionMismatch";
327+
}
328+
292329
/**
293330
* Parse the graph from a JSON object.
294331
* @param {object} o The JSON object to read from
295332
* @return {PatternGraph}
296333
*/
297334
PatternGraph.fromJson = function (o) {
335+
if (!PatternGraph.checkVersion(o)) {
336+
throw new VersionMismatch(o.version);
337+
}
298338
const graph = graphlib.json.read(o.graph);
299-
return new PatternGraph(graph, o.timestamp);
339+
return new PatternGraph(graph, o.timestamp, o.version);
300340
};
301341

302342
/**
@@ -328,6 +368,9 @@ PatternGraph.loadFromFile = function (patternlab, file) {
328368
}
329369

330370
const obj = fs.readJSONSync(jsonGraphFile);
371+
if (!PatternGraph.checkVersion(obj)) {
372+
return PatternGraph.empty(obj.version)
373+
}
331374
return this.fromJson(obj);
332375
};
333376

@@ -356,5 +399,6 @@ PatternGraph.exportToDot = function (patternlab, file) {
356399
};
357400

358401
module.exports = {
359-
PatternGraph: PatternGraph
402+
PatternGraph: PatternGraph,
403+
PATTERN_GRAPH_VERSION: PATTERN_GRAPH_VERSION
360404
};

core/lib/patternlab.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,8 +513,17 @@ var patternlab_engine = function (config) {
513513

514514
let patternsToBuild = patternlab.patterns;
515515

516+
let graphNeedsUpgrade = !PatternGraph.checkVersion(patternlab.graph);
517+
// Incremental builds are enabled, but we cannot use them
518+
if (!deletePatternDir && graphNeedsUpgrade) {
519+
plutils.log.info("Due to an upgrade, a complete rebuild is required. " +
520+
"Incremental build is available again on the next run.");
521+
// Ensure that the freshly built graph has the latest version again.
522+
patternlab.graph.upgradeVersion();
523+
}
516524
//delete the contents of config.patterns.public before writing
517-
if (deletePatternDir) {
525+
//Also if the serialized graph must be updated
526+
if (deletePatternDir || graphNeedsUpgrade) {
518527
fs.removeSync(paths.public.patterns);
519528
fs.emptyDirSync(paths.public.patterns);
520529
} else {

test/pattern_graph_tests.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33

44
var PatternGraph = require('../core/lib/pattern_graph').PatternGraph;
5+
var VERSION = require('../core/lib/pattern_graph').PATTERN_GRAPH_VERSION;
56
var Pattern = require('../core/lib/object_factory').Pattern;
67
var CompileState = require('../core/lib/object_factory').CompileState;
78
var tap = require('tap');
@@ -20,6 +21,16 @@ var mockGraph = function () {
2021
return PatternGraph.empty();
2122
};
2223

24+
tap.test("checkVersion - Current version returns true", (test) => {
25+
test.same(PatternGraph.checkVersion({"version": VERSION}), true);
26+
test.end();
27+
});
28+
29+
tap.test("checkVersion - Older version returns false", (test) => {
30+
test.same(PatternGraph.checkVersion({"version": VERSION - 1}), false);
31+
test.end();
32+
});
33+
2334
tap.test("Loading an empty graph works", (test) => {
2435
var g = PatternGraph.loadFromFile(patternlab, "does not exist");
2536
tap.equal(g.graph.nodes().length, 0,"foo");
@@ -34,6 +45,14 @@ tap.test("PatternGraph.fromJson() - Loading a graph from JSON", (test) => {
3445
test.end();
3546
});
3647

48+
tap.test("PatternGraph.fromJson() - Loading a graph from JSON using an older version throws error", (test) => {
49+
test.throws(function () {
50+
PatternGraph.fromJson({version: 0});
51+
}, {}, /Version of graph on disk.*/g);
52+
53+
test.end();
54+
});
55+
3756
tap.test("toJson() - Storing a graph to JSON correctly", (test) => {
3857
var graph = mockGraph();
3958
graph.timestamp = 1337;
@@ -43,7 +62,7 @@ tap.test("toJson() - Storing a graph to JSON correctly", (test) => {
4362
graph.add(moleculeFoo);
4463
graph.link(moleculeFoo, atomFoo);
4564
test.same(graph.toJson(),
46-
{"timestamp":1337,"graph":{"options":{"directed":true,"multigraph":false,"compound":false},"nodes":[{"v":"atom-foo","value":{"compileState":"clean"}},{"v":"molecule-foo","value":{"compileState":"clean"}}],"edges":[{"v":"molecule-foo","w":"atom-foo","value":{}}]}});
65+
{"version": VERSION, "timestamp":1337,"graph":{"options":{"directed":true,"multigraph":false,"compound":false},"nodes":[{"v":"atom-foo","value":{"compileState":"clean"}},{"v":"molecule-foo","value":{"compileState":"clean"}}],"edges":[{"v":"molecule-foo","w":"atom-foo","value":{}}]}});
4766
// For generating the above output:console.log(JSON.stringify(graph.toJson()));
4867
test.end();
4968
});
@@ -61,6 +80,7 @@ tap.test("Storing and loading a graph from JSON return the identical graph", (te
6180
var newGraph = PatternGraph.fromJson(oldGraph.toJson());
6281

6382
// assert
83+
test.same(newGraph.version, VERSION);
6484
test.same(newGraph.timestamp, 1337);
6585
test.same(newGraph.graph.nodes(), ["atom-foo", "molecule-foo"]);
6686
test.same(newGraph.graph.edges(), [ {w: "atom-foo", v:"molecule-foo"}]);
@@ -83,6 +103,7 @@ tap.test("clone()", (test) => {
83103
var newGraph = oldGraph.clone();
84104

85105
// assert
106+
test.same(newGraph.version, VERSION);
86107
test.same(newGraph.timestamp, 1337);
87108
test.same(newGraph.graph.nodes(), ["atom-foo", "molecule-foo"]);
88109
test.same(newGraph.graph.edges(), [ {w: "atom-foo", v:"molecule-foo"}]);

test/public/testDependencyGraph.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"timestamp":1337,"graph":{"options":{"directed":true,"multigraph":false,"compound":false},"nodes":[{"v":"atom-foo","value":{"compileState":"clean"}},{"v":"molecule-foo","value":{"compileState":"clean"}}],"edges":[{"v":"molecule-foo","w":"atom-foo","value":{}}]}}
1+
{"version":1,"timestamp":1337,"graph":{"options":{"directed":true,"multigraph":false,"compound":false},"nodes":[{"v":"atom-foo","value":{"compileState":"clean"}},{"v":"molecule-foo","value":{"compileState":"clean"}}],"edges":[{"v":"molecule-foo","w":"atom-foo","value":{}}]}}

0 commit comments

Comments
 (0)