Skip to content

Aplicación serverless para consumir un api que sigue estandares REST y guardar la data en formato .csv o .json en un bucket de aws s3, según se configure

Notifications You must be signed in to change notification settings

CrissAlvarezH/api-extractor

Repository files navigation

API Extractor

Esta aplicación serverles permite consumir un api que sigue estandares REST y guardar la data en un archivo .csv o .json, según se configure, en un bucket de aws s3 especificado en la configuración.

Caracteristicas

  • Autenticación por token
  • Refresco de token automaticamente
  • Parseo de información de JSON a csv
  • Permite programar la ejecución o ejecutar manualmente
  • Permite seleccionar los campos especificos que se desea extraer
  • Soporta paginación en las apis, ya sea por links o por paginas sequenciales
  • La data sensible como tokens, client ids, etc, se pueden guardar en aws secrets y referencialos en la configuración del extractor
  • Permite aplicar transformaciones a la data
  • El extractor tiene api propia para hacer la configuración y ejecución del mismo
  • Etc.

Documentación

Infraestructura

Despliegue

El despliegue se hace en una cuenta de AWS, para eso es necesario los siguientes pre-requisitos

  • Instalar Serverless framework en la maquina local, el cual es el framework usado para desplegar el proyecto en los ambientes en aws.

  • access key id y access secret key de un usuario de AWS IAM con permisos de administrador que será usado por serverless framework para crear la infraestructura vía linea de comandos.

  • Configurar los access key del usuario del paso anterior en la maquina local, esto se puede hacer usando el CLI de aws y corriendo el comando: aws configure

  • (Opcional o en caso de obtener un error) Configurar variables de entorno del proyecto, para esto copiamos y pegamosel archivo .env.example y lo renombramos a .env, esto lo podemos hacer a mano, o con el siguiente comando (dentro de la carpeta del proyecto):

     cp .env.example .env
    

    luego establecemos los valores como queramos, el primer valor es EVENT_BRIDGE_SCHEDULE el cual por defecto es 0 1 * * ? * y es la expresión cron que indica la ejecución automatica de las extracciones.

    La segunda variable de entorno es DEFAULT_OUTPUT_BUCKET_SUFFIX que por defecto es 1 y es un texto que se pone al final del nombre del bucket por defecto donde se guarda el output de las extracciones, el cual es api-extractor-output-prod y al final del nombre se agregar este suffix. Esto será necesario configurarlo porque el nombre de los bucket deben ser unicos y si el nombre actual ya esta ocupado entonces debemos añadir un sufijo para hacerlo unico, usando esta variable de entorno, el error que arroja el deploy que nos indica esto es el siguiente:

     Error:
     CREATE_FAILED: ExtractorOutputBucket (AWS::S3::Bucket)
     api-extractor-output-prod-1 already exists
    
  • (Opcional) Si estamos en Linux o Mac podemos instalar Make usando

      # Linux
      sudo apt install make
    
      # Mac
      brew install make
    

Una vez cumplidos los puntos anteriores estamos listo para desplegar, para esto corremos el comando

make deploy

Usando make, o si no queremos instalar make, podemos correr

sls deploy --verbose --stage prod

El anterior comando nos debe dar un output al final como el siguiente:

Stack Outputs:
  ApiExtractorLambdaFunctionQualifiedArn: *******
  ConfigApiLambdaFunctionQualifiedArn: *******
  RootApiKey: 8b189c1d8fadhf8h4nk0fadh3nd8848667
  HttpApiId: ********
  ServerlessDeploymentBucketName: *********
  HttpApiUrl: https://had73had0.us-east-2.amazonaws.com

De todo esto lo que nos insteresa es el HttpApiUrl y el RootApiKey que nos servirán para consumir el api del lambda extractor.

Nota: una vez hecho el depliegue, por seguridad, borre el usuario que creó con permisos de administrador para propositos del despliegue

Destrucción de recursos

