Giter Site home page Giter Site logo

openfsharp2019-unitconversionservice's Introduction

Unit Conversion Service Workshop

Let’s see how F# language features make it simple to create a unit conversion service and deploy it to Azure Functions.

Module 0: Background

Q: Who made this Workshop?
A: Brett Rowberry, who works at ExxonMobil

Q: When was this workshop first given?
A: Friday September 27th, 2019 at Open F# in San Francisco

Q: How long does this workshop take?
A: 110 minutes

Q: What prerequisites will I need?
A:

Q: Why Azure Functions?
A:

  1. Why serverless?
    • Potentially less expensive
    • Potentially a simpler programming model
  2. Why Azure Functions and not AWS Lambda or other serverless offerings?
    • I use Azure at work

Q: What are the programming/deployment models supported by Azure Functions?
A:

  • Script
  • Compiled (what we'll use in this workshop)
  • Container

Module 1: C# Azure Function

There isn't an official F# template at the moment, so we'll start with a C# tutorial.

  1. Open the module1 directory in Visual Studio Code
  2. Navigate to Create your first function using Visual Studio Code
  3. Here are the main sections that we will go over together:
    1. Prerequisites
    2. Create your Functions project using Visual Studio Code's Command Palette
      • Accept all the defaults
    3. Run the function locally and call it
    4. Publish the project to Azure using Visual Studio Code's Command Palette
      • Use the basic publish option (not advanced), Azure Functions: Deploy to Function App...
      • Name your app module1<yourname>
      • Use the Azure region closest to you. We'll use West US region since we're in San Francisco.
  4. Call the deployed API

Module 2: F# Azure Function

  1. Open the module2 directory in Visual Studio Code
  2. Create the C# project again, this time using the Azure Functions extension GUI
    • The button looks like a folder with a lightning bolt and the tooltip says Create New Project...
    • Change the function name to HttpTriggerFSharp
    • Accept other defaults
  3. Navigate to Azure Functions With F#. Thank you Aaron Powell for your post and for allowing us to use it in this workshop!
    1. Copy the code to the source file and change the extension from .cs to .fs (Ionide might look really upset at the file for a while, don't worry!)
    2. Change the extension of the project file from .csproj to .fsproj
    3. In the .fsproj file below the first <ItemGroup> section paste
<ItemGroup>
  <Compile Include="HttpTriggerFSharp.fs" />
</ItemGroup>
  1. Run it to make sure it works
  2. POSTs aren't very fun to test. Let's change the function to a GET that uses query parameters like in Module 1.
    • Paste over the code with
namespace Company.Function

open Microsoft.Azure.WebJobs
open Microsoft.Azure.WebJobs.Extensions.Http
open Microsoft.AspNetCore.Http
open Microsoft.Extensions.Logging
open Microsoft.AspNetCore.Mvc
open System
open Microsoft.Extensions.Primitives

module HttpTrigger =
    [<FunctionName("HttpTrigger")>]
    let Run([<HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = null)>] 
            req: HttpRequest,
            log: ILogger) 
            = 

        let stringValues = req.Query.Item "name"

        if StringValues.IsNullOrEmpty stringValues
        then
            log.LogInformation("no name was passed")
            BadRequestObjectResult("Include a 'name' as a query string.") :> ActionResult
        else 
            let name = stringValues.[0]
            log.LogInformation(sprintf "name was '%s'" name)
            OkObjectResult(name) :> ActionResult
  1. Run the function locally and call it.
    • Note that we switched the authorization from Function to Anonymous
  2. Publish the project to Azure using the GUI
  3. There will be a prompt to stream logs, accept it
  4. Call your app, inspect the logs
  5. Navigate to https://portal.azure.com
  6. Select your Function App
  7. Disable and reenable the app
  8. Run a test

Module 3: Unit Conversion Service

  1. Open the module3 directory in Visual Studio Code
  2. Create the same project as in Module 2
    • Name the app UnitConversionAPI
    • This time we'll use route parameters instead of query parameters
    • Here's the code:
namespace API

open System
open Microsoft.AspNetCore.Http
open Microsoft.Azure.WebJobs
open Microsoft.Azure.WebJobs.Extensions.Http
open Microsoft.Extensions.Logging

module Length =
    [<FunctionName("Length")>]
    let Run([<HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "Length/{source}/{target}/{input}")>] 
            req: HttpRequest,
            source: string,
            target: string,
            input: string,
            log: ILogger) 
            = 
        let inputs = String.Join("|", source, target, input)
        log.LogInformation(sprintf "Inputs: '%s'" inputs)
        inputs 
  1. Run the function locally and call it
  2. Now that we have a working app, let's implement the conversion logic. Add a file above the existing file named Length.fs
namespace UnitConversion

open System.Collections.Generic
open System.Linq
open System

module Length =
    let private lengthsAndFactors =
        let fsharpDict =
            dict [
                "meter", 1.0
                "millimeter", 1e-3
                "kilometer", 1e3 ]
        Dictionary<string, float>(fsharpDict)

    let private tryGetUnitFactor name =
        match lengthsAndFactors.TryGetValue name with
        | true, factor -> Some factor
        | _ -> None

    let private lengths = 
        let lengths = lengthsAndFactors.Keys.ToArray()
        String.Join(", ", lengths)

    let convert source target input =
        match (tryGetUnitFactor source, tryGetUnitFactor target)  with
        | None, Some _ ->
            sprintf "Length unit '%s' not found. Try %s." source lengths |> Error
        | Some _, None -> 
            sprintf "Length unit '%s' not found. Try %s." target lengths |> Error
        | None, None -> 
            sprintf "Length units '%s' and '%s' not found. Try %s." source target lengths |> Error
        | Some s, Some t -> 
            input * s / t |> Ok
  1. Change your functions file to be:
namespace API

open Microsoft.Azure.WebJobs
open Microsoft.Azure.WebJobs.Extensions.Http
open Microsoft.AspNetCore.Http
open Microsoft.Extensions.Logging
open Microsoft.AspNetCore.Mvc
open System
open UnitConversion

module LengthAPI =
    open UnitConversion.Length
    [<FunctionName("LengthAPI")>]
    let Run([<HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "length/{source}/{target}/{input}")>] 
            req: HttpRequest,
            source: string,
            target: string,
            input: float,
            log: ILogger) 
            = 
        let inputs = String.Join("|", source, target, input)
        log.LogInformation(sprintf "Inputs: '%s'" inputs)
        
        match Length.convert source target input with
        | Ok result ->
            log.LogInformation (sprintf "Conversion result: %f" result)
            OkObjectResult result :> ActionResult
        | Error msg ->
            NotFoundObjectResult msg :> ActionResult
  1. Run the function locally and call it
  2. Publish the project to Azure and call it
    • Name your app module3<yourname>

More resources

openfsharp2019-unitconversionservice's People

Contributors

brettrowberry avatar ilmaestro avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar

Forkers

ilmaestro

openfsharp2019-unitconversionservice's Issues

Make F# better

Replace custom result type with standard one
Make module names unique

Use nameof

Use F# 4.7 preview feature nameof where possible

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.