The project that my team is tackling at work is a new employee portal that user will land on after logging in. One of the requirements is that we support multiple languages. I looked at the Microsoft Text Translator API to translate the web page but that leads into a couple other sub-requirements. First is we only want to translate our static content; labels, navigation menu, etc. Our customers are organizations and they have told us that they do not want us to translate their content, alerts to employees and such. The second sub-requirement is that we don't want to have to make an additional service to translate each page as it is rendered. So with those two additional requirements that killed passing the entire HTML page to Azure. Currently we have a single JSON document that contains all of the messages in different languages and we use that as a look. That works fine for now, but it has a few flaws that would prevent it from scaling well. First, every time we add a new label we have to add it to the main English lookup and manually translate it and add it to each other language we support (currently we support English, Spanish, and Polish). Secondly, if we ever wanted to support additional languages we would have to manually translate each element which would be very time consuming. With those issues in mind I took a second look at the Text Translator API to see if I could make it work for my use case.
The REST Interface
While reading through the online documents I found the API Reference for the REST interface. I first looked at the TranslateArray endpoint, but decided against using it. Firstly, my source document is already over the 10,000 character limit for a single call and I didn't want to manage chucking the request. Secondly, I wanted to make sure that each translated word could still be matched with the lookup key. After giving up on the TranslateArray I started looking at the Translate endpoint. I settled on the Translate endpoint mainly due to being able to match the lookup key with the word after it has been translated. Since the translate endpoint only supports a single word or phrase at a time I set about writing a small application that would loop over the messages, call the Text API, match the translated word with the lookup key and finally write the translated file to disk.
Translator Console App
In my previous post I walked through the initial creation of a Register User Logic App, checking if the user already exists, and then returning an error if they are already registered. Today I'm going to cover validating the password against a set of rules (and what happens if that validation fails), inserting a new record into the database and finally returning a success response back to the user. Without further ado, let's get started.
Password Validator Function
To begin I'm going to create a new Azure Function that will handle the password validation. I'm going to use my Simple Password Checker as the basis for the function. Since I know that this user isn't registered I deleted the check for previous password. The Char.isSymbol is a little weird, it didn't match on the ! symbol like I would have expected. Check out the MSDN reference for more information about what symbols are supported. Below is the completed code for the function. Now that we have the function created, it's time to go back and edit our Register User Logic App.
Password Validator Function
Updating the Logic App
We already handle the if condition (the user is already registered) so now we need to handle the false condition (new user). Let's add an action that will call the function we just created and pass in the original request body. First click on 'Add an action' then select the Azure Functions connector. Next select the Function App name then the individual function you want to call and finally pass the request body to the function. Since this function can return different status codes we need to add another if condition inside the current if false condition.
We will use the status code returned from the function as the basis for our decision. If the status code is 200 that means the password is valid and we can insert the row into our database. We are passing in the username and password that we received from the body of the original request and using utcNow() function for the two date fields. After we insert the row we are returning a success response to the user so they know they have registered their account. If the status code is anything other than 200 we are returning the status code and any messages to the user.
Testing
Time to fire up Postman and test out the workflow. I copied the URL from the trigger for the logic app and pasted it into postman. First I tried a new user with an invalid password. I kept getting this strange message about the condition failed. So I tried to register a new user with a valid password. When that worked first time next I tried a duplicate username and I got the error I was excepting. So after a little digging I found the answer.
In order get the nested condition to work I had open the code view of the logic app and find the condition that was throwing the error. The fix was simple enough, I just had to add the Failed to the run after object for the function app. Once I updated the run after conditions then the invalid password workflow returned the error messages as desired.
Final Thoughts
Having to change the code for the logic app to handle the nested if condition was strange, but other than that everything worked great. I kept this simple as a Proof of Concept and as such I skipped certain things you'd want to do in production. In production you should never ever pass a password in plain text. You always should encrypt the password in transit and then hash it at rest. Also, if you wanted to you could add additional logging when the user failed password check. You probably would want to split the the password if false condition so you could handle invalid password differently from server errors. So while this is a very simple PoC I think it really shows the power of Logic Apps. The only code we wrote was the stored procedure to check if the user is already registered and then function app that validates the password. All the connections to SQL Server and inserting the data was handled by Azure for us.
If you don't know, Function Apps are one of Azure's Serverless offering. They are small pieces of code that you can write in C#, F#, Javascript and Java (with more languages coming). Being serverless doesn't mean that the code doesn't run on a server, it certainly does, it just means that you can think less about servers. Functions can be triggered by a number of events, the one we will focus on is a HTTP call. Each function that gets created has its own unique endpoint that can be called from anywhere. In this tutorial we are going to create a very simple API that responds to Gets and Posts calls, add a proxy in front of the functions and test everything in postman. Let's get started. To begin go to the Azure Portal, sign in and create a new Resource Group that will contain your functions. It is a recommended practice to house all the things needed for an application (like the functions, data store, message queues, etc) in a single resource group. We now have an empty resource group. Let's fix that and add a function app. On the resource blade click Create Resource. Next search for Function App and create one from the Web & Mobile category. This function app is a container for functions. We will be creating the individual functions later.
In my last post I mentioned a new F# repo on Github for a Simple Password Checker. In that example I was comparing and storing passwords in clear text. Since this is a terrible idea and something that you should NEVER EVER do in production I decided to come back and create a function to hash my passwords. During the course of development I ended up rewriting the function several times to be more function. So I thought I would share my process and walk through going from a C#/OO mindset to a more F#/ FP coding style. Before you get started you will need to add 2 open declarations to get the required modules. Required Modules
With that out of the way, let's start by looking how I would solve this problem with a C#/OO mindset. Get Password Hash Original
This function works, it does the job but there are several intermediate variables that are only used once and 1 mutable variable. So let's see how we can clean this up. Immutable by default and no state are some of the corner stones of functional programming, so let's get rid of that loop first. But how do we do that? We need something that will go over the array, accept an initial state, do some action and return the new state before repeating the process again. It turns out that the creators of F# thought about this and gave us the Seq.Fold function. Get Password Hash after removing loop
That's much better. All variables are immutable, we removed a loop and we are letting the compiler take care of building the hash string output. Next on our list is tackling those intermediate, single use variables. In C# we could chain all those calls into a single method call, but that would be difficult to read and we would have to start at the inner most method and work our way out. It would be nice to be able to read from left to right and clearly understand what is happening. That would be a dream, some would say even a pipe dream (sorry for the terrible pun :-)). Get Password Hash written functionally
And there we have it. We have gone from a 7 line function with 5 intermediate, single use variables, 1 loop and 1 mutable variable to a 2 line function with a single intermediate variable, 0 loops, and no mutable variables. I left the sha512 variable by itself because I wanted to use the 'use' keyword. Use in F# is the same as using in C#. That's it for today. Stay tuned as I add user registration via a front end API and publish all this to Azure.
Hope you learned something and enjoying this post. As always, Happy Coding. I've been reading Isaac Abraham's book Get Programming with F#. It's been a very good introduction to the F# language, syntax, and operators. During one of the chapters that discussed folding, mapping, and filtering lists, arrays and sequences we created a simple validator program. I decided to extend it a bit and add the ability to look up previous passwords in a database. I created a repo on github and started on my simple password checker. After searching for recommended data access solutions I settled on trying Fsharp.Data.SqlClient as my ORM. Being totally new to Fsharp and this library I ran into a few issues and thought I'd share my learnings in this, the second, entry to the Quick Tips series. 1). The first thing I learned was that the client is activity checking your database schema as you are typing your query. So if you misspell a column name or don't add [] around an object that is a reserved SQL Server keyword you will get compile time errors. This was totally new to me and took me awhile to figure why I was getting a compiler error. Now that I know that's what happening I really like the feature. The downside that I can see is that it forces you to design and create your database before you start coding. 2). I was having trouble mapping the output results to my domain record. So I started experimenting with the different ResultType options. Here you can see how I specified the result type when creating the command provider. If you do not supply a type then the default of Records will be used. The command also provides a normal Execute function, which I could have used since I just called Async.RunSynchronously right away. 3). After continued experimenting, I found that I did not actually need a domain object for this example. Normally, in C# I would have created a simple POCO and used that as a DTO. Since the F# compiler has a stronger type system, even for dynamic objects, I did not need to create a DTO. Instead what I was able to do was access the columns by name in the rules engine without any translations. And then I was able to simplify my query. Notice that I dropped the AsyncExecute and the Seq.Map. The ResultType.Records is the default so you do not need to supply it like I did. That's it for this quick tip. Let me know what you like and as always. Happy Coding.
|
AuthorWelcome to The Blind Squirrel (because even a blind squirrel occasionally finds a nut). I'm a full-stack web and mobile developer that writes about tips and tricks that I've learned in Swift, C#, Azure, F# and more. Archives
April 2018
Categories
All
|