En algunos casos será necesario destruir todos los recursos de aws que fueron creados en el deploy, para esto primero tenemos que limpiar el bucket creado para guardar el output de las extracciones api-extractor-output-prod, luego podemos proceder a destruir los recursos con el siguiente comando

Usando make:

make remove

Sin make:

sls remove --stage prod

API

El lamnda api extractor tiene un api para realizar la configuración, ejecutar manualmente y monitorear las ejecuciones del mismo

Colección de Postman

El archivo docs/postman_collection.json dentro de este repo lo podemos importar en Postman. Una vez importado veremos los siguientes endpoints para consumir

Api keys

Para poder hacer uso del api es necesario usar un api key, al realizar el despliegue este nos arroja un api key que pertenece al usuario root llamada RootApiKey, con esta key podemos consumir el api pero es recomendable crear mas api key para cada persona que va a hacer cambios en la configuración del api, ya que el nombre de cada api key queda guardado en el historial de cambios de la configuración, por lo que al tener un api key para cada persona podemos rastrear cada cambio de cada persona.

Para crear, refrescar y borrar api keys usaremos la carpeta

Importante: Solo el api key del root puede crear otras api keys y no se puede borrar desde el api

Estas api keys se guardan en un secret de aws el cual es creado en el deploy y tiene el nombre api-extractor-config/prod/apikeys, desde aquí es la unica forma de borrar el api key del root, el resto se pueden borrar desde el api por Postman.

Configuración para el api extractor

La configuración del extractor se hace mediante el api, si usted importó la colección de Postman podrá ver los siguientes endpoints ahí

De los cuales los endpoints que nos sirven para crear, modificar y borrar una configuración para un api extractor son

  • List api configs para listar todas las configuraciones existentes
  • Retrieve api config para ver una configuracion especificada por el id en los parametros del endpoint
  • Create api config para crear una configuración nueva
  • Update api config para actualizar pasando el id
  • Delete api config para borrar una configuración

Estructura general

{
    "name": "<string>",
    "auth": {
        "refresh_token": {
            "endpoint":  <Endpoint>,
            "response_token_key":  <JsonField>
        },
        "access_token": "<string>"
    },
    "extractions": [
        {...},
        {...},
        {...}
    ]
}

Endpoint

El endpoint es la representació, en la configuración del extractor, de un endpoint en la vida real, con su url y demas parametros como los query_params que son los parametros que van en la url, los headers y el body. El unico campo obligatorio es la url, el resto son opcionales si no se necesitan.

{
    "url": "<string>",
    "query_params": "<json>",
    "headers": "<json>",
    "body": "<json>"
}

Este objeto se usa en el modulo de auth para especificar el endpoint al cual se tiene que apuntar para obtener el refresco del token, y tambien se usa en cada extracción para especificar el endpoint del cual se va a extraer la data

JsonField

Se usa para especificar un campo en un json, cada campo se separa por un punto, por ejemplo, para el json

{
    "user": {
        "name": "Cristian",
        "account": {
            "number": 2334,
            "type": "credit"
        }
    }
}

Para obtener el number del account el user, se usa el JsonField

user.account.number

El cual retornará 2334

Autenticación por tokens

En la configuración se puede especificar el access token que se va a usar en el apartado de auth.access_token y este se puede usar luego en las extracciones usando referencias.

Refresco de token automatico

Si el token tiene algun tiempo limite de uso y luego explira, podemos configurar el refresco del token, para esto debemos introducir la configuración en auth.refresh_token en el cual podemos especificar el endpoit en el cual se realiza el refresh y luego, especificar cual campo de la respuesta de ese endpoint contiene el token renovado, usando el campo auth.refresh_token.response_token_key

Por ejemplo: suponiendo que para renovar un token debo hacer una petición al endpoint

https://accounts.com/oauth/v2/token?refresh_token=afe21e5ac3f9ea12639

