Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ACS WhatsApp interactive message #43782

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions sdk/communication/azure-communication-messages/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
# Release History

## 1.2.0-beta.1 (Unreleased)
## 1.2.0-beta.1 (2025-01-15)

### Features Added

### Breaking Changes

### Bugs Fixed

### Other Changes
- Added Interactive Message.
- Added Reaction Message.
- Added Sticker Message.

## 1.1.0 (2024-10-23)

### Features Added
- Added ImageNotificationContent to send image messgae.
- Added ImageNotificationContent to send image message.
- Added DocumentNotificationContent to send document message.
- Added VideoNotificationContent to send video message.
- Added AudioNotificationContent to send audio message.
Expand Down
2 changes: 1 addition & 1 deletion sdk/communication/azure-communication-messages/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo" : "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath" : "java",
"TagPrefix" : "java/communication/azure-communication-messages",
"Tag" : "java/communication/azure-communication-messages_d7c7441f3b"
"Tag" : "java/communication/azure-communication-messages_b6d2e1239f"
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.javadoc.Javadoc;
import com.github.javaparser.javadoc.description.JavadocDescription;
import com.github.javaparser.javadoc.description.JavadocSnippet;
import org.slf4j.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class MessagesSdkCustomization extends Customization {

@Override
Expand All @@ -32,13 +38,24 @@ public void customize(LibraryCustomization libraryCustomization, Logger logger)
customizeMessageTemplateLocation(modelsPackage);
customizeMessageTemplateItemModel(modelsPackage);

//Handle Interactive message content models
updateModelClassModifierToAbstract(modelsPackage, "MessageContent");
updateModelClassModifierToAbstract(modelsPackage, "ActionBindings");
updateJavaDocForMethodFromJson(modelsPackage, "ActionBindings");
updateJavaDocForMethodFromJson(modelsPackage, "MessageContent");
customizeInteractiveMessage(modelsPackage);

PackageCustomization channelsModelsPackage = libraryCustomization.getPackage(
"com.azure.communication.messages.models.channels");
updateWhatsAppMessageTemplateItemWithBinaryDataContent(channelsModelsPackage);

AddDeprecateAnnotationToMediaNotificationContent(modelsPackage);
addDeprecateAnnotationToMediaNotificationContent(modelsPackage);

addDeprecateAnnotationForImageV0CommunicationKind(modelsPackage);

AddDeprecateAnnotationForImageV0CommunicationKind(modelsPackage);
customizeActionGroup(modelsPackage);
customizeActionGroupContent(modelsPackage);
customizeButtonSetContent(modelsPackage);
}

private void updateModelClassModifierToAbstract(PackageCustomization modelsPackage, String className) {
Expand Down Expand Up @@ -174,6 +191,45 @@ private void customizeMessageTemplateLocation(PackageCustomization modelsPackage
});
}

private void customizeInteractiveMessage(PackageCustomization modelsPackage) {
modelsPackage.getClass("InteractiveMessage").customizeAst(ast -> {
ast.getClassByName("InteractiveMessage").ifPresent(clazz -> {
clazz.addMethod("getHeader", Modifier.Keyword.PUBLIC)
.setType(clazz.getMethodsByName("getHeaderProperty").get(0).getType())
.setBody(clazz.getMethodsByName("getHeaderProperty").get(0).getBody().get())
.setJavadocComment(clazz.getMethodsByName("getHeaderProperty").get(0).getJavadocComment().get());

clazz.getMethodsByName("getHeaderProperty").forEach(Node::remove);

MethodDeclaration setHeaderMethodDeclaration = clazz.getMethodsByName("setHeaderProperty").get(0);
List<Parameter> parameters = setHeaderMethodDeclaration
.getParameters()
.stream()
.map(p -> p.setName("header"))
.collect(Collectors.toList());
String methodBodyContent = setHeaderMethodDeclaration
.getBody()
.get()
.toString()
.replace("headerProperty;", "header;");

String docComment = setHeaderMethodDeclaration
.getJavadocComment()
.get()
.getContent()
.replace("headerProperty", "header");

clazz.addMethod("setHeader", Modifier.Keyword.PUBLIC)
.setParameters(new NodeList<Parameter>(parameters))
.setType(setHeaderMethodDeclaration.getType())
.setBody(StaticJavaParser.parseBlock(methodBodyContent))
.setJavadocComment(new Javadoc(JavadocDescription.parseText(docComment)));

clazz.getMethodsByName("setHeaderProperty").forEach(Node::remove);
});
});
}

