- Overview
- Building and Running 🔨 🏃
- How to Use Harmonia
- What is an RFC?
- How do I structure an RFC?
- Typical Harmonia Workflow
- Step 1: Create your RFC
- Step 2: Submit your RFC via
/submitRequest - Step 3: Wait for Stakeholder Responses to come in via
/reviewRequest - Step 4: Analyze Feedback and Submit Updates via
/updateRequest - Step 5: Wait for Another Round of Stakeholder Responses to come in via
/reviewRequest - Step 6: Get your RFC Accepted into the Schema!
In Greek mythology, Harmonia (/hɑːrˈmoʊniə/; Ancient Greek: Ἁρμονία) is the immortal goddess of harmony and concord.
Harmonia is an API that allows for easy manipulation and review of data schemas via an RFC process that leverages Git as a backing store.
- First, go ahead and set up the environment variables depicted below.
Environment Variables
| Variable Name | Description | Default Value |
|---|---|---|
| IS_LOCAL | Set to true if you are running the stack locally |
true |
| GIT_TOKEN | Set to GitHub user access token | None |
| GIT_MACHINE_TOKEN | Set to GitHub machine access token | None |
| TRACKING_REPOSITORY | Set to GitHub tracking repository | None |
For convenience, a script has been provided to set these environment variables locally. Simply run the following to initialize your local environment.
make local && source localenv
- Run
make compileto compile the source code into thebindirectory. - Run
make runto run the compiled source. - View your changes locally here!
- Considering this is a local build, you must use the
httpscheme in Swagger.
This section goes over the fundamentals of how Harmonia should be used to enact schema changes!
An RFC or "Request For Comments" in Harmonia is the payload structure used to submit a proposal for any desired changes that you may have for the existing schema set. More succinctly, it is a list of actions that you want performed to the schema set. As the RFC goes through our approval process, it is constantly updated with new actions to track the life of your request.
At its core an RFC is a list of actions [{action 1}, {action 2}, {action 3}...]
Each action has an actionType, which must be one of: add, update, comment, approve or load. As stated above,
each actionType is either an action you want performed on the schema OR action metadata on the submitted RFC. For
example, the add and update action types would be used to add and/or update a schema entity. But, the comment,
approve and load actions correspond to actions that occurred either by you or others during the lifecycle of the
RFC.
The next piece of the RFC is what the action is acting upon, also known as the target. The target is an object
that looks like the following:
{
"targetType": ...
"targetDescriptor": ...
"lookupKey": ...
"lookupValue": ...
"relType": ...
}
targetType must be one of item, action or rfc. Use item if you are acting on a schema entity. Use action if
you are acting on an RFC action within the current RFC (this will mainly be used by Harmonia behind the scenes to track
comments). Lastly, use rfc if you are acting on this RFC as a whole (again, this will mainly be used by Harmonia
behind the scenes for approval flow).
targetDescriptor is optional, unless your targetType is item because Harmonia needs to know which item
category you want to work on.
lookupKey is the attribute field that Harmonia should use to actually find your target. For example, this could be
an id.
lookupValue is the value that the lookupKey should match to on the target. For example, if you are looking for an
entity with id 3 then lookupKey should be id and lookupValue should be 3.
Lastly, there is a freeform data object that can exist at the same level as the actionType and target properties
of the RFC. This data object can contain various properties that are specific to your data schema.
Now we will outline a common workflow of taking an RFC from ideation to approval and acceptance into the specification.
One schema change request that occurs often is editing the list of accepted values for a field in our database. Let's
say we want to add acceptedValue4 to our field named OurField.
Craft an RFC that tells Harmonia we want to add acceptedValueFour to our accepted values for the OurField field. Our
RFC would look like this:
{
"actions": [
{
"actionType": "update",
"data": {
"acceptedValues": "acceptedValue1;acceptedValue2;acceptedValue3;acceptedValueFour"
},
"target": {
"targetDescriptor": "acceptedValueChecker",
"targetType": "item",
"lookupKey": "name",
"lookupValue": "OurField"
}
}
]
}
In the above RFC, we are adding acceptedValueFour to the accepted values for OurField. As you can see, the update
action acts as an overwrite on the existing data instead of an addition to it. It is important to keep this in mind
when performing updates. The reason we didn't use an add action here is because the OurField acceptedValueChecker
already exists, we simply want to update it.
Now that we have our RFC, we can submit a POST request to the /submitRequest endpoint to officially offer our RFC up
for review.
Now is the time when stakeholders of the OurField field will want to weigh in on our request.
Let's say a stakeholder comes along and thinks that you shouldn't be using the word "Four" inside your accepted
value. Meaning instead he or she wishes our new value was AcceptedValue4. He or she would submit the following
payload via the /reviewRequest endpoint.
{
"rfcIdentifier": "123456", // this will be known after submitting your initial request
"topLevelComment": "nice! But you shouldn't use the word "Four" in the value, instead just use "4"",
"type": "COMMENT"
}
If you look at the /reviewRequest endpoint you will notice that we also accept an object of comments. So why do we
have a topLevelComment and a comments object? Well in GitHub during a review you can comment on the entire pull
request and individual lines. Our goal is to emulate this functionality. Therefore, use the topLevelComment to comment
on the entire RFC and use the comments object, with individual action signatures as keys and your desired comments
as the values, to comment on a specific action in the RFC.
To give more insight into the rfc and action target types described here, after the
above comment is added the RFC will be updated in the background to include the following action:
{
"actionType": "comment",
"target": {
"targetType": "rfc",
"lookupKey": "signature",
"lookupValue": "thisisthesignatureoftherfc"
},
"data": {
"comment": "nice! But you shouldn't use the word "Four" in the value, instead just use "4""
}
}
If there were comments on individual actions that were created via the comments object then we would see a single
comment action like the one above for each comment targeting individual actions by using the action targetType.
Lastly, there are three types of reviews allowed via this endpoint: COMMENT, REQUEST_CHANGES and APPROVE, which
all correspond directly back to their analogs in GitHub when reviewing a pull request.
At this point, you would notice the comment on your RFC and could submit an update to your RFC to match the suggestions.
Note: the way that the /updateRequest endpoint works is that it takes whatever you give it, as the new RFC in total.
It doesn't merge or append your update to the existing RFC. Because of this point, our payload would look like this if
we were to address the comment from the stakeholder:
{
"rfc": {
"actions": [
{
"actionType": "update",
"data": {
"enum": "acceptedValue1;acceptedValue2;acceptedValue3;acceptedValue4"
},
"target": {
"targetDescriptor": "acceptedValueChecker",
"targetType": "item",
"lookupKey": "name",
"lookupValue": "OurField"
}
}
]
},
"rfcIdentifier": "123456
}
You can see we still listed all the accepted values, and if there were other actions in the original RFC that should still be included we would want to include those in our update RFC above or else they would be overwritten!
After submitting the update, the stakeholders could again review. Let's say that everything looks good to them! They
will submit an approval via the /reviewRequest endpoint. Their review payload would look like this:
{
"rfcIdentifier": "123456",
"topLevelComment": "awesome work!,
"type": "APPROVE"
}
Once your RFC has the desired number of approvals it will automatically be integrated into the specification. You can
easily check the status of the loading process of your RFC by using the /status endpoint with your assigned
rfcIdentifier.