Y este responde con:

{
    "mytoken": "fa146b8e6a67366fd991c7d65",
    "api_domain": "https://www.api.com",
    "token_type": "Bearer",
    "expires_in": 3600
}

Para este caso la configuración deberá ser la siguiente

{
    "auth": {
        "endpoint": {
            "url": "https://accounts.com/oauth/v2/",
            "query_params": {
                "refresh_token": "afe21e5ac3f9ea12639"
            }
        },
        "response_token_key": "mytoken"
    }
}

Ejecutar una configuración

Para ejecutar una configuració ya creada manualmente usamos el endpoint

En el parametro config_id debemos poner el id de la configuración antes de dar click en send

Una vez enviada, se lanzará el lambda de extracción para ejecutar dicha configuración, el endpoint responderá de inmediato pero el proceso de ejecución tardará segun la configuración del mismo y la cantidad de data que deba extraer.

Logs de ejecución

Para ver los logs del proceso debemos utilizar el siguiente endpoint

Es importante poner el extraction_id el cual no se debe confundir con el id de la configuración, el extraction_id es el id que tiene cada item dentro de el campo extractions dentro de la configuración

Los logs tienen la siguiente estructura:

{
    "extraction_name": "",
    "extraction_id": "",
    "config_name": "",
    "config_id": "",
    "success": "true | false",
    "error": "null | error_message", // error en caso de ocurrir
    "data_inserted_len": "number", // cantidad de data extraida
    "destiny": "", // ruta en s3 del archivo .csv
    "last": "<json>", // ultimo item extraido la
    "created_at": "", // fecha de inserción del log
}

Extracciones

Las extracciones van configuradas en el capo extractions de la configuración del extractor vista en el punto de la estructura general. Cada extracción tiene la siguiente estructura

{
    "name": "<string>",
    "endpoint": <Endpoint>,
    "data_key": <JsonField>,
    "format": "csv | json", # formato a guardar en el destino,
    "output_params": {},
    "transformations": [],
    "pagination": {
        "type": "sequential | link",
        "parameters": <PaginationParameters>
    },
    "data_schema": "<json>",
    "s3_destiny": {
        "bucket": "<string>",
        "folder": "<string>"
    }
}

Endpoint a extraer

Este es el endpoint de la configuración de la extracción, y funciona exactamente igual que el Endpoint explicado previamente.

En este caso representa el endpoint al cual se le va a consulta la data a extraer, para obtener esta data se usa el campo data_key el cual indica en cual campo de la respuesta del endpoint esta la data a extraer.

Por ejemplo, si la respuesta del endpoint es

{
    "result": [
        ...,
    ],
    "pagination": {
        "current_page": 1,
        "total_page": 3,
        "on_page": 50
    }
}

Entonces el data_key debería ser result

Transformaciones

Las transformaciones permite aplicar funciones que modifiquen la data de las columnas despues de mapearlas por el data_schema, existen varios tipos de normalizaciones, a continuación se explica la estructura a definir:

{
    "extractions": [
        ...,
        {
            ...,
            "transformations": [
                ...,
                {
                    "action": "<string>",
                    "priority": "<number>",
                    "on": [],
                    "new_column_prefix": "<string>",
                    "params": {}
                }
            ]
        }
    ]
}

action es el nombre de la transformación, dependiendo de esta los params serán unos u otros, en la siguiente sección están todas las action disponibles y sus respectivos parametros. priority le indica al programa en que orden va a ejecutar las transformaciones, se ordenan de mayor a menos, por defecto es 0. on es una lista de nombres de columnas sobre las cuales se va a aplicar la transformación. new_column_prefix lo usamos cuando queremos que el resultado de la transformación no sobreescriba la columna sobre la cual se está aplicando, si no mas bien, que cree una nueva columna, y esta nueva columna usará este valor como prefijo en su nombre, si no se define este parametro entonces se sobreescribirá la columna con el resultado de la normalización. params son los parametros que recibe la normalización, los cuales cambian dependiendo de cual sea esta.

Actions

A continuación se enumaran las actions disponibles para usar en las transformaciones, así como tambien sus respectivos params:

  • replace Es usado para remplazar unos caracteres por otros, los params de esta action son to_replace el cual el string que se va a reemplazar por el parametro value
  • trim Se utilia para borrar los espacios en blanco a la derecha e izquierda de los valores de la columna donde se aplique, si solo queremos borrar los espacios de la derecha, usamos en su lugar rtrim, o si es a la izquierda, entonces usamos ltrim
  • lower Nos permite pasar a minusculas los valores
  • upper Nos permite pasar a mayusculas los valores
  • title Pone en mayusculas solo la primera letra de cada palabra
  • capitalize Unicamente la primera letra de todo el texto será establecida en mayusculas
  • split Divide el texto partiendolo por un determinado caracter, devolviendo así una lista de strings, para especificar el caracter por el cual se va a devidir el texto, usamor el param char
  • join Nos permite unir una lista convirtiendola en una cadena de texto, los elementos de la lista serán unidos usando un caracter el cual es parado por params como sep
  • slice Es usado para obtener un sub porción de un texto, para esto debemos especificar la posición desde la empieza y termina esta extracción, para eso usamos los params start y stop respectivamente.

Paginación

Muchas apis estan paginadas, por lo que es necesario dar soporte a la paginación, por lo general existen dos tipos de paginación

  • Paginación secuencial Se trata simplemente de una paginación donde existe un parametro page que indica en cual pagina nos encontramos, y cada pagina corresponde a un numero

  • Paginación por link En este caso no necesariamente hay un parametro llamado page, mas bien en la paginación nos dan el siguiente link al cual debemos apuntar para obtener la siguiente pagina de la data

Dependiendo de cual sea la paginación del api que queremos consumir, debemos agregar unos parametros u otros, estos parametros se explican a continuación

PaginationParameters

Dependiendo del tipo de paginación los parametros son distintos. Para una paginación de tipo sequential los parametros son:

{
    "param_name": "<string>",
    "start_from": "<number>",
    "step": "<number>",
    "there_are_more_pages": <ConditionExpression | JsonField>,
    "continue_while_status_code_is": "<number>",
    "stop_when_response_body_is_empty": "<boolean>"
}
  • param_name: expresa cual es el nombre del parametro que le indica al api el numero de la pagina, usualmente es page
  • start_from: indica desde cual pagina empezaremos
  • step: es el number de incremento de pagina en pagina
  • there_are_more_pages: es una expression condicional explicada mas adelante, o un field del json de la respuesta del api el cual es boolean e indica si hay mas paginas por recorrer
  • continue_while_status_code_is: es el codigo del response del endpoint que se esta consumientos que queremos que tenga para continuar la paginación, si responde con un codigo distinto se detiene.
  • stop_when_response_body_is_empty: cuando es True indica que, sin importar los otros parametros, va a detener la paginación si el response body es vacío

IMPORTANTE: Los parametros continue_while_status_code_is y stop_when_response_body_is_empty no pueden estar configurados los dos, debe elegir uno de los dos para configurar la paginación.

Cuando es de tipo link los parametros son

{
    "next_link": <JsonField>
}

Este next_link hace referencia al link de la proxima pagina, el cual viene en el json del body, por tanto se debe especificar como accederlo, por ejemplo, un valor valido sería pagination.next_link para el caso de que el response tenga el link ubicado ahí.

ConditionExpression

Hace referencia a una condición que puede ser verdadera o falsa que tiene la siguiente estructura

<field> <operator> <field>

Los field pueden ser, ya sea, un campo en el json de la respuesta de endpoint (por ejemplo info.current_page) o un valor como tal (por ejemplo 23) El operator puede ser ==, <=, >=, !=, > ó <.

