Embedded Inspections
Controla todo el flujo de la inspección realizando una integración de punta a punta.
Intro
Esta guía te proporcionará los pasos a seguir para implementar Autoinspector de una manera en la que tendrás el control de la mayor parte del flujo de una inspección.
Obtener x-api-key
Para obtener la x-api-key se necesita que un rol apto. Comunícate con el administrador propietario de tu empresa en caso de que no lo tengas.
Toda interacción con Autoinspector API requiere de un código secreto de autenticación. Para encontrar el tuyo, puedes seguir los siguientes pasos:
1. Dirígite al dashboard de Autoinspector
2. Haz click en el ícono de tu perfil y luego haz click en Configuración
3. Dirígite a la sección de developers
4. Encuentra tu API token en esta página
Crear template
Para poder crear inspecciones necesitas hacer referencia a un template: manifiesto que define el flujo de una inspección. Para eso debes crear un 'template' personalizado. Como ejemplo utilizaremos una inspección de tipo car
y allí se definirán las especificaciones necesarias para el flujo de inspección. Luego este template se utilizará para crear inspecciones.
Para crear un template, debes seguir los siguientes pasos:
1. Dirígite al dashboard de Autoinspector
2. Haz click en el ícono de tu perfil y luego en la opción de Configuración
3. Dirígite a la sección de inspecciones
4. Dirígite al botón de administrar templates y haz click sobre él
4. Haz click sobre el botón de crear inspección y luego selecciona la opción "auto"
5. Una vez dentro del editor de templates, agrega las imágenes y campos que necesites
6. Finalmente copia el identificador del template que se encuentra en la esquina superior izquierda
Creando la aplicación / Servidor
La aplicación que se comunicará con Autoinspector tiene que ser de tipo backend, es decir, no puede correr dentro del browser de algún cliente debido a que estarías exponiendo credenciales sensibles como x-api-key
.
- NodeJS
- PHP
npm init -y
composer init -n --name php/my-app
Luego debes instalar algunas dependencias para poder crear el servicio:
- NodeJS
- PHP
npm i express autoinspector dotenv
composer require autoinspector/autoinspector-php slim/slim vlucas/phpdotenv
Por último, debes crear un archivo con el nombre .env
en donde guardarás las credenciales:
AUTOINSPECTOR_API_KEY=YOUR_X_API_KEY
Con las credenciales ya guardadas en un archivo, necesitarás cargarlas en la aplicación para poder utilizarlas:
- NodeJS
- PHP
const dotenv = require('dotenv')
dotenv.config({
path: '.env',
})
require("../vendor/autoload.php")
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
Ya estás en condiciones de empezar a usar Autoinspector SDK, instalada anteriormente como dependencia. Para eso, debes crear una instancia pasando las credenciales:
- NodeJS
- PHP
const Autoinspector = require('autoinspector')
const autoinspector = new Autoinspector({
apikey: process.env.AUTOINSPECTOR_API_KEY,
})
$autoinspector = new \Autoinspector\AutoinspectorClient($_ENV['AUTOINSPECTOR_API_KEY']);
Finalmente, debes preparar la aplicación para que al momento de levantarla se encuentre lista y empieces a recibir peticiones.
- NodeJS
- PHP
const express = require('express')
const app = express()
app.listen(3000, () => {
console.log('HTTP server listening on port 3000')
})
<?php
$app = new \Slim\App;
$app->run();
Creación de una inspección
El punto de partida de una inspección es su creación. Puedes crear diferentes tipos de inspección, y dependiendo de los mismos, determinados productos pueden ser asignados a dicha inspección. En esta guía crearemos y completaremos una inspección de tipo car
. Sin embargo, la implementación es exactamente igual con el resto de los tipos de inspección.
Para crear una inspección, debes agregar una ruta en tu servidor:
- NodeJS
- PHP
app.post('/inspection/car', (req, res) => {
autoinspector.inspections.car
.create({
locale: 'es_AR',
inputs: [
//Aquí rellena con los campos que agregaste en el template y que se necesitan para la creación
],
consumer: {
firstName: req.body.consumer.firstName,
lastName: req.body.consumer.lastName,
email: req.body.consumer.email,
identification: req.body.identification,
},
producer: {
internalId: '123',
},
car: {
plate: req.body.car.plate,
},
templateId: 'YOUR_CUSTOM_TEMPLATE_ID', //Aqui reemplaza YOUR_CUSTOM_TEMPLATE_ID por el id del template que creaste anteriormente
initialStatus: 'started',
delivery: {
disabled: true, //Deshabilito el delivery de inspección debido a que la propagación del link la quiero realizar de manera personalizada
},
})
.then(inspection => {
res.status(201).json(inspection)
})
.catch(err => {
console.error(err)
res.status(500).json({ message: 'Something went wrong' })
})
})
$app->post('/inspection/car', function ($request, $response, $args) {
$product = $args['product'];
$body = $app->request->getBody();
try {
$inspection = $autoinspector->inspections->car->create(
[
'inputs' => [
//Aquí rellena con los campos que agregaste en el template
],
'consumer' => [
'firstName' => $body['consumer']['firstName'],
'lastName' => $body['consumer']['lastName'],
'email' => $body['consumer']['email'],
'identification' => $body['consumer']['identification'],
],
'car' => [
'plate' => $body['car']['plate'],
'year' => $body['car']['year']
],
'templateId' => "YOUR_CUSTOM_TEMPLATE_ID" //Aqui reemplaza YOUR_CUSTOM_TEMPLATE_ID por el id del template que creaste anteriormente
'producer' => [
'internalId' => '123'
],
'initialStatus' => 'started'
]
);
$response->status->setStatus(201);
$response->body(json_encode($response));
} catch (\Throwable $th) {
$response->status->setStatus(500);
$response->body(json_encode(["message" => "something went wrong"]));
}
return $response;
})
Asegúrate siempre de agregar un middleware para sanitizar y validar que la información que recibes en una 'request' sea válida. Aquí no tendremos en cuenta esa parte por ser un ejemplo.
Procesamiento de imágenes
El proceso de validación de los productos que se encuentran en una inspección, es a través de imágenes.
Para requerir a Autoinspector la subida de cierta imagen, debes crear un token
. Este token es la representación de la solicitud de subida de una imagen, la cual tiene una expiración de 10 minutos y es de único uso. Puedes crear image tokens cuantas veces quieras, siempre y cuando se cumpla con las especificaciones del template.
Para ello, debes agregar una nueva ruta a tu servidor que se encargara de ello:
- NodeJS
- PHP
app.post("/image/:productId", (req, res) => {
const token = await autoinspector.images.generateToken({
side:req.body.side,
productId:req.params.productId
})
res.status(201).json({token})
});
$app->post('/image/:productId', function ($request, $response, $args) {
$body = $app->request->getBody();
$token = $autoinspector->images->generateToken(
[
"side" => $body["side"],
"productId" => $args["productId"]
]
);
$response->status->setStatus(201);
$response->body(
json_encode([
"token" => $token
])
);
return $response;
})
Crear webhook
Autoinspector notifica vía webhooks cuando ocurre algún evento en el flujo de una inspección. Para indicar a Autoinspector hacia donde tiene que enviar dichos eventos, debes crear un webhook con un endpoint accessible.
Para recibir dichos eventos, debes agregar una nueva ruta en tu servidor:
- NodeJS
- PHP
app.post('/webhook', (req, res) => {
switch (req.body.event) {
case 'image_processed':
// When a image was processed, you will receive this event
break
case 'inspection_completed':
// When a inspection was finished and all photos attached to it was processed, you will receive this event
break
default:
// another events like inspection_started, inspection_blocked
break
}
res.status(200).json({ message: 'webhook received.' })
})
$app->post('/webhook', function ($request, $response, $args) {
$body = $app->request->getBody();
switch ($body['event']) {
case "image_processed":
// When a image was processed, you will receive this event
break;
case "inspection_completed":
// When a inspection was finished and all photos attached to it was processed, you will receive this event
break;
default:
// another events like inspection_started, inspection_blocked
break;
}
$response->status->setStatus(200);
$response->body(
json_encode(
[
"message" => "webhook received"
]
)
);
return $response
})
Una vez que ya tienes la ruta que recibirá los eventos de Autoinspector, puedes crear un webhook dentro del Dashboard.
Para crear un webhook, debes seguir los siguientes pasos:
1. Dirígite al Dashboard de Autoinspector
2. Haz click en el ícono de tu perfil y luego haz click en Configuración
3. Dirígite a la sección de developers
4. Haz click en el botón de añadir un endpoint
5. Rellena el formulario con la información necesaria
6. Una vez completado el formulario, haz click sobre Crear Endpoint
El endpoint de tu servidor tiene que ser accessible desde internet. Es por ello que si deseas testear los eventos que Autoinspector envía en tu host, puedes usar una herramienta llamada ngrok para crear un túnel desde internet a tu host.
Cierre de inspección
El punto final en el ciclo de inspección es su finalización, a partir de la cual podrás obtener un veredicto: approved
o disapproved
.
Desde el momento en que una inspección se finaliza, se vuelve inmutable y el resultado se envía vía webhooks.
Para finalizar una inspección, debes crear una ruta en tu servidor que se encargue de eso:
- NodeJS
- PHP
app.post('/inspection/finish/:inspectionId', (req, res) => {
autoinspector.inspections
.finish({
inspectionId: req.params.inspectionId,
})
.then(res => {
res.status(200).json({ message: 'inspection completed successfully' })
})
.catch(err => {
res.status(500).json({ message: 'Something went wrong' })
})
})
$app->post('/inspection/finish/{inspectionId}', function ($request, $response, $args) {
$body = $app->request->getBody();
try {
$autoinspector->inspections->finish($args['inspectionId']);
$response->status->setStatus(200);
$response-> $response->body(
json_encode(
[
"message" => "inspection completed successfully"
]
)
);
} catch (\Throwable $th) {
$response->status->setStatus(500);
$response->body(json_encode(["message" => "something went wrong"]));
}
return $response
})
Correr la aplicación
Una vez que ya tienes el servidor conectado a Autoinspector, puedes levantarlo corriendo el siguiente comando:
- NodeJS
- PHP
Antes de levantar el servidor, necesitas agregar lo siguiente en el package.json
:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start":"node ./src/main.js"
}
Finalmente, ya puedes correr el siguiente comando:
npm run start
php -S localhost:3000
Creando la aplicacion / Cliente
Una vez que ya tienes una aplicación segura con la que puedes comunicarte con Autoinspector, es MOMENTO de crear una aplicaciÓn con una interfaz gráfica que te permita comunicarte con tu servidor.
Inicio del proyecto
Como primer paso, te ayudaremos a iniciar el proyecto:
- React
npx create-react-app my-app
cd my-app
Formulario para solicitar una inspección
Una vez que ya tenemos el proyecto, vamos a crear un formulario para la solicitud de inspección.
- React
import React, { useState } from 'react'
export const CreateInspectionForm = ({ onInspectionCreated }) => {
const [inspection, setInspection] = useState({})
const onSubmit = e => {
e.preventDefault()
fetch({
method: 'POST',
url: 'http://localhost:3000/inspection/car',
body: JSON.stringify(inspection),
})
.then(res => res.json())
.then(inspectionDetails => {
onInspectionCreated(inspectionDetails)
})
.catch(err => {
console.error(err)
})
}
return (
<div>
<h1>Create Inspection | Car</h1>
<form onSubmit={onSubmit}>
<label>Nombre</label>
<input
onChange={e =>
setInspection({
...inspection,
firstName: e.target.value,
})
}
name='firstName'
type='text'
/>
<label>Apellido</label>
<input
onChange={e =>
setInspection({
...inspection,
lastName: e.target.value,
})
}
name='lastName'
type='text'
/>
<label>Identificacion</label>
<input
onChange={e =>
setInspection({
...inspection,
identification: e.target.value,
})
}
name='identification'
type='text'
/>
<label>Email</label>
<input
onChange={e =>
setInspection({
...inspection,
email: e.target.value,
})
}
name='email'
type='email'
/>
<label>Patente</label>
<input
onChange={e =>
setInspection({
...inspection,
plate: e.target.value,
})
}
name='plate'
type='plate'
/>
<button type='submit'>Crear Inspeccion</button>
</form>
</div>
)
}
Formulario para subir imágenes
Luego vamos a crear otro componente para poder subir imágenes de un producto y empezar a procesarlas:
- React
export const DoInspectionForm = ({
productId,
inspectionId,
onInspectionCompleted,
}) => {
const UploadButton = ({ side }) => {
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const onUploadImage = (e) => {
setLoading(true);
fetch({
method: "POST",
url: "http://localhost:3000/image/" + productId,
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ side }),
})
.then((res) => res.json())
.then((token) => {
const formData = new FormData();
formData.append("image", e.target.files[0]);
fetch({
method: "POST",
body: formData,
url: "https://api.autoinspector.ai/v1/inspection/image/" + productId,
headers: {
"x-image-token": token,
},
})
.then((res) => res.json())
.then(() => setSuccess(true))
.finally(() => setLoading(false));
});
};
return (
<div>
<label>Imagen {side}</label>
{loading ? (
"Subiendo imagen..."
) : (
<input name={"image-" + side} onChange={onUploadImage} type="file" />
)}
{success && "Imagen subida con éxito!"}
</div>
);
};
const [loading, setLoading] = useState(false);
const onSubmit = (e) => {
setLoading(true);
e.preventDefault();
fetch({
method: "POST",
url: http://localhost:3000/inspection/finish/" + inspectionId,
})
.then((res) => res.json())
.then(() => onInspectionCompleted())
.catch((err) => {
console.error(err);
})
.finally(() => setLoading(false));
};
return (
<form onSubmit={onSubmit}>
<div>
<label>Imagen trasera del vehículo</label>
<UploadButton side="back" />
</div>
<div>
<label>Imagen delantera del vehículo</label>
<UploadButton side="front" />
</div>
{loading ? (
"Finalizando inspección..."
) : (
<button type="submit">Finalizar inspección</button>
)}
</form>
);
};
Correr aplicación
Finalmente vamos a actualizar nuestra página principal para agregar los nuevos componentes que ya creamos:
- React
import './index.css'
import { useState } from 'react'
import CreateInspectionForm from './CreateInspectionForm'
import DoInspectionForm from './DoInspectionForm'
export default function App() {
const [inspection, setInspection] = useState()
const [wasCompleted, setWasCompleted] = useState(false)
return (
<div>
{!inspection && (
<CreateInspectionForm
onInspectionCreated={inspection => setInspection(inspection)}
/>
)}
{inspection && !wasCompleted && (
<DoInspectionForm
productId={inspection.productId}
inspectionId={inspection.inspectionId}
onInspectionCompleted={() => setWasCompleted(true)}
/>
)}
{wasCompleted && inspection && (
<div>
<p>Inspección {inspection.inspectionId} completada correctamente!</p>
<button
onClick={() => {
setWasCompleted(false)
setInspection(undefined)
}}
>
Crear otra inspección
</button>
</div>
)}
</div>
)
}
Una vez que la página principal fue actualizada, puedes correr el siguiente comando:
- React
npm run start
Si abres el navegador y te diriges a localhost:3000, vas a poder observar la app terminada.