1 | # @stoplight/graphite
|
2 | [![Maintainability](https://api.codeclimate.com/v1/badges/ac453dbde4e365fe9cce/maintainability)](https://codeclimate.com/repos/5bd8e4484426590257001c75) [![Test Coverage](https://api.codeclimate.com/v1/badges/ac453dbde4e365fe9cce/test_coverage)](https://codeclimate.com/repos/5bd8e4484426590257001c75)
|
3 |
|
4 |
|
5 |
|
6 | Nodes'n things.
|
7 |
|
8 | - Explore the components: [Storybook](https://stoplightio.github.io/graphite)
|
9 | - View the changelog: [Releases](https://github.com/stoplightio/graphite/releases)
|
10 |
|
11 | ## Installation
|
12 |
|
13 | Supported in modern browsers and node.
|
14 |
|
15 | ```bash
|
16 | # latest stable
|
17 | yarn add @stoplight/graphite
|
18 | ```
|
19 |
|
20 | ## Usage
|
21 |
|
22 | Note, this is not all implemented, but rather an example of what it might look like.
|
23 |
|
24 | ```ts
|
25 | import {
|
26 | Graphite,
|
27 | FilesystemPlugin,
|
28 | JsonPlugin,
|
29 | YamlPlugin,
|
30 | Oas2Plugin
|
31 | } from "@stoplight/graphite";
|
32 |
|
33 | const graphite = Graphite();
|
34 |
|
35 | graphite.registerPlugins(
|
36 | FilesystemSource(),
|
37 | JsonPlugin(),
|
38 | YamlPlugin(),
|
39 | Oas2Plugin()
|
40 | );
|
41 |
|
42 | // Mirror two Graphite instances. The mirroredGraphite instance has no plugins, and simply applies the results of the graphite instance.
|
43 | const mirroredGraphite = Graphite();
|
44 | graphite.on("did_patch", mirroredGraphite.applyPatch);
|
45 |
|
46 | // Add a single SourceNode of type file
|
47 | const n = graphite.addSourceNode({
|
48 | type: FilesystemPlugin.File,
|
49 | path: "/foo.json"
|
50 | });
|
51 |
|
52 | // Queue up a read task for that node
|
53 | n.read();
|
54 |
|
55 | // Wait until all processing is done
|
56 | await graphite.tasksProcessed();
|
57 |
|
58 | // The two graphs should be identical, ids and all.
|
59 | // Note, the mirroredGraph did NO work - all the file reading, parsing, etc, was done by the plugins in the main graphite instance.
|
60 | expect(graphite.dehydrate()).toEqual(mirroredGraphite.dehydrate());
|
61 | ```
|
62 |
|
63 | ## Concepts
|
64 |
|
65 | ### Graph
|
66 |
|
67 | - Holds nodes and edges.
|
68 | - Exposes methods to `add` and `remove` nodes/edges.
|
69 | - Responsible for managing node/edge lifecycle.
|
70 |
|
71 | #### Nodes
|
72 |
|
73 | - They hold data.
|
74 | - There are three node categories (described below): `source`, `source_map`, and `virtual`.
|
75 |
|
76 | #### Edges
|
77 |
|
78 | - They represent relationships between nodes.
|
79 |
|
80 | ### Graphite
|
81 |
|
82 | - Manages a single graph instance.
|
83 | - Exposes `applyPatch` method.
|
84 | - Emits events as patches are processed.
|
85 | - Exposes convenience methods for common patterns, such as `addSourceNode`, that simply build and a patch or task and call `applyPatch` or `queueTask`.
|
86 | - Manages tasks.
|
87 |
|
88 | ### Mutations
|
89 |
|
90 | - ALL changes, both internal and external, pass through the `graphite.applyPatch` method.
|
91 |
|
92 | #### JsonPatch
|
93 |
|
94 | - A group of `JsonOperations`.
|
95 |
|
96 | #### GraphPatch
|
97 |
|
98 | - A group of `JsonOperations`, and their inverse. This is similar to the concept of a "transaction".
|
99 | - If one operation fails, they all fail, and a rollback is attempted.
|
100 |
|
101 | #### GraphTask
|
102 |
|
103 | - Describes a single change to be made to the graph.
|
104 | - Any operations that cannot be accomplished via `JsonPatch` must be queued up via a `GraphTask`.
|
105 | - Examples include `add_node`, `read_node`, `write_node`, `parse_node`, `compute_node_source_map`.
|
106 | - Plugins can define their own tasks, such as `oas2_lint_node`.
|
107 | - The result of a `GraphTask` must always be a `GraphPatch`.
|
108 | - When a task is run, the `GraphPatch` it returns is applied to the graph.
|
109 |
|
110 | #### Scheduler
|
111 |
|
112 | - Manages one or more task queues.
|
113 | - We will at the very least have `high` and `low` priority queues.
|
114 | - Tasks such as `add_node` and `read_node` will go into a `high` priority queue.
|
115 | - Tasks such as `oas2_lint_node` and `resolve_node` will go into a `low` priority queue.
|
116 |
|
117 | #### Notifier
|
118 |
|
119 | - Manages events like a boss.
|
120 |
|
121 | ### Sources
|
122 |
|
123 | #### SourceNode
|
124 |
|
125 | - Source nodes are the only node category
|
126 | - Exposes 4 primary properties - `original`, `raw`, `parsed` (TODO), and `isDirty`.
|
127 | - Exposes 4 primary methods - `read`, `write`, `updateRaw`, and `updateParsed`.
|
128 |
|
129 | #### SourceSink
|
130 |
|
131 | - Responsible for reading data from some data source, and adding the appropriate source nodes.
|
132 | - Responsible for refreshing the `original` property of a `SourceNode` in response to `read_node` tasks.
|
133 | - Responsible for writing the `SourceNode` raw property back to the data source in response to `write_node` tasks.
|
134 | - Implements `ISourceReader` and/or `ISourceWriter`.
|
135 |
|
136 | #### SourceParser
|
137 |
|
138 | - Targets one or more `SourceNodes`.
|
139 | - Responsible for computing its `parsed` value when `raw` changes.
|
140 | - Responsible for computing its `raw` value when `parsed` changes.
|
141 |
|
142 | #### SourceMapNode
|
143 |
|
144 | - A specific type of node that is a child of a `SourceNode`.
|
145 | - Its `uri` points to a real location in the original source.
|
146 | - Its data property points to a value in its parent `SourceNode.parsed`, according to its `uri`.
|
147 | - Exposes an `update` method that queues a `GraphPatch` to update its source node parsed value.
|
148 |
|
149 | #### SourceTree
|
150 |
|
151 | - Defines a `ISourceTreeMap` that describes how a `SourceNode.parsed` value should be translated into `SourceMapNodes`.
|
152 |
|
153 | ### VirtualNode
|
154 |
|
155 | - Anything that is not a `SourceNode` or `SourceTreeNode`
|
156 | - Examples: linting results, transformed http operation and http service nodes, etc
|
157 |
|
158 | ## Contributing
|
159 |
|
160 | 1. Clone repo.
|
161 | 2. Create / checkout `feature/{name}`, `chore/{name}`, or `fix/{name}` branch.
|
162 | 3. Install deps: `yarn`.
|
163 | 4. Make your changes.
|
164 | 5. Run tests: `yarn test.prod`.
|
165 | 6. Stage relevant files to git.
|
166 | 7. Commit: `yarn commit`. _NOTE: Commits that don't follow the [conventional](https://github.com/marionebl/commitlint/tree/master/%40commitlint/config-conventional) format will be rejected. `yarn commit` creates this format for you, or you can put it together manually and then do a regular `git commit`._
|
167 | 8. Push: `git push`.
|
168 | 9. Open PR targeting the `next` branch.
|