Ejemplo:

Para el response:

{
    "data": [
        // ...
    ],
    "info": {
        "current_page": 2,
        "total_pages": 10
    }
}

La condición podría ser

info.current_page < info.total_pages

Lo cual retornará false siempre que aun falten paginas por recorrer, el cual es el objetivo de este expression, retornar true cuando hay mas paginas por recorrer y false cuando ya no hay mas

Esquema de la data

Este campo es el data_schema, si no se especifica va a tomar toda la data tal cual como viene del api y la va a guardar en el archivo .csv o .json, pero si queremos darle un formato especifico a la data y seleccionar solo ciertos campos podemos usar este esquema para eso, de la siguiente manera:

El esquema es un json en el cual especificas los campos a guardar en el .csv, el nombre del key en cada json corresponde al key en la data del endpoint, y el nombre del value es el nombre de la columna que tendrá en el .csv, por ejemplo:

Si el response del endpoint es:

{
    "result": [
        {
            "name": "Cristian",
            "age": 26,
            "acount": 11111,
            "city": "Montería"
        },
        {
            "name": "Jose",
            "age": 23,
            "acount": 2222,
            "city": "Bogotá"
        }
    ]
}

Y queremos guardar solo el name, account y city, podemos crear un data_schema así:

{
    "name": "user_name",
    "account": "user_account",
    "city": "city"
}

Esto dará como resultado el siguiente .csv

user_name user_account city
Cristian 11111 Montería
Juan 2222 Bogotá

Subelementos

Si necesitas acceder a un elemento que esta anidado, la sintaxis seriá la siguiente.

Data:

[
    {
        "type": "A",
        "user": {
            "name": "Cristian",
            "account": {
                "number": 3344
            }
        }
    },
    {
        "type": "B",
        "user": {
            "name": "Juan",
            "account": {
                "number": 5555
            }
        }
    }
]

Esquema:

{
    "type": "type",
    "user": {
        "name": "user_name",
        "account": {
            "number": "user_acc_number"
        }
    }
}

Resultado:

type user_name user_acc_number
A Cristian 3344
B Juan 5555

Mapeo de items agregando data de otro endpoint

Podemos configurar un endpoint del cual extraer detalles de cada item y anexarlos al json orinal de ese item, esto es muy util cuando el endpoint que lista toda la data no tiene los detalles de cada item que necesitamos

{
    "mapping_fetch": {
        "endpoint": <Endpoint>,
        "prefix": "string",
        "data_schema": "json"
    }
}

El mapeo consiste en que por cada item del array de datos se realizará una petición a mapping_fetch.endpoint, una vez hecha esa petición el json resultante será pasado a travez del data_schema y el output de eso será anexado con el prefix configurado al json original del item

Output params

Estos parametros definen como se va a exportar el archivo final, se establecen en la extracción y los posibles valores son los siguientes

{
    "extractions": [
        {
            ...,
            "output_params": {
                "csv_separator": "<character>"
            }
        }
    ]
}

csv_separator es el caracter con el cual se va a delimitar la data en el archivo de exportación en el caso que el format sera csv, el valor por defecto es ,

Destino en S3

Es la ruta en donde guardará el archivo .csv con el resultado de la extracción, en la configuración es s3_destiny y se compone por un bucket y un folder

{
    "bucket": "<string>", // default: api-extractor-output-prod
    "folder": "<string>" // default: sin folder
}

Si no se especifica el s3_destinty tomará los valores default

Referencias

En la sitaxis de la configuración es permitido agregar referencias a valores que son dinamicos, ya sea porque estan guardados en otro lugar como en la base de datos o en un secreto de aws, o porque es un valor cambiante dentro de la misma configuración y necesitamos referenciar siempre el valor actual de ese campo.

Un ejemplo de sintaxis para declarar una referencia es $(self::auth.access_token, token_default). La estructura es $(<tipo>::<referencia>, <valor por defecto>)

