All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.azure.messaging.eventgrid.EventGridPublisherAsyncClient Maven / Gradle / Ivy

There is a newer version: 4.27.0
Show newest version
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.messaging.eventgrid;

import com.azure.core.annotation.ReturnType;
import com.azure.core.annotation.ServiceClient;
import com.azure.core.annotation.ServiceMethod;
import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.credential.AzureSasCredential;
import com.azure.core.http.HttpHeaders;
import com.azure.core.http.HttpPipeline;
import com.azure.core.http.policy.AddHeadersFromContextPolicy;
import com.azure.core.http.rest.Response;
import com.azure.core.models.CloudEvent;
import com.azure.core.util.BinaryData;
import com.azure.core.util.Context;
import com.azure.core.util.CoreUtils;
import com.azure.core.util.logging.ClientLogger;
import com.azure.core.util.tracing.Tracer;
import com.azure.messaging.eventgrid.implementation.Constants;
import com.azure.messaging.eventgrid.implementation.EventGridPublisherClientImpl;
import com.azure.messaging.eventgrid.implementation.EventGridPublisherClientImplBuilder;
import com.fasterxml.jackson.databind.util.RawValue;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.azure.core.util.FluxUtil.monoError;
import static com.azure.core.util.FluxUtil.withContext;

/**
 * A service client that publishes events to an EventGrid topic or domain asynchronously.
 * Use {@link EventGridPublisherClientBuilder} to create an instance of this client.
 *
 * 

Create EventGridPublisherAsyncClient for CloudEvent Samples

* *
 * // Create a client to send events of CloudEvent schema (com.azure.core.models.CloudEvent)
 * EventGridPublisherAsyncClient<CloudEvent> cloudEventPublisherClient = new EventGridPublisherClientBuilder()
 *     .endpoint(System.getenv("AZURE_EVENTGRID_CLOUDEVENT_ENDPOINT"))  // make sure it accepts CloudEvent
 *     .credential(new AzureKeyCredential(System.getenv("AZURE_EVENTGRID_CLOUDEVENT_KEY")))
 *     .buildCloudEventPublisherAsyncClient();
 * 
* * *

Send CloudEvent Samples

* *
 * // Create a com.azure.models.CloudEvent.
 * User user = new User("Stephen", "James");
 * CloudEvent cloudEventDataObject = new CloudEvent("/cloudevents/example/source", "Example.EventType",
 *     BinaryData.fromObject(user), CloudEventDataFormat.JSON, "application/json");
 *
 * // Send a single CloudEvent
 * cloudEventPublisherClient.sendEvent(cloudEventDataObject).block();
 *
 * // Send a list of CloudEvents to the EventGrid service altogether.
 * // This has better performance than sending one by one.
 * cloudEventPublisherClient.sendEvents(Arrays.asList(
 *     cloudEventDataObject
 *     // add more CloudEvents objects
 * )).block();
 * 
* * *

Create EventGridPublisherAsyncClient for EventGridEvent Samples

* *
 * // Create a client to send events of EventGridEvent schema
 * EventGridPublisherAsyncClient<EventGridEvent> eventGridEventPublisherClient = new EventGridPublisherClientBuilder()
 *     .endpoint(System.getenv("AZURE_EVENTGRID_EVENT_ENDPOINT"))  // make sure it accepts EventGridEvent
 *     .credential(new AzureKeyCredential(System.getenv("AZURE_EVENTGRID_EVENT_KEY")))
 *     .buildEventGridEventPublisherAsyncClient();
 * 
* * *

Send EventGridEvent Samples

* *
 * // Create an EventGridEvent
 * User user = new User("John", "James");
 * EventGridEvent eventGridEvent = new EventGridEvent("/EventGridEvents/example/source",
 *     "Example.EventType", BinaryData.fromObject(user), "0.1");
 *
 * // Send a single EventGridEvent
 * eventGridEventPublisherClient.sendEvent(eventGridEvent).block();
 *
 * // Send a list of EventGridEvents to the EventGrid service altogether.
 * // This has better performance than sending one by one.
 * eventGridEventPublisherClient.sendEvents(Arrays.asList(
 *     eventGridEvent
 *     // add more EventGridEvents objects
 * )).block();
 * 
* * *

Create EventGridPublisherAsyncClient for Custom Event Schema Samples

* *
 * // Create a client to send events of custom event
 * EventGridPublisherAsyncClient<BinaryData> customEventPublisherClient = new EventGridPublisherClientBuilder()
 *     .endpoint(System.getenv("AZURE_CUSTOM_EVENT_ENDPOINT"))  // make sure it accepts custom events
 *     .credential(new AzureKeyCredential(System.getenv("AZURE_CUSTOM_EVENT_KEY")))
 *     .buildCustomEventPublisherAsyncClient();
 * 
* * *

Send Custom Event Schema Samples

* *
 * // Create an custom event object (both POJO and Map work)
 * Map<String, Object> customEvent = new HashMap<String, Object>() {
 *     {
 *         put("id", UUID.randomUUID().toString());
 *         put("subject", "Test");
 *         put("foo", "bar");
 *         put("type", "Microsoft.MockPublisher.TestEvent");
 *         put("data", 100.0);
 *         put("dataVersion", "0.1");
 *     }
 * };
 *
 * // Send a single custom event
 * customEventPublisherClient.sendEvent(BinaryData.fromObject(customEvent)).block();
 *
 * // Send a list of EventGridEvents to the EventGrid service altogether.
 * // This has better performance than sending one by one.
 * customEventPublisherClient.sendEvents(Arrays.asList(
 *     BinaryData.fromObject(customEvent)
 *     // add more custom events in BinaryData
 * )).block();
 * 
* * * @see EventGridEvent * @see com.azure.core.models.CloudEvent */ @ServiceClient(builder = EventGridPublisherClientBuilder.class, isAsync = true) public final class EventGridPublisherAsyncClient { private static final String PARTNER_CHANNEL_HEADER_NAME = "aeg-channel-name"; private final String hostname; private final EventGridPublisherClientImpl impl; private final ClientLogger logger = new ClientLogger(EventGridPublisherAsyncClient.class); private final Class eventClass; private final Tracer tracer; private static final DateTimeFormatter SAS_DATE_TIME_FORMATER = DateTimeFormatter.ofPattern("M/d/yyyy h:m:s a"); private static final String HMAC_SHA256 = "hmacSHA256"; private static final String API_VERSION = "api-version"; private static final ClientLogger LOGGER = new ClientLogger(EventGridPublisherAsyncClient.class); EventGridPublisherAsyncClient(HttpPipeline pipeline, String hostname, EventGridServiceVersion serviceVersion, Class eventClass) { this.impl = new EventGridPublisherClientImplBuilder() .pipeline(pipeline) .apiVersion(serviceVersion.getVersion()) .buildClient(); this.hostname = hostname; this.eventClass = eventClass; this.tracer = pipeline.getTracer(); } /** * Generate a shared access signature to provide time-limited authentication for requests to the Event Grid * service with the latest Event Grid service API defined in {@link EventGridServiceVersion#getLatest()}. * @param endpoint the endpoint of the Event Grid topic or domain. * @param expirationTime the time in which the signature should expire, no longer providing authentication. * @param keyCredential the access key obtained from the Event Grid topic or domain. * * @return the shared access signature string which can be used to construct an instance of * {@link AzureSasCredential}. * * @throws NullPointerException if endpoint, keyCredential or expirationTime is {@code null}. * @throws RuntimeException if java security doesn't have algorithm "hmacSHA256". */ public static String generateSas(String endpoint, AzureKeyCredential keyCredential, OffsetDateTime expirationTime) { return generateSas(endpoint, keyCredential, expirationTime, EventGridServiceVersion.getLatest()); } /** * Generate a shared access signature to provide time-limited authentication for requests to the Event Grid * service. * @param endpoint the endpoint of the Event Grid topic or domain. * @param expirationTime the time in which the signature should expire, no longer providing authentication. * @param keyCredential the access key obtained from the Event Grid topic or domain. * @param apiVersion the EventGrid service api version defined in {@link EventGridServiceVersion} * * @return the shared access signature string which can be used to construct an instance of * {@link AzureSasCredential}. * * @throws NullPointerException if endpoint, keyCredential or expirationTime is {@code null}. * @throws RuntimeException if java security doesn't have algorithm "hmacSHA256". */ public static String generateSas(String endpoint, AzureKeyCredential keyCredential, OffsetDateTime expirationTime, EventGridServiceVersion apiVersion) { if (Objects.isNull(endpoint)) { throw LOGGER.logExceptionAsError(new NullPointerException("'endpoint' cannot be null.")); } if (Objects.isNull(keyCredential)) { throw LOGGER.logExceptionAsError(new NullPointerException("'keyCredetial' cannot be null.")); } if (Objects.isNull(expirationTime)) { throw LOGGER.logExceptionAsError(new NullPointerException("'expirationTime' cannot be null.")); } try { String resKey = "r"; String expKey = "e"; String signKey = "s"; Charset charset = StandardCharsets.UTF_8; endpoint = String.format("%s?%s=%s", endpoint, API_VERSION, apiVersion.getVersion()); String encodedResource = URLEncoder.encode(endpoint, charset.name()); String encodedExpiration = URLEncoder.encode(expirationTime.atZoneSameInstant(ZoneOffset.UTC).format( SAS_DATE_TIME_FORMATER), charset.name()); String unsignedSas = String.format("%s=%s&%s=%s", resKey, encodedResource, expKey, encodedExpiration); Mac hmac = Mac.getInstance(HMAC_SHA256); hmac.init(new SecretKeySpec(Base64.getDecoder().decode(keyCredential.getKey()), HMAC_SHA256)); String signature = new String(Base64.getEncoder().encode( hmac.doFinal(unsignedSas.getBytes(charset))), charset); String encodedSignature = URLEncoder.encode(signature, charset.name()); return String.format("%s&%s=%s", unsignedSas, signKey, encodedSignature); } catch (NoSuchAlgorithmException | UnsupportedEncodingException | InvalidKeyException e) { throw LOGGER.logExceptionAsError(new RuntimeException(e)); } } /** * Publishes the given events to the set topic or domain. * @param events the events to publish. * * @return A {@link Mono} that completes when the events are sent to the service. * @throws NullPointerException if events is {@code null}. */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono sendEvents(Iterable events) { return withContext(context -> sendEvents(events, context)); } @SuppressWarnings("unchecked") Mono sendEvents(Iterable events, Context context) { if (this.eventClass == CloudEvent.class) { return this.sendCloudEvents((Iterable) events, context); } else if (this.eventClass == EventGridEvent.class) { return this.sendEventGridEvents((Iterable) events, context); } else { return this.sendCustomEvents((Iterable) events, context); } } /** * Publishes the given events to the set topic or domain and gives the response issued by EventGrid. * @param events the events to publish. * * @return the response from the EventGrid service. * @throws NullPointerException if events is {@code null}. */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono> sendEventsWithResponse(Iterable events) { return withContext(context -> this.sendEventsWithResponse(events, context)); } /** * Publishes the given events to the set topic or domain and gives the response issued by EventGrid. * @param events the events to publish. * @param channelName the channel name to send to Event Grid service. This is only applicable for sending * Cloud Events to a partner topic in partner namespace. For more details, refer to * Partner Events Overview. * @return the response from the EventGrid service. * @throws NullPointerException if events is {@code null}. */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono> sendEventsWithResponse(Iterable events, String channelName) { return withContext(context -> this.sendEventsWithResponse(events, channelName, context)); } @SuppressWarnings("unchecked") Mono> sendEventsWithResponse(Iterable events, Context context) { return sendEventsWithResponse(events, null, context); } @SuppressWarnings("unchecked") Mono> sendEventsWithResponse(Iterable events, String channelName, Context context) { if (context == null) { context = Context.NONE; } if (!CoreUtils.isNullOrEmpty(channelName)) { String requestHttpHeadersKey = AddHeadersFromContextPolicy.AZURE_REQUEST_HTTP_HEADERS_KEY; Map keyValues = context.getValues(); if (keyValues != null && keyValues.containsKey(requestHttpHeadersKey)) { // if the given Context instance already contains custom headers, // add partner channel header to HttpHeaders Object value = keyValues.get(requestHttpHeadersKey); if (value instanceof HttpHeaders) { HttpHeaders headers = (HttpHeaders) value; headers.add(PARTNER_CHANNEL_HEADER_NAME, channelName); } } else { context = context.addData(requestHttpHeadersKey, new HttpHeaders().add(PARTNER_CHANNEL_HEADER_NAME, channelName)); } } if (this.eventClass == CloudEvent.class) { return this.sendCloudEventsWithResponse((Iterable) events, context); } else if (this.eventClass == EventGridEvent.class) { return this.sendEventGridEventsWithResponse((Iterable) events, context); } else { return this.sendCustomEventsWithResponse((Iterable) events, context); } } /** * Publishes the given events to the set topic or domain. * @param event the event to publish. * * @return A {@link Mono} that completes when the event is sent to the service. * @throws NullPointerException if events is {@code null}. */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono sendEvent(T event) { List events = Collections.singletonList(event); return withContext(context -> sendEvents(events, context)); } Mono sendEventGridEvents(Iterable events, Context context) { if (events == null) { return monoError(logger, new NullPointerException("'events' cannot be null.")); } final Context finalContext = context != null ? context : Context.NONE; return Flux.fromIterable(events) .map(EventGridEvent::toImpl) .collectList() .flatMap(list -> this.impl.publishEventGridEventsAsync(this.hostname, list, finalContext)); } Mono sendCloudEvents(Iterable events, Context context) { if (events == null) { return monoError(logger, new NullPointerException("'events' cannot be null.")); } final Context finalContext = context != null ? context : Context.NONE; this.addCloudEventTracePlaceHolder(events); return Flux.fromIterable(events) .collectList() .flatMap(list -> this.impl.publishCloudEventEventsAsync(this.hostname, list, null, finalContext)); } Mono sendCustomEvents(Iterable events, Context context) { if (events == null) { return monoError(logger, new NullPointerException("'events' cannot be null.")); } final Context finalContext = context != null ? context : Context.NONE; return Flux.fromIterable(events) .map(event -> (Object) new RawValue(event.toString())) .collectList() .flatMap(list -> this.impl.publishCustomEventEventsAsync(this.hostname, list, finalContext)); } Mono> sendEventGridEventsWithResponse(Iterable events, Context context) { if (events == null) { return monoError(logger, new NullPointerException("'events' cannot be null.")); } final Context finalContext = context != null ? context : Context.NONE; return Flux.fromIterable(events) .map(EventGridEvent::toImpl) .collectList() .flatMap(list -> this.impl.publishEventGridEventsWithResponseAsync(this.hostname, list, finalContext)); } Mono> sendCloudEventsWithResponse(Iterable events, Context context) { if (events == null) { return monoError(logger, new NullPointerException("'events' cannot be null.")); } final Context finalContext = context != null ? context : Context.NONE; this.addCloudEventTracePlaceHolder(events); return Flux.fromIterable(events) .collectList() .flatMap(list -> this.impl.publishCloudEventEventsWithResponseAsync(this.hostname, list, null, finalContext)); } Mono> sendCustomEventsWithResponse(Iterable events, Context context) { if (events == null) { return monoError(logger, new NullPointerException("'events' cannot be null.")); } final Context finalContext = context != null ? context : Context.NONE; return Flux.fromIterable(events) .map(event -> (Object) new RawValue(event.toString())) .collectList() .flatMap(list -> this.impl.publishCustomEventEventsWithResponseAsync(this.hostname, list, finalContext)); } private void addCloudEventTracePlaceHolder(Iterable events) { if (tracer.isEnabled()) { for (CloudEvent event : events) { if (event.getExtensionAttributes() == null || (event.getExtensionAttributes().get(Constants.TRACE_PARENT) == null && event.getExtensionAttributes().get(Constants.TRACE_STATE) == null)) { event.addExtensionAttribute(Constants.TRACE_PARENT, Constants.TRACE_PARENT_PLACEHOLDER_UUID); event.addExtensionAttribute(Constants.TRACE_STATE, Constants.TRACE_STATE_PLACEHOLDER_UUID); } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy