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}