github.com/mweagle/Sparta@v1.15.0/docs_source/content/reference/apigateway/slack.md (about)

     1  ---
     2  date: 2016-03-09T19:56:50+01:00
     3  title: Slack SlashCommand
     4  weight: 21
     5  ---
     6  
     7  ![SlashLogo](/images/apigateway/slack/slack_rgb.png)
     8  
     9  In this example, we'll walk through creating a [Slack Slash Command](https://api.slack.com/slash-commands) service.  The source for
    10  this is the [SpartaSlackbot](https://github.com/mweagle/SpartaSlackbot) repo.
    11  
    12  Our initial command handler won't be very sophisticated, but will show the steps necessary to provision and configure a Sparta AWS Gateway-enabled Lambda function.
    13  
    14  # Define the Lambda Function
    15  
    16  This lambda handler is a bit more complicated than the other examples, primarily because of the [Slack Integration](https://api.slack.com/slash-commands) requirements.  The full source is:
    17  
    18  ```go
    19  import (
    20    spartaAWSEvents "github.com/mweagle/Sparta/aws/events"
    21  )
    22  ////////////////////////////////////////////////////////////////////////////////
    23  // Hello world event handler
    24  //
    25  func helloSlackbot(ctx context.Context,
    26    apiRequest spartaAWSEvents.APIGatewayRequest) (map[string]interface{}, error) {
    27    logger, _ := ctx.Value(sparta.ContextKeyLogger).(*logrus.Logger)
    28  
    29    bodyParams, bodyParamsOk := apiRequest.Body.(map[string]interface{})
    30    if !bodyParamsOk {
    31      return nil, fmt.Errorf("Failed to type convert body. Type: %T", apiRequest.Body)
    32    }
    33  
    34    logger.WithFields(logrus.Fields{
    35      "BodyType":  fmt.Sprintf("%T", bodyParams),
    36      "BodyValue": fmt.Sprintf("%+v", bodyParams),
    37    }).Info("Slack slashcommand values")
    38  
    39    // 2. Create the response
    40    // Slack formatting:
    41    // https://api.slack.com/docs/formatting
    42    responseText := "Here's what I understood"
    43    for eachKey, eachParam := range bodyParams {
    44      responseText += fmt.Sprintf("\n*%s*: %+v", eachKey, eachParam)
    45    }
    46  
    47    // 4. Setup the response object:
    48    // https://api.slack.com/slash-commands, "Responding to a command"
    49    responseData := map[string]interface{}{
    50      "response_type": "in_channel",
    51      "text":          responseText,
    52      "mrkdwn":        true,
    53    }
    54    return responseData, nil
    55  }
    56  ```
    57  
    58  There are a couple of things to note in this code:
    59  
    60  1. **Custom Event Type**
    61    - The inbound Slack `POST` request is `application/x-www-form-urlencoded` data.  This
    62    data is unmarshalled into the same _spartaAWSEvent.APIGatewayRequest_ using
    63    a customized [mapping template](https://github.com/mweagle/Sparta/blob/master/resources/provision/apigateway/inputmapping_formencoded.vtl).
    64  
    65  1. **Response Formatting**
    66  The lambda function extracts all Slack parameters and if defined, sends the `text` back with a bit of [Slack Message Formatting](https://api.slack.com/docs/formatting):
    67  
    68          ```go
    69          responseText := "Here's what I understood"
    70          for eachKey, eachParam := range bodyParams {
    71            responseText += fmt.Sprintf("\n*%s*: %+v", eachKey, eachParam)
    72          }
    73          ```
    74  
    75  1. **Custom Response**
    76    - The Slack API expects a [JSON formatted response](https://api.slack.com/slash-commands), which is created in step 4:
    77  
    78          ```go
    79          responseData := sparta.ArbitraryJSONObject{
    80            "response_type": "in_channel",
    81            "text":          responseText,
    82          }
    83          ```
    84  
    85  # Create the API Gateway
    86  
    87  With our lambda function defined, we need to setup an API Gateway so that it's publicly available:
    88  
    89  ```go
    90  apiStage := sparta.NewStage("v1")
    91  apiGateway := sparta.NewAPIGateway("SpartaSlackbot", apiStage)
    92  ```
    93  
    94  The `apiStage` value implies that we want to deploy this API Gateway Rest API as part of Sparta's `provision` step.
    95  
    96  # Create Lambda Binding & Resource
    97  
    98  Next we create an `sparta.LambdaAWSInfo` struct that references the `s3ItemInfo` function:
    99  
   100  ```go
   101  func spartaLambdaFunctions(api *sparta.API) []*sparta.LambdaAWSInfo {
   102    var lambdaFunctions []*sparta.LambdaAWSInfo
   103    lambdaFn, _ := sparta.NewAWSLambda(sparta.LambdaName(helloSlackbot),
   104      helloSlackbot,
   105      iamDynamicRole)
   106  
   107    if nil != api {
   108      apiGatewayResource, _ := api.NewResource("/slack", lambdaFn)
   109      _, err := apiGatewayResource.NewMethod("POST", http.StatusCreated)
   110      if nil != err {
   111        panic("Failed to create /hello resource")
   112      }
   113    }
   114    return append(lambdaFunctions, lambdaFn)
   115  }
   116  ```
   117  
   118  A few items to note here:
   119  
   120    * We're using an empty `sparta.IAMRoleDefinition{}` definition because our go lambda function doesn't access any additional AWS services.
   121    * Our lambda function will be accessible at the _/slack_ child path of the deployed API Gateway instance
   122    * Slack supports both [GET and POST](https://api.slack.com/slash-commands) integration types, but we're limiting our lambda function to `POST` only
   123  
   124  # Provision
   125  
   126  With everything configured, we then configure our `main()` function to forward to Sparta:
   127  
   128  ```go
   129  func main() {
   130    // Register the function with the API Gateway
   131    apiStage := sparta.NewStage("v1")
   132    apiGateway := sparta.NewAPIGateway("SpartaSlackbot", apiStage)
   133  
   134    // Deploy it
   135    sparta.Main("SpartaSlackbot",
   136      fmt.Sprintf("Sparta app that responds to Slack commands"),
   137      spartaLambdaFunctions(apiGateway),
   138      apiGateway,
   139      nil)
   140  }
   141  
   142  ```
   143  
   144  and provision the service:
   145  
   146  ```nohighlight
   147  S3_BUCKET=<MY_S3_BUCKETNAME> go run slack.go --level info provision
   148  ```
   149  
   150  Look for the _Stack output_ section of the log, you'll need the **APIGatewayURL** value to configure Slack in the next step.
   151  
   152  ```nohighlight
   153  INFO[0083] Stack output Description=API Gateway URL Key=APIGatewayURL Value=https://75mtsly44i.execute-api.us-west-2.amazonaws.com/v1
   154  INFO[0083] Stack output Description=Sparta Home Key=SpartaHome Value=https://github.com/mweagle/Sparta
   155  INFO[0083] Stack output Description=Sparta Version Key=SpartaVersion Value=0.1.3
   156  ```
   157  
   158  
   159  # Configure Slack
   160  
   161  At this point our lambda function is deployed and is available through the API Gateway (_https://75mtsly44i.execute-api.us-west-2.amazonaws.com/v1/slack_ in the current example).
   162  
   163  The next step is to configure Slack with this custom integration:
   164  
   165    1. Visit https://slack.com/apps/build and choose the "Custom Integration" option:
   166  
   167      ![Custom integration](/images/apigateway/slack/customIntegration.jpg)
   168  
   169    1. On the next page, choose "Slash Commands":
   170  
   171      ![Slash Commands](/images/apigateway/slack/slashCommandMenu.jpg)
   172  
   173    1. The next screen is where you input the command that will trigger your lambda function.  Enter `/sparta`
   174  
   175      ![Slash Chose Command](/images/apigateway/slack/chooseCommand.jpg)
   176  
   177      - and click the "Add Slash Command Integration" button.
   178  
   179    1. Finally, scroll down the next page to the **Integration Settings** section and provide the API Gateway URL of your lambda function.
   180  
   181      ![Slash URL](/images/apigateway/slack/integrationSettings.jpg)
   182  
   183      * Leave the _Method_ field unchanged (it should be `POST`), to match how we configured the API Gateway entry above.
   184  
   185    1. Save it
   186  
   187      ![Save it](/images/apigateway/slack/saveIntegration.jpg)
   188  
   189  
   190  There are additional Slash Command Integration options, but for this example the **URL** option is sufficient to trigger our command.
   191  
   192  # Test
   193  
   194  With everything configured, visit your team's Slack room and verify the integration via `/sparta` slash command:
   195  
   196  ![Sparta Response](/images/apigateway/slack/slackResponse.jpg)
   197  
   198  # Cleaning Up
   199  
   200  Before moving on, remember to decommission the service via:
   201  
   202  ```nohighlight
   203  go run slack.go delete
   204  ```
   205  
   206  # Wrapping Up
   207  
   208  This example provides a good overview of Sparta & Slack integration, including how to handle external requests that are not `application/json` formatted.