1. 程式人生 > >Usable configuration with Git | Anvil

Usable configuration with Git | Anvil

« Anvil Cookbook
The configuration dilemma

As developers, almost every app we write has configuration. Often, that configuration should really be accessible to our less technical colleagues: feature flags, rate limits, deployment signoffs, and so forth.

However, these changes also need to be tracked and audited. “The app just broke. Did someone change the config?” “Quick, revert it to last week’s settings!”

As programmers, we know exactly the right tool for this: Text files in version control. They’re diffable, trackable and comprehensible, and if anything goes badly wrong we can dive in with a text editor.

The problem comes when we present this solution to our non-technical colleagues. “So, I open the terminal? And then I type git clone

and a string of gibberish you made me memorise?”

It’s tempting to give up and say, “I’ll do it for you”. Developers end up as gatekeepers, with every change coming through us.

This isn’t great either. Years ago, I used to develop SMS-based services for a mobile carrier in south-east Asia. This was the bad old days, before Twilio and friends, and the carrier had to sign off on every minor UI change – often at the very last minute. I spent many late nights waiting for a meeting on the other side of the world to finish, just so I could change one line in a config file.

GitHub API to the rescue

We can fix this. With the GitHub API, we can build an app in minutes that empowers our colleagues to change configuration on their own – with all the power of Git’s versioning and auditing.

Here’s a simple app, hosted on Heroku (source at github.com/anvil-ph-test/edit-demo). It has a configuration file (called config.json) that determines vital parameters such as the font and background colour.

Here’s how I built an Anvil app to edit that configuration, with less than a dozen lines of code:

Getting the config

First, we need to grab the latest version of our config file:

self.gh_record = anvil.http.request("https://api.github.com/repos/anvil-ph-test/edit-demo/contents/config.json", json=True, username="abc", password="123")

Github returns some information about this file, and its content in base64:

{
  "name": "config.json",
  "encoding": "base64",
  "size": 67
  ...several other bits omitted...
  "content": "eyJ1cHBlcmNhc2UiOnRydWUsImZvbnQiOiJIZWx2ZXRpY2EiLCJiYWNrZ3Jv\ndW5kIjoiYmxhbmNoZWRhbG1vbmQifQ==\n",
  "sha": "bfb17ee5edf43a54f6756f032603872ca7dce320",
}

The content is what we care about:

self.item = json.loads(base64.b64decode(self.gh_record["content"]))

The decoded data looks like this:

{
  "background": "blanchedalmond",
  "font": "Helvetica",
  "uppercase": true
}

All we need now is to design our configuration interface. With Anvil’s data bindings, it’s all drag-and-drop - we can just specify which JSON key (in self.item) each text-box or check-box corresponds to. That’s all we need for a read-only interface:

Committing our config

Now we have read-only access to our configuration, the next step is to save our changes. As we interact with the text-boxes and check-box, self.item is automatically updated.

Now we just push this data back to the server, with an HTTP PUT request to the same URL. All GitHub needs is the new content for the file, a commit message, and the previous SHA hash of this file:

new_record = {'content', base64.b64encode(json.dumps(self.item)),
              'message', 'Edit from the web'}
              'sha': self.gh_record["sha"]}

anvil.http.request("https://api.github.com/repos/anvil-ph-test/edit-demo/contents/config.json", method="PUT", data=new_record, json=True, username="abc", password="123")

And here’s the working app. Why not try changing some settings?

Once you’ve saved your changes, scroll up and refresh the example app. Be patient - it may take a few seconds to re-deploy with the new config.

Increased security

OK, we’re not quite done. So far, we’re doing everything on the client side, which means everyone with the URL can access our authentication information! Even if we only give that URL out to people we (mostly) trust, it’s far too easy for it to end up in the wrong hands.

Instead, we’ll do our GitHub API calls on the server side, and expose only two functions to the client: save_config and load_config. All the rest is safely on the server, where the user can’t see it:

# This code runs on the server
@anvil.server.callable
def load_config():
  gh_record = anvil.http.request("https://api.github.com/repos/anvil-ph-test/edit-demo/contents/config.json", json=True, username="abc", password="123")

  return (gh_record['sha'], json.loads(base64.b64decode(gh_record['content'])))


@anvil.server.callable
def save_config(data, last_sha):
  new_record = {'content': base64.b64encode(json.dumps(data)),
                'message': 'Edit from the web',
                'sha': last_sha}
  r = anvil.http.request("https://api.github.com/repos/anvil-ph-test/edit-demo/contents/config.json", method="PUT", data=new_record, json=True, username="abc", password="123")

  return r['content']['sha']

(In fact, I’ve been using this version of the code for all the example apps embedded in this blog post. I’m afraid wasn’t feeling generous enough to share my GitHub authentication tokens with anyone who can View Source. Sorry to disappoint anyone who tried.)

Job done.

There you have it - a secure, functional configuration editor, ready for our non-technical colleagues to use. You don’t need to know Git to use it, but it does have full tracing and history of every change.

Conclusions

#1: We don’t have to sacrifice the benefits of Git for our configuration, just in order to get a user-friendly admin interface. We can have both!

#2: The GitHub API is awesome.

#3: Anvil lets you build useful web apps very, very quickly.



Why not clone this example app, read its source code, and try it out yourself?

Learn More

Get the best out of Anvil with our free email course.