Solicitud-Respuesta Encadenada con OAuth
¿Cómo puedo utilizar la salida de una llamada sincrónica como la entrada de otra llamada sincrónica?
Este ejemplo de conector demuestra las siguientes características de los conectores.
-
Message log ging
-
Cadena de llamadas de conectores de solicitud-respuesta (salida de una llamada como entrada para la siguiente)
-
Solicite decoración para seguridad
-
Carga de medios
@startuml
actor User as user
participant RequestReplySendConnector as rrsc
participant "Twitter API" as t
user -> rrsc: MediaUpload("/path/to/my/img.jpg")
rrsc -> t: POST media/upload.json (with img binary)
t -> rrsc: 200 OK, media_id=111
rrsc -> user: MediaUploadResponse(mediaId=111)
user -> rrsc: StatusUpdate("update", media_id=111)
rrsc -> t: POST statuses/update.json, {"status":".."}
t -> rrsc: 200 OK, {"status":"..",..}
rrsc -> user: StatusUpdateResponse
@enduml
Esta guía también está disponible en el separado connector-samples Repositorio de Git
aquí.
|
Configuración
Este ejemplo fue escrito originalmente cuando era mucho más sencillo comenzar a desarrollar aplicaciones para Twitter. API. Desafortunadamente, hay algunos obstáculos que debemos superar para configurar las cosas antes de ejecutar el ejemplo, ¡pero esperamos que valga la pena!
Cuenta de Twitter
Para ejecutar este ejemplo, usted necesitará una cuenta de Twitter. Si ya tiene uno, entonces salte al siguiente paso.
Puede registrarse en Twitter utilizando este link.
Cuenta de Desarrollador de Twitter
También necesita una cuenta de desarrollador, ya que estaremos creando una aplicación de Twitter para llamar. APIs en nombre de su usuario.
Debería poder registrarse para una cuenta de desarrollador yendo a la developer portal.
Aplicación de Twitter
Una vez que tenga una cuenta de desarrollador, debe poder crear una nueva aplicación desde esto.https://developer.twitter.com/en/apps[página].
Acceso Elevado
Para utilizar la función de carga, nuestra aplicación debe tener privilegios elevados. API acceso. Para obtener esto, debemos solicitarlo, aunque es solo un breve cuestionario para asegurarnos de que no tenemos ningún plan nefasto con todos sus datos.
Se puede solicitar acceso elevado en este page.
API Claves
En este momento, nuestra aplicación de Twitter debería estar lista para funcionar. Todo lo que necesitamos hacer es generar algunas credenciales que pueden ser utilizadas para autenticar solicitudes a Twitter. APIs en nombre de nosotros. La página de la aplicación debe tener una pestaña para "Claves y tokens" donde se pueden generar todas las credenciales.
Las primeras de las credenciales son las Consumer Keys.
Puede que los haya guardado cuando creó la aplicación, pero si no, puede regenerarlos.
El segundo conjunto de credenciales es un Token de Acceso y un Secreto del Token de Acceso, los cuales deben ser generados.
Una vez que se hayan generado todas las credenciales, se deben establecer las siguientes variables de entorno utilizando ellas.
.setOAuthConsumerKey(System.getenv("TWITTER_CONSUMER_KEY")) // API KEY
.setOAuthConsumerSecret(System.getenv("TWITTER_CONSUMER_SECRET")) // API KEY SECRET
.setOAuthAccessToken(System.getenv("TWITTER_ACCESS_TOKEN"))
.setOAuthAccessTokenSecret(System.getenv("TWITTER_ACCESS_TOKEN_SECRET"))
Tipos de Dominio
Nuestro conector de Twitter admite dos operaciones, y como resultado tenemos dos tipos de dominio que ambos extienden el `TwitterRequest`tipo abstracto. También podríamos crear dos solicitudes separadas para cada uno de estos casos si así lo deseamos.
-
UploadMediaRequest: Cargue algunos medios para compartir en un tuit más tarde -
StatusUpdateRequest: Actualice un estado de Twitter
Y también tenemos sus respectivas respuestas, bajo el TwitterResponse tipo abstracto.
-
UploadMediaResponse: Respuesta a la carga de medios (que contiene el ID de los medios) -
StatusUpdateResponse: Respuesta al tweet (que contiene el tweet recién creado)
Configuración del conector
Primero creamos un HTTP solicitud-respuesta connector transport como así.
return new HttpConnectorTransport.Builder<TwitterRequest>()
.withName("TwitterHttp")
.withActorSystem(actorSystem)
.withConfigRootPath("chained-request-reply-example")
.withEnricher(httpRequest -> { (1)
HttpHeader authorization = HttpHeader.parse("Authorization", getAuthorizationHeader(httpRequest));
HttpRequest request = httpRequest.addHeader(authorization);
return CompletableFuture.completedFuture(request);
})
.build();
| 1 | Tenga en cuenta el enriquecedor aquí, que añade el Authorization encabezado que contiene el importante contexto de usuario de OAuth 1. 0 que Twitter autentificará.
Los insumos para este método son los HTTP método (siempre POST) y la URL (varía dependiendo de si se trata de una carga de medios o una actualización de estado) |
A continuación, creamos el siguiente RequestReplySendConnector.
var connector = new RequestReplySendConnector
.Builder<TwitterRequest, TwitterRequest, TwitterResponse, TwitterResponse>("Twitter")
.withConnectorTransport(connectorTransport)
.withActorSystem(actorSystem)
.withMessageLogger(logger()) (1)
.withSendTransportMessageConverter(request -> { (2)
var messageHeaders = new MessageHeaders(Map.of(
"httpUrl", getUrl(request),
"httpMethod", "POST"
));
if (request instanceof UploadMediaRequest) {
byte[] bytes = ((UploadMediaRequest) request).getData();
var entity = HttpEntities.create(ContentTypes.APPLICATION_OCTET_STREAM, bytes);
var payload = createStrictFormDataFromParts(createFormDataBodyPartStrict("media", entity)).toEntity();
return new TransportMessage(messageHeaders, payload);
}
return new TransportMessage(messageHeaders, "");
})
.withReceiveTransportMessageConverter(transportMessage -> { (3)
if (transportMessage.getPayload().toString().contains("media_id_string")) {
return fromJson(UploadMediaResponse.class).convert(transportMessage);
} else {
return fromJson(StatusUpdateResponse.class).convert(transportMessage);
}
})
.withManualStart(false)
.build();
| 1 | Aquí estamos definiendo un MessageLogger interfaz. Tomamos tanto los mensajes enviados como los recibidos y los registramos. Esto puede ser reemplazado por una implementación de base de datos donde se almacenan todas las interacciones de mensajes relacionadas con esta asociación de mensajes. |
| 2 | Aquí estamos definiendo una función que toma el TwitterRequest que queremos enviar, y creando un
`TransportMessage`fuera de ello, que es la representación de la solicitud a través de la red.
En este caso, necesitamos definir una URL diferente según si se trata de una actualización de estado o de una carga de medios. |
| 3 | Dado que este es un conector de solicitud-respuesta, también necesitamos definir la operación inversa, que convierte la respuesta recibida de nuevo en un modelo POJO que entendemos. Utilizamos Jackson para determinar si esta es una respuesta de actualización de medios o una respuesta de actualización de estado, y asignarla en consecuencia al POJO correcto. |
Cadena de Llamadas
El Twitter documentación para publicar medios (imágenes, GIFs, videos, etc.) establece que debemos cargar el medio primero utilizando uno de los métodos de carga de medios (simple o en fragmentos), lo que devolverá un media_id que expira en 24 horas a menos que se utilice en un tweet antes de entonces.
Luego utilizamos eso media_id en nuestra próxima llamada de actualización de estado, que adjuntará los medios previamente cargados al tweet.
connector
.send(ProcessingContext.unknown(), new UploadMediaRequest(kittenPicBytes())) (1)
.thenApply(twitterResponse -> (UploadMediaResponse) twitterResponse) (2)
.thenCompose(uploadMediaResponse -> {
String status = String.format("I am a status update at %s! Also check out this cat photo", LocalDateTime.now());
var statusUpdateRequest = new StatusUpdateRequest(status, List.of(uploadMediaResponse.getMediaId()));
return connector.send(ProcessingContext.unknown(), statusUpdateRequest); (3)
})
.toCompletableFuture()
.join();
| 1 | Llamando al conector por primera vez para cargar la foto del gatito |
| 2 | Lanzando la respuesta en un UploadMediaResponse |
| 3 | Llamando al conector por segunda vez, con el mediaId from UploadMediaResponse para publicar un tuit que contenga el medio como un archivo adjunto |
Ejercicios
Ejercicio 1: Recupere una Imagen Remota
En este momento, la muestra se está cargando.kitten.jpg from src/test/resources.
Intente en su lugar agregar una llamada a la cadena para-por ejemplo-la NASA Imagen Astronómica del Día (APOD) API para recuperar dinámicamente una imagen en su lugar.
El APOD API devuelve una estructura como esta.
{
"copyright": "Giancarlo Tinè",
"date": "2021-11-15",
"explanation": "What happening above that volcano? Something very unusual -- a volcanic light pillar. More typically, light pillars are caused by sunlight and so appear as a bright column that extends upward above a rising or setting Sun. Alternatively, other light pillars -- some quite colorful -- have been recorded above street and house lights. This light pillar, though, was illuminated by the red light emitted by the glowing magma of an erupting volcano. The volcano is Italy's Mount Etna, and the featured image was captured with a single shot a few hours after sunset in mid-June. Freezing temperatures above the volcano's ash cloud created ice-crystals either in cirrus clouds high above the volcano -- or in condensed water vapor expelled by Mount Etna. These ice crystals -- mostly flat toward the ground but fluttering -- then reflected away light from the volcano's caldera. Explore Your Universe: Random APOD Generator",
"hdurl": "https://apod.nasa.gov/apod/image/2111/EtnaLightPillar_Tine_5100.jpg",
"media_type": "image",
"service_version": "v1",
"title": "Light Pillar over Volcanic Etna",
"url": "https://apod.nasa.gov/apod/image/2111/EtnaLightPillar_Tine_960.jpg"
}
Intente extender la cadena actual añadiendo dos llamadas más al principio.
-
Un nuevo
RequestReplySendConnectorllamada para recuperar el enlace a la imagen del día -
A new
RequestReplySendConnectorllame para solicitar la imagen ubicada enurldesde la respuesta (deberá convertirlo en un arreglo de bytes)
A continuación, deberá cambiar el contenido de MediaUpload para hacer referencia al arreglo de bytes de la imagen APOD en lugar de la imagen local del gatito.
Ejercicio 2: Uso de la carga de medios en fragmentos API
La muestra actualmente utiliza el "simple" API para cargar imágenes en Twitter, lo cual es un método frágil para cargar medios grandes, ya que no es resistente a cortes de red y no se puede reanudar. Twitter ofrece un método más resistente para cargar medios llamado carga de medios en fragmentos. API que admite pausar y reanudar las cargas.
La documentación para eso se puede encontrar aquí.
Intente cambiar la implementación existente para utilizar la carga de medios en fragmentos. API, y quizás subir algunos medios más grandes, como un video o GIF. Los pasos serían.
-
Llamar a
INITpara declarar el inicio de una carga de medios -
Múltiples llamadas a
APPENDfragmentos de los medios como partes, con un índice incremental -
Llamar a
FINALIZEla carga de medios y recibir el todo importantemedia_id -
Llamado para actualizar nuestro estado utilizando el
media_iddesde el paso 3