private void updateWhatsAppMessageTemplateItemWithBinaryDataContent(PackageCustomization channelsModelsPackage) {
channelsModelsPackage.getClass("WhatsAppMessageTemplateItem").customizeAst(ast -> {
// ast.addImport("com.azure.core.util.BinaryData");
Expand Down Expand Up @@ -206,7 +262,7 @@ private void removeJsonKnownDiscriminatorMethod(PackageCustomization modelPackag
});
}

private void AddDeprecateAnnotationToMediaNotificationContent(PackageCustomization modelsPackage) {
private void addDeprecateAnnotationToMediaNotificationContent(PackageCustomization modelsPackage) {
modelsPackage.getClass("MediaNotificationContent").customizeAst(ast -> {
ast.getClassByName("MediaNotificationContent").ifPresent(clazz -> {
clazz.addAnnotation(Deprecated.class);
Expand All @@ -222,7 +278,7 @@ private void AddDeprecateAnnotationToMediaNotificationContent(PackageCustomizati
});
}

private void AddDeprecateAnnotationForImageV0CommunicationKind(PackageCustomization modelsPackage) {
private void addDeprecateAnnotationForImageV0CommunicationKind(PackageCustomization modelsPackage) {
modelsPackage.getClass("CommunicationMessageKind").customizeAst(ast -> {
ast.getClassByName("CommunicationMessageKind")
.flatMap(clazz -> clazz.getFieldByName("IMAGE_V0"))
Expand All @@ -240,4 +296,72 @@ private void AddDeprecateAnnotationForImageV0CommunicationKind(PackageCustomiza
});
});
}

private void updateJavaDocForMethodFromJson(PackageCustomization modelPackage, String className) {
String originalDocText = String.format("@throws IOException If an error occurs while reading the %s.", className);
modelPackage.getClass(className).customizeAst(ast -> {
ast.getClassByName(className).ifPresent( clazz -> {
String fromJsonDoc = clazz.getMethodsByName("fromJson")
.get(0).getJavadoc().get().toText()
.replace(originalDocText,
"@throws IllegalStateException If the deserialized JSON object was missing any required properties.\n" +
originalDocText);
clazz.getMethodsByName("fromJson").get(0).setJavadocComment(fromJsonDoc);
});
});
}

private void customizeActionGroup(PackageCustomization modelsPackage) {
modelsPackage.getClass("ActionGroup").customizeAst(ast -> {
ast.getClassByName("ActionGroup")
.flatMap(clazz -> clazz.getConstructorByParameterTypes(String.class, List.class))
.ifPresent(c -> {
String body = c.getBody().toString().replace("this.items = items;",
"this.items = new ArrayList<>(items);");
c.setBody(StaticJavaParser.parseBlock(body));
});

ast.getClassByName("ActionGroup").ifPresent(clazz -> {
String getItemsBody = clazz.getMethodsByName("getItems").get(0).getBody().get().toString()
.replace("return this.items;", "return new ArrayList<>(this.items);");
clazz.getMethodsByName("getItems").get(0).setBody(StaticJavaParser.parseBlock(getItemsBody));
});
});
}

private void customizeActionGroupContent(PackageCustomization modelsPackage) {
modelsPackage.getClass("ActionGroupContent").customizeAst(ast -> {
ast.getClassByName("ActionGroupContent")
.flatMap(clazz -> clazz.getConstructorByParameterTypes(String.class, List.class))
.ifPresent(c -> {
String body = c.getBody().toString().replace("this.groups = groups;",
"this.groups = new ArrayList<>(groups);");
c.setBody(StaticJavaParser.parseBlock(body));
});

ast.getClassByName("ActionGroupContent").ifPresent(clazz -> {
String getItemsBody = clazz.getMethodsByName("getGroups").get(0).getBody().get().toString()
.replace("return this.groups;", "return new ArrayList<>(this.groups);");
clazz.getMethodsByName("getGroups").get(0).setBody(StaticJavaParser.parseBlock(getItemsBody));
});
});
}

private void customizeButtonSetContent(PackageCustomization modelsPackage) {
modelsPackage.getClass("ButtonSetContent").customizeAst(ast -> {
ast.getClassByName("ButtonSetContent")
.flatMap(clazz -> clazz.getConstructorByParameterTypes(List.class))
.ifPresent(c -> {
String body = c.getBody().toString().replace("this.buttons = buttons;",
"this.buttons = new ArrayList<>(buttons);");
c.setBody(StaticJavaParser.parseBlock(body));
});

ast.getClassByName("ButtonSetContent").ifPresent(clazz -> {
String getItemsBody = clazz.getMethodsByName("getButtons").get(0).getBody().get().toString()
.replace("return this.buttons;", "return new ArrayList<>(this.buttons);");
clazz.getMethodsByName("getButtons").get(0).setBody(StaticJavaParser.parseBlock(getItemsBody));
});
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ public enum MessagesServiceVersion implements ServiceVersion {
/**
* Enum value 2024-08-30.
*/
V2024_08_30("2024-08-30");
V2024_08_30("2024-08-30"),

/**
* Enum value 2025-01-15-preview.
*/
V2025_01_15_PREVIEW("2025-01-15-preview");

private final String version;

Expand All @@ -40,6 +45,6 @@ public String getVersion() {
* @return The latest {@link MessagesServiceVersion}.
*/
public static MessagesServiceVersion getLatest() {
return V2024_08_30;
return V2025_01_15_PREVIEW;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public final class NotificationMessagesAsyncClient {
* <pre>
* {@code
* {
* kind: String(text/image/image_v0/document/video/audio/template) (Required)
* kind: String(text/image/image_v0/document/video/audio/template/sticker/reaction/interactive) (Required)
* channelRegistrationId: String (Required)
* to (Required): [
* String (Required)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public final class NotificationMessagesClient {
* <pre>
* {@code
* {
* kind: String(text/image/image_v0/document/video/audio/template) (Required)
* kind: String(text/image/image_v0/document/video/audio/template/sticker/reaction/interactive) (Required)
* channelRegistrationId: String (Required)
* to (Required): [
* String (Required)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ Response<BinaryData> downloadMediaSync(@HostParam("endpoint") String endpoint,
* <pre>
* {@code
* {
* kind: String(text/image/image_v0/document/video/audio/template) (Required)
* kind: String(text/image/image_v0/document/video/audio/template/sticker/reaction/interactive) (Required)
* channelRegistrationId: String (Required)
* to (Required): [
* String (Required)
Expand Down Expand Up @@ -284,7 +284,7 @@ public Mono<Response<BinaryData>> sendWithResponseAsync(BinaryData notificationC
* <pre>
* {@code
* {
* kind: String(text/image/image_v0/document/video/audio/template) (Required)
* kind: String(text/image/image_v0/document/video/audio/template/sticker/reaction/interactive) (Required)
* channelRegistrationId: String (Required)
* to (Required): [
* String (Required)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Code generated by Microsoft (R) TypeSpec Code Generator.
package com.azure.communication.messages.models;

import com.azure.communication.messages.models.channels.WhatsAppButtonActionBindings;
import com.azure.communication.messages.models.channels.WhatsAppListActionBindings;
import com.azure.communication.messages.models.channels.WhatsAppUrlActionBindings;
import com.azure.core.annotation.Generated;
import com.azure.core.annotation.Immutable;
import com.azure.json.JsonReader;
import com.azure.json.JsonSerializable;
import com.azure.json.JsonToken;
import com.azure.json.JsonWriter;
import java.io.IOException;

/**
* Binding actions to the interactive message.
*/
@Immutable
public abstract class ActionBindings implements JsonSerializable<ActionBindings> {

/*
* Kind of the MessageActionBinding.
*/
@Generated
private MessageActionBindingKind kind = MessageActionBindingKind.fromString("ActionBindings");

/**
* Creates an instance of ActionBindings class.
*/
@Generated
protected ActionBindings() {
}

/**
* Get the kind property: Kind of the MessageActionBinding.
*
* @return the kind value.
*/
@Generated
public MessageActionBindingKind getKind() {
return this.kind;
}

/**
* {@inheritDoc}
*/
@Generated
@Override
public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
jsonWriter.writeStartObject();
jsonWriter.writeStringField("kind", this.kind == null ? null : this.kind.toString());
return jsonWriter.writeEndObject();
}

/**
* Reads an instance of ActionBindings from the JsonReader.
*
* @param jsonReader The JsonReader being read.
* @return An instance of ActionBindings if the JsonReader was pointing to an instance of it, or null if it was
* pointing to JSON null.
* @throws IllegalStateException If the deserialized JSON object was missing any required properties.
* @throws IOException If an error occurs while reading the ActionBindings.
*/
@Generated
public static ActionBindings fromJson(JsonReader jsonReader) throws IOException {
return jsonReader.readObject(reader -> {
String discriminatorValue = null;
try (JsonReader readerToUse = reader.bufferObject()) {
// Prepare for reading
readerToUse.nextToken();
while (readerToUse.nextToken() != JsonToken.END_OBJECT) {
String fieldName = readerToUse.getFieldName();
readerToUse.nextToken();
if ("kind".equals(fieldName)) {
discriminatorValue = readerToUse.getString();
break;
} else {
readerToUse.skipChildren();
}
}
// Use the discriminator value to determine which subtype should be deserialized.
if ("whatsAppListAction".equals(discriminatorValue)) {
return WhatsAppListActionBindings.fromJson(readerToUse.reset());
} else if ("whatsAppButtonAction".equals(discriminatorValue)) {
return WhatsAppButtonActionBindings.fromJson(readerToUse.reset());
} else if ("whatsAppUrlAction".equals(discriminatorValue)) {
return WhatsAppUrlActionBindings.fromJson(readerToUse.reset());
} else {
throw new IllegalStateException("Invalid Kind value - " + discriminatorValue);
}
}
});
}
}
Loading
Loading