001/* 002 * Copyright 2024 Vonage 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.vonage.client.messages; 017 018import com.fasterxml.jackson.annotation.*; 019import com.vonage.client.Jsonable; 020import com.vonage.client.JsonableBaseObject; 021import com.vonage.client.messages.whatsapp.ConversationType; 022import java.net.URI; 023import java.time.Instant; 024import java.util.Currency; 025import java.util.Map; 026import java.util.UUID; 027 028/** 029 * This class represents the data of a callback which is invoked after a message has been sent. 030 * It provides metadata about the message, such as its status, how much it cost, when it was sent, 031 * what service (channel) it was sent via, sender and recipient, message response ID, client reference etc. 032 */ 033public class MessageStatus extends JsonableBaseObject { 034 035 public enum Status { 036 SUBMITTED, 037 DELIVERED, 038 REJECTED, 039 UNDELIVERABLE, 040 READ; 041 042 @JsonCreator 043 public static Status fromString(String value) { 044 if (value == null) return null; 045 return Status.valueOf(value.toUpperCase()); 046 } 047 048 @JsonValue 049 @Override 050 public String toString() { 051 return name().toLowerCase(); 052 } 053 } 054 055 /** 056 * Describes the error that was encountered when sending the message. 057 */ 058 public static final class Error extends JsonableBaseObject { 059 @JsonProperty("type") URI type; 060 @JsonProperty("title") String title; 061 @JsonProperty("detail") String detail; 062 @JsonProperty("instance") String instance; 063 064 /** 065 * The type of error encountered. Follow this URL for more details. 066 * 067 * @return The error type as a URI. 068 */ 069 public URI getType() { 070 return type; 071 } 072 073 /** 074 * The error code encountered when sending the message. See 075 * <a href="https://developer.nexmo.com/api-errors/messages-olympus#_ga=2.161830718.381409930.1654502864-1953235162.1648459099"> 076 * our error list</a> for possible values. 077 * 078 * @return The error code as a String. 079 */ 080 public String getTitle() { 081 return title; 082 } 083 084 /** 085 * Text describing the error. See 086 * <a href="https://developer.nexmo.com/api-errors/messages-olympus#_ga=2.201342739.381409930.1654502864-1953235162.1648459099"> 087 * our error list</a> for possible values. 088 * 089 * @return The error message description. 090 */ 091 public String getDetail() { 092 return detail; 093 } 094 095 /** 096 * The record id of this error's occurrence. 097 * 098 * @return The error instance ID. 099 */ 100 public String getInstance() { 101 return instance; 102 } 103 } 104 105 /** 106 * Describes the charge incurred for sending the message. 107 */ 108 public static final class Usage extends JsonableBaseObject { 109 @JsonProperty("price") double price; 110 @JsonProperty("currency") Currency currency; 111 112 void setCurrency(String currency) { 113 this.currency = Currency.getInstance(currency); 114 } 115 116 /** 117 * The charge currency in ISO 4217 format. Usually will be {@code EUR} (Euros). 118 * 119 * @return The Currency. 120 */ 121 public Currency getCurrency() { 122 return currency; 123 } 124 125 void setPrice(String price) { 126 this.price = Double.parseDouble(price); 127 } 128 129 /** 130 * The amount charged for the message. 131 * 132 * @return The amount, as a double. 133 */ 134 public double getPrice() { 135 return price; 136 } 137 } 138 139 static class Destination extends JsonableBaseObject { 140 @JsonProperty("network_code") String networkCode; 141 } 142 143 static class Sms extends JsonableBaseObject { 144 @JsonProperty("count_total") Integer countTotal; 145 } 146 147 static class Whatsapp extends JsonableBaseObject { 148 static class Conversation extends JsonableBaseObject { 149 static class Origin extends JsonableBaseObject { 150 @JsonProperty("type") ConversationType type; 151 } 152 @JsonProperty("id") String id; 153 @JsonProperty("origin") Origin origin; 154 } 155 @JsonProperty("conversation") Conversation conversation; 156 } 157 158 protected MessageStatus() { 159 } 160 161 @JsonAnySetter protected Map<String, Object> unknownProperties; 162 163 @JsonProperty("timestamp") protected Instant timestamp; 164 @JsonProperty("message_uuid") protected UUID messageUuid; 165 @JsonProperty("to") protected String to; 166 @JsonProperty("from") protected String from; 167 @JsonProperty("status") protected Status status; 168 @JsonProperty("channel") protected Channel channel; 169 @JsonProperty("client_ref") protected String clientRef; 170 @JsonProperty("error") protected Error error; 171 @JsonProperty("usage") protected Usage usage; 172 173 @JsonProperty("destination") private Destination destination; 174 @JsonProperty("sms") private Sms sms; 175 @JsonProperty("whatsapp") private Whatsapp whatsapp; 176 177 178 /** 179 * Unique identifier of the message that was sent, as returned in {@link MessageResponse#getMessageUuid()}. 180 * 181 * @return The UUID of the message. 182 */ 183 public UUID getMessageUuid() { 184 return messageUuid; 185 } 186 187 /** 188 * The 'to' number used in the outbound {@link MessageRequest}. 189 * 190 * @return The recipient number or ID. 191 */ 192 public String getTo() { 193 return to; 194 } 195 196 /** 197 * The 'from' number used in the outbound {@link MessageRequest}. 198 * 199 * @return The sender number or ID. 200 */ 201 public String getFrom() { 202 return from; 203 } 204 205 /** 206 * The datetime of when the event occurred. 207 * 208 * @return The timestamp as an Instant. 209 */ 210 public Instant getTimestamp() { 211 return timestamp; 212 } 213 214 /** 215 * The status of the message. 216 * 217 * @return The message status as an enum. 218 */ 219 public Status getStatus() { 220 return status; 221 } 222 223 /** 224 * The service used to send the message. 225 * 226 * @return The channel, as an enum. 227 */ 228 public Channel getChannel() { 229 return channel; 230 } 231 232 /** 233 * Client reference of up to 40 characters. The reference will be present if set 234 * in the original outbound {@link MessageRequest}. 235 * 236 * @return The client reference, or {@code null} if unset. 237 */ 238 public String getClientRef() { 239 return clientRef; 240 } 241 242 /** 243 * If the message encountered a problem a descriptive error will be supplied in this object. 244 * 245 * @return The error object, or {@code null} if there was no problem to report. 246 */ 247 public Error getError() { 248 return error; 249 } 250 251 /** 252 * Describes the cost of the message that was sent. 253 * 254 * @return The usage object, or {@code null} if absent. 255 */ 256 public Usage getUsage() { 257 return usage; 258 } 259 260 /** 261 * If {@linkplain #getChannel()} is {@linkplain Channel#SMS} or {@linkplain Channel#MMS}, 262 * returns the network code for the destination. 263 * 264 * @return The mobile network code as a string, or {@code null} if not applicable. 265 * 266 * @since 8.1.0 267 */ 268 @JsonIgnore 269 public String getDestinationNetworkCode() { 270 return destination != null ? destination.networkCode : null; 271 } 272 273 /** 274 * {@linkplain #getChannel()} is {@linkplain Channel#SMS}, returns the number of SMS messages concatenated together 275 * to comprise the submitted message. SMS messages are 160 characters, if a submitted message exceeds that size it 276 * is sent as multiple SMS messages. This number indicates how many SMS messages are required. 277 * 278 * @return The number of SMS messages used for this message, or {@code null} if not applicable. 279 * 280 * @since 8.1.0 281 */ 282 @JsonIgnore 283 public Integer getSmsTotalCount() { 284 return sms != null ? sms.countTotal : null; 285 } 286 287 /** 288 * If the {@linkplain #getChannel()} is {@linkplain Channel#WHATSAPP} and {@linkplain #getStatus()} is 289 * {@linkplain Status#DELIVERED}, returns the conversation's origin type. 290 * 291 * @return The WhatsApp conversation category as an enum, {@code null} if absent or not applicable. 292 * 293 * @since 8.1.0 294 */ 295 @JsonIgnore 296 public ConversationType getWhatsappConversationType() { 297 return whatsapp != null && 298 whatsapp.conversation != null && 299 whatsapp.conversation.origin != null ? 300 whatsapp.conversation.origin.type : null; 301 } 302 303 /** 304 * If the {@linkplain #getChannel()} is {@linkplain Channel#WHATSAPP} and {@linkplain #getStatus()} is 305 * {@linkplain Status#DELIVERED}, returns the conversation ID of the message that triggered this callback. 306 * 307 * @return The WhatsApp conversation ID, {@code null} if absent or not applicable. 308 * 309 * @since 8.1.0 310 */ 311 @JsonIgnore 312 public String getWhatsappConversationId() { 313 return whatsapp != null && whatsapp.conversation != null ? whatsapp.conversation.id : null; 314 } 315 316 /** 317 * Catch-all for properties which are not mapped by this class during deserialization. 318 * 319 * @return Additional (unknown) properties as a Map, or {@code null} if absent. 320 */ 321 @JsonAnyGetter 322 public Map<String, ?> getAdditionalProperties() { 323 return unknownProperties; 324 } 325 326 /** 327 * Creates an instance of this class from a JSON payload. 328 * 329 * @param json The JSON string to parse. 330 * 331 * @return An instance of this class with the fields populated, if present. 332 */ 333 @JsonCreator 334 public static MessageStatus fromJson(String json) { 335 return Jsonable.fromJson(json); 336 } 337}