Los tipos de referencia son

  • self

Este hace referencia al mismo json de configuración, es muy usado por ejemplo para hacer referencia al token, ya que este se refresca cada vez que se ejecuta, para poder usarlo podemos usar la referencia, por ejemplo:

{
    "auth": {
        "refresh_token": {
            "endpoint": {
                "url": "https://accounts.com/oauth/v2/",
                "query_params": {
                    "refresh_token": "afe21e5ac3f9ea12639"
                }
            },
            "response_token_key": "mytoken"
        },
        "access_token": ""
    },
    "extractions": [
        {
            ...,
            "endpoint": {
                ...,
                "headers": {
                    "Authorization": "Bearer ${self::auth.access_token}"
                }
            }
        }
    ]
}

En este caso estamo mandando el token guardado en auth.access_token dentro de la misma configuració y que es renovado cada vez gracias a la configuracion del refresh_token.

  • last

Cada vez que se ejecuta una extracción se insertan logs de la ejecución dentro del cual se guarda el ultimo item extraido, el last hace referencia a este ultimo item, por lo que si queremos hacer referencia a algun campo del ultimo item de la ejecución previa lo podemos hacer de esta forma:

{
    ...,
    "extractions": [
        {
            ...,
            "endpoint": {
                ...,
                "query_params": {
                    "start_from": "$(last::id, 1)"
                }
            }
        }
    ]
}

Es esta configuración estamos mandando al endpoint de extracción el id del ultimo item extraido a travez del query param start_from y en caso de ser la primera vez en ejecutar o de no tener un item previo guardado, tomará el valor por dejecto 1

  • secret

Esta referencia va a buscar el valor dentro del secret llamado api-extractor-config/prod/extractor-secrets el cual es creado en el deploy. La sintaxis es igual que el resto, y esta tiene especial uso para el modulo de auth ya que ahí se suele colocar valores delicados como api keys, client ids, etc, por ejemplo:

{
    "auth": {
        "refresh_token": {
            "endpoint": {
                "url": "https://accounts.com/oauth/v2/token",
                "query_params": {
                    "refresh_token": "${secret::refresh_token}",
                    "client_id": "${secret::client_id}",
                    "client_secret": "${secret::client_secret}",
                    "grant_type": "refresh_token"
                }
            },
            "response_token_key": "access_token"
        },
        "access_token": ""
    }
}

Testing

Para ejecutar las pruebas primero que todo debes tener desplegado el proyecto, si no lo está lo podemos hacer con el comando

make deploy

En aws secret manager, en los secretos del proyecto debe estar base_secret con el mismo valor que tiene la variable de entorno FAKE_API_SECRET_TO_GET_TOKEN en el .env

IMPORTANTE: La base de datos DynamoDB no debe tener registros que puedan interferir con las pruebas

Despues es necesario levantar un api creado especificamento con data dummy para hacer pruebas, para esto corremos el siguiente comando

make fakeapi

Esto levantará un servidor con un api rest creado en la carpeta /fake-api del proyecto. Luego, debemos exponer esta api a internet, para eso usamos ngrok (debemos tenerlo instalado y configurado) y lo hacemos con el siguiente comando

make ngrok

Con todo lo anterior debemos establecer las siguientes variables de entorno en el archivo .env

FAKE_API_SECRET_TO_GET_TOKEN=
FAKE_API_TOKEN=
CONFIG_API_BASE_URL=
CONFIG_API_KEY=
FAKE_API_DOMAIN=

Los valores para FAKE_API_SECRET_TO_GET_TOKEN y FAKE_API_TOKEN puede ser cualquier texto, pero las demas variables deben estar establecidas con los valores arrojados por el deploy y el levantamiento del fake api.

Ya con lo anterior listo podemos lanzar las pruebas con pytest usando el comando

pytest

Ejemplos

Zoho Deals

La configuración para el api de zoho deals es la siguiente:

