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.conversations;
017
018import com.fasterxml.jackson.annotation.*;
019import com.vonage.client.common.ChannelType;
020import com.vonage.client.users.BaseUser;
021import com.vonage.client.users.channels.Channel;
022import java.util.Objects;
023import java.util.UUID;
024
025/**
026 * Represents a conversation membership.
027 */
028public class Member extends BaseMember {
029        private String conversationId, memberIdInviting, from, invitedBy;
030        private UUID knockingId;
031        private MemberChannel channel;
032        private MemberMedia media;
033        private MemberInitiator initiator;
034        private MemberTimestamp timestamp;
035
036        protected Member() {
037        }
038
039        Member(Builder builder) {
040                user = Objects.requireNonNull(builder.user, "User is required.");
041                state = Objects.requireNonNull(builder.state, "State is required.");
042                channel = Objects.requireNonNull(builder.channel, "Channel is required.");
043                media = builder.media;
044                knockingId = builder.knockingId;
045                memberIdInviting = builder.memberIdInviting;
046                from = builder.from;
047        }
048
049        @JsonSetter("conversation_id")
050        void setConversationId(String conversationId) {
051                this.conversationId = conversationId;
052        }
053
054        /**
055         * Unique identifier for this member's conversation.
056         * 
057         * @return The conversation ID, or {@code null} if this is a request.
058         */
059        @JsonIgnore
060        public String getConversationId() {
061                return conversationId;
062        }
063
064        /**
065         * Channel details for this membership.
066         * 
067         * @return The channel.
068         */
069        @JsonProperty("channel")
070        public MemberChannel getChannel() {
071                return channel;
072        }
073
074        /**
075         * Media settings for this member.
076         * 
077         * @return The media settings object, or {@code null} if unspecified.
078         */
079        @JsonProperty("media")
080        public MemberMedia getMedia() {
081                return media;
082        }
083
084        /**
085         * Unique knocking identifier.
086         * 
087         * @return The knocking ID, or {@code null} if unspecified.
088         */
089        @JsonProperty("knocking_id")
090        public UUID getKnockingId() {
091                return knockingId;
092        }
093
094        /**
095         * Unique member ID this member was invited by.
096         * This will be set when the invite has been created but not accepted.
097         * 
098         * @return The inviting member ID, or {@code null} if unspecified.
099         * @see #getInvitedBy()
100         */
101        @JsonProperty("member_id_inviting")
102        public String getMemberIdInviting() {
103                return memberIdInviting;
104        }
105
106        /**
107         * TODO document this
108         * 
109         * @return The from field, or {@code null} if unspecified.
110         */
111        @JsonProperty("from")
112        public String getFrom() {
113                return from;
114        }
115
116        /**
117         * Unique member ID this member was invited by.
118         * This will be set when the invite has been accepted.
119         * 
120         * @return The inviting member ID, or {@code null} if unknown / not applicable.
121         * @see #getMemberIdInviting()
122         */
123        @JsonProperty("invited_by")
124        public String getInvitedBy() {
125                return invitedBy;
126        }
127
128        /**
129         * Describes how this member was initiated.
130         * 
131         * @return The initiator details, or {@code null} if unspecified.
132         */
133        @JsonProperty("initiator")
134        public MemberInitiator getInitiator() {
135                return initiator;
136        }
137
138        /**
139         * Timestamps for this member.
140         *
141         * @return The timestamps object, or {@code null} if this is a request.
142         */
143        @JsonProperty("timestamp")
144        public MemberTimestamp getTimestamp() {
145                return timestamp;
146        }
147
148        /**
149         * Entry point for constructing an instance of this class.
150         * 
151         * @return A new Builder.
152         */
153        public static Builder builder() {
154                return new Builder();
155        }
156        
157        public static class Builder {
158                private BaseUser user;
159                private MemberState state;
160                private MemberChannel channel;
161                private MemberMedia media;
162                private UUID knockingId;
163                private String memberIdInviting, from;
164        
165                Builder() {}
166
167                private MemberChannel initChannel() {
168                        if (channel == null) channel = new MemberChannel();
169                        return channel;
170                }
171
172                /**
173                 * (REQUIRED) Either the user ID or unique name is required. This method will automatically
174                 * determine which is provided. User IDs start with {@code USR-} followed by a UUID.
175                 *
176                 * @param userNameOrId Either the unique user ID, or the name.
177                 *
178                 * @return This builder.
179                 */
180                public Builder user(String userNameOrId) {
181                        if (userNameOrId == null || userNameOrId.trim().isEmpty()) {
182                                throw new IllegalArgumentException("Invalid user name or ID.");
183                        }
184                        if (userNameOrId.startsWith("USR-") && userNameOrId.length() == 40) {
185                                user = new BaseUser(userNameOrId, null);
186                        }
187                        else {
188                                user = new BaseUser(null, userNameOrId);
189                        }
190                        return this;
191                }
192
193                /**
194                 * (REQUIRED) Invite or join a member to a conversation.
195                 *
196                 * @param state The state as an enum.
197                 *
198                 * @return This builder.
199                 */
200                public Builder state(MemberState state) {
201                        this.state = state;
202                        return this;
203                }
204
205                /**
206                 * (REQUIRED) Top-level channel type. You should also provide {@linkplain #fromChannel(Channel)} and
207                 * {@linkplain #toChannel(Channel)}. If this is set to anything other than {@linkplain ChannelType#APP},
208                 * then both {@linkplain #fromChannel(Channel)} and {@linkplain #toChannel(Channel)}
209                 * must be of the same type as each other.
210                 *
211                 * @param channelType The channel type as an enum.
212                 *
213                 * @return This builder.
214                 */
215                public Builder channelType(ChannelType channelType) {
216                        initChannel().type = channelType;
217                        return this;
218                }
219
220                /**
221                 * (OPTIONAL) Concrete channel to use when sending messages.
222                 * See {@linkplain com.vonage.client.users.channels} for options.
223                 *
224                 * @param from The sender channel.
225                 *
226                 * @return This builder.
227                 * @see #channelType(ChannelType)
228                 */
229                public Builder fromChannel(Channel from) {
230                        (initChannel().from = from).setTypeField();
231                        return this;
232                }
233
234                /**
235                 * (OPTIONAL) Concrete channel to use when receiving messages.
236                 * See {@linkplain com.vonage.client.users.channels} for options.
237                 *
238                 * @param to The receiver channel.
239                 *
240                 * @return This builder.
241                 * @see #channelType(ChannelType)
242                 */
243                public Builder toChannel(Channel to) {
244                        (initChannel().to = to).setTypeField();
245                        return this;
246                }
247
248                /**
249                 * (OPTIONAL) Media settings for this member.
250                 *
251                 * @param media The media settings object.
252                 *
253                 * @return This builder.
254                 */
255                public Builder media(MemberMedia media) {
256                        this.media = media;
257                        return this;
258                }
259
260                /**
261                 * (OPTIONAL) Unique knocking identifier.
262                 *
263                 * @param knockingId The knocking ID as a string.
264                 *
265                 * @return This builder.
266                 */
267                public Builder knockingId(String knockingId) {
268                        this.knockingId = UUID.fromString(knockingId);
269                        return this;
270                }
271
272                /**
273                 * (OPTIONAL) Unique member ID to invite.
274                 *
275                 * @param memberIdInviting The inviting member ID, or {@code null} if unspecified.
276                 *
277                 * @return This builder.
278                 */
279                public Builder memberIdInviting(String memberIdInviting) {
280                        this.memberIdInviting = memberIdInviting;
281                        return this;
282                }
283
284                /**
285                 * (OPTIONAL) TODO document this
286                 *
287                 * @param from The from field.
288                 *
289                 * @return This builder.
290                 */
291                public Builder from(String from) {
292                        this.from = from;
293                        return this;
294                }
295        
296                /**
297                 * Builds the {@linkplain Member}.
298                 *
299                 * @return An instance of Member, populated with all fields from this builder.
300                 */
301                public Member build() {
302                        return new Member(this);
303                }
304        }
305}