Step Functions para no morir. Parte 2: Lenguaje ASL
Tags: aws, javascript, stepfunctions, serverless
Published: 2022-02-08 13:16:34

Hola! 
![Gif patricio diciendo hola](https://media.giphy.com/media/3ogwFGEHrVxusDbDjO/giphy.gif)

Fuaaa, ~~casi no~~ llegué a la segunda semana de posteo. 
Es este segundo post vamos a ver los siguientes temas: 

- Que es ASL?
- Tipos de State
- Manejo de Errores
- Serverless

##¿Qué es ASL?

Cuando hablamos de ASL en AWS hacemos referencia al `Amazon States Language`.
Es un lenguaje estructurado basado en el formato JSON que se utiliza para definir las máquinas de estado, las cuales, a fin de cuentas, son una colección de Tasks. Estas Tasks pueden realizar una tarea (**Task state**), determinar a qué estados se debe pasar a continuación (**Choice state**), detener una ejecución con un error (**Fail state**), entre otros. 

###State Machines en ASL
Un ejemplo de una state machine orquestada en asl sería el siguiente: 


```
{
  "Comment": "A Hello World example demonstrating various state types of the Amazon States Language",
  "StartAt": "Pass",
  "States": {
    "Pass": {
      "Comment": "A Pass state passes its input to its output, without performing work. Pass states are useful when constructing and debugging state machines.",
      "Type": "Pass",
      "Next": "Hello World example?"
    },
    "Hello World example?": {
      "Comment": "A Choice state adds branching logic to a state machine. Choice rules can implement 16 different comparison operators, and can be combined using And, Or, and Not",
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.IsHelloWorldExample",
          "BooleanEquals": true,
          "Next": "Yes"
        },
        {
          "Variable": "$.IsHelloWorldExample",
          "BooleanEquals": false,
          "Next": "No"
        }
      ],
      "Default": "Yes"
    },
    "Yes": {
      "Type": "Pass",
      "Next": "Hello World"
    },
    "No": {
      "Type": "Fail",
      "Cause": "Not Hello World"
    },
    "Hello World": {
      "Type": "Pass",
      "End": true
    }
  }
}
```


En las estructuras de las state machine contamos con los siguientes campos: 

- Comment (Opcional) : Es en donde escribimos la descripción, ya sea de la tarea o de la máquina de estado.
- StartAt (Obligatorio): Indica el nombre en formato string que coincide exactamente con la tarea que inicia la máquina de estado (es `Case Sensitive`)
- TimeoutSeconds (Opcional): Indica la cantidad máxima de segundos que puede durar la ejecución de la máquina.
- States (Obligatorio): Contiene el set de tareas que componen la máquina de estado. 

##Tipos de States
A la hora de trabajar con máquinas de estado, tenemos un total de 8 tipos de States que nos van a ayudar a la hora de la orquestación. 

###[Task](https://docs.aws.amazon.com/es_es/step-functions/latest/dg/amazon-states-language-task-state.html)
Un State de tipo Task representa una única unidad de trabajo realizada por una máquina de estado.
Una Task realiza el trabajo mediante una actividad o una función de AWS Lambda, o pasando parámetros para consumir APIs de otros servicios. 

```
 "ActivityState": {
  "Type": "Task",
  "Resource": "arn:aws:states:us-east-1:123456789012:activity:HelloWorld",
  "Next": "NextState"
}
```

Los campos campos obligatorios son;
- Type: En este caso estamos hablando del tipo Task.
- Resource: Un ARN que identifica de forma inequívoca la tarea específica que se va a ejecutar.
- Next o End: El campo next indica cual es la tarea que se va a correr una vez que se termina de ejecutar la tarea actual. Si queremos que el flujo finalice en esta tarea vamos a reemplazarlo por un `“End” : true`

###[Pass](https://docs.aws.amazon.com/es_es/step-functions/latest/dg/amazon-states-language-pass-state.html)

Este state toma la información del input y la entrega como output sin hacer ningún trabajo o actividad. 
Es decir, , solo pass la información. 

![Guiño Guiño Jefe Gorgory](https://pbs.twimg.com/media/ExW8UWvXEAUF2o9?format=jpg&name=small)

Bueno, ahora en serio... 
Este es un ejemplo de un State de tipo Pass.
```
"No-op": {
  "Type": "Pass",
  "Result": {
    "x-datum": 0.381018,
    "y-datum": 622.2269926397355
  },
  "ResultPath": "$.coords",
  "Next": "End"
}

```

En este tipo de state los campos obligatorios son Type y Next o End. 
Lo opcionales son 
- Result : Es la data que se pasa a la siguiente tarea, y en caso de tener un `ResulthPath`, es la posición que toma dentro del resultado. 
- ResultPath: Especifica dónde (en la entrada) se coloca la "salida" de la tarea virtual especificada en Result.

Es decir, si la entrada  es 

```
{
  "georefOf": "Home"
}
```

La salida combinando el `Result` con el `ResulthPath` es

```
{
  "georefOf": "Home",
  "coords": {
    "x-datum": 0.381018,
    "y-datum": 622.2269926397355
  }
}
```

###[Wait](https://docs.aws.amazon.com/es_es/step-functions/latest/dg/amazon-states-language-wait-state.html)

Este state espera hasta el tiempo asignado y luego continúa con la task siguiente

```
"wait_ten_seconds": {
    "Type": "Wait",
    "Seconds": 10,
    "Next": "NextState"
}
```

El tiempo asignado pueden ser segundos, declarándose en el campo `Seconds`, o puede ser un tiempo absoluto declarando la fecha en el campo `Timestamp`.
```
 "Timestamp": "2016-03-14T01:59:00Z"
```

###[Choice](https://docs.aws.amazon.com/es_es/step-functions/latest/dg/amazon-states-language-choice-state.html)

Es la que agrega la lógica de ramificación a la máquina de estado.

```
 "ChoiceState": {
    "Type": "Choice",
    "Choices": [
        {
            "Variable": "$.name",
            "StringEquals": "Blancanieves",
            "Next": "Eat Apple"
        }
    ],
    "Default": "Not Eat Apple"
}
```

Los campos obligatorios son Type y Choices. 
En `Choices` van las distintas ramificaciones que puede tener la máquina de estado.
Existen distintos tipos de operadores de comparación, sus nombres son bastantes descriptivos.

- And
- BooleanEquals,BooleanEqualsPath
- IsBoolean
- IsNull
- IsNumeric
- IsPresent
- IsString
- IsTimestamp
- Not
- NumericEquals,NumericEqualsPath
- NumericGreaterThan,NumericGreaterThanPath
- NumericGreaterThanEquals,NumericGreaterThanEqualsPath
- NumericLessThan,NumericLessThanPath
- NumericLessThanEquals,NumericLessThanEqualsPath
- Or
- StringEquals,StringEqualsPath
- StringGreaterThan,StringGreaterThanPath
- StringGreaterThanEquals,StringGreaterThanEqualsPath
- StringLessThan,StringLessThanPath
- StringLessThanEquals,StringLessThanEqualsPath
- StringMatches
- TimestampEquals,TimestampEqualsPath
- TimestampGreaterThan,TimestampGreaterThanPath
- TimestampGreaterThanEquals,TimestampGreaterThanEqualsPath
- TimestampLessThan,TimestampLessThanPath
- TimestampLessThanEquals,TimestampLessThanEqualsPath

Es recomendable que la Task Choice tenga un campo llamado `Default` para que en caso de que no coincida con ninguna choice pueda continuar el flujo. 

###[Parallel](https://docs.aws.amazon.com/es_es/step-functions/latest/dg/amazon-states-language-parallel-state.html)

El state de tipo Parallel te permite ejecutar tareas de manera paralela. 

```
{
    "LookupCustomerInfo": {
        "Type": "Parallel",
        "End": true,
        "Branches": [
            {
                "StartAt": "LookupAddress",
                "States": {
                    "LookupAddress": {
                        "Type": "Task",
                        "Resource": "arn:aws:lambda:us-east-1:123456789012:function:AddressFinder",
                        "End": true
                    }
                }
            },
            {
                "StartAt": "LookupPhone",
                "States": {
                    "LookupPhone": {
                        "Type": "Task",
                        "Resource": "arn:aws:lambda:us-east-1:123456789012:function:PhoneFinder",
                        "End": true
                    }
                }
            }
        ]
    }
}
```

Los campos obligatorios son el Type, Next o End y Branches. 
El campo `Branches` son los flujos que se van a ejecutar de manera paralela, y siguen la sintaxis de una máquina de estado. 

###[Succeed](https://docs.aws.amazon.com/es_es/step-functions/latest/dg/amazon-states-language-succeed-state.html)

Es la task utilizada para terminar las ejecución de manera exitosa. Es un tipo de state que no necesita un campo Next o End. 

```
"SuccessState": {
    "Type": "Succeed"
}
```

###[Fail](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-fail-state.html)

Termina la ejecución con de la máquina de estado con una ejecución fallida. 
Este state tampoco necesita de un campo Next o End. 

```
"FailState": {
    "Type": "Fail",
    "Cause": "Invalid response.",
    "Error": "ErrorA"
}
```
Los campos `Cause` y `Error` son opcionales. 

###[Map](https://docs.aws.amazon.com/es_es/step-functions/latest/dg/amazon-states-language-map-state.html)

Es el state que permite ejecutar varios pasos para cada iteración de un elemento. 
Es decir, mientras que el `Parallel` ejecuta distintos flujos al mismo tiempo, el `Map` ejecuta el mismo flujo para distintos elementos. 

```
"Validate-All": {
    "Type": "Map",
    "Iterator": {
        "StartAt": "Validate",
        "States": {
            "Validate": {
                "Type": "Task",
                "Resource": "arn:aws:lambda:us-east-1:123456789012:function:ship-val",
                "End": true
            }
        }
    },
    "End": true
}
```

Los campos obligatorios son Type e `Iterator`, en este campo se define la máquina de estado que va a procesar la data.

##Manejo de errores
Para el manejo de errores tenemos dos campos que podemos utilizar dentro de las states. 

- Retry : se va a encargar de la cantidad de reintentos de un state en caso de que falle. Solo está permitido en los states de tipo `Task` y `Parallel`. 

- Catch: Se encarga de atrapar el error y redireccionarlo al flujo al que corresponde. Solo está permitido en los states de tipo `Task`, `Map` y `Parallel`. 

####Ejemplo
Suponiendo que tenemos un state de tipo Task, necesitamos que 
- Si falla la ejecución, deben hacerse 3 intentos.
- Cuando falla la tercera ejecución, queremos manejar el error para enviarlo a un flujo en particular. 
 

```
"ValidateCreditCard": {
    "Type": "Task",
    "Resource": "arn:aws:states:us-east-1:123456789012:function:Validate",
    "Retry": [
        {
            "ErrorEquals": [
                "States.ALL"
            ],
            "MaxAttempts": 3
        }
    ],
    "Catch": [
        {
            "ErrorEquals": [
                "Error"
            ],
            "Next": "Format Error Response",
        }
    ],
    "Next": "NextState"
}
```

Para los reintentos tenemos el campo `Retry`, y dentro de este tenemos otros dos campos importantes.
- ErrorEquals: va a ser la condición para los reintentos, es decir, solo cuando el error coincida con el mencionado, vamos a tener un reintento. 
- MaxAttemps: es la cantidad de reintentos para nuestra ejecución. 

```
"Retry": [
    {
        "ErrorEquals": [
            "States.ALL"
        ],
        "MaxAttempts": 3
    }
]
```

Para el manejo de error vamos a usar el campo `Catch`, y este lleva los siguientes campos.
- ErrorEquals:  va a ser el error que queremos atrapar
- Next es el nombre del próximo step si coincide con el error. 

```
"Catch": [
    {
        "ErrorEquals": [
            "Error"
        ],
        "Next": "Format Error Response",
    }
],
```

Tanto el campo `Catch` como el `Retry` son arreglos de objetos, lo que permite el manejo de distintos tipos de errores. 

##Serverless 
¿Qué es `serverless`? Es un modelo de ejecución, en el cual un proveedor de la nube (AWS, Google, o Azure) es responsable de ejecutar un fragmento de código mediante la asignación dinámica de los recursos. Y solo cobrando por la cantidad de recursos utilizados para ejecutar el código.

A la hora de trabajar con este modelo podemos recurrir a distintos frameworks. Uno de ellos es `SAM`(Serverless Application Model), el cual es un framework de código abierto creado para las arquitecturas serverless. Es el framework de AWS. 
El framework que vamos a estar utilizando para crear nuestras máquinas de estados en esta serie va a ser `Serverless Framework`, el cual es un framework también de código abierto que fue el primero en ser creado para este tipo de arquitectura y es el más usado. 

El modelo `serverless` también suele ser conocido como `FaaS` (Function as a Service).

####Para su instalación necesitamos...
1 - Tener NodeJs instalado. (Instalar NodeJs https://nodejs.org/en/)
2 - Correr este comando en la consola (Docu instalación : https://www.serverless.com/framework/docs/getting-started)

```
npm install -g serverless
```

*Recomiendo tener esto instalado para el cuarto capítulo.*


##Fin
Este ha sido otro capítulo con material teórico.
En el próxima vamos a estar viendo un par de servicios de AWS que vamos a necesitar conocer para poder pasar luego a la creación de las máquinas de estado. 

Para esto voy a necesitar que creen una cuenta en [AWS](https://aws.amazon.com/es/console/), si es que no la tienen. 


![Se viene lo chido meme](https://i.ytimg.com/vi/ONNTDstfzsg/mqdefault.jpg)

Si el contenido les sirvió, no olviden que pueden invitarme un cafecito. 

[![Invitame un café en cafecito.app](https://cdn.cafecito.app/imgs/buttons/button_2.svg)](https://cafecito.app/giulianaeolmos)

Nos vemos la próxima semana.