Making Dangerous Hard : Admin Functions
If you read my last post about internal tools you will have noticed that I used an internal tool I built we call "Admin Functions" as the example throughout the post. This post will go a little more in-depth into what those Admin Functions are and how they work.
Why did we make Admin Functions?
We wanted an easy, safe, auditable way to perform administrative functions.
Before Admin Functions, we had a document of "recipes" you could copy, modify, and then paste into the production console to perform functions that didn't have any home in the UI of our app. It's pretty easy to see that allowing everyone to execute arbitrary code in our production console could be improved upon.
Admin Functions are the response to this problem we had.
What are Admin Functions?
The whole of Admin Functions is essentially split into 2 different parts. The first part is all the individual Admin Functions themselves which are service objects. The second part is the "framework" that standardizes how each Admin Function is built, presented, and used.
Admin Function service objects
Basically, any given Admin Function is a service object that does four things:
- Provide information about itself.
- Find the record(s) that will be acted upon.
- Verify that the function can be performed based on the record(s) found and the params provided by the user.
- Perform the actual function using the found record(s) and the params passed in and say if it succeeded or not.
The Admin Functions "framework" is everything else that is responsible for presenting the various Admin Functions and producing auditable logs. It's responsibilities include:
- Iterate over all the Admin Function service objects and display them in a searchable index.
- Restrict access to Admin Functions based on the role of the current user.
- Present a standardized user-flow for each Admin Function.
- Record the records acted upon, the user, the params inputted, and the results in an auditable log.
- Present those audit logs in a searchable index.
How do you use Admin Functions?
The easiest way to answer that question is to show you. In this example, I will take you through the entire cycle of our Admin Function "Add or Change Memo on Adjustment"
This is the searchable index of all Admin Functions. There are many more, but the image is cut off to save space. Each of the columns is information provided in each Admin Function service object about itself.
This is the first step after you choose an Admin Function on the previous screen. You enter the params required by the Admin Function and then you are given a chance to preview the changes.
After you submit your params, the Admin Function service object finds the record(s) you'll act upon, inspects your params, and verifies that you can perform this action. Notice that this page shows you a readout of the records you are going to act upon, the params you passed, and shows you the source code of exactly what will happen if you click submit
After you click submit, the Admin Function is performed and you are taken back to the index with a message explaining whether it was successful or not.
If you have sufficient permissions, you can then view the "Admin Function Audit Logs" page. This is the searchable index for all the logs created during the use of Admin Functions.
This is the audit log for the Admin Function we completed above. You can see who performed the Admin Function, when it was performed, the results, the params passed, the record(s) as they existed before the perform happened, and the record(s) as they exist after the perform happened.
How do you create an Admin Function?
To make it easy for any developer to add a new Admin Function, I created a code generator. It asks for a few details about the Admin Function you want to write and then generates the files you need in the correct place. It generates the Admin Function service object, the view partial with the params to be passed in, and a spec file. All the developer has to do is replace the logic in the service object in order for it to start working.
We had a problem. The easiest way to perform some work was to copy and paste code into a production console. We solved that problem by creating the Admin Functions framework. Standardizing the workflow makes it easy, safe, and auditable to perform the same functions we were before. We also created a code generator to allow any developer to easily create new Admin Functions.