{
    "name": "Zoho",
    "auth": {
        "refresh_token": {
            "endpoint": {
                "url": "https://accounts.zoho.com/oauth/v2/token",
                "query_params": {
                    "refresh_token": "${secret::zoho_refresh_token}",
                    "client_id": "${secret::zoho_client_id}",
                    "client_secret": "${secret::zoho_client_secret}",
                    "grant_type": "refresh_token"
                }
            },
            "response_token_key": "access_token"
        },
        "access_token": ""
    },
    "extractions": [
        {
            "name": "deals",
            "endpoint": {
                "url": "https://www.zohoapis.com/crm/v2/deals",
                "headers": {
                    "Authorization": "Bearer ${self::auth.access_token}",
                    "If-Modified-Since": "${last::Modified_Time, 2020-09-03T17:40:53-05:00}"
                },
                "query_params": {
                    "sort_by": "Modified_Time",
                    "sort_order": "asc"
                }
            },
            "s3_destiny": {
                "folder": "zoho/deals/"
            },
            "data_key": "data",
            "format": "csv",
            "transformations": [
                {
                    "action": "replace",
                    "on": [
                        "description"
                    ],
                    "params": {
                        "to_replace": ";",
                        "value": "."
                    }
                }
            ],
            "output_params": {
                "csv_separator": ";"
            },
            "data_schema": {
                "Owner": {
                    "name": "owner_name",
                    "id": "owner_id",
                    "email": "owner_email"
                },
                "Description": "description",
                "$currency_symbol": "currency_symbol",
                "$field_states": "field_states",
                "$review_process": {
                    "approve": "review_process_approve",
                    "reject": "review_process_reject",
                    "resubmit": "review_process_resubmit"
                },
                "Duraci_n_del_contrato": "duracion_del_contrato",
                "$followers": "followers",
                "Numero_de_cotizaci_n": "numero_de_cotizacion",
                "Closing_Date": "closing_date",
                "Causas_de_perdida": "causas_de_perdida",
                "Last_Activity_Time": "last_activity_time",
                "Opex": "opex",
                "Modified_By": {
                    "name": "modified_by_name",
                    "id": "modified_by_id",
                    "email": "modified_by_email"
                },
                "$review": "review",
                "Lead_Conversion_Time": "lead_conversion_time",
                "$state": "state",
                "$process_flow": "process_flow",
                "Deal_Name": "deal_name",
                "Expected_Revenue": "expected_revenue",
                "Overall_Sales_Duration": "overall_sales_duration",
                "Stage": "stage",
                "Account_Name": {
                    "name": "account_name_name",
                    "id": "account_name_id"
                },
                "id": "id",
                "Preventa_2": "preventa_2",
                "$approved": "approved",
                "$approval": {
                    "delegate": "approval_delegate",
                    "approve": "approval_approve",
                    "reject": "approval_reject",
                    "resubmit": "approval_resubmit"
                },
                "Modified_Time": "modified_time",
                "Created_Time": "created_time",
                "Amount": "amount",
                "$followed": "followed",
                "Probability": "probability",
                "$editable": "editable",
                "$orchestration": "orchestration",
                "Contact_Name": {
                    "name": "contact_name_name",
                    "id": "contact_name_id"
                },
                "Sales_Cycle_Duration": "sales_cycle_duration",
                "Type": "type",
                "$in_merge": "in_merge",
                "Capex": "capex",
                "Lead_Source": "lead_source",
                "Servicio": "servicio",
                "Created_By": {
                    "name": "created_by_name",
                    "id": "created_by_id",
                    "email": "created_by_email"
                },
                "Tag": "tag",
                "$approval_state": "approval_state",
                "$pathfinder": "pathfinder"
            },
            "pagination": {
                "type": "sequential",
                "parameters": {
                    "param_name": "page",
                    "start_from": 1,
                    "there_are_more_pages": "info.more_records"
                }
            }
        }
    ]
}

Zoho Tickets

La configuración para el api de zoho tikets es la siguiente:

{
    "name": "Zoho Tickets",
    "auth": {
        "refresh_token": {
            "endpoint": {
                "url": "https://accounts.zoho.com/oauth/v2/token",
                "query_params": {
                    "refresh_token": "${secret::zoho_tickets_refresh_token}",
                    "client_id": "${secret::zoho_tickets_client_id}",
                    "client_secret": "${secret::zoho_tickets_client_secret}",
                    "grant_type": "refresh_token"
                }
            },
            "response_token_key": "access_token"
        },
        "access_token": ""
    },
    "extractions": [
        {
            "name": "tickets",
            "endpoint": {
                "url": "https://desk.zoho.com/api/v1/tickets",
                "headers": {
                    "Authorization": "Zoho-oauthtoken ${self::auth.access_token}"
                },
                "query_params": {
                    "sortBy": "ticketNumber",
                    "include": "contacts,products,departments,team,isRead,assignee",
                    "limit": 100
                }
            },
            "s3_destiny": {
                "folder": "zoho/tickets/",
		"filename": "ticketsdata"
            },
            "data_key": "data",
            "transformations": [],
            "format": "csv",
            "output_params": {
                "csv_separator": ";"
            },
            "data_schema": {
                "id": "id",
                "ticketNumber": "ticket_number",
                "layoutId": "layout_id",
                "email": "email",
                "phone": "phone",
                "subject": "subject",
                "status": "status",
                "statusType": "status_type",
                "createdTime": "created_time",
                "category": "category",
                "language": "language",
                "subCategory": "sub_category",
                "priority": "priority",
                "channel": "channel",
                "dueDate": "due_date",
                "responseDueDate": "response_due_date",
                "commentCount": "comment_count",
                "sentiment": "sentiment",
                "threadCount": "thread_count",
                "closedTime": "closed_time",
                "onholdTime": "onhold_time",
                "departmentId": "department_id",
                "contactId": "contact_id",
                "productId": "product_id",
                "assigneeId": "assignee_id",
                "teamId": "team_id",
                "department": {
                    "id": "department__id",
                    "name": "department__name"
                },
                "contact": {
                    "firstName": "contact__first_name",
                    "lastName": "contact__last_name",
                    "email": "contact__email",
                    "mobile": "contact__mobile",
                    "phone": "contact__phone",
                    "type": "contact__type",
                    "account": {
                        "accountName": "contact__account__account_name",
                        "website": "contact__account__website",
                        "id": "contact__account__id"
                    },
                    "id": "contact__id"
                },
                "team": "team",
                "assignee": {
                    "id": "assignee__id",
                    "email": "assignee__email",
                    "photoURL": "assignee__photo_url",
                    "firstName": "assignee__first_name",
                    "lastName": "assignee__last_name"
                },
                "product": "product",
                "webUrl": "web_url",
                "channelCode": "channel_code",
                "isRead": "is_read",
                "isSpam": "is_spam",
                "source": {
                    "appName": "source__app_name",
                    "appPhotoURL": "source__app_photo_url",
                    "permalink": "source__permalink",
                    "type": "source__type",
                    "extId": "source__ext_id"
                },
                "lastThread": "last_thread",
                "customerResponseTime": "customer_response_time",
                "isArchived": "is_archived"
            },
            "pagination": {
                "type": "sequential",
                "parameters": {
                    "param_name": "from",
                    "start_from": 1,
                    "step": 100,
                    "continue_while_status_code_is": 200
                }
            }
        }
    ]
}

Una vez creada esta configuración se debe agregar al secret api-extractor-config/prod/extractor-secrets lo valores que estan en la parte de auth, así:

About

Aplicación serverless para consumir un api que sigue estandares REST y guardar la data en formato .csv o .json en un bucket de aws s3, según se configure

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages