diff --git a/.travis.yml b/.travis.yml index c71a5afe..9e3235b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,3 +5,4 @@ script: mvn test -Ptest -DskipTests=false -Dhttps.protocols=TLSv1.2 -DmessageBir jdk: - oraclejdk11 - openjdk11 + - openjdk8 diff --git a/README.md b/README.md index 39d62596..fbacf4fe 100644 --- a/README.md +++ b/README.md @@ -115,16 +115,6 @@ If you server doesn't have a direct connection to the internet you can setup a p messageBirdService.setProxy(proxy); ``` -##### Conversations WhatsApp Sandbox -To use the whatsapp sandbox you need to add `MessageBirdClient.ENABLE_CONVERSATION_API_WHATSAPP_SANDBOX` to the list of features you want enabled. Don't forget to replace `YOUR_ACCESS_KEY` with your actual access key. - -```java - // Create a MessageBirdService - final MessageBirdService messageBirdService = new MessageBirdServiceImpl("YOUR_ACCESS_KEY"); - // Add the service to the client - final MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService, List.of(MessageBirdClient.Feature.ENABLE_CONVERSATION_API_WHATSAPP_SANDBOX)); -``` - Documentation ------------- Complete documentation, instructions, and examples are available at: diff --git a/api/pom.xml b/api/pom.xml index 65bdd1ba..b64ee8b0 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -4,10 +4,7 @@ com.messagebird messagebird-api - 3.0.0 + 6.4.0 jar ${project.groupId}:${project.artifactId} @@ -30,6 +27,12 @@ MessageBird https://www.messagebird.com + + Venkateswaran S + venkat.sankaran@bird.com + MessageBird + https://www.messagebird.com + Sam Wierema sam@messagebird.com @@ -42,7 +45,7 @@ scm:git:git@github.com:messagebird/java-rest-api.git scm:git:git@github.com:messagebird/java-rest-api.git git@github.com:messagebird/java-rest-api.git - HEAD + HEAD @@ -59,19 +62,51 @@ + test false + + + UTF-8 + + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + **/ContactTest.java + **/MessageBirdClientTest.java + + + + + + + + + integration + + false + UTF-8 + disable-doclint - [11,) + [1.8,11) none - true UTF-8 @@ -79,26 +114,42 @@ + + + + + org.codehaus.plexus + plexus-utils + 3.6.1 + + + + com.fasterxml.jackson.core jackson-annotations - 2.9.8 + 2.13.2 com.fasterxml.jackson.core jackson-databind - 2.9.8 + 2.14.0-rc1 + + + com.auth0 + java-jwt + 4.4.0 - com.fasterxml.jackson.dataformat - jackson-dataformat-csv - 2.9.8 + org.apache.maven + maven-artifact + 3.9.6 junit junit - 4.11 + 4.13.1 test @@ -113,15 +164,14 @@ 2.21.0 test + + org.jetbrains + annotations + 13.0 + provided + - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - @@ -149,6 +199,9 @@ + + none + @@ -177,8 +230,8 @@ maven-compiler-plugin 3.7.0 - 11 - 11 + 1.8 + 1.8 @@ -213,14 +266,22 @@ - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 + + org.sonatype.central + central-publishing-maven-plugin + 0.7.0 true - ossrh - https://oss.sonatype.org/ - false + central + false + validated diff --git a/api/src/main/java/com/messagebird/Base64.java b/api/src/main/java/com/messagebird/Base64.java deleted file mode 100644 index 876beeb0..00000000 --- a/api/src/main/java/com/messagebird/Base64.java +++ /dev/null @@ -1,496 +0,0 @@ -package com.messagebird; - -/** - * Cutted version of iharder's base64 implementation - * @todo replace with actual library on next major bump - * - *

Encodes and decodes to and from Base64 notation.

- *

Homepage: http://iharder.net/base64.

- * - *

- * I am placing this code in the Public Domain. Do with it as you will. - * This software comes with no guarantees or warranties but with - * plenty of well-wishing instead! - * Please visit http://iharder.net/base64 - * periodically to check for updates or to contribute improvements. - *

- * - * @author Robert Harder - * @author rob@iharder.net - * @version 2.3.7 - */ -class Base64 { - -/* ******** P U B L I C F I E L D S ******** */ - - - /** No options specified. Value is zero. */ - public final static int NO_OPTIONS = 0; - - /** Specify that gzipped data should not be automatically gunzipped. */ - public final static int DONT_GUNZIP = 4; - - /** - * Encode using Base64-like encoding that is URL- and Filename-safe as described - * in Section 4 of RFC3548: - * http://www.faqs.org/rfcs/rfc3548.html. - * It is important to note that data encoded this way is not officially valid Base64, - * or at the very least should not be called Base64 without also specifying that is - * was encoded using the URL- and Filename-safe dialect. - */ - public final static int URL_SAFE = 16; - - - /** - * Encode using the special "ordered" dialect of Base64 described here: - * http://www.faqs.org/qa/rfcc-1940.html. - */ - public final static int ORDERED = 32; - - -/* ******** P R I V A T E F I E L D S ******** */ - - - /** The equals sign (=) as a byte. */ - private final static byte EQUALS_SIGN = (byte)'='; - - - /** Preferred encoding. */ - private final static String PREFERRED_ENCODING = "US-ASCII"; - - - private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding - private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding - - -/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ - - /** - * Translates a Base64 value to either its 6-bit reconstruction value - * or a negative number indicating some other meaning. - **/ - private final static byte[] _STANDARD_DECODABET = { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - 62, // Plus sign at decimal 43 - -9,-9,-9, // Decimal 44 - 46 - 63, // Slash at decimal 47 - 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' - 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' - -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 - 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' - 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' - -9,-9,-9,-9,-9 // Decimal 123 - 127 - ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 - }; - - -/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ - - /** - * Used in decoding URL- and Filename-safe dialects of Base64. - */ - private final static byte[] _URL_SAFE_DECODABET = { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 62, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' - 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' - -9,-9,-9,-9, // Decimal 91 - 94 - 63, // Underscore at decimal 95 - -9, // Decimal 96 - 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' - 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' - -9,-9,-9,-9,-9 // Decimal 123 - 127 - ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 - }; - - - -/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ - - /** - * Used in decoding the "ordered" dialect of Base64. - */ - private final static byte[] _ORDERED_DECODABET = { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 0, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M' - 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z' - -9,-9,-9,-9, // Decimal 91 - 94 - 37, // Underscore at decimal 95 - -9, // Decimal 96 - 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm' - 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z' - -9,-9,-9,-9,-9 // Decimal 123 - 127 - ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 - }; - - -/* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ - - /** - * Returns one of the _SOMETHING_DECODABET byte arrays depending on - * the options specified. - * It's possible, though silly, to specify ORDERED and URL_SAFE - * in which case one of them will be picked, though there is - * no guarantee as to which one will be picked. - */ - private final static byte[] getDecodabet( int options ) { - if( (options & URL_SAFE) == URL_SAFE) { - return _URL_SAFE_DECODABET; - } else if ((options & ORDERED) == ORDERED) { - return _ORDERED_DECODABET; - } else { - return _STANDARD_DECODABET; - } - } // end getAlphabet - - - - /** Defeats instantiation. */ - private Base64(){} - - - -/* ******** D E C O D I N G M E T H O D S ******** */ - - - /** - * Decodes four bytes from array source - * and writes the resulting bytes (up to three of them) - * to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 4 for - * the source array or destOffset + 3 for - * the destination array. - * This method returns the actual number of bytes that - * were converted from the Base64 encoding. - *

This is the lowest level of the decoding methods with - * all possible parameters.

- * - * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @param options alphabet type is pulled from this (standard, url-safe, ordered) - * @return the number of decoded bytes converted - * @throws NullPointerException if source or destination arrays are null - * @throws IllegalArgumentException if srcOffset or destOffset are invalid - * or there is not enough room in the array. - * @since 1.3 - */ - private static int decode4to3( - byte[] source, int srcOffset, - byte[] destination, int destOffset, int options ) { - - // Lots of error checking and exception throwing - if( source == null ){ - throw new NullPointerException( "Source array was null." ); - } // end if - if( destination == null ){ - throw new NullPointerException( "Destination array was null." ); - } // end if - if( srcOffset < 0 || srcOffset + 3 >= source.length ){ - throw new IllegalArgumentException( String.format( - "Source array with length %d cannot have offset of %d and still process four bytes.", source.length, srcOffset ) ); - } // end if - if( destOffset < 0 || destOffset +2 >= destination.length ){ - throw new IllegalArgumentException( String.format( - "Destination array with length %d cannot have offset of %d and still store three bytes.", destination.length, destOffset ) ); - } // end if - - - byte[] DECODABET = getDecodabet( options ); - - // Example: Dk== - if( source[ srcOffset + 2] == EQUALS_SIGN ) { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - return 1; - } - - // Example: DkL= - else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); - return 2; - } - - // Example: DkLE - else { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) - // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) - | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); - - - destination[ destOffset ] = (byte)( outBuff >> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); - destination[ destOffset + 2 ] = (byte)( outBuff ); - - return 3; - } - } // end decodeToBytes - - - - - /** - * Low-level access to decoding ASCII characters in - * the form of a byte array. Ignores GUNZIP option, if - * it's set. This is not generally a recommended method, - * although it is used internally as part of the decoding process. - * Special case: if len = 0, an empty array is returned. Still, - * if you need more speed and reduced memory footprint (and aren't - * gzipping), consider this method. - * - * @param source The Base64 encoded data - * @param off The offset of where to begin decoding - * @param len The length of characters to decode - * @param options Can specify options such as alphabet type to use - * @return decoded data - * @throws java.io.IOException If bogus characters exist in source data - * @since 1.3 - */ - public static byte[] decode( byte[] source, int off, int len, int options ) - throws java.io.IOException { - - // Lots of error checking and exception throwing - if( source == null ){ - throw new NullPointerException( "Cannot decode null source array." ); - } // end if - if( off < 0 || off + len > source.length ){ - throw new IllegalArgumentException( String.format( - "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, len ) ); - } // end if - - if( len == 0 ){ - return new byte[0]; - }else if( len < 4 ){ - throw new IllegalArgumentException( - "Base64-encoded string must have at least four characters, but length specified was " + len ); - } // end if - - byte[] DECODABET = getDecodabet( options ); - - int len34 = len * 3 / 4; // Estimate on array size - byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output - int outBuffPosn = 0; // Keep track of where we're writing - - byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space - int b4Posn = 0; // Keep track of four byte input buffer - int i = 0; // Source array counter - byte sbiDecode = 0; // Special value from DECODABET - - for( i = off; i < off+len; i++ ) { // Loop through source - - sbiDecode = DECODABET[ source[i]&0xFF ]; - - // White space, Equals sign, or legit Base64 character - // Note the values such as -5 and -9 in the - // DECODABETs at the top of the file. - if( sbiDecode >= WHITE_SPACE_ENC ) { - if( sbiDecode >= EQUALS_SIGN_ENC ) { - b4[ b4Posn++ ] = source[i]; // Save non-whitespace - if( b4Posn > 3 ) { // Time to decode? - outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); - b4Posn = 0; - - // If that was the equals sign, break out of 'for' loop - if( source[i] == EQUALS_SIGN ) { - break; - } // end if: equals sign - } // end if: quartet built - } // end if: equals sign or better - } // end if: white space, equals sign or better - else { - // There's a bad input character in the Base64 stream. - throw new java.io.IOException( String.format( - "Bad Base64 input character decimal %d in array position %d", ((int)source[i])&0xFF, i ) ); - } // end else: - } // each input character - - byte[] out = new byte[ outBuffPosn ]; - System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); - return out; - } // end decode - - - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @return the decoded data - * @throws java.io.IOException If there is a problem - * @since 1.4 - */ - public static byte[] decode( String s ) throws java.io.IOException { - return decode( s, NO_OPTIONS ); - } - - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @param options encode options such as URL_SAFE - * @return the decoded data - * @throws java.io.IOException if there is an error - * @throws NullPointerException if s is null - * @since 1.4 - */ - public static byte[] decode( String s, int options ) throws java.io.IOException { - - if( s == null ){ - throw new NullPointerException( "Input string was null." ); - } // end if - - byte[] bytes; - try { - bytes = s.getBytes( PREFERRED_ENCODING ); - } // end try - catch( java.io.UnsupportedEncodingException uee ) { - bytes = s.getBytes(); - } // end catch - // - - // Decode - bytes = decode( bytes, 0, bytes.length, options ); - - // Check to see if it's gzip-compressed - // GZIP Magic Two-Byte Number: 0x8b1f (35615) - boolean dontGunzip = (options & DONT_GUNZIP) != 0; - if( (bytes != null) && (bytes.length >= 4) && (!dontGunzip) ) { - - int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); - if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) { - java.io.ByteArrayInputStream bais = null; - java.util.zip.GZIPInputStream gzis = null; - java.io.ByteArrayOutputStream baos = null; - byte[] buffer = new byte[2048]; - int length = 0; - - try { - baos = new java.io.ByteArrayOutputStream(); - bais = new java.io.ByteArrayInputStream( bytes ); - gzis = new java.util.zip.GZIPInputStream( bais ); - - while( ( length = gzis.read( buffer ) ) >= 0 ) { - baos.write(buffer,0,length); - } // end while: reading input - - // No error? Get new bytes. - bytes = baos.toByteArray(); - - } // end try - catch( java.io.IOException e ) { - e.printStackTrace(); - // Just return originally-decoded bytes - } // end catch - finally { - try{ baos.close(); } catch( Exception e ){} - try{ gzis.close(); } catch( Exception e ){} - try{ bais.close(); } catch( Exception e ){} - } // end finally - - } // end if: gzipped - } // end if: bytes.length >= 2 - - return bytes; - } // end decode - -} // end class Base64 diff --git a/api/src/main/java/com/messagebird/MessageBirdClient.java b/api/src/main/java/com/messagebird/MessageBirdClient.java index 3b60b2a3..583dcb40 100644 --- a/api/src/main/java/com/messagebird/MessageBirdClient.java +++ b/api/src/main/java/com/messagebird/MessageBirdClient.java @@ -4,20 +4,49 @@ import com.messagebird.exceptions.NotFoundException; import com.messagebird.exceptions.UnauthorizedException; import com.messagebird.objects.*; -import com.messagebird.objects.conversations.*; -import com.messagebird.objects.voicecalls.*; +import com.messagebird.objects.conversations.Conversation; +import com.messagebird.objects.conversations.ConversationList; +import com.messagebird.objects.conversations.ConversationMessage; +import com.messagebird.objects.conversations.ConversationMessageList; +import com.messagebird.objects.conversations.ConversationMessageRequest; +import com.messagebird.objects.conversations.ConversationSendRequest; +import com.messagebird.objects.conversations.ConversationSendResponse; +import com.messagebird.objects.conversations.ConversationStartRequest; +import com.messagebird.objects.conversations.ConversationStatus; +import com.messagebird.objects.conversations.ConversationUpdateRequest; +import com.messagebird.objects.conversations.ConversationWebhook; +import com.messagebird.objects.conversations.ConversationWebhookCreateRequest; +import com.messagebird.objects.conversations.ConversationWebhookList; +import com.messagebird.objects.conversations.ConversationWebhookUpdateRequest; +import com.messagebird.objects.integrations.Template; +import com.messagebird.objects.integrations.TemplateList; +import com.messagebird.objects.integrations.TemplateResponse; +import com.messagebird.objects.voicecalls.RecordingResponse; +import com.messagebird.objects.voicecalls.TranscriptionResponse; +import com.messagebird.objects.voicecalls.VoiceCall; +import com.messagebird.objects.voicecalls.VoiceCallFlowList; +import com.messagebird.objects.voicecalls.VoiceCallFlowRequest; +import com.messagebird.objects.voicecalls.VoiceCallFlowResponse; import com.messagebird.objects.voicecalls.VoiceCallLeg; import com.messagebird.objects.voicecalls.VoiceCallLegResponse; - +import com.messagebird.objects.voicecalls.VoiceCallResponse; +import com.messagebird.objects.voicecalls.VoiceCallResponseList; +import com.messagebird.objects.voicecalls.Webhook; +import com.messagebird.objects.voicecalls.WebhookList; +import com.messagebird.objects.voicecalls.WebhookResponseData; import java.io.UnsupportedEncodingException; import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.util.*; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Set; /** * Message bird general client @@ -43,11 +72,14 @@ public class MessageBirdClient { * can, however, override this behaviour by providing absolute URLs * ourselves. */ - private static final String BASE_URL_CONVERSATIONS = "https://conversations.messagebird.com/v1"; - private static final String BASE_URL_CONVERSATIONS_WHATSAPP_SANDBOX = "https://whatsapp-sandbox.messagebird.com/v1"; - + static final String CONVERSATIONS_BASE_URL = "https://conversations.messagebird.com/v1"; static final String VOICE_CALLS_BASE_URL = "https://voice.messagebird.com"; + static final String NUMBERS_CALLS_BASE_URL = "https://numbers.messagebird.com/v1"; + static final String MESSAGING_BASE_URL = "https://messaging.messagebird.com/v1"; + static final String INTEGRATIONS_BASE_URL_V2 = "https://integrations.messagebird.com/v2"; + static final String INTEGRATIONS_BASE_URL_V3 = "https://integrations.messagebird.com/v3"; private static String[] supportedLanguages = {"de-DE", "en-AU", "en-UK", "en-US", "es-ES", "es-LA", "fr-FR", "it-IT", "nl-NL", "pt-BR"}; + static final String PARTNER_ACCOUNTS_BASE_URL = "https://partner-accounts.messagebird.com"; private static final String BALANCEPATH = "/balance"; private static final String CONTACTPATH = "/contacts"; @@ -57,35 +89,48 @@ public class MessageBirdClient { private static final String LOOKUPPATH = "/lookup"; private static final String MESSAGESPATH = "/messages"; private static final String VERIFYPATH = "/verify"; + private static final String VERIFYEMAILPATH = "/verify/messages/email"; private static final String VOICEMESSAGESPATH = "/voicemessages"; - private static final String CONVERSATION_PATH = "/conversations"; - private static final String CONVERSATION_MESSAGE_PATH = "/messages"; - private static final String CONVERSATION_WEBHOOK_PATH = "/webhooks"; + static final String CONVERSATION_PATH = "/conversations"; + static final String CONVERSATION_SEND_PATH = "/send"; + static final String CONVERSATION_MESSAGE_PATH = "/messages"; + static final String CONVERSATION_WEBHOOK_PATH = "/webhooks"; + static final String INTEGRATIONS_WHATSAPP_PATH = "/platforms/whatsapp"; static final String VOICECALLSPATH = "/calls"; static final String LEGSPATH = "/legs"; static final String RECORDINGPATH = "/recordings"; static final String TRANSCRIPTIONPATH = "/transcriptions"; static final String WEBHOOKS = "/webhooks"; + static final String VOICECALLFLOWPATH = "/call-flows"; private static final String VOICELEGS_SUFFIX_PATH = "/legs"; + static final String FILES_PATH = "/files"; + static final String TEMPLATES_PATH = "/templates"; + static final String UNPAUSE_TEMAPLATE_PATH = "/unpause"; + static final String OUTBOUND_SMS_PRICING_PATH = "/pricing/sms/outbound"; + static final String OUTBOUND_SMS_PRICING_SMPP_PATH = "/pricing/sms/outbound/smpp/%s"; - private MessageBirdService messageBirdService; - private String conversationsBaseUrl; + static final String RECORDING_DOWNLOAD_FORMAT = ".wav"; - public enum Feature { - ENABLE_CONVERSATION_API_WHATSAPP_SANDBOX - } + static final String TRANSCRIPTION_DOWNLOAD_FORMAT = ".txt"; + + private static final int DEFAULT_MACHINE_TIMEOUT_VALUE = 7000; + private static final int MIN_MACHINE_TIMEOUT_VALUE = 400; + private static final int MAX_MACHINE_TIMEOUT_VALUE = 10000; + + private static final String[] MESSAGE_LIST_FILTERS_VALS = {"originator", "recipient", "direction", "searchterm", "type", "contact_id", "status", "from", "until"}; + private static final Set MESSAGE_LIST_FILTERS = new HashSet<>(Arrays.asList(MESSAGE_LIST_FILTERS_VALS)); + + private static final String[] CONVERSATION_MESSAGE_LIST_FILTERS_VALS = {"ids", "from"}; + private static final Set CONVERSATION_MESSAGE_LIST_FILTERS = new HashSet<>(Arrays.asList(CONVERSATION_MESSAGE_LIST_FILTERS_VALS)); + + private final String DOWNLOADS = "Downloads"; + + private MessageBirdService messageBirdService; public MessageBirdClient(final MessageBirdService messageBirdService) { this.messageBirdService = messageBirdService; - this.conversationsBaseUrl = BASE_URL_CONVERSATIONS; } - public MessageBirdClient(final MessageBirdService messageBirdService, List features) { - this(messageBirdService); - if(features.indexOf(Feature.ENABLE_CONVERSATION_API_WHATSAPP_SANDBOX) >= 0) { - this.conversationsBaseUrl = BASE_URL_CONVERSATIONS_WHATSAPP_SANDBOX; - } - } /****************************************************************************************************/ /** Balance and HRL methods **/ /****************************************************************************************************/ @@ -221,15 +266,22 @@ public MessageResponse sendFlashMessage(final String originator, final String bo } public MessageList listMessages(final Integer offset, final Integer limit) throws UnauthorizedException, GeneralException { - if (offset != null && offset < 0) { - throw new IllegalArgumentException("Offset must be > 0"); - } - if (limit != null && limit < 0) { - throw new IllegalArgumentException("Limit must be > 0"); - } + verifyOffsetAndLimit(offset, limit); return messageBirdService.requestList(MESSAGESPATH, offset, limit, MessageList.class); } + public MessageList listMessagesFiltered(final Integer offset, final Integer limit, final Map filters) throws UnauthorizedException, GeneralException { + verifyOffsetAndLimit(offset, limit); + + for (String filter : filters.keySet()) { + if (!MESSAGE_LIST_FILTERS.contains(filter)) { + throw new IllegalArgumentException("Invalid filter name: " + filter); + } + } + + return messageBirdService.requestList(MESSAGESPATH, filters, offset, limit, MessageList.class); + } + /** * Delete a message from the Messagebird server * @@ -272,6 +324,8 @@ public MessageResponse viewMessage(final String id) throws UnauthorizedException * @throws GeneralException general exception */ public VoiceMessageResponse sendVoiceMessage(final VoiceMessage voiceMessage) throws UnauthorizedException, GeneralException { + addDefaultMachineTimeoutValueIfNotExists(voiceMessage); + checkMachineTimeoutValueIsInRange(voiceMessage); return messageBirdService.sendPayLoad(VOICEMESSAGESPATH, voiceMessage, VoiceMessageResponse.class); } @@ -286,6 +340,8 @@ public VoiceMessageResponse sendVoiceMessage(final VoiceMessage voiceMessage) th */ public VoiceMessageResponse sendVoiceMessage(final String body, final List recipients) throws UnauthorizedException, GeneralException { final VoiceMessage message = new VoiceMessage(body, recipients); + addDefaultMachineTimeoutValueIfNotExists(message); + checkMachineTimeoutValueIsInRange(message); return messageBirdService.sendPayLoad(VOICEMESSAGESPATH, message, VoiceMessageResponse.class); } @@ -302,9 +358,23 @@ public VoiceMessageResponse sendVoiceMessage(final String body, final List recipients, final String reference) throws UnauthorizedException, GeneralException { final VoiceMessage message = new VoiceMessage(body, recipients); message.setReference(reference); + addDefaultMachineTimeoutValueIfNotExists(message); + checkMachineTimeoutValueIsInRange(message); return messageBirdService.sendPayLoad(VOICEMESSAGESPATH, message, VoiceMessageResponse.class); } + private void addDefaultMachineTimeoutValueIfNotExists(final VoiceMessage voiceMessage){ + if (voiceMessage.getMachineTimeout() == 0){ + voiceMessage.setMachineTimeout(DEFAULT_MACHINE_TIMEOUT_VALUE); //default machine timeout value + } + } + + private void checkMachineTimeoutValueIsInRange(final VoiceMessage voiceMessage){ + if (voiceMessage.getMachineTimeout() < MIN_MACHINE_TIMEOUT_VALUE || voiceMessage.getMachineTimeout() > MAX_MACHINE_TIMEOUT_VALUE){ + throw new IllegalArgumentException("Please define machine timeout value between " + MIN_MACHINE_TIMEOUT_VALUE + " and " + MAX_MACHINE_TIMEOUT_VALUE); + } + } + /** * Delete a voice message from the Messagebird server * @@ -344,12 +414,7 @@ public VoiceMessageResponse viewVoiceMessage(final String id) throws Unauthorize * @throws GeneralException general exception */ public VoiceMessageList listVoiceMessages(final Integer offset, final Integer limit) throws UnauthorizedException, GeneralException { - if (offset != null && offset < 0) { - throw new IllegalArgumentException("Offset must be > 0"); - } - if (limit != null && limit < 0) { - throw new IllegalArgumentException("Limit must be > 0"); - } + verifyOffsetAndLimit(offset, limit); return messageBirdService.requestList(VOICEMESSAGESPATH, offset, limit, VoiceMessageList.class); } @@ -408,13 +473,27 @@ public Verify verifyToken(String id, String token) throws NotFoundException, Gen * @throws UnauthorizedException if client is unauthorized * @throws GeneralException general exception */ - Verify getVerifyObject(String id) throws NotFoundException, GeneralException, UnauthorizedException { + public Verify getVerifyObject(String id) throws NotFoundException, GeneralException, UnauthorizedException { if (id == null || id.isEmpty()) { throw new IllegalArgumentException("ID cannot be empty for verify"); } return messageBirdService.requestByID(VERIFYPATH, id, Verify.class); } + /** + * @param messageId is for the email message part of a verify object + * @return Verify object + * @throws NotFoundException if id is not found + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public VerifyMessage getVerifyEmailMessage(String messageId) throws UnauthorizedException, GeneralException, NotFoundException { + if (messageId == null || messageId.isEmpty()) { + throw new IllegalArgumentException("ID cannot be empty for verify email message"); + } + return messageBirdService.requestByID(VERIFYEMAILPATH, messageId, VerifyMessage.class); + } + /** * @param id id for deleting verify object * @throws NotFoundException if id is not found @@ -548,6 +627,92 @@ public LookupHlr viewLookupHlr(final BigInteger phoneNumber) throws Unauthorized return this.viewLookupHlr(lookupHlr); } + /** + * Convenient function to list all call flows + * + * @param offset + * @param limit + * @return VoiceCallFlowList + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public VoiceCallFlowList listVoiceCallFlows(final Integer offset, final Integer limit) + throws UnauthorizedException, GeneralException { + verifyOffsetAndLimit(offset, limit); + String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLFLOWPATH); + + return messageBirdService.requestList(url, offset, limit, VoiceCallFlowList.class); + } + + /** + * Retrieves the information of an existing Call Flow. You only need to supply + * the unique call flow ID that was returned upon creation or receiving. + * @param id String + * @return VoiceCallFlowResponse + * @throws NotFoundException + * @throws GeneralException + * @throws UnauthorizedException + */ + public VoiceCallFlowResponse viewVoiceCallFlow(final String id) throws NotFoundException, GeneralException, UnauthorizedException { + if (id == null) { + throw new IllegalArgumentException("Call Flow ID must be specified."); + } + String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLFLOWPATH); + + return messageBirdService.requestByID(url, id, VoiceCallFlowResponse.class); + } + + /** + * Convenient function to create a call flow + * + * @param voiceCallFlowRequest VoiceCallFlowRequest + * @return VoiceCallFlowResponse + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public VoiceCallFlowResponse sendVoiceCallFlow(final VoiceCallFlowRequest voiceCallFlowRequest) + throws UnauthorizedException, GeneralException { + String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLFLOWPATH); + + return messageBirdService.sendPayLoad(url, voiceCallFlowRequest, VoiceCallFlowResponse.class); + } + + /** + * Updates an existing Call Flow. You only need to supply the unique id that + * was returned upon creation. + * @param id String + * @param voiceCallFlowRequest VoiceCallFlowRequest + * @return VoiceCallFlowResponse + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public VoiceCallFlowResponse updateVoiceCallFlow(String id, VoiceCallFlowRequest voiceCallFlowRequest) + throws UnauthorizedException, GeneralException { + if (id == null) { + throw new IllegalArgumentException("Call Flow ID must be specified."); + } + String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLFLOWPATH); + String request = url + "/" + id; + + return messageBirdService.sendPayLoad("PUT", request, voiceCallFlowRequest, VoiceCallFlowResponse.class); + } + + /** + * Convenient function to delete call flow + * + * @param id String + * @return void + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public void deleteVoiceCallFlow(final String id) throws NotFoundException, GeneralException, UnauthorizedException { + if (id == null) { + throw new IllegalArgumentException("Voice Call Flow ID must be specified."); + } + String url = String.format("%s%s", VOICE_CALLS_BASE_URL, VOICECALLFLOWPATH); + messageBirdService.deleteByID(url, id); + } + /** * Deletes an existing contact. You only need to supply the unique id that * was returned upon creation. @@ -724,7 +889,7 @@ public Conversation viewConversation(final String id) throws NotFoundException, if (id == null) { throw new IllegalArgumentException("Id must be specified"); } - String url = this.conversationsBaseUrl + CONVERSATION_PATH; + String url = CONVERSATIONS_BASE_URL + CONVERSATION_PATH; return messageBirdService.requestByID(url, id, Conversation.class); } @@ -740,8 +905,12 @@ public Conversation updateConversation(final String id, final ConversationStatus if (id == null) { throw new IllegalArgumentException("Id must be specified."); } - String url = String.format("%s%s/%s", this.conversationsBaseUrl, CONVERSATION_PATH, id); - return messageBirdService.sendPayLoad("PATCH", url, status, Conversation.class); + if (status == null) { + throw new IllegalArgumentException("An updated conversation status must be specified"); + } + ConversationUpdateRequest payload = new ConversationUpdateRequest(status); + String url = String.format("%s%s/%s", CONVERSATIONS_BASE_URL, CONVERSATION_PATH, id); + return messageBirdService.sendPayLoad("PATCH", url, payload, Conversation.class); } /** @@ -753,7 +922,7 @@ public Conversation updateConversation(final String id, final ConversationStatus */ public ConversationList listConversations(final int offset, final int limit) throws UnauthorizedException, GeneralException { - String url = this.conversationsBaseUrl + CONVERSATION_PATH; + String url = CONVERSATIONS_BASE_URL + CONVERSATION_PATH; return messageBirdService.requestList(url, offset, limit, ConversationList.class); } @@ -777,10 +946,22 @@ public ConversationList listConversations() throws UnauthorizedException, Genera */ public Conversation startConversation(ConversationStartRequest request) throws UnauthorizedException, GeneralException { - String url = String.format("%s%s/start", this.conversationsBaseUrl, CONVERSATION_PATH); + String url = String.format("%s%s/start", CONVERSATIONS_BASE_URL, CONVERSATION_PATH); return messageBirdService.sendPayLoad(url, request, Conversation.class); } + /** + * sendMessage allows you to send message to users over any communication platform supported by Programmable Conversations + * + * @param request Data for this request. + * @return The created Message in ConversationSendResponse object. + */ + public ConversationSendResponse sendMessage(ConversationSendRequest request) + throws UnauthorizedException, GeneralException { + String url = String.format("%s%s", CONVERSATIONS_BASE_URL, CONVERSATION_SEND_PATH); + return messageBirdService.sendPayLoad(url, request, ConversationSendResponse.class); + } + /** * Gets a ConversationMessage listing with specified pagination options. * @@ -796,7 +977,7 @@ public ConversationMessageList listConversationMessages( ) throws UnauthorizedException, GeneralException { String url = String.format( "%s%s/%s%s", - this.conversationsBaseUrl, + CONVERSATIONS_BASE_URL, CONVERSATION_PATH, conversationId, CONVERSATION_MESSAGE_PATH @@ -827,10 +1008,27 @@ public ConversationMessageList listConversationMessages( */ public ConversationMessage viewConversationMessage(final String messageId) throws NotFoundException, GeneralException, UnauthorizedException { - String url = this.conversationsBaseUrl + CONVERSATION_MESSAGE_PATH; + String url = CONVERSATIONS_BASE_URL + CONVERSATION_MESSAGE_PATH; return messageBirdService.requestByID(url, messageId, ConversationMessage.class); } + /** + * Gets conversation messages based on query param. + * + * @param queryParams only `ids` and `from` is available as an option + * @return The retrieved messages. + */ + public ConversationMessageList listConversationMessagesWithQueryParam(Map queryParams) + throws NotFoundException, GeneralException, UnauthorizedException { + for (String queryParam : queryParams.keySet()) { + if (!CONVERSATION_MESSAGE_LIST_FILTERS.contains(queryParam)) { + throw new IllegalArgumentException("Invalid filter name: " + queryParam); + } + } + String url = CONVERSATIONS_BASE_URL + CONVERSATION_MESSAGE_PATH; + return messageBirdService.requestByID(url, null, queryParams, ConversationMessageList.class); + } + /** * Sends a message to an existing Conversation. * @@ -844,7 +1042,7 @@ public ConversationMessage sendConversationMessage( ) throws UnauthorizedException, GeneralException { String url = String.format( "%s%s/%s%s", - this.conversationsBaseUrl, + CONVERSATIONS_BASE_URL, CONVERSATION_PATH, conversationId, CONVERSATION_MESSAGE_PATH @@ -859,7 +1057,7 @@ public ConversationMessage sendConversationMessage( */ public void deleteConversationWebhook(final String webhookId) throws NotFoundException, GeneralException, UnauthorizedException { - String url = this.conversationsBaseUrl + CONVERSATION_WEBHOOK_PATH; + String url = CONVERSATIONS_BASE_URL + CONVERSATION_WEBHOOK_PATH; messageBirdService.deleteByID(url, webhookId); } @@ -871,7 +1069,7 @@ public void deleteConversationWebhook(final String webhookId) */ public ConversationWebhook sendConversationWebhook(final ConversationWebhookCreateRequest request) throws UnauthorizedException, GeneralException { - String url = this.conversationsBaseUrl + CONVERSATION_WEBHOOK_PATH; + String url = CONVERSATIONS_BASE_URL + CONVERSATION_WEBHOOK_PATH; return messageBirdService.sendPayLoad(url, request, ConversationWebhook.class); } @@ -886,7 +1084,7 @@ public ConversationWebhook updateConversationWebhook(final String id, final Conv throw new IllegalArgumentException("Conversation webhook ID must be specified."); } - String url = this.conversationsBaseUrl + CONVERSATION_WEBHOOK_PATH + "/" + id; + String url = CONVERSATIONS_BASE_URL + CONVERSATION_WEBHOOK_PATH + "/" + id; return messageBirdService.sendPayLoad("PATCH", url, request, ConversationWebhook.class); } @@ -897,7 +1095,7 @@ public ConversationWebhook updateConversationWebhook(final String id, final Conv * @return The retrieved webhook. */ public ConversationWebhook viewConversationWebhook(final String webhookId) throws NotFoundException, GeneralException, UnauthorizedException { - String url = this.conversationsBaseUrl + CONVERSATION_WEBHOOK_PATH; + String url = CONVERSATIONS_BASE_URL + CONVERSATION_WEBHOOK_PATH; return messageBirdService.requestByID(url, webhookId, ConversationWebhook.class); } @@ -908,9 +1106,9 @@ public ConversationWebhook viewConversationWebhook(final String webhookId) throw * @param limit Number of objects to skip. * @return List of webhooks. */ - ConversationWebhookList listConversationWebhooks(final int offset, final int limit) + public ConversationWebhookList listConversationWebhooks(final int offset, final int limit) throws UnauthorizedException, GeneralException { - String url = this.conversationsBaseUrl + CONVERSATION_WEBHOOK_PATH; + String url = CONVERSATIONS_BASE_URL + CONVERSATION_WEBHOOK_PATH; return messageBirdService.requestList(url, offset, limit, ConversationWebhookList.class); } @@ -919,7 +1117,7 @@ ConversationWebhookList listConversationWebhooks(final int offset, final int lim * * @return List of webhooks. */ - public ConversationWebhookList listConversationWebHooks() throws UnauthorizedException, GeneralException { + public ConversationWebhookList listConversationWebhooks() throws UnauthorizedException, GeneralException { final int offset = 0; final int limit = 10; @@ -1099,6 +1297,114 @@ public RecordingResponse viewRecording(String callID, String legId, String recor return messageBirdService.requestByID(url, callID, params, RecordingResponse.class); } + /** + * Downloads the record in .wav format by using callId, legId and recordId and stores to basePath. basePath is not mandatory to set. + * If basePath is not set, default download will be the /Download folder in user group. + * + * @param callID Voice call ID + * @param legId Leg ID + * @param recordingId Recording ID + * @param basePath store location. It should be directory. Property is Optional if $HOME is accessible + * @return the path that file is stored + * @throws NotFoundException if the recording does not found + * @throws GeneralException general exception + * @throws UnauthorizedException if client is unauthorized + */ + public String downloadRecording(String callID, String legId, String recordingId, String basePath) throws NotFoundException, GeneralException, UnauthorizedException { + + if (callID == null) { + throw new IllegalArgumentException("Voice call ID must be specified."); + } + + if (legId == null) { + throw new IllegalArgumentException("Leg ID must be specified."); + } + + if (recordingId == null) { + throw new IllegalArgumentException("Recording ID must be specified."); + } + + String url = String.format( + "%s%s/%s%s/%s%s/%s%s", + VOICE_CALLS_BASE_URL, + VOICECALLSPATH, + callID, + LEGSPATH, + legId, + RECORDINGPATH, + recordingId, + RECORDING_DOWNLOAD_FORMAT + ); + String fileName = String.format("%s%s",recordingId, RECORDING_DOWNLOAD_FORMAT); + return messageBirdService.getBinaryData(url, basePath, fileName); + } + + /** + * List the all recordings related to CallID and LegId + * + * @param callID Voice call ID + * @param legId Leg ID + * @param offset Number of objects to skip. + * @param limit Number of objects to take. + * @return Recordings for CallID and LegID + * @throws GeneralException if client is unauthorized + * @throws UnauthorizedException general exception + */ + public RecordingResponse listRecordings(String callID, String legId, final Integer offset, final Integer limit) + throws GeneralException, UnauthorizedException { + verifyOffsetAndLimit(offset, limit); + if (callID == null) { + throw new IllegalArgumentException("Voice call ID must be specified."); + } + + if (legId == null) { + throw new IllegalArgumentException("Leg ID must be specified."); + } + + String url = String.format( + "%s%s/%s%s/%s%s", + VOICE_CALLS_BASE_URL, + VOICECALLSPATH, + callID, + LEGSPATH, + legId, + RECORDINGPATH); + + return messageBirdService.requestList(url, offset, limit, RecordingResponse.class); + } + + /** + * Deletes a voice recording + * + * @param callID Voice call ID + * @param legID Leg ID + * @param recordingID Recording ID + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public void deleteRecording(final String callID, final String legID, final String recordingID) + throws NotFoundException, GeneralException, UnauthorizedException { + if (callID == null) { + throw new IllegalArgumentException("Call ID must be specified."); + } + if (legID == null) { + throw new IllegalArgumentException("Leg ID must be specified."); + } + if (recordingID == null) { + throw new IllegalArgumentException("Recording ID must be specified."); + } + String url = String.format( + "%s%s/%s%s/%s%s", + VOICE_CALLS_BASE_URL, + VOICECALLSPATH, + callID, + LEGSPATH, + legID, + RECORDINGPATH + ); + messageBirdService.deleteByID(url, recordingID); + } + /** * Function to view recording by call id , leg id and recording id * @@ -1152,11 +1458,56 @@ public TranscriptionResponse createTranscription(String callID, String legId, St * @param callID Voice call ID * @param legId Leg ID * @param recordingId Recording ID - * @return TranscriptionResponseList + * @param transcriptionId Transcription ID + * @return Transcription * @throws UnauthorizedException if client is unauthorized * @throws GeneralException general exception + * @throws NotFoundException If transcription is not found + */ + public TranscriptionResponse viewTranscription(String callID, String legId, String recordingId, String transcriptionId) throws UnauthorizedException, GeneralException, NotFoundException { + if (callID == null) { + throw new IllegalArgumentException("Voice call ID must be specified."); + } + + if (legId == null) { + throw new IllegalArgumentException("Leg ID must be specified."); + } + + if (recordingId == null) { + throw new IllegalArgumentException("Recording ID must be specified."); + } + + if(transcriptionId == null) { + throw new IllegalArgumentException("Transcription ID must be specified."); + } + + String url = String.format( + "%s%s/%s%s/%s%s/%s%s", + VOICE_CALLS_BASE_URL, + VOICECALLSPATH, + callID, + LEGSPATH, + legId, + RECORDINGPATH, + recordingId, + TRANSCRIPTIONPATH); + + return messageBirdService.requestByID(url, transcriptionId, TranscriptionResponse.class); + } + + /** + * Lists the Transcription of callId, legId and recordId + * + * @param callID Voice call ID + * @param legId Leg ID + * @param recordingId Recording ID + * @param page page to fetch (can be null - will return first page), number of first page is 1 + * @param pageSize page size + * @return List of Transcription + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception */ - public TranscriptionResponse viewTranscription(String callID, String legId, String recordingId, Integer page, Integer pageSize) throws UnauthorizedException, GeneralException { + public TranscriptionResponse listTranscriptions(String callID, String legId, String recordingId, Integer page, Integer pageSize) throws UnauthorizedException, GeneralException { if (callID == null) { throw new IllegalArgumentException("Voice call ID must be specified."); } @@ -1170,31 +1521,75 @@ public TranscriptionResponse viewTranscription(String callID, String legId, Stri } String url = String.format( - "%s%s/%s%s/%s%s/%s", + "%s%s/%s%s/%s%s/%s%s", VOICE_CALLS_BASE_URL, VOICECALLSPATH, callID, LEGSPATH, legId, RECORDINGPATH, - recordingId); + recordingId, + TRANSCRIPTIONPATH); return messageBirdService.requestList(url, new PagedPaging(page, pageSize), TranscriptionResponse.class); } /** - * Function to create web hook + * Downloads the transcription in .txt format by using callId, legId, recordId and transcriptionId and stores to basePath. basePath is not mandatory to set. + * If basePath is not set, default download will be the /Download folder in user group. * - * @param webhook title, url and token of webHook - * @return WebHookResponseData + * @param callID Voice call ID + * @param legId Leg ID + * @param recordingId Recording ID + * @param transcriptionId Transcription ID + * @param basePath store location. It should be directory. Property is Optional if $HOME is accessible + * @return the path that file is stored + * @throws NotFoundException if the recording does not found + * @throws GeneralException general exception * @throws UnauthorizedException if client is unauthorized - * @throws GeneralException general exception */ - public WebhookResponseData createWebHook(Webhook webhook) throws UnauthorizedException, GeneralException { - if (webhook.getTitle() == null) { - throw new IllegalArgumentException("Title of webhook must be specified."); + public String downloadTranscription(String callID, String legId, String recordingId, String transcriptionId, String basePath) + throws UnauthorizedException, GeneralException, NotFoundException { + if (callID == null) { + throw new IllegalArgumentException("Voice call ID must be specified."); + } + + if (legId == null) { + throw new IllegalArgumentException("Leg ID must be specified."); + } + + if (recordingId == null) { + throw new IllegalArgumentException("Recording ID must be specified."); + } + + if (transcriptionId == null) { + throw new IllegalArgumentException("Transcription ID must be specified."); } + String fileName = String.format("%s%s", transcriptionId, TRANSCRIPTION_DOWNLOAD_FORMAT); + String url = String.format( + "%s%s/%s%s/%s%s/%s%s/%s", + VOICE_CALLS_BASE_URL, + VOICECALLSPATH, + callID, + LEGSPATH, + legId, + RECORDINGPATH, + recordingId, + TRANSCRIPTIONPATH, + fileName); + + return messageBirdService.getBinaryData(url, basePath, fileName); + } + /** + * Function to create a webhook + * + * @param webhook webhook to create + * @return WebhookResponseData created webhook + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public WebhookResponseData createWebhook(Webhook webhook) throws UnauthorizedException, GeneralException { if (webhook.getUrl() == null) { throw new IllegalArgumentException("URL of webhook must be specified."); } @@ -1204,19 +1599,689 @@ public WebhookResponseData createWebHook(Webhook webhook) throws UnauthorizedExc } /** - * Function to view webhook + * Function to update a webhook + * + * @param webhook webhook fields to update + * @return WebhookResponseData updated webhook + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public WebhookResponseData updateWebhook(String id, Webhook webhook) throws UnauthorizedException, GeneralException { + if (id == null) { + throw new IllegalArgumentException("Id of webhook must be specified."); + } + + String url = String.format("%s%s/%s", VOICE_CALLS_BASE_URL, WEBHOOKS, id); + return messageBirdService.sendPayLoad("PUT", url, webhook, WebhookResponseData.class); + } + + /** + * Function to view a webhook * - * @param id webHook id - * @return WebHookResponseData + * @param id id of a webhook + * @return WebhookResponseData * @throws UnauthorizedException if client is unauthorized * @throws GeneralException general exception */ - public WebhookResponseData viewWebHook(String id) throws NotFoundException, GeneralException, UnauthorizedException { + public WebhookResponseData viewWebhook(String id) throws NotFoundException, GeneralException, UnauthorizedException { if (id == null) { - throw new IllegalArgumentException("Id of webHook must be specified."); + throw new IllegalArgumentException("Id of webhook must be specified."); } String url = String.format("%s%s", VOICE_CALLS_BASE_URL, WEBHOOKS); return messageBirdService.requestByID(url, id, WebhookResponseData.class); } -} \ No newline at end of file + + /** + * Function to list webhooks + * + * @param offset offset for result list + * @param limit limit for result list + * @return WebhookList + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public WebhookList listWebhooks(final Integer offset, final Integer limit) throws UnauthorizedException, GeneralException { + verifyOffsetAndLimit(offset, limit); + + String url = String.format("%s%s", VOICE_CALLS_BASE_URL, WEBHOOKS); + return messageBirdService.requestList(url, offset, limit, WebhookList.class); + } + + /** + * Function to delete a webhook + * + * @param id A unique random ID which is created on the MessageBird platform + * @throws NotFoundException if id is not found + * @throws GeneralException general exception + * @throws UnauthorizedException if client is unauthorized + */ + public void deleteWebhook(String id) throws NotFoundException, GeneralException, UnauthorizedException { + if (id == null) { + throw new IllegalArgumentException("Webhook ID must be specified."); + } + + String url = String.format("%s%s", VOICE_CALLS_BASE_URL, WEBHOOKS); + messageBirdService.deleteByID(url, id); + } + + private void verifyOffsetAndLimit(Integer offset, Integer limit) { + if (offset != null && offset < 0) { + throw new IllegalArgumentException("Offset must be > 0"); + } + if (limit != null && limit < 0) { + throw new IllegalArgumentException("Limit must be > 0"); + } + } + + /** + * Checks whether a particular country code is a recognized ISO Country. + * + * @param countryCode The country code in which the Number should be purchased. + * @throws IllegalArgumentException for invalid country code + */ + private void countryCodeIsValid(String countryCode) throws IllegalArgumentException { + final boolean isValid = Arrays.asList(Locale.getISOCountries()).contains(countryCode); + if (!isValid) { + throw new IllegalArgumentException("Invalid Country Code Provided."); + } + } + + /** + * Lists Numbers that are available to purchase in a particular country code, without any filters. + * + * @param countryCode The country code in which the Number should be purchased. + * @throws GeneralException general exception + * @throws UnauthorizedException if client is unauthorized + * @throws NotFoundException if the resource is missing + * @throws IllegalArgumentException if the country code provided is invalid + */ + public PhoneNumbersResponse listNumbersForPurchase(String countryCode) throws GeneralException, UnauthorizedException, NotFoundException, IllegalArgumentException { + countryCodeIsValid(countryCode); + final String url = String.format("%s/available-phone-numbers", NUMBERS_CALLS_BASE_URL); + return messageBirdService.requestByID(url, countryCode, PhoneNumbersResponse.class); + } + + /** + * Lists Numbers that are available to purchase in a particular country code, according to specified search criteria. + * + * @param countryCode The country code in which the Number should be purchased. + * @param params Parameters to filter the resulting phone numbers returned. + * @throws GeneralException general exception + * @throws UnauthorizedException if client is unauthorized + * @throws NotFoundException if the resource is missing + * @throws IllegalArgumentException if the country code provided is invalid + */ + public PhoneNumbersResponse listNumbersForPurchase(String countryCode, PhoneNumbersLookup params) throws GeneralException, UnauthorizedException, NotFoundException, IllegalArgumentException { + countryCodeIsValid(countryCode); + final String url = String.format("%s/available-phone-numbers", NUMBERS_CALLS_BASE_URL); + return messageBirdService.requestByID(url, countryCode, params.toHashMap(), PhoneNumbersResponse.class); + } + + /** + * Purchases a phone number. To be used in conjunction with listNumbersForPurchase to identify available numbers. + * + * @param number The number to purchase. + * @param countryCode The country code in which the Number should be purchased. + * @throws GeneralException general exception + * @throws UnauthorizedException if client is unauthorized + * @throws IllegalArgumentException if the country code provided is invalid + */ + public PurchasedNumberCreatedResponse purchaseNumber(String number, String countryCode, int billingIntervalMonths) throws UnauthorizedException, GeneralException, IllegalArgumentException { + countryCodeIsValid(countryCode); + final String url = String.format("%s/phone-numbers", NUMBERS_CALLS_BASE_URL); + final Map payload = new LinkedHashMap(); + payload.put("number", number); + payload.put("countryCode", countryCode); + if (!Arrays.asList(1, 3, 6, 9).contains(billingIntervalMonths)) { + throw new IllegalArgumentException("Billing Interval Must Be Either 1, 3, 6, or 9."); + } + payload.put("billingIntervalMonths", billingIntervalMonths); + + return messageBirdService.sendPayLoad(url, payload, PurchasedNumberCreatedResponse.class); + } + + /** + * Lists Numbers that were purchased using the account credentials that the client was initialized with. + * + * @param filter Filters the list of purchased numbers according to search criteria. + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException if the resource is missing + */ + public PurchasedNumbersResponse listPurchasedNumbers(PurchasedNumbersFilter filter) throws UnauthorizedException, GeneralException, NotFoundException { + final String url = String.format("%s/phone-numbers", NUMBERS_CALLS_BASE_URL); + return messageBirdService.requestByID(url, null, filter.toHashMap(), PurchasedNumbersResponse.class); + } + + /** + * Returns a Number that has already been purchased on the initialized account. + * + * @param number The number whose data should be returned. + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException if the Number is missing + */ + public PurchasedNumber viewPurchasedNumber(String number) throws UnauthorizedException, GeneralException, NotFoundException { + final String url = String.format("%s/phone-numbers", NUMBERS_CALLS_BASE_URL); + return messageBirdService.requestByID(url, number, PurchasedNumber.class); + } + + /** + * Updates tags on a particular existing Number. Any number of parameters after the number can be given to apply multiple tags. + * + * @param number The number to update. + * @param tags A tag to apply to the number. + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public PurchasedNumber updateNumber(String number, String... tags) throws UnauthorizedException, GeneralException { + final String url = String.format("%s/phone-numbers/%s", NUMBERS_CALLS_BASE_URL, number); + final Map> payload = new HashMap>(); + payload.put("tags", Arrays.asList(tags)); + return messageBirdService.sendPayLoad("PATCH", url, payload, PurchasedNumber.class); + } + + /** + * Cancels a particular number. + * + * @param number The number to cancel. + * @throws GeneralException general exception + * @throws UnauthorizedException if client is unauthorized + * @throws NotFoundException if the resource is missing + */ + public void cancelNumber(String number) throws UnauthorizedException, GeneralException, NotFoundException { + final String url = String.format("%s/phone-numbers", NUMBERS_CALLS_BASE_URL); + messageBirdService.deleteByID(url, number); + } + + /** + * Uploads a file and returns the assigned ID. + * + * @param binary the bytes of the file to upload. + * @param contentType the content type of the file (e.g. "image/png"). + * @param filename optional filename to set in the upload request headers. + * @return FileUploadResponse + * @throws GeneralException general exception + * @throws UnauthorizedException if client is unauthorized + * @see #downloadFile + */ + public FileUploadResponse uploadFile(byte[] binary, String contentType, String filename) throws GeneralException, UnauthorizedException { + if (binary == null) { + throw new IllegalArgumentException("File binary must be specified."); + } + if (contentType == null) { + throw new IllegalArgumentException("Content type must be specified."); + } + + final String url = MESSAGING_BASE_URL + FILES_PATH; + final Map headers = new HashMap<>(); + headers.put("Content-Type", contentType); + if (filename != null) { + headers.put("filename", filename); + } + return messageBirdService.sendPayLoad("POST", url, headers, binary, FileUploadResponse.class); + } + + /** + * Downloads a file and stores it with the provided filename in the basePath directory. The + * basePath may be null. If basePath is null, the default download directory will be the + * /Download folder in the user home directory. The filename may be null. If filename is null, + * the provided id will be used as the filename instead. + * + * @param id the ID of the file, provided when the file was uploaded + * @param basePath store location. It should be a directory. Property is nullable if $HOME is accessible + * @param filename the name of the file to download to. + * @return the path where the downloaded file is stored + * @throws NotFoundException if the file does not exist + * @throws GeneralException general exception + * @throws UnauthorizedException if client is unauthorized + * @see #uploadFile + */ + public String downloadFile(String id, String filename, String basePath) throws GeneralException, UnauthorizedException, NotFoundException { + if (id == null) { + throw new IllegalArgumentException("File ID must be specified."); + } + + if (filename == null) { + filename = id; + } + + final String url = String.format("%s%s/%s", MESSAGING_BASE_URL, FILES_PATH, id); + return messageBirdService.getBinaryData(url, basePath, filename); + } + + /****************************************************************************************************/ + /** WhatsApp Templates **/ + /****************************************************************************************************/ + + /** + * Create a WhatsApp message template through messagebird. + * + * @param template {@link Template} object to be created + * @return {@link TemplateResponse} response object + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws IllegalArgumentException invalid template format + */ + public TemplateResponse createWhatsAppTemplate(final Template template) + throws UnauthorizedException, GeneralException, IllegalArgumentException { + template.validate(); + + String url = String.format( + "%s%s%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH + ); + return messageBirdService.sendPayLoad(url, template, TemplateResponse.class); + } + + /** + * Update a WhatsApp message template through MessageBird. + * + * @param template {@link Template} object to be created + * @param templateName A name as returned by getWhatsAppTemplateBy in the name variable + * @param language A language code as returned by getWhatsAppTemplateBy in the language variable + * @return {@link TemplateResponse} response object + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws IllegalArgumentException invalid template format + */ + public TemplateResponse updateWhatsAppTemplate(final Template template, final String templateName, final String language) + throws UnauthorizedException, GeneralException, IllegalArgumentException { + template.validate(); + + String url = String.format( + "%s%s%s/%s/%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH, + templateName, + language); + + return messageBirdService.sendPayLoad("PUT",url, template, TemplateResponse.class); + } + /** + * Gets a WhatsAppTemplate listing with specified pagination options. + * + * @param offset Number of objects to skip. + * @param limit Number of objects to take. + * @return List of templates. + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public TemplateList listWhatsAppTemplates(final int offset, final int limit) + throws UnauthorizedException, GeneralException { + String url = String.format( + "%s%s%s", + INTEGRATIONS_BASE_URL_V3, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH + ); + return messageBirdService.requestList(url, offset, limit, TemplateList.class); + } + + /** + * Gets a WhatsAppTemplate listing with specified pagination options and a wabaID or channelID filter. + * + * @param offset Number of objects to skip. + * @param limit Number of objects to take. + * @param wabaID The WABA ID to filter templates by. + * @param channelID A channel ID filter to return only templates that can be sent via that channel. + * @return List of templates. + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws IllegalArgumentException if the provided arguments are not valid + */ + public TemplateList listWhatsAppTemplates(final int offset, final int limit, final String wabaID, final String channelID) + throws UnauthorizedException, GeneralException, IllegalArgumentException { + validateWABAIDAndChannelIDArguments(wabaID, channelID); + + Map map = new LinkedHashMap<>(); + if (wabaID != null) map.put("wabaId", wabaID); + if (channelID != null) map.put("channelId", channelID); + String url = String.format( + "%s%s%s", + INTEGRATIONS_BASE_URL_V3, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH + ); + return messageBirdService.requestList(url, map, offset, limit, TemplateList.class); + } + + /** + * Gets a template listing with default pagination options. + * + * @return List of whatsapp templates. + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public TemplateList listWhatsAppTemplates() throws UnauthorizedException, GeneralException { + final int offset = 0; + final int limit = 10; + + return listWhatsAppTemplates(offset, limit); + } + + /** + * Retrieves the template of an existing template name. + * + * @param templateName A name as returned by getWhatsAppTemplateBy in the name variable + * @return {@code List} template list + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException if template name is not found + * @throws IllegalArgumentException if the provided arguments are not valid + */ + public List getWhatsAppTemplatesBy(final String templateName) + throws GeneralException, UnauthorizedException, NotFoundException, IllegalArgumentException { + if (templateName == null) { + throw new IllegalArgumentException("Template name must be specified."); + } + + String url = String.format( + "%s%s%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH + ); + + return messageBirdService.requestByIdAsList(url, templateName, TemplateResponse.class); + } + + /** + * Retrieves the template of an existing template name. + * + * @param templateName A name as returned by getWhatsAppTemplateBy in the name variable + * @param wabaID An optional WABA ID to look for the template ID under. + * @param channelID An optional channel ID to specify. If the template can be sent via the channel, it will return the template. + * + * @return {@code List} template list + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException if template name is not found under the given WABA or cannot be sent under the supplied channel ID + * @throws IllegalArgumentException if the provided arguments are not valid + */ + public List getWhatsAppTemplatesBy(final String templateName, final String wabaID, final String channelID) + throws GeneralException, UnauthorizedException, NotFoundException, IllegalArgumentException { + if (templateName == null) { + throw new IllegalArgumentException("Template name must be specified."); + } + + String id = String.format("%s%s", templateName, getWabaIDOrChannelIDQuery(wabaID, channelID)); + String url = String.format( + "%s%s%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH + ); + + return messageBirdService.requestByIdAsList(url, id, TemplateResponse.class); + } + + /** + * Retrieves the template of an existing template name and language under the first waba connected to the requesting user. + * + * @param templateName A name as returned by getWhatsAppTemplateBy in the name variable + * @param language A language code as returned by getWhatsAppTemplateBy in the language variable + * + * @return {@code TemplateResponse} template + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException if template name and language are not found under the first waba connected to the requesting user. + * @throws IllegalArgumentException if the provided arguments are not valid + */ + public TemplateResponse fetchWhatsAppTemplateBy(final String templateName, final String language) + throws GeneralException, UnauthorizedException, NotFoundException, IllegalArgumentException { + if (templateName == null || language == null) { + throw new IllegalArgumentException("Template name and language must be specified."); + } + + String url = String.format( + "%s%s%s/%s/%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH, + templateName, + language + ); + return messageBirdService.request(url, TemplateResponse.class); + } + + /** + * Retrieves the template of an existing template name and language under a WABA or for a channel. + * + * @param templateName A name as returned by getWhatsAppTemplateBy in the name variable + * @param language A language code as returned by getWhatsAppTemplateBy in the language variable + * @param wabaID An optional WABA ID to look for the template ID under. + * @param channelID An optional channel ID to specify. If the template can be sent via the channel, it will return the template. + * + * @return {@code TemplateResponse} template + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException if template name and language are not found under the given WABA or cannot be sent under the supplied channel ID. + * @throws IllegalArgumentException if the provided arguments are not valid + */ + public TemplateResponse fetchWhatsAppTemplateBy(final String templateName, final String language, final String wabaID, final String channelID) + throws GeneralException, UnauthorizedException, NotFoundException, IllegalArgumentException { + if (templateName == null || language == null) { + throw new IllegalArgumentException("Template name and language must be specified."); + } + + String url = String.format( + "%s%s%s/%s/%s%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH, + templateName, + language, + getWabaIDOrChannelIDQuery(wabaID, channelID) + ); + return messageBirdService.request(url, TemplateResponse.class); + } + + /** + * Validates the WABA ID and Channel ID argument pair. + * + * @param wabaID A WABA ID. + * @param channelID A channel ID. + * @throws IllegalArgumentException if the argument pair is invalid. + */ + private void validateWABAIDAndChannelIDArguments(String wabaID, String channelID) + throws IllegalArgumentException { + if (wabaID == null && channelID == null) { + throw new IllegalArgumentException("wabaID or channelID must be specified"); + } + + if (wabaID != null && channelID != null) { + throw new IllegalArgumentException("only supply wabaID or channelID - not both"); + } + } + + /** + * Validates the WABA ID and Channel ID argument pair and returns a valid query parameter string. + * + * @param wabaID A WABA ID. + * @param channelID A channel ID. + * @throws IllegalArgumentException if the argument pair is invalid. + */ + private String getWabaIDOrChannelIDQuery(String wabaID, String channelID) + throws IllegalArgumentException { + validateWABAIDAndChannelIDArguments(wabaID, channelID); + + String query = ""; + + if (wabaID != null) { + query = String.format("?wabaId=%s", wabaID); + } + if (channelID != null) { + query = String.format("?channelId=%s", channelID); + } + + return query; + } + + /** + * Delete templates of an existing template name. + * + * @param templateName A template name which is created on the MessageBird platform + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException if template name is not found + */ + public void deleteTemplatesBy(final String templateName) + throws UnauthorizedException, GeneralException, NotFoundException { + if (templateName == null) { + throw new IllegalArgumentException("Template name must be specified."); + } + + String url = String.format( + "%s%s%s/%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH, + templateName + ); + messageBirdService.delete(url, null); + } + + public void unpauseTemplatesByTemplateName(final String templateName) + throws UnauthorizedException, GeneralException { + if (templateName == null) { + throw new IllegalArgumentException("Template name must be specified."); + } + + String url = String.format( + "%s%s%s%s/%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH, + UNPAUSE_TEMAPLATE_PATH, + templateName + ); + messageBirdService.sendPayLoad("POST", url, "", null); + } + + /** + * Function to create a child account + * + * @param childAccountRequest of child account to create + * @return ChildAccountResponse created + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public ChildAccountCreateResponse createChildAccount(final ChildAccountRequest childAccountRequest) throws UnauthorizedException, GeneralException { + if (childAccountRequest.getName() == null || childAccountRequest.getName().isEmpty()) { + throw new IllegalArgumentException("Name must be specified."); + } + + String url = String.format("%s%s", PARTNER_ACCOUNTS_BASE_URL, "/child-accounts"); + return messageBirdService.sendPayLoad(url, childAccountRequest, ChildAccountCreateResponse.class); + } + + /** + * Function to update a child account + * + * @param id of child account to update + * @return ChildAccountResponse created + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public ChildAccountResponse updateChildAccount(final String name, final String id) throws UnauthorizedException, GeneralException { + if (name == null) { + throw new IllegalArgumentException("Name must be specified."); + } + + if (id == null) { + throw new IllegalArgumentException("Child account id must be specified."); + } + final ChildAccountRequest childAccountRequest = new ChildAccountRequest(); + childAccountRequest.setName(name); + final String url = String.format("%s/child-accounts/%s", PARTNER_ACCOUNTS_BASE_URL, id); + return messageBirdService.sendPayLoad("PATCH", url, childAccountRequest, ChildAccountResponse.class); + } + + /** + * Function to get a child account + * + * @param id of child account to update + * @return ChildAccountResponse created + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException if id is not found + */ + public ChildAccountDetailedResponse getChildAccountById(final String id) throws UnauthorizedException, GeneralException, NotFoundException { + if (id == null) { + throw new IllegalArgumentException("Child account id must be specified."); + } + return messageBirdService.requestByID(PARTNER_ACCOUNTS_BASE_URL + "/child-accounts", id, ChildAccountDetailedResponse.class); + } + + /** + * Function to get a child account + * + * @return ChildAccountResponse created + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + public List getChildAccounts(final Integer offset, final Integer limit) throws UnauthorizedException, GeneralException { + verifyOffsetAndLimit(offset, limit); + return messageBirdService.requestList(PARTNER_ACCOUNTS_BASE_URL + "/child-accounts", offset, limit, List.class); + } + + /** + * Function to delete a child account + * + * @param id of child account to delete + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException if id is not found + */ + public void deleteChildAccount(final String id) throws UnauthorizedException, GeneralException, NotFoundException { + if (id == null) { + throw new IllegalArgumentException("Child account id must be specified."); + } + + String url = String.format("%s/child-accounts", PARTNER_ACCOUNTS_BASE_URL); + System.out.println("url: " + url); + messageBirdService.deleteByID(url, id); + } + + /** + * Returns outbound pricing for the default SMS configuration for the authenticated account. + * + * @return outbound pricing for the default SMS configuration for the authenticated account + * + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException if pricing information could not be found + * + * @see Pricing API + */ + public OutboundSmsPriceResponse getOutboundSmsPrices() throws GeneralException, UnauthorizedException, NotFoundException { + return messageBirdService.request(OUTBOUND_SMS_PRICING_PATH, OutboundSmsPriceResponse.class); + } + + /** + * Returns outbound SMS pricing for a specific SMPP username. + * + * @param smppUsername the SMPP SystemID provided by MessageBird + * + * @return outbound SMS pricing for the given SMPP username + * + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException if pricing information could not be found for the given SMPP username + * + * @see Pricing API + */ + public OutboundSmsPriceResponse getOutboundSmsPrices(final String smppUsername) throws GeneralException, UnauthorizedException, NotFoundException { + if (smppUsername == null) { + throw new IllegalArgumentException("SMPP username must be specified."); + } + + final String url = String.format(OUTBOUND_SMS_PRICING_SMPP_PATH, smppUsername); + return messageBirdService.request(url, OutboundSmsPriceResponse.class); + } +} diff --git a/api/src/main/java/com/messagebird/MessageBirdService.java b/api/src/main/java/com/messagebird/MessageBirdService.java index 61775534..43201fb7 100644 --- a/api/src/main/java/com/messagebird/MessageBirdService.java +++ b/api/src/main/java/com/messagebird/MessageBirdService.java @@ -1,16 +1,30 @@ package com.messagebird; -import java.util.Map; - import com.messagebird.exceptions.GeneralException; import com.messagebird.exceptions.NotFoundException; import com.messagebird.exceptions.UnauthorizedException; import com.messagebird.objects.PagedPaging; +import java.util.List; +import java.util.Map; /** * Created by rvt on 1/7/15. */ public interface MessageBirdService { + + /** + * Send GET request . It will retrieve a json object R back. + * + * @author ssk910 + * @param request path to the request, for example "/messages/id/language" + * @param clazz Class type to return + * @return new class with returned dataset + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException if id not found + */ + R request(String request, Class clazz) throws UnauthorizedException, GeneralException, NotFoundException; + /** * Execute a object by ID request. It will add the id to the request parameter and retreive a json object R back. * @@ -25,6 +39,20 @@ public interface MessageBirdService { R requestByID(String request, String id, Map params, Class clazz) throws UnauthorizedException, GeneralException, NotFoundException; + /** + * Execute a object by ID request. It will add the id to the request parameter and retrieve a list of an object E back. + * + * @author ssk910 + * @param request path to the request, for example "/messages" + * @param id id of the object to request. id can be null in case request's that don't need a id, for example /balance + * @param elementClass Class type of List to return + * @return new list of elementClass + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException + */ + List requestByIdAsList(String request, String id, Class elementClass) throws UnauthorizedException, GeneralException, NotFoundException; + /** * Delete a object by ID. * @@ -36,6 +64,19 @@ public interface MessageBirdService { */ void deleteByID(String request, String id) throws UnauthorizedException, GeneralException, NotFoundException; + /** + * Send DELETE request. It will retrieve a json object R back. + * + * @author ssk910 + * @param request path to the request, for example "/messages/id/language" + * @param clazz Class type to return + * @return class with returned dataset + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException if id not found + */ + R delete(String request, Class clazz) throws UnauthorizedException, GeneralException, NotFoundException; + /** * Request a List 'of' object. * Allow to request a listMessage or listViewMessages objects. @@ -51,6 +92,22 @@ public interface MessageBirdService { */ R requestList(String request, Integer offset, Integer limit, Class clazz) throws UnauthorizedException, GeneralException; + /** + * Request a List 'of' object. + * Allow to request a listMessage or listViewMessages objects. + * @see com.messagebird.objects.MessageList + * + * @param request request from client + * @param params additional query params + * @param offset offset of data to return + * @param limit limit number of objects, incase you notice you pass in '1' a lot, please consider using requestByID if you know the ID of the message + * @param clazz object type to return + * @return base class + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + R requestList(String request, Map params, Integer offset, Integer limit, Class clazz) throws UnauthorizedException, GeneralException; + /** * Request a List 'of' object. * Allow to request a listMessage or listViewMessages objects. @@ -90,4 +147,31 @@ public interface MessageBirdService { * @throws GeneralException general exception */ R sendPayLoad(String method, String request, P payload, Class clazz) throws UnauthorizedException, GeneralException; + + /** + * Send a payload with the provided method and headers and receive a payload object. + * + * @param method HTTP method to use for the request + * @param request path to the request, for example "/messages" + * @param headers additional headers to set on the request + * @param payload payload to send to the server + * @param clazz object type to return + * @return base class + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + */ + R sendPayLoad(String method, String request, Map headers, P payload, Class clazz) throws UnauthorizedException, GeneralException; + + /** + * Gets the data from the request URL and stores it to basePath/fileName + * + * @param request path to the request, for example "/messages" + * @param basePath base path for storing directory + * @param fileName the fileName that is going to be stored. + * @return the path that file is stored + * @throws UnauthorizedException if client is unauthorized + * @throws GeneralException general exception + * @throws NotFoundException if the file is not found + */ + String getBinaryData(String request, String basePath, String fileName) throws UnauthorizedException, GeneralException, NotFoundException; } diff --git a/api/src/main/java/com/messagebird/MessageBirdServiceImpl.java b/api/src/main/java/com/messagebird/MessageBirdServiceImpl.java index 09a4f758..ef179b45 100644 --- a/api/src/main/java/com/messagebird/MessageBirdServiceImpl.java +++ b/api/src/main/java/com/messagebird/MessageBirdServiceImpl.java @@ -1,6 +1,20 @@ package com.messagebird; -import java.io.*; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.*; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.ErrorReport; +import com.messagebird.objects.PagedPaging; +import org.apache.maven.artifact.versioning.ComparableVersion; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.HttpURLConnection; @@ -10,17 +24,16 @@ import java.nio.charset.StandardCharsets; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.*; - -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.messagebird.exceptions.GeneralException; -import com.messagebird.exceptions.NotFoundException; -import com.messagebird.exceptions.UnauthorizedException; -import com.messagebird.objects.ErrorReport; -import com.messagebird.objects.PagedPaging; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; /** * Implementation of MessageBirdService @@ -42,22 +55,24 @@ public class MessageBirdServiceImpl implements MessageBirdService { private static final String METHOD_GET = "GET"; private static final String METHOD_PATCH = "PATCH"; private static final String METHOD_POST = "POST"; + private static final String METHOD_PUT = "PUT"; - private static final List REQUEST_METHODS = Arrays.asList(METHOD_DELETE, METHOD_GET, METHOD_PATCH, METHOD_POST); - private static final List REQUEST_METHODS_WITH_PAYLOAD = Arrays.asList(METHOD_PATCH, METHOD_POST); + private static final List REQUEST_METHODS = Arrays.asList(METHOD_DELETE, METHOD_GET, METHOD_PATCH, METHOD_POST, METHOD_PUT); + private static final List REQUEST_METHODS_WITH_PAYLOAD = Arrays.asList(METHOD_PATCH, METHOD_POST, METHOD_PUT); private static final String[] PROTOCOL_LISTS = new String[]{"http://", "https://"}; private static final List PROTOCOLS = Arrays.asList(PROTOCOL_LISTS); - // Used when the actual version can not be parsed. - private static final double DEFAULT_JAVA_VERSION = 0.0; + private static final ComparableVersion JAVA_VERSION = getJavaVersion(); // Indicates whether we've overridden HttpURLConnection's behaviour to // allow PATCH requests yet. Also see docs on allowPatchRequestsIfNeeded(). private static boolean isPatchRequestAllowed = false; + private static final int BUFFER_SIZE = 4096; + private final String accessKey; private final String serviceUrl; - private final String clientVersion = "3.0.0"; + private final String clientVersion = "6.3.1"; private final String userAgentString; private Proxy proxy = null; @@ -71,17 +86,20 @@ public MessageBirdServiceImpl(final String accessKey, final String serviceUrl) { this.accessKey = accessKey; this.serviceUrl = serviceUrl; this.userAgentString = determineUserAgentString(); + } - private String determineUserAgentString() { - double javaVersion = DEFAULT_JAVA_VERSION; + private static ComparableVersion getJavaVersion() { try { - javaVersion = getVersion(); - } catch (GeneralException e) { - // Do nothing: leave the version at its default. + String version = System.getProperty("java.version"); + return new ComparableVersion(version); + } catch (IllegalArgumentException e) { + return new ComparableVersion("0.0"); } + } - return String.format("MessageBird Java/%s ApiClient/%s", javaVersion, clientVersion); + private String determineUserAgentString() { + return String.format("MessageBird Java/%s ApiClient/%s", JAVA_VERSION, clientVersion); } /** @@ -93,6 +111,12 @@ public MessageBirdServiceImpl(final String accessKey) { this(accessKey, "https://rest.messagebird.com"); } + @Override + public R request(String request, Class clazz) + throws UnauthorizedException, GeneralException, NotFoundException { + return getJsonData(request, null, "GET", clazz); + } + @Override public R requestByID(String request, String id, Class clazz) throws UnauthorizedException, GeneralException, NotFoundException { String path = ""; @@ -116,11 +140,27 @@ public R requestByID(String request, String id, Map params, return getJsonData(request + path + queryParams, null, "GET", clazz); } + @Override + public List requestByIdAsList(String request, String id, Class elementClass) + throws UnauthorizedException, GeneralException, NotFoundException { + String path = ""; + if (id != null) { + path = "/" + id; + } + + return getJsonDataAsList(request + path, null, "GET", elementClass); + } + @Override public void deleteByID(String request, String id) throws UnauthorizedException, GeneralException, NotFoundException { getJsonData(request + "/" + id, null, "DELETE", null); } + @Override + public R delete(String request, Class clazz) throws UnauthorizedException, GeneralException, NotFoundException { + return getJsonData(request, null, "DELETE", clazz); + } + @Override public R requestList(String request, Integer offset, Integer limit, Class clazz) throws UnauthorizedException, GeneralException { Map map = new LinkedHashMap<>(); @@ -133,6 +173,17 @@ public R requestList(String request, Integer offset, Integer limit, Class } } + @Override + public R requestList(String request, Map params, Integer offset, Integer limit, Class clazz) throws UnauthorizedException, GeneralException { + if (offset != null) params.put("offset", String.valueOf(offset)); + if (limit != null) params.put("limit", String.valueOf(limit)); + try { + return getJsonData(request + "?" + getPathVariables(params), null, "GET", clazz); + } catch (NotFoundException e) { + throw new GeneralException(e); + } + } + @Override public R requestList(String request, PagedPaging pagedPaging, Class clazz) throws UnauthorizedException, GeneralException { Map map = new LinkedHashMap<>(); @@ -153,19 +204,54 @@ public R sendPayLoad(String request, P payload, Class clazz) throws Un @Override public R sendPayLoad(String method, String request, P payload, Class clazz) throws UnauthorizedException, GeneralException { + return sendPayLoad(method, request, new HashMap<>(), payload, clazz); + } + + @Override + public R sendPayLoad(String method, String request, Map headers, P payload, Class clazz) throws UnauthorizedException, GeneralException { if (!REQUEST_METHODS_WITH_PAYLOAD.contains(method)) { throw new IllegalArgumentException(String.format(REQUEST_METHOD_NOT_ALLOWED, method)); } try { - return getJsonData(request, payload, method, clazz); + return getJsonData(request, payload, method, headers, clazz); } catch (NotFoundException e) { throw new GeneralException(e); } } + @Override + public String getBinaryData(String request, String basePath, String fileName) throws GeneralException, UnauthorizedException, NotFoundException { + if (basePath == null) { + String homePath = System.getProperty("user.home"); + //Home path is not existing + if (homePath == null) { + throw new IllegalArgumentException("BasePath must be specified."); + } + basePath = String.format("%s/%s",homePath,"Downloads"); + } + File file = new File(basePath); + if(!file.exists()) { + throw new IllegalArgumentException("basePath must be existed as directory."); + } + + if(!file.isDirectory()) { + throw new IllegalArgumentException("basePath must be a directory."); + } + String filePath = String.format("%s/%s", basePath, fileName); + return doGetRequestForFileAndStore(request, filePath); + } + public T getJsonData(final String request, final P payload, final String requestType, final Class clazz) throws UnauthorizedException, GeneralException, NotFoundException { + return getJsonData(request, payload, requestType, new HashMap<>(), clazz); + } + + public List getJsonDataAsList(final String request, final P payload, final String requestType, final Class elementClass) throws UnauthorizedException, GeneralException, NotFoundException { + return getJsonDataAsList(request, payload, requestType, new HashMap<>(), elementClass); + } + + public T getJsonData(final String request, final P payload, final String requestType, final Map headers, final Class clazz) throws UnauthorizedException, GeneralException, NotFoundException { if (request == null) { throw new IllegalArgumentException(REQUEST_VALUE_MUST_BE_SPECIFIED); } @@ -174,26 +260,84 @@ public T getJsonData(final String request, final P payload, final String if (!isURLAbsolute(url)) { url = serviceUrl + url; } - - final APIResponse apiResponse = doRequest(requestType, url, payload); + final APIResponse apiResponse = doRequest(requestType, url, headers, payload); final String body = apiResponse.getBody(); final int status = apiResponse.getStatus(); - if (status == HttpURLConnection.HTTP_OK || status == HttpURLConnection.HTTP_CREATED) { - final ObjectMapper mapper = new ObjectMapper(); + if (status == HttpURLConnection.HTTP_OK || status == HttpURLConnection.HTTP_CREATED || status == HttpURLConnection.HTTP_ACCEPTED) { + try { + final ObjectMapper mapper = new ObjectMapper(); + // If we as new properties, we don't want the system to fail, we rather want to ignore them + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + // Enable case insensitivity to avoid parsing errors if parameters' case in api response doesn't match sdk's + mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES); + mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS); + + // Prevents mismatched exception when clazz is null + return clazz == null + ? null + : this.readValue(mapper, body, clazz); + } catch (IOException ioe) { + throw new GeneralException(ioe); + } + } else if (status == HttpURLConnection.HTTP_NO_CONTENT) { + return null; // no content doesn't mean an error + } + handleHttpFailStatuses(status, body); + return null; + } + + // todo: need to refactor for duplicated code. + public List getJsonDataAsList(final String request, + final P payload, final String requestType, final Map headers, final Class elementClass) + throws UnauthorizedException, GeneralException, NotFoundException { + if (request == null) { + throw new IllegalArgumentException(REQUEST_VALUE_MUST_BE_SPECIFIED); + } - // If we as new properties, we don't want the system to fail, we rather want to ignore them - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + String url = request; + if (!isURLAbsolute(url)) { + url = serviceUrl + url; + } + final APIResponse apiResponse = doRequest(requestType, url, headers, payload); + final String body = apiResponse.getBody(); + final int status = apiResponse.getStatus(); + + if (status == HttpURLConnection.HTTP_OK || status == HttpURLConnection.HTTP_CREATED || status == HttpURLConnection.HTTP_ACCEPTED) { try { - return mapper.readValue(body, clazz); + final ObjectMapper mapper = new ObjectMapper(); + // If we as new properties, we don't want the system to fail, we rather want to ignore them + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + // Enable case insensitivity to avoid parsing errors if parameters' case in api response doesn't match sdk's + mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES); + mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS); + + // Prevents mismatched exception when clazz is null + return this.readValueAsList(mapper, body, elementClass); } catch (IOException ioe) { throw new GeneralException(ioe); } } else if (status == HttpURLConnection.HTTP_NO_CONTENT) { - return null; // no content doesn't mean an error - } else if (status == HttpURLConnection.HTTP_UNAUTHORIZED) { + return Collections.emptyList(); // no content doesn't mean an error + } + handleHttpFailStatuses(status, body); + return Collections.emptyList(); + } + + private T readValue(ObjectMapper mapper, String content, Class clazz) + throws JsonProcessingException { + return mapper.readValue(content, clazz); + } + + private List readValueAsList(ObjectMapper mapper, String content, final Class elementClass) + throws JsonProcessingException { + return mapper.readValue(content, mapper.getTypeFactory().constructCollectionType(List.class, elementClass)); + } + + private void handleHttpFailStatuses(final int status, String body) throws UnauthorizedException, NotFoundException, GeneralException { + if (status == HttpURLConnection.HTTP_UNAUTHORIZED) { final List errorReport = getErrorReportOrNull(body); throw new UnauthorizedException(NOT_AUTHORISED_MSG, errorReport); } else if (status >= 400 && status < 500) { // Any code in the 400 range will have a list of error codes attached @@ -206,17 +350,17 @@ public T getJsonData(final String request, final P payload, final String throw new GeneralException(FAILED_DATA_RESPONSE_CODE + status, status); } } - /** * Actually sends a HTTP request and returns its body and HTTP status code. * * @param method HTTP method. * @param url Absolute URL. + * @param headers additional headers to set on the request. * @param payload Payload to JSON encode for the request body. May be null. * @param

Type of the payload. * @return APIResponse containing the response's body and status. */ -

APIResponse doRequest(final String method, final String url, final P payload) throws GeneralException { +

APIResponse doRequest(final String method, final String url, final Map headers, final P payload) throws GeneralException { HttpURLConnection connection = null; InputStream inputStream = null; @@ -231,13 +375,16 @@

APIResponse doRequest(final String method, final String url, final P payload } try { - connection = getConnection(url, payload, method); + connection = getConnection(url, payload, method, headers); int status = connection.getResponseCode(); if (APIResponse.isSuccessStatus(status)) { inputStream = connection.getInputStream(); } else { inputStream = connection.getErrorStream(); + if (inputStream == null) { + throw new IOException("Server returned HTTP error code " + status + " with no body."); + } } return new APIResponse(readToEnd(inputStream), status); @@ -252,6 +399,66 @@

APIResponse doRequest(final String method, final String url, final P payload } } + /** + * + * Do get request for file from input url and stores the file in filepath. + * @param url Absolute URL. + * @param filePath the path where the downloaded file is going to be stored. + * @return if it succeed, it returns filepath otherwise null or exception. + */ + private String doGetRequestForFileAndStore(final String url, final String filePath) throws GeneralException, UnauthorizedException, NotFoundException { + HttpURLConnection connection = null; + InputStream inputStream = null; + + try { + connection = getConnection(url, null, METHOD_GET); + int status = connection.getResponseCode(); + + if (APIResponse.isSuccessStatus(status)) { + inputStream = connection.getInputStream(); + } else { + inputStream = connection.getErrorStream(); + if (inputStream == null) { + throw new GeneralException("Error stream was empty"); + } + } + if (status == HttpURLConnection.HTTP_OK) { + return writeInputStreamToFile(inputStream, filePath); + } + String body = readToEnd(inputStream); + handleHttpFailStatuses(status, body); + } catch (IOException ioe) { + throw new GeneralException(ioe); + } finally { + saveClose(inputStream); + if (connection != null) { + connection.disconnect(); + } + } + return null; + } + + /** + * Writes input stream from IO to filepath. + * @param inputStream stream that has been collected file input + * @param filepath the storage path for the file + * @return if it succeed, it returns filepath otherwise null or exception. + * @throws IOException + */ + private String writeInputStreamToFile(InputStream inputStream, String filepath) throws IOException { + // opens an output stream to save into file + FileOutputStream outputStream = new FileOutputStream(filepath); + + int bytesRead = -1; + byte[] buffer = new byte[BUFFER_SIZE]; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + + outputStream.close(); + return filepath; + } + /** * By default, HttpURLConnection does not support PATCH requests. We can * however work around this with reflection. Many thanks to okutane on @@ -302,6 +509,7 @@ private static String[] getAllowedMethods(String[] existingMethods) { allowedMethods.addAll(Arrays.asList(existingMethods)); allowedMethods.add(METHOD_PATCH); + allowedMethods.add(METHOD_PUT); return allowedMethods.toArray(new String[0]); } @@ -339,18 +547,32 @@ private boolean isURLAbsolute(String url) { * Create a HttpURLConnection connection object * * @param serviceUrl URL that needs to be requested - * @param postData PostDATA, must be not null for requestType is POST + * @param body body could not be empty for POST or PUT requests * @param requestType Request type POST requests without a payload will generate a exception * @return base class * @throws IOException io exception */ - public

HttpURLConnection getConnection(final String serviceUrl, final P postData, final String requestType) throws IOException { + public

HttpURLConnection getConnection(final String serviceUrl, final P body, final String requestType) throws IOException { + return getConnection(serviceUrl, body, requestType, new HashMap<>()); + } + + /** + * Create a HttpURLConnection connection object + * + * @param serviceUrl URL that needs to be requested + * @param body body could not be empty for POST or PUT requests + * @param requestType Request type POST requests without a payload will generate a exception + * @param headers additional headers to set on the request + * @return base class + * @throws IOException io exception + */ + public

HttpURLConnection getConnection(final String serviceUrl, final P body, final String requestType, final Map headers) throws IOException { if (requestType == null || !REQUEST_METHODS.contains(requestType)) { throw new IllegalArgumentException(String.format(REQUEST_METHOD_NOT_ALLOWED, requestType)); } - if (postData == null && "POST".equals(requestType)) { - throw new IllegalArgumentException("POST detected without a payload, please supply a payload with a POST request"); + if (body == null && ("POST".equals(requestType) || "PUT".equals(requestType))) { + throw new IllegalArgumentException("Empty body is not allowed for POST or PUT requests"); } final URL restService = new URL(serviceUrl); @@ -368,75 +590,74 @@ public

HttpURLConnection getConnection(final String serviceUrl, final P post connection.setRequestProperty("Authorization", "AccessKey " + accessKey); connection.setRequestProperty("User-agent", userAgentString); - if ("POST".equals(requestType) || "PATCH".equals(requestType)) { + if ("POST".equals(requestType) || "PUT".equals(requestType) || "PATCH".equals(requestType)) { connection.setRequestMethod(requestType); connection.setDoOutput(true); connection.setRequestProperty("Content-Type", "application/json"); ObjectMapper mapper = new ObjectMapper(); mapper.setSerializationInclusion(Include.NON_NULL); - // Specifically set the date format for POST requests so scheduled // messages and other things relying on specific date formats don't // fail when sending. DateFormat df = getDateFormat(); mapper.setDateFormat(df); - final String json = mapper.writeValueAsString(postData); - connection.getOutputStream().write(json.getBytes(String.valueOf(StandardCharsets.UTF_8))); + setAdditionalHeaders(connection, headers); + + byte[] bodyBytes; + if (body instanceof byte[]) { + bodyBytes = (byte[]) body; + } else { + final String json = mapper.writeValueAsString(body); + bodyBytes = json.getBytes(StandardCharsets.UTF_8); + } + connection.getOutputStream().write(bodyBytes); } else if ("DELETE".equals(requestType)) { // could have just used rquestType as it is connection.setDoOutput(false); connection.setRequestMethod("DELETE"); connection.setRequestProperty("Content-Type", "text/plain"); + + setAdditionalHeaders(connection, headers); } else { connection.setDoOutput(false); connection.setRequestMethod("GET"); connection.setRequestProperty("Content-Type", "text/plain"); + + setAdditionalHeaders(connection, headers); } return connection; } - private DateFormat getDateFormat() { - double javaVersion = DEFAULT_JAVA_VERSION; - try { - javaVersion = getVersion(); - } catch (GeneralException e) { - // Do nothing: leave the version at its default. + private void setAdditionalHeaders(HttpURLConnection connection, Map headers) { + for (Map.Entry header : headers.entrySet()) { + connection.setRequestProperty(header.getKey(), header.getValue()); } + } - if (javaVersion > 1.6) { + private DateFormat getDateFormat() { + ComparableVersion java6 = new ComparableVersion("1.6"); + if (JAVA_VERSION.compareTo(java6) > 0) { return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); } return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZ"); } - private double getVersion() throws GeneralException { - String version = System.getProperty("java.version"); - - try { - int pos = version.indexOf('.'); - pos = version.indexOf('.', pos + 1); - - return Double.parseDouble(version.substring(0, pos)); - } catch (RuntimeException e) { - // Thrown if the index is out of bounds, or when we can't parse a - // double for some reason. - throw new GeneralException(e); - } - } - /** * Get the MessageBird error report data. * - * @param body Raw request body. + * @param body Raw response body. * @return Error report, or null if the body can not be deserialized. */ private List getErrorReportOrNull(final String body) { ObjectMapper objectMapper = new ObjectMapper(); - try { JsonNode jsonNode = objectMapper.readValue(body, JsonNode.class); + if(!jsonNode.has("errors")) { + return null; + } + ErrorReport[] errors = objectMapper.readValue(jsonNode.get("errors").toString(), ErrorReport[].class); List result = Arrays.asList(errors); @@ -523,6 +744,17 @@ private void saveClose(final InputStream is) { } } + /** + * Encodes a key/value pair with percent encoding. + * + * @param key the key name to be used + * @param value the value to be assigned to that key + * @return String + */ + private String encodeKeyValuePair(String key, Object value) throws UnsupportedEncodingException { + return URLEncoder.encode(key, String.valueOf(StandardCharsets.UTF_8)) + "=" + URLEncoder.encode(String.valueOf(value), String.valueOf(StandardCharsets.UTF_8)); + } + /** * Build a path variable for GET requests * @@ -536,7 +768,30 @@ private String getPathVariables(final Map map) { bpath.append("&"); } try { - bpath.append(URLEncoder.encode(param.getKey(), String.valueOf(StandardCharsets.UTF_8))).append("=").append(URLEncoder.encode(String.valueOf(param.getValue()), String.valueOf(StandardCharsets.UTF_8))); + // Check to see if the value is a Collection + if (param.getValue() instanceof Collection) { + // If it is, cast the value as a Collection explicitly + // so it can be iterated over. Its values should be + // appended to the querystring parameters using the + // original key provided (e.g., ?features=sms&features=mms) + Collection col = (Collection) param.getValue(); + Iterator iterator = col.iterator(); + int count = 0; + // While there are still remaining iterables + while (iterator.hasNext()) { + // Append & if not the first iterable + if (count > 0) { + bpath.append("&"); + } + // Append the encoded querystring key/value pair. + // the value is returned from the next() call + bpath.append(encodeKeyValuePair(param.getKey(), iterator.next())); + count++; + } + } else { + // If the value is not a collection, create the querystring value directly. + bpath.append(encodeKeyValuePair(param.getKey(), param.getValue())); + } } catch (UnsupportedEncodingException exception) { // Do nothing } diff --git a/api/src/main/java/com/messagebird/Request.java b/api/src/main/java/com/messagebird/Request.java index 41d9283b..46048b0e 100644 --- a/api/src/main/java/com/messagebird/Request.java +++ b/api/src/main/java/com/messagebird/Request.java @@ -5,7 +5,10 @@ /** * Holds request data needed to calculate a signature hash for incoming * webhooks. + * + * @deprecated This class is being deprecated together with {@link RequestSigner} */ +@Deprecated public class Request { private final String timestamp; @@ -17,11 +20,13 @@ public class Request { /** * Constructs a new request instance. * - * @param timestamp Timestamp provided in the MessageBird-Request-Timestamp - * header. + * @param timestamp Timestamp provided in the MessageBird-Request-Timestamp + * header. * @param queryParameters Query parameters in abc=foo&def=ghi format. - * @param data Raw body of this request. + * @param data Raw body of this request. + * @deprecated */ + @Deprecated public Request(String timestamp, String queryParameters, byte[] data) { if (timestamp == null || timestamp.isEmpty()) { throw new IllegalArgumentException("Timestamp can not be null or empty"); diff --git a/api/src/main/java/com/messagebird/RequestSigner.java b/api/src/main/java/com/messagebird/RequestSigner.java index e2f91d93..d79e464c 100644 --- a/api/src/main/java/com/messagebird/RequestSigner.java +++ b/api/src/main/java/com/messagebird/RequestSigner.java @@ -4,26 +4,30 @@ import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import java.util.Base64; /** * RequestSigner is used to verify HTTP requests and is an implementation of: * https://developers.messagebird.com/docs/verify-http-requests. Retrieve your * signing key at https://dashboard.messagebird.com/developers/settings. + * + * @deprecated This class is being deprecated. + *

Use {@link RequestValidator} instead.

*/ +@Deprecated public class RequestSigner { private static final String ALGORITHM_SHA256 = "SHA-256"; private static final String ALGORITHM_HMAC_SHA256 = "HmacSHA256"; private static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8; - private SecretKeySpec secret; + private final SecretKeySpec secret; /** * Constructs a new RequestSigner instance. @@ -31,7 +35,9 @@ public class RequestSigner { * @param key Signing key. Can be retrieved through * https://dashboard.messagebird.com/developers/settings. This * is NOT your API key. + * @deprecated Use {@link RequestValidator#RequestValidator(String)} )} instead. */ + @Deprecated public RequestSigner(byte[] key) { this.secret = new SecretKeySpec(key, ALGORITHM_HMAC_SHA256); } @@ -42,13 +48,15 @@ public RequestSigner(byte[] key) { * * @param expectedSignature Signature from the MessageBird-Signature * header in its original base64 encoded state. - * @param request Request containing the values from the incoming webhook. + * @param request Request containing the values from the incoming webhook. * @return True if the computed signature matches the expected signature. + * @deprecated Use {@link RequestValidator#validateSignature(String, String, byte[])} instead. */ + @Deprecated public boolean isMatch(String expectedSignature, Request request) { try { - return isMatch(Base64.decode(expectedSignature), request); - } catch (IOException e) { + return isMatch(Base64.getDecoder().decode(expectedSignature), request); + } catch (IllegalArgumentException e) { throw new RequestSigningException(e); } } @@ -59,9 +67,11 @@ public boolean isMatch(String expectedSignature, Request request) { * * @param expectedSignature Decoded (with base64) signature * from the MessageBird-Signature header - * @param request Request containing the values from the incoming webhook. + * @param request Request containing the values from the incoming webhook. * @return True if the computed signature matches the expected signature. + * @deprecated Use {@link RequestValidator#validateSignature(String, String, byte[])} instead. */ + @Deprecated public boolean isMatch(byte[] expectedSignature, Request request) { return Arrays.equals(computeSignature(request), expectedSignature); } @@ -93,7 +103,7 @@ private byte[] getSha256Hash(byte[] bytes) { /** * Stitches the two arrays together and returns a new one. * - * @param first Start of the new array. + * @param first Start of the new array. * @param second End of the new array. * @return New array based on first and second. */ diff --git a/api/src/main/java/com/messagebird/RequestValidator.java b/api/src/main/java/com/messagebird/RequestValidator.java new file mode 100644 index 00000000..24ae6343 --- /dev/null +++ b/api/src/main/java/com/messagebird/RequestValidator.java @@ -0,0 +1,204 @@ +package com.messagebird; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier.BaseVerification; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.exceptions.SignatureVerificationException; +import com.auth0.jwt.interfaces.Claim; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.auth0.jwt.interfaces.JWTVerifier; +import com.messagebird.exceptions.RequestValidationException; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Clock; + +/** + * RequestValidator validates request signature signed by MessageBird services. + * + * @see Verify HTTP Requests + */ +public class RequestValidator { + + /** + * Signature of signed request is set with header name 'MessageBird-Signature-JWT' + */ + public static final String SIGNATURE_HEADER = "MessageBird-Signature-JWT"; + private static final String ALGORITHM_SHA256 = "SHA-256"; + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', + 'e', 'f'}; + + private final Algorithm HMAC256, HMAC384, HMAC512; + + /** + * This field instructs Validator to not validate url_hash claim. + * It is recommended to not skip URL validation to ensure high security. + * but the ability to skip URL validation is necessary in some cases, e.g. + * your service is behind proxy or when you want to validate it yourself. + * Note that when true, no query parameters should be trusted. + * Defaults to false. + */ + private final boolean skipURLValidation; + + /** + * RequestValidator validates request signature with a customer signature key. + * + * @param signatureKey customer signature key. Can be retrieved through + * Developer Settings. + * This is NOT your API key. + * @see Verify HTTP Requests + */ + public RequestValidator(String signatureKey) { + this(signatureKey, false); + } + + /** + * RequestValidator validates webhook signature with a customer signature key. + * + * @param signatureKey customer signature key. Can be retrieved through + * Developer Settings. + * This is NOT your API key. + * @param skipURLValidation whether url_hash claim validation should be skipped. + * Note that when true, no query parameters should be trusted. + * @see Verify HTTP Requests + */ + public RequestValidator(String signatureKey, boolean skipURLValidation) { + this.HMAC256 = Algorithm.HMAC256(signatureKey); + this.HMAC384 = Algorithm.HMAC384(signatureKey); + this.HMAC512 = Algorithm.HMAC512(signatureKey); + this.skipURLValidation = skipURLValidation; + } + + /** + * Returns raw signature payload after validating a signature successfully, + * otherwise throws {@code RequestValidationException}. + *

+ * This JWT is signed with a MessageBird account unique secret key, ensuring the request is from MessageBird and + * a specific account. + * The JWT contains the following claims: + *

+ *
    + *
  • "url_hash" - the raw URL hashed with SHA256 ensuring the URL wasn't altered.
  • + *
  • "payload_hash" - the raw payload hashed with SHA256 ensuring the payload wasn't altered.
  • + *
  • "jti" - a unique token ID to implement an optional non-replay check (NOT validated by default).
  • + *
  • "nbf" - the not before timestamp.
  • + *
  • "exp" - the expiration timestamp is ensuring that a request isn't captured and used at a later time.
  • + *
  • "iss" - the issuer name, always MessageBird.
  • + *
+ * + * @param clock custom {@link Clock} instance to validate timestamp claims. + * @param signature the actual signature. + * @param url the raw url including the protocol, hostname and query string, + * {@code https://example.com/?example=42}. + * @param requestBody the raw request body. + * @return raw signature payload as {@link DecodedJWT} object. + * @throws RequestValidationException when the signature is invalid. + * @see Verify HTTP Requests + */ + public DecodedJWT validateSignature(Clock clock, String signature, String url, byte[] requestBody) + throws RequestValidationException { + if (signature == null || signature.length() == 0) + throw new RequestValidationException("The signature can not be empty."); + + if (!skipURLValidation && (url == null || url.length() == 0)) + throw new RequestValidationException("The url can not be empty."); + + DecodedJWT jwt = JWT.decode(signature); + + Algorithm algorithm; + switch (jwt.getAlgorithm()) { + case "HS256": + algorithm = HMAC256; + break; + case "HS384": + algorithm = HMAC384; + break; + case "HS512": + algorithm = HMAC512; + break; + default: + throw new RequestValidationException(String.format("The signing method '%s' is invalid.", jwt.getAlgorithm())); + } + + BaseVerification builder = (BaseVerification) JWT.require(algorithm) + .withIssuer("MessageBird") + .ignoreIssuedAt() + .acceptLeeway(1); + + if (!skipURLValidation) + builder.withClaim("url_hash", calculateSha256(url.getBytes())); + + Claim payloadHashClaim = jwt.getClaim("payload_hash"); + boolean payloadHashClaimExist = !(payloadHashClaim.isNull() || payloadHashClaim.isMissing()); + if (requestBody != null && requestBody.length > 0) { + if (!payloadHashClaimExist) { + throw new RequestValidationException("The Claim 'payload_hash' is not set but payload is present."); + } + builder.withClaim("payload_hash", calculateSha256(requestBody)); + } else if (payloadHashClaimExist) { + throw new RequestValidationException("The Claim 'payload_hash' is set but actual payload is missing."); + } + + JWTVerifier verifier = clock == null ? builder.build() : builder.build(clock); + + try { + return verifier.verify(jwt); + } catch (SignatureVerificationException e) { + throw new RequestValidationException("Signature is invalid.", e); + } catch (JWTVerificationException e) { + throw new RequestValidationException(e.getMessage(), e.getCause()); + } + } + + /** + * Returns raw signature payload after validating a signature successfully, + * otherwise throws {@code RequestValidationException}. + * + * @param signature the actual signature. + * @param url the raw url including the protocol, hostname and query string, + * {@code https://example.com/?example=42}. + * @param requestBody the raw request body. + * @return raw signature payload as {@link DecodedJWT} object. + * @throws RequestValidationException when the signature is invalid. + * @see RequestValidator#validateSignature(Clock, String, String, byte[]) + */ + public DecodedJWT validateSignature(String signature, String url, byte[] requestBody) + throws RequestValidationException { + return validateSignature(null, signature, url, requestBody); + } + + /** + * Validates request signature with URL validation disabled. + * Note that no query parameters should be trusted and this only works if {@code RequestValidator} is constructed + * with {@code skipURLValidation} set to true. + * + * @param signature the actual signature. + * @param requestBody the raw request body. + * @return raw signature payload as {@link DecodedJWT} object. + * @throws RequestValidationException when the signature is invalid. + * @see RequestValidator#validateSignature(String, String, byte[]) + */ + public DecodedJWT validateSignature(String signature, byte[] requestBody) + throws RequestValidationException { + return validateSignature(null, signature, null, requestBody); + } + + private static String calculateSha256(byte[] bytes) { + try { + return encodeHex(MessageDigest.getInstance(ALGORITHM_SHA256).digest(bytes)); + } catch (NoSuchAlgorithmException e) { + throw new RequestValidationException(e); + } + } + + private static String encodeHex(final byte[] data) { + final int l = data.length; + final char[] out = new char[l << 1]; + for (int i = 0, j = 0; i < l; i++) { + out[j++] = HEX_DIGITS[(0xF0 & data[i]) >>> 4]; + out[j++] = HEX_DIGITS[0x0F & data[i]]; + } + return new String(out); + } +} diff --git a/api/src/main/java/com/messagebird/exceptions/RequestSigningException.java b/api/src/main/java/com/messagebird/exceptions/RequestSigningException.java index 9f993375..d69a1922 100644 --- a/api/src/main/java/com/messagebird/exceptions/RequestSigningException.java +++ b/api/src/main/java/com/messagebird/exceptions/RequestSigningException.java @@ -2,7 +2,10 @@ /** * Thrown if an error occurs during request signing. + * + * @deprecated This class is being deprecated together with {@link com.messagebird.RequestSigner} */ +@Deprecated public class RequestSigningException extends RuntimeException { public RequestSigningException() { diff --git a/api/src/main/java/com/messagebird/exceptions/RequestValidationException.java b/api/src/main/java/com/messagebird/exceptions/RequestValidationException.java new file mode 100644 index 00000000..f759b211 --- /dev/null +++ b/api/src/main/java/com/messagebird/exceptions/RequestValidationException.java @@ -0,0 +1,22 @@ +package com.messagebird.exceptions; + +/** + * Thrown if an error occurs during request signing. + */ +public class RequestValidationException extends RuntimeException { + + public RequestValidationException() { + } + + public RequestValidationException(String message) { + super(message); + } + + public RequestValidationException(Throwable cause) { + super(cause); + } + + public RequestValidationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/api/src/main/java/com/messagebird/objects/AccessKey.java b/api/src/main/java/com/messagebird/objects/AccessKey.java new file mode 100644 index 00000000..4d529711 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/AccessKey.java @@ -0,0 +1,98 @@ +package com.messagebird.objects; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class AccessKey { + private String id; + @JsonProperty("access_key") + private String accessKey; + private String mode; + private String description; + @JsonProperty("core_user_id") + private int coreUserId; + @JsonProperty("user_id") + private int userId; + @JsonProperty("external_id") + private int externalId; + private List roles; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getAccessKey() { + return accessKey; + } + + public void setAccessKey(String accessKey) { + this.accessKey = accessKey; + } + + public String getMode() { + return mode; + } + + public void setMode(String mode) { + this.mode = mode; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getCoreUserId() { + return coreUserId; + } + + public void setCoreUserId(int coreUserId) { + this.coreUserId = coreUserId; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public int getExternalId() { + return externalId; + } + + public void setExternalId(int externalId) { + this.externalId = externalId; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + @Override + public String toString() { + return "AccessKey{" + + "id='" + id + '\'' + + ", access_key='" + accessKey + '\'' + + ", mod='" + mode + '\'' + + ", description='" + description + '\'' + + ", core_user_id=" + coreUserId + + ", user_id=" + userId + + ", external_id=" + externalId + + ", roles=" + roles + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/Balance.java b/api/src/main/java/com/messagebird/objects/Balance.java index 993bcb6c..ddf653cb 100644 --- a/api/src/main/java/com/messagebird/objects/Balance.java +++ b/api/src/main/java/com/messagebird/objects/Balance.java @@ -14,7 +14,7 @@ public class Balance implements Serializable{ private String payment; private String type; - private Integer amount; + private float amount; public Balance() { } @@ -48,7 +48,7 @@ public String getType() { * The amount of balance of the payment type. When postpaid is your payment method, the amount will be 0. * @return */ - public Integer getAmount() { + public float getAmount() { return amount; } } diff --git a/api/src/main/java/com/messagebird/objects/ChildAccountCreateResponse.java b/api/src/main/java/com/messagebird/objects/ChildAccountCreateResponse.java new file mode 100644 index 00000000..40ec38a4 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/ChildAccountCreateResponse.java @@ -0,0 +1,53 @@ +package com.messagebird.objects; + +import java.util.List; + +public class ChildAccountCreateResponse extends ChildAccountResponse{ + private List accessKeys; + private String signingKey; + private String invoiceAggregation; + private String paymentMoment; + + public List getAccessKeys() { + return accessKeys; + } + + public void setAccessKeys(List accessKeys) { + this.accessKeys = accessKeys; + } + + public String getSigningKey() { + return signingKey; + } + + public void setSigningKey(String signingKey) { + this.signingKey = signingKey; + } + + public String getInvoiceAggregation() { + return invoiceAggregation; + } + + public void setInvoiceAggregation(String invoiceAggregation) { + this.invoiceAggregation = invoiceAggregation; + } + + public String getPaymentMoment() { + return paymentMoment; + } + + public void setPaymentMoment(String paymentMoment) { + this.paymentMoment = paymentMoment; + } + + @Override + public String toString() { + return "ChildAccountCreateResponse{" + + "id='" + getId() + '\'' + + ", name='" + getName() + '\'' + + ", accessKeys=" + accessKeys + '\'' + + ", invoiceAggregation='" + invoiceAggregation + '\'' + + ", paymentMoment='" + paymentMoment + '\'' + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/ChildAccountDetailedResponse.java b/api/src/main/java/com/messagebird/objects/ChildAccountDetailedResponse.java new file mode 100644 index 00000000..e39d5969 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/ChildAccountDetailedResponse.java @@ -0,0 +1,22 @@ +package com.messagebird.objects; + +public class ChildAccountDetailedResponse extends ChildAccountResponse{ + private String invoiceAggregation; + + public String getInvoiceAggregation() { + return invoiceAggregation; + } + + public void setInvoiceAggregation(String invoiceAggregation) { + this.invoiceAggregation = invoiceAggregation; + } + + @Override + public String toString() { + return "ChildAccountDetailedResponse{" + + "id='" + getId() + '\'' + + ", name='" + getName() + '\'' + + ", invoiceAggregation='" + invoiceAggregation + '\'' + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/ChildAccountRequest.java b/api/src/main/java/com/messagebird/objects/ChildAccountRequest.java new file mode 100644 index 00000000..aa0e9e5f --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/ChildAccountRequest.java @@ -0,0 +1,13 @@ +package com.messagebird.objects; + +public class ChildAccountRequest { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/api/src/main/java/com/messagebird/objects/ChildAccountResponse.java b/api/src/main/java/com/messagebird/objects/ChildAccountResponse.java new file mode 100644 index 00000000..de232f05 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/ChildAccountResponse.java @@ -0,0 +1,34 @@ +package com.messagebird.objects; + +import java.io.Serializable; + +public class ChildAccountResponse implements Serializable { + private static final long serialVersionUID = -8605510461438669942L; + + private String id; + private String name; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "ChildAccountResponse{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/DataCodingType.java b/api/src/main/java/com/messagebird/objects/DataCodingType.java index 986cea4a..3f78d21d 100644 --- a/api/src/main/java/com/messagebird/objects/DataCodingType.java +++ b/api/src/main/java/com/messagebird/objects/DataCodingType.java @@ -7,7 +7,8 @@ */ public enum DataCodingType { plain("plain"), - unicode("unicode"); + unicode("unicode"), + auto("auto"); final String value; diff --git a/api/src/main/java/com/messagebird/objects/ErrorReport.java b/api/src/main/java/com/messagebird/objects/ErrorReport.java index 00c74aa9..afd7dcf9 100644 --- a/api/src/main/java/com/messagebird/objects/ErrorReport.java +++ b/api/src/main/java/com/messagebird/objects/ErrorReport.java @@ -1,32 +1,46 @@ package com.messagebird.objects; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.io.Serializable; + /** * When MessageBird returns a 4xx, you will find a list of any error codes in your return dataset. * you will receive a list of errors from the API in such case. * * Created by rvt on 1/5/15. */ -public class ErrorReport { +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class ErrorReport implements Serializable { + + private static final long serialVersionUID = -8611665867089703268L; + private Integer code; private String description; private String parameter; + private String message; public ErrorReport() { } - public ErrorReport(Integer code, String description, String parameter) { + public ErrorReport(Integer code, String description, String parameter, String message) { this.code = code; this.description = description; this.parameter = parameter; + this.message = message; } @Override public String toString() { - return "ErrorReport{" + - "code=" + code + - ", description='" + description + '\'' + - ", parameter='" + parameter + '\'' + - '}'; + String str = "ErrorReport{code=" + code; + if (message != null && !message.isEmpty()) { + str = str.concat(", message='" + message + "'"); + } else { + str = str.concat(", description='" + description + "'"); + str = str.concat(", parameter='" + parameter + "'"); + } + str = str.concat("}"); + return str; } /** @@ -53,4 +67,11 @@ public String getParameter() { return parameter; } + /** + * message not null for only voice API response + * @return + */ + public String getMessage() { + return message; + } } diff --git a/api/src/main/java/com/messagebird/objects/FileUploadResponse.java b/api/src/main/java/com/messagebird/objects/FileUploadResponse.java new file mode 100644 index 00000000..29155a63 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/FileUploadResponse.java @@ -0,0 +1,21 @@ +package com.messagebird.objects; + +public class FileUploadResponse { + + private String id; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public String toString() { + return "FileUploadResponse{" + + "id='" + id + '\'' + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/Language.java b/api/src/main/java/com/messagebird/objects/Language.java index 6ce76027..ccb63351 100644 --- a/api/src/main/java/com/messagebird/objects/Language.java +++ b/api/src/main/java/com/messagebird/objects/Language.java @@ -1,5 +1,7 @@ package com.messagebird.objects; +import com.fasterxml.jackson.annotation.JsonValue; + /** * Created by faizan on 09/12/15. */ @@ -11,7 +13,7 @@ public enum Language { EN_US("en-us"), ES_ES("es-es"), FR_FR("fr-fr"), - RU_RU("ru_ru"), + RU_RU("ru-ru"), ZH_CN("zh-cn"), EN_AU("en-au"), ES_MX("es-mx"), @@ -25,13 +27,21 @@ public enum Language { PT_BR("pt-br"), RO_RO("ro-ro"); - private String code; + final String code; Language(String code) { this.code = code; } + @JsonValue + public String getCode() { + return code; + } + + @Override public String toString() { - return this.code; + return "Language{" + + "code='" + code + '\'' + + '}'; } } diff --git a/api/src/main/java/com/messagebird/objects/ListBase.java b/api/src/main/java/com/messagebird/objects/ListBase.java index 354eafa1..da54e709 100644 --- a/api/src/main/java/com/messagebird/objects/ListBase.java +++ b/api/src/main/java/com/messagebird/objects/ListBase.java @@ -13,6 +13,7 @@ public class ListBase { private Integer limit; private Integer totalCount; private Links links; + private List items; public ListBase() { } @@ -28,7 +29,6 @@ public String toString() { '}'; } - private List items; public Integer getOffset() { return offset; diff --git a/api/src/main/java/com/messagebird/objects/MClassType.java b/api/src/main/java/com/messagebird/objects/MClassType.java index 1f566703..4474b15e 100644 --- a/api/src/main/java/com/messagebird/objects/MClassType.java +++ b/api/src/main/java/com/messagebird/objects/MClassType.java @@ -30,7 +30,6 @@ public Integer toJson() { return getValue(); } - @JsonCreator public static MClassType forValue(String value) { if ("0".equals(value)) { return flash; diff --git a/api/src/main/java/com/messagebird/objects/MessageReference.java b/api/src/main/java/com/messagebird/objects/MessageReference.java index da5fb596..26778292 100644 --- a/api/src/main/java/com/messagebird/objects/MessageReference.java +++ b/api/src/main/java/com/messagebird/objects/MessageReference.java @@ -4,6 +4,7 @@ public class MessageReference { private String href; private int totalCount; + private String lastMessageId; public String getHREF() { return href; @@ -21,11 +22,16 @@ public void setTotalCount(int totalCount) { this.totalCount = totalCount; } + public String getLastMessageId() { + return lastMessageId; + } + @Override public String toString() { return "MessageReference{" + "href='" + href + '\'' + ", totalCount=" + totalCount + + ", lastMessageId='" + lastMessageId + '\'' + '}'; } } diff --git a/api/src/main/java/com/messagebird/objects/MessageResponse.java b/api/src/main/java/com/messagebird/objects/MessageResponse.java index be132d4f..aa104e52 100644 --- a/api/src/main/java/com/messagebird/objects/MessageResponse.java +++ b/api/src/main/java/com/messagebird/objects/MessageResponse.java @@ -1,6 +1,9 @@ package com.messagebird.objects; +import org.jetbrains.annotations.Nullable; + import java.io.Serializable; +import java.math.BigDecimal; import java.math.BigInteger; import java.util.Date; import java.util.List; @@ -194,7 +197,7 @@ public Map getTypeDetails() { static public class Recipients implements Serializable { private static final long serialVersionUID = 547164972757802213L; - + private Integer totalCount; private Integer totalSentCount; private Integer totalDeliveredCount; private Integer totalDeliveryFailedCount; @@ -206,13 +209,18 @@ public Recipients() { @Override public String toString() { return "Recipients{" + - "totalSentCount=" + totalSentCount + + "totalCount=" + totalCount + + ", totalSentCount=" + totalSentCount + ", totalDeliveredCount=" + totalDeliveredCount + ", totalDeliveryFailedCount=" + totalDeliveryFailedCount + ", items=" + items + '}'; } + public Integer getTotalCount() { + return totalCount; + } + /** * The count of recipients that have the message pending (status sent, and buffered). * @@ -258,8 +266,20 @@ static public class Items implements Serializable { private static final long serialVersionUID = -4104837036540050532L; private BigInteger recipient; + private BigInteger originator; private String status; private Date statusDatetime; + private String recipientCountry; + private Integer recipientCountryPrefix; + private String recipientOperator; + private Integer messageLength; + private String statusReason; + @Nullable + private Price price; + private String mccmnc; + private String mcc; + private String mnc; + private int messagePartCount; public Items() { } @@ -268,9 +288,20 @@ public Items() { public String toString() { return "Items{" + "recipient=" + recipient + + ", originator=" + originator + ", status='" + status + '\'' + ", statusDatetime=" + statusDatetime + - "}"; + ", recipientCountry='" + recipientCountry + '\'' + + ", recipientCountryPrefix=" + recipientCountryPrefix + + ", recipientOperator='" + recipientOperator + '\'' + + ", messageLength=" + messageLength + + ", statusReason='" + statusReason + '\'' + + ", price=" + price + + ", mccmnc='" + mccmnc + '\'' + + ", mcc='" + mcc + '\'' + + ", mnc='" + mnc + '\'' + + ", messagePartCount=" + messagePartCount + + '}'; } /** @@ -300,6 +331,84 @@ public Date getStatusDatetime() { return statusDatetime; } + public Price getPrice() { + return price; + } + + public BigInteger getOriginator() { + return originator; + } + + public String getRecipientCountry() { + return recipientCountry; + } + + public Integer getRecipientCountryPrefix() { + return recipientCountryPrefix; + } + + public String getRecipientOperator() { + return recipientOperator; + } + + public Integer getMessageLength() { + return messageLength; + } + + public String getStatusReason() { + return statusReason; + } + + public String getMccmnc() { + return mccmnc; + } + + public String getMcc() { + return mcc; + } + + public String getMnc() { + return mnc; + } + + public int getMessagePartCount() { + return messagePartCount; + } + } + + /** + * Response price of items + */ + static public class Price implements Serializable { + + private static final long serialVersionUID = -4104837036540050532L; + + private BigDecimal amount; + private String currency; + + public Price() { + } + + @Override + public String toString() { + return "Price{" + + "amount=" + amount + + ", currency=" + currency + + "}"; + } + + public float getAmount() { + return amount.floatValue(); + } + + public BigDecimal getAmountDecimal() { + return amount; + } + + public String getCurrency() { + return currency; + } + } } diff --git a/api/src/main/java/com/messagebird/objects/MsgType.java b/api/src/main/java/com/messagebird/objects/MsgType.java index dc9af724..bfc421c8 100644 --- a/api/src/main/java/com/messagebird/objects/MsgType.java +++ b/api/src/main/java/com/messagebird/objects/MsgType.java @@ -7,6 +7,7 @@ */ public enum MsgType { sms("sms"), + mms("mms"), binary("binary"), premium("premium"), flash("flash"); diff --git a/api/src/main/java/com/messagebird/objects/OutboundSmsPrice.java b/api/src/main/java/com/messagebird/objects/OutboundSmsPrice.java new file mode 100644 index 00000000..3733cf69 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/OutboundSmsPrice.java @@ -0,0 +1,60 @@ +package com.messagebird.objects; + +import java.math.BigDecimal; + +public class OutboundSmsPrice { + private BigDecimal price; + private String currencyCode; + private String mccmnc; + private String mcc; + private String mnc; + private String countryName; + private String countryIsoCode; + private String operatorName; + + public BigDecimal getPrice() { + return price; + } + + public String getCurrencyCode() { + return currencyCode; + } + + public String getMccmnc() { + return mccmnc; + } + + public String getMcc() { + return mcc; + } + + public String getMnc() { + return mnc; + } + + public String getCountryName() { + return countryName; + } + + public String getCountryIsoCode() { + return countryIsoCode; + } + + public String getOperatorName() { + return operatorName; + } + + @Override + public String toString() { + return "OutboundSmsPrice{" + + "price=" + price + + ", currencyCode='" + currencyCode + '\'' + + ", mccmnc='" + mccmnc + '\'' + + ", mcc='" + mcc + '\'' + + ", mnc='" + mnc + '\'' + + ", countryName='" + countryName + '\'' + + ", countryIsoCode='" + countryIsoCode + '\'' + + ", operatorName='" + operatorName + '\'' + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/OutboundSmsPriceResponse.java b/api/src/main/java/com/messagebird/objects/OutboundSmsPriceResponse.java new file mode 100644 index 00000000..6d036882 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/OutboundSmsPriceResponse.java @@ -0,0 +1,36 @@ +package com.messagebird.objects; + +import java.util.List; + +public class OutboundSmsPriceResponse { + private int gateway; + private String currencyCode; + private int totalCount; + private List prices; + + public int getGateway() { + return gateway; + } + + public String getCurrencyCode() { + return currencyCode; + } + + public int getTotalCount() { + return totalCount; + } + + public List getPrices() { + return prices; + } + + @Override + public String toString() { + return "OutboundSmsPriceResponse{" + + "gateway=" + gateway + + ", currencyCode='" + currencyCode + '\'' + + ", totalCount=" + totalCount + + ", prices=" + prices + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/PhoneNumber.java b/api/src/main/java/com/messagebird/objects/PhoneNumber.java new file mode 100644 index 00000000..5fdda19f --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/PhoneNumber.java @@ -0,0 +1,50 @@ +package com.messagebird.objects; + +import com.messagebird.objects.PhoneNumberFeature; + +import java.util.EnumSet; + +public class PhoneNumber { + private String number; + private String country; + private String region; + private String locality; + private EnumSet features; + private String type; + + public String getNumber() { + return this.number; + } + + public String getCountry() { + return this.country; + } + + public String getRegion() { + return this.region; + } + + public String getLocality() { + return this.locality; + } + + public EnumSet getFeatures() { + return this.features; + } + + public String getType() { + return this.type; + } + + @Override + public String toString() { + return "PhoneNumber{" + + "number='" + number + "\'" + + ", country='" + country + "\'" + + ", region='" + region + "\'" + + ", locality='" + locality + "\'" + + ", features=" + features + + ", type='" + type + "\'" + + "}"; + } +} diff --git a/api/src/main/java/com/messagebird/objects/PhoneNumberFeature.java b/api/src/main/java/com/messagebird/objects/PhoneNumberFeature.java new file mode 100644 index 00000000..790177c9 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/PhoneNumberFeature.java @@ -0,0 +1,19 @@ +package com.messagebird.objects; + +public enum PhoneNumberFeature { + + SMS("sms"), + MMS("mms"), + VOICE("voice"); + + private String type; + + PhoneNumberFeature(String type) { + this.type = type; + } + + @Override + public String toString() { + return this.type; + } +} \ No newline at end of file diff --git a/api/src/main/java/com/messagebird/objects/PhoneNumberSearchPattern.java b/api/src/main/java/com/messagebird/objects/PhoneNumberSearchPattern.java new file mode 100644 index 00000000..cf0b3099 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/PhoneNumberSearchPattern.java @@ -0,0 +1,18 @@ +package com.messagebird.objects; + +public enum PhoneNumberSearchPattern { + START("start"), + ANYWHERE("anywhere"), + END("end"); + + private String type; + + PhoneNumberSearchPattern(String type) { + this.type = type; + } + + @Override + public String toString() { + return this.type; + } +} \ No newline at end of file diff --git a/api/src/main/java/com/messagebird/objects/PhoneNumberType.java b/api/src/main/java/com/messagebird/objects/PhoneNumberType.java new file mode 100644 index 00000000..82908203 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/PhoneNumberType.java @@ -0,0 +1,18 @@ +package com.messagebird.objects; + +public enum PhoneNumberType { + LANDLINE("landline"), + MOBILE("mobile"), + PREMIUM_RATE("premium_rate"); + + private String type; + + PhoneNumberType(String type) { + this.type = type; + } + + @Override + public String toString() { + return this.type; + } +} \ No newline at end of file diff --git a/api/src/main/java/com/messagebird/objects/PhoneNumbersLookup.java b/api/src/main/java/com/messagebird/objects/PhoneNumbersLookup.java new file mode 100644 index 00000000..8b8396fa --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/PhoneNumbersLookup.java @@ -0,0 +1,93 @@ +package com.messagebird.objects; + +import com.messagebird.exceptions.GeneralException; +import com.messagebird.objects.PhoneNumberFeature; +import com.messagebird.objects.PhoneNumberType; +import com.messagebird.objects.PhoneNumberSearchPattern; + +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashMap; +import java.lang.reflect.Field; + +public class PhoneNumbersLookup { + + private Number number; + private Number limit; + private EnumSet features; + private PhoneNumberType type; + private PhoneNumberSearchPattern searchPattern; + + public Number getNumber() { + return this.number; + } + + public EnumSet getFeatures() { + return this.features; + } + + public PhoneNumberType getType() { + return this.type; + } + + public Number getLimit() { + return this.limit; + } + + public PhoneNumberSearchPattern getSearchPattern() { + return this.searchPattern; + } + + public void setNumber(Number number) { + this.number = number; + } + + public void setFeatures(EnumSet features) { + this.features = features; + } + + public void setFeatures(PhoneNumberFeature... features) { + EnumSet featuresEnum = EnumSet.noneOf(PhoneNumberFeature.class); + featuresEnum.addAll(Arrays.asList(features)); + this.features = featuresEnum; + } + + public void setType(PhoneNumberType type) { + this.type = type; + } + + public void setLimit(Number limit) { + this.limit = limit; + } + + public void setSearchPattern(PhoneNumberSearchPattern searchPattern) { + this.searchPattern = searchPattern; + } + + public HashMap toHashMap() throws GeneralException { + final HashMap map = new HashMap(); + for (Field f: getClass().getDeclaredFields()) { + try { + Object value = f.get(this); + String key = f.getName(); + if (value != null) { + map.put(key, value); + } + } catch (IllegalAccessException exception) { + throw new GeneralException("Error Converting PhoneNumbersLookup Class to HashMap."); + } + } + return map; + } + + @Override + public String toString() { + return "PhoneNumbersLookup{" + + " number='" + number + "'" + + ", features='" + features + "'" + + ", type='" + type + "'" + + ", limit='" + limit + "'" + + ", searchPattern='" + searchPattern + "'" + + "}"; + } +} \ No newline at end of file diff --git a/api/src/main/java/com/messagebird/objects/PhoneNumbersResponse.java b/api/src/main/java/com/messagebird/objects/PhoneNumbersResponse.java new file mode 100644 index 00000000..68e9ef4d --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/PhoneNumbersResponse.java @@ -0,0 +1,38 @@ +package com.messagebird.objects; + +import com.messagebird.objects.PhoneNumber; + +import java.util.List; + +import java.io.Serializable; + +public class PhoneNumbersResponse implements Serializable { + /** + * + */ + private static final long serialVersionUID = 6177098534499444839L; + private Number limit; + private Number offset; + private List items; + + public Number getLimit() { + return this.limit; + } + + public Number getOffset() { + return this.offset; + } + + public List getItems() { + return this.items; + } + + @Override + public String toString() { + return "PhoneNumbersResponse{" + + "limit=" + limit + + ", offset=" + offset + + ", items=" + items + + "}"; + } +} \ No newline at end of file diff --git a/api/src/main/java/com/messagebird/objects/PurchasedNumber.java b/api/src/main/java/com/messagebird/objects/PurchasedNumber.java new file mode 100644 index 00000000..fb07a686 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/PurchasedNumber.java @@ -0,0 +1,30 @@ +package com.messagebird.objects; + +import java.util.List; + +public class PurchasedNumber extends PhoneNumber { + private List tags; + private String status; + + public List getTags() { + return tags; + } + + public String getStatus() { + return status; + } + + @Override + public String toString() { + return "PhoneNumber{" + + "number='" + this.getNumber() + "\'" + + ", country='" + this.getCountry() + "\'" + + ", region='" + this.getRegion() + "\'" + + ", locality='" + this.getLocality() + "\'" + + ", features=" + this.getFeatures() + + ", type='" + this.getType() + "\'" + + ", tags='" + tags + "\'" + + ", status='" + status + "\'" + + "}"; + } +} diff --git a/api/src/main/java/com/messagebird/objects/PurchasedNumberCreatedResponse.java b/api/src/main/java/com/messagebird/objects/PurchasedNumberCreatedResponse.java new file mode 100644 index 00000000..8922db2e --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/PurchasedNumberCreatedResponse.java @@ -0,0 +1,32 @@ +package com.messagebird.objects; + +import java.util.Date; + +public class PurchasedNumberCreatedResponse extends PurchasedNumber { + private Date createdAt; + private Date renewalAt; + + public Date getCreatedAt() { + return createdAt; + } + + public Date getRenewalAt() { + return renewalAt; + } + + @Override + public String toString() { + return "PhoneNumber{" + + "number='" + this.getNumber() + "\'" + + ", country='" + this.getCountry() + "\'" + + ", region='" + this.getRegion() + "\'" + + ", locality='" + this.getLocality() + "\'" + + ", features=" + this.getFeatures() + + ", type='" + this.getType() + "\'" + + ", tags='" + this.getTags() + "\'" + + ", status='" + this.getStatus() + "\'" + + ", createdAt='" + createdAt + "\'" + + ", renewalAt='" + renewalAt + "\'" + + "}"; + } +} diff --git a/api/src/main/java/com/messagebird/objects/PurchasedNumbersFilter.java b/api/src/main/java/com/messagebird/objects/PurchasedNumbersFilter.java new file mode 100644 index 00000000..40c6dcd9 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/PurchasedNumbersFilter.java @@ -0,0 +1,135 @@ +package com.messagebird.objects; + +import com.messagebird.exceptions.GeneralException; + +import java.io.Serializable; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; + +public class PurchasedNumbersFilter implements Serializable { + private int limit = 10; + private int offset = 0; + private EnumSet features = EnumSet.noneOf(PhoneNumberFeature.class); + private ArrayList tags = new ArrayList<>(); + private String number; + private String region; + private String locality; + private PhoneNumberType type; + + public int getLimit() { + return limit; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public int getOffset() { + return offset; + } + + public void setOffset(int offset) { + this.offset = offset; + } + + public EnumSet getFeatures() { + return features; + } + + public void addFeature(PhoneNumberFeature... features) { + Collections.addAll(this.features, features); + } + + public void removeFeature(PhoneNumberFeature... features) { + for (PhoneNumberFeature feature: features) { + this.features.remove(feature); + } + } + + public ArrayList getTags() { + return tags; + } + + public void addTag(String... tags) { + for (String tag: tags) { + if (!this.tags.contains(tag)) { + this.tags.add(tag); + } + } + } + + public void removeTag(String... tags) { + for (String tag: tags) { + this.tags.remove(tag); + } + } + + public void clearTags() { + this.tags.clear(); + } + + public String getNumber() { + return number; + } + + public void setNumber(String number) { + this.number = number; + } + + public String getRegion() { + return region; + } + + public void setRegion(String region) { + this.region = region; + } + + public String getLocality() { + return locality; + } + + public void setLocality(String locality) { + this.locality = locality; + } + + public PhoneNumberType getType() { + return type; + } + + public void setType(PhoneNumberType type) { + this.type = type; + } + + public HashMap toHashMap() throws GeneralException { + final HashMap map = new HashMap(); + for (Field f: getClass().getDeclaredFields()) { + try { + Object value = f.get(this); + String key = f.getName(); + if (value != null) { + map.put(key, value); + } + } catch (IllegalAccessException exception) { + throw new GeneralException("Error converting to HashMap."); + } + } + return map; + } + + @Override + public String toString() { + return "PurchasedNumbersFilter{" + + "limit=" + limit + + ", offset=" + offset + + ", features=" + features + + ", tags=" + tags + + ", number='" + number + '\'' + + ", region='" + region + '\'' + + ", locality='" + locality + '\'' + + ", type=" + type + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/PurchasedNumbersResponse.java b/api/src/main/java/com/messagebird/objects/PurchasedNumbersResponse.java new file mode 100644 index 00000000..b61928ca --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/PurchasedNumbersResponse.java @@ -0,0 +1,42 @@ +package com.messagebird.objects; + +import java.util.List; + +public class PurchasedNumbersResponse { + private int offset; + private int limit; + private int count; + private int totalCount; + private List items; + + public int getOffset() { + return offset; + } + + public int getLimit() { + return limit; + } + + public int getCount() { + return count; + } + + public int getTotalCount() { + return totalCount; + } + + public List getItems() { + return items; + } + + @Override + public String toString() { + return "PurchasedNumbersResponse{" + + "offset=" + offset + + ", limit=" + limit + + ", count=" + count + + ", totalCount=" + totalCount + + ", items=" + items + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/VerifyMessage.java b/api/src/main/java/com/messagebird/objects/VerifyMessage.java new file mode 100644 index 00000000..b2929f11 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/VerifyMessage.java @@ -0,0 +1,35 @@ +package com.messagebird.objects; + +import java.io.Serializable; + +/** + * Created by leandro.pinto on 22/06/15. + */ +public class VerifyMessage implements Serializable { + + private String id; + private String status; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String toString() { + return "VerifyMessage {" + " " + + "id=" + this.id + " " + + "status=" + this.status + " " + + "}"; + } +} diff --git a/api/src/main/java/com/messagebird/objects/VerifyRequest.java b/api/src/main/java/com/messagebird/objects/VerifyRequest.java index 45a6b194..bee684e9 100644 --- a/api/src/main/java/com/messagebird/objects/VerifyRequest.java +++ b/api/src/main/java/com/messagebird/objects/VerifyRequest.java @@ -15,8 +15,10 @@ public class VerifyRequest implements Serializable { private String template; private Integer timeout; private Integer tokenLength; + private Integer maxAttempts; private Gender voice; private Language language; + private String subject; public VerifyRequest(String recipient) { this.recipient = recipient; @@ -54,6 +56,10 @@ public void setType(VerifyType type) { this.type = type; } + public void setType(String type) { + this.type = VerifyType.valueOf(type.toUpperCase()); + } + /** * The datacoding used by the template. * @@ -112,4 +118,19 @@ public void setLanguage(Language language) { this.language = language; } + public void setSubject(String subject) { + this.subject = subject; + } + + public String getSubject() { + return subject; + } + + public Integer getMaxAttempts() { + return maxAttempts; + } + + public void setMaxAttempts(Integer maxAttempts) { + this.maxAttempts = maxAttempts; + } } diff --git a/api/src/main/java/com/messagebird/objects/VerifyType.java b/api/src/main/java/com/messagebird/objects/VerifyType.java index 8965e7a2..630e8e53 100644 --- a/api/src/main/java/com/messagebird/objects/VerifyType.java +++ b/api/src/main/java/com/messagebird/objects/VerifyType.java @@ -9,7 +9,8 @@ public enum VerifyType { FLASH("flash"), SMS("sms"), - TTS("tts"); + TTS("tts"), + EMAIL("email"); final String value; diff --git a/api/src/main/java/com/messagebird/objects/VoiceMessage.java b/api/src/main/java/com/messagebird/objects/VoiceMessage.java index 8d941051..0674851e 100644 --- a/api/src/main/java/com/messagebird/objects/VoiceMessage.java +++ b/api/src/main/java/com/messagebird/objects/VoiceMessage.java @@ -22,6 +22,7 @@ public class VoiceMessage implements MessageBase, Serializable { private VoiceType voice; private Integer repeat; private IfMachineType ifMachine; + private int machineTimeout; private Date scheduledDatetime; public VoiceMessage(String body, List recipients) { @@ -171,6 +172,22 @@ public void setIfMachine(IfMachineType ifMachine) { this.ifMachine = ifMachine; } + /** + * The time (in milliseconds) to analyze if a machine has picked up the phone. + * Used in combination with the delay and hangup values of the ifMachine attribute. + * Minimum: 400, maximum: 10000. Default: 7000 + * @return value of machine timeout + */ + public int getMachineTimeout() { return machineTimeout; } + + /** + * The time (in milliseconds) to analyze if a machine has picked up the phone. + * Used in combination with the delay and hangup values of the ifMachine attribute. + * Minimum: 400, maximum: 10000. Default: 7000 + * @param machineTimeout value of machine timeout + */ + public void setMachineTimeout(int machineTimeout) { this.machineTimeout = machineTimeout; } + @Override public Date getScheduledDatetime() { return scheduledDatetime; diff --git a/api/src/main/java/com/messagebird/objects/VoiceMessageResponse.java b/api/src/main/java/com/messagebird/objects/VoiceMessageResponse.java index fe8d56f1..6563919e 100644 --- a/api/src/main/java/com/messagebird/objects/VoiceMessageResponse.java +++ b/api/src/main/java/com/messagebird/objects/VoiceMessageResponse.java @@ -21,6 +21,7 @@ public class VoiceMessageResponse implements MessageResponseBase, Serializable { private VoiceType voice; private Integer repeat; private IfMachineType ifMachine; + private int machineTimeout; private Date scheduledDatetime; private Date createdDatetime; private MessageResponse.Recipients recipients; @@ -40,6 +41,7 @@ public String toString() { ", voice=" + voice + ", repeat=" + repeat + ", ifMachine=" + ifMachine + + ", machineTimeout=" + machineTimeout + ", scheduledDatetime=" + scheduledDatetime + ", createdDatetime=" + createdDatetime + ", recipients=" + recipients + @@ -114,6 +116,14 @@ public IfMachineType getIfMachine() { return ifMachine; } + /** + * The time (in milliseconds) to analyze if a machine has picked up the phone. + * Used in combination with the delay and hangup values of the ifMachine attribute. + * Minimum: 400, maximum: 10000. Default: 7000 + * @return value of machine timeout + */ + public int getMachineTimeout() { return machineTimeout; } + /** * The scheduled date and time of the message * diff --git a/api/src/main/java/com/messagebird/objects/VoiceStep.java b/api/src/main/java/com/messagebird/objects/VoiceStep.java index 25d56c20..8958f8b8 100644 --- a/api/src/main/java/com/messagebird/objects/VoiceStep.java +++ b/api/src/main/java/com/messagebird/objects/VoiceStep.java @@ -1,6 +1,9 @@ package com.messagebird.objects; +import com.messagebird.objects.voicecalls.VoiceCallCondition; + import java.io.Serializable; +import java.util.Arrays; public class VoiceStep implements Serializable { @@ -10,6 +13,11 @@ public class VoiceStep implements Serializable { private String action; private VoiceStepOption options; + private VoiceCallCondition[] conditions; + + private String onKeypressGoto; + private String onKeypressVar; + public String getId() { return id; } @@ -34,12 +42,43 @@ public void setOptions(VoiceStepOption options) { this.options = options; } + public VoiceCallCondition[] getConditions() { + return conditions; + } + + public void setConditions(VoiceCallCondition[] conditions) { + this.conditions = conditions; + } + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public String getOnKeypressGoto() { + return onKeypressGoto; + } + + public void setOnKeypressGoto(String onKeypressGoto) { + this.onKeypressGoto = onKeypressGoto; + } + + public String getOnKeypressVar() { + return onKeypressVar; + } + + public void setOnKeypressVar(String onKeypressVar) { + this.onKeypressVar = onKeypressVar; + } + @Override public String toString() { return "VoiceStep{" + "id='" + id + '\'' + ", action='" + action + '\'' + ", options=" + options + + ", conditions=" + Arrays.toString(conditions) + + ", onKeypressGoto='" + onKeypressGoto + '\'' + + ", onKeypressVar='" + onKeypressVar + '\'' + '}'; } } diff --git a/api/src/main/java/com/messagebird/objects/VoiceStepOption.java b/api/src/main/java/com/messagebird/objects/VoiceStepOption.java index 179513e3..70c0670b 100644 --- a/api/src/main/java/com/messagebird/objects/VoiceStepOption.java +++ b/api/src/main/java/com/messagebird/objects/VoiceStepOption.java @@ -10,7 +10,7 @@ public class VoiceStepOption implements Serializable { private String payload; private String language; private String voice; - private String repeat; + private int repeat; private String media; private int length; private int maxLength; @@ -23,6 +23,10 @@ public class VoiceStepOption implements Serializable { private String ifMachine; private int machineTimeout; private String onFinish; + private boolean mask; + private String keys; + private int duration; + private int interval; public String getDestination() { return destination; @@ -56,11 +60,11 @@ public void setVoice(String voice) { this.voice = voice; } - public String getRepeat() { + public int getRepeat() { return repeat; } - public void setRepeat(String repeat) { + public void setRepeat(int repeat) { this.repeat = repeat; } @@ -160,6 +164,26 @@ public void setOnFinish(String onFinish) { this.onFinish = onFinish; } + public boolean isMask() { + return mask; + } + + public void setMask(boolean mask) { + this.mask = mask; + } + + public String getKeys() { return keys; } + + public void setKeys(String keys) { this.keys = keys; } + + public int getDuration() { return duration; } + + public void setDuration(int duration) { this.duration = duration; } + + public int getInterval() { return interval; } + + public void setInterval(int interval) { this.interval = interval; } + @Override public String toString() { return "VoiceStepOption{" + @@ -180,6 +204,10 @@ public String toString() { ", ifMachine='" + ifMachine + '\'' + ", machineTimeout=" + machineTimeout + ", onFinish='" + onFinish + '\'' + + ", mask=" + mask + '\'' + + ", keys='" + keys + '\'' + + ", interval='" + interval + '\'' + + ", duration='" + duration + '}'; } } diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationChannel.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationChannel.java index 32db29e6..2c2bd06e 100644 --- a/api/src/main/java/com/messagebird/objects/conversations/ConversationChannel.java +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationChannel.java @@ -11,6 +11,8 @@ public class ConversationChannel { private String id; private String name; + // See: ConversationPlatformConstants + private String platformId; private ConversationChannelStatus status; private Date createdDatetime; private Date updatedDatetime; @@ -55,11 +57,20 @@ public void setUpdatedDatetime(final Date updatedDatetime) { this.updatedDatetime = updatedDatetime; } + public String getPlatformId() { + return platformId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + @Override public String toString() { return "ConversationChannel{" + "id='" + id + '\'' + ", name='" + name + '\'' + + ", platformId=" + platformId + ", status=" + status + ", createdDatetime=" + createdDatetime + ", updatedDatetime=" + updatedDatetime + diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationContent.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationContent.java index 2edfb5b3..8b4fe164 100644 --- a/api/src/main/java/com/messagebird/objects/conversations/ConversationContent.java +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationContent.java @@ -1,9 +1,16 @@ package com.messagebird.objects.conversations; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + /** * ConversationContent wraps actual content. The field that should be set here * is indicated by ConversationContentType. + * + *

Unknown keys are ignored on deserialization so that consumers parsing + * webhook payloads in their own handlers are not broken by content fields added + * after their SDK version. Serialization is unaffected. */ +@JsonIgnoreProperties(ignoreUnknown = true) public class ConversationContent { private ConversationContentMedia audio; @@ -11,6 +18,7 @@ public class ConversationContent { private ConversationContentHsm hsm; private ConversationContentMedia image; private ConversationContentLocation location; + private ConversationContentEmail email; private String text; private ConversationContentMedia video; @@ -70,6 +78,14 @@ public void setVideo(ConversationContentMedia video) { this.video = video; } + public ConversationContentEmail getEmail() { + return email; + } + + public void setEmail(ConversationContentEmail email) { + this.email = email; + } + @Override public String toString() { return "ConversationContent{" + @@ -78,6 +94,7 @@ public String toString() { ", hsm=" + hsm + ", image=" + image + ", location=" + location + + ", email=" + email + ", text='" + text + '\'' + ", video=" + video + '}'; diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationContentEmail.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentEmail.java new file mode 100644 index 00000000..3ba8b2d8 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentEmail.java @@ -0,0 +1,133 @@ +package com.messagebird.objects.conversations; + +import java.util.List; +import java.util.Map; + +public class ConversationContentEmail { + private String id; + private ConversationEmailRecipient from; + private List to; + private String subject; + private ConversationEmailContent content; + private String replyTo; + private String returnPath; + private Map headers; + private ConversationEmailTracking tracking; + private boolean performSubstitutions; + private List attachments; + private List inlineImages; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public ConversationEmailRecipient getFrom() { + return from; + } + + public void setFrom(ConversationEmailRecipient from) { + this.from = from; + } + + public List getTo() { + return to; + } + + public void setTo(List to) { + this.to = to; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public ConversationEmailContent getContent() { + return content; + } + + public void setContent(ConversationEmailContent content) { + this.content = content; + } + + public String getReplyTo() { + return replyTo; + } + + public void setReplyTo(String replyTo) { + this.replyTo = replyTo; + } + + public String getReturnPath() { + return returnPath; + } + + public void setReturnPath(String returnPath) { + this.returnPath = returnPath; + } + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public ConversationEmailTracking getTracking() { + return tracking; + } + + public void setTracking(ConversationEmailTracking tracking) { + this.tracking = tracking; + } + + public boolean isPerformSubstitutions() { + return performSubstitutions; + } + + public void setPerformSubstitutions(boolean performSubstitutions) { + this.performSubstitutions = performSubstitutions; + } + + public List getAttachments() { + return attachments; + } + + public void setAttachments(List attachments) { + this.attachments = attachments; + } + + public List getInlineImages() { + return inlineImages; + } + + public void setInlineImages(List inlineImages) { + this.inlineImages = inlineImages; + } + + @Override + public String toString() { + return "ConversationContentEmail{" + + "id='" + id + '\'' + + ", from=" + from + + ", to=" + to + + ", subject='" + subject + '\'' + + ", content=" + content + + ", replyTo='" + replyTo + '\'' + + ", returnPath='" + returnPath + '\'' + + ", headers=" + headers + + ", tracking=" + tracking + + ", performSubstitutions=" + performSubstitutions + + ", attachments=" + attachments + + ", inlineImages=" + inlineImages + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationContentHsm.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentHsm.java index 3da74c2c..824f7fc8 100644 --- a/api/src/main/java/com/messagebird/objects/conversations/ConversationContentHsm.java +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentHsm.java @@ -18,17 +18,20 @@ public class ConversationContentHsm { private String templateName; private ConversationHsmLanguage language; private List params; + private List components; public ConversationContentHsm( final String namespace, final String templateName, final ConversationHsmLanguage language, - final List params + final List params, + final List components ) { this.namespace = namespace; this.templateName = templateName; this.language = language; this.params = params; + this.components = components; } public ConversationContentHsm( @@ -85,6 +88,14 @@ public void setParams(final List params) { this.params = params; } + public List getComponents() { + return components; + } + + public void setComponents(List components) { + this.components = components; + } + @Override public String toString() { return "ConversationContentHsm{" + @@ -92,6 +103,7 @@ public String toString() { ", templateName='" + templateName + '\'' + ", language=" + language + ", params=" + params + + ", components=" + components + '}'; } } diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationContentMedia.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentMedia.java index b2d1e4ca..7ac29e8f 100644 --- a/api/src/main/java/com/messagebird/objects/conversations/ConversationContentMedia.java +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentMedia.java @@ -7,6 +7,12 @@ public class ConversationContentMedia { private String url; + private String caption; + + public ConversationContentMedia(final String url, final String caption) { + this.url = url; + this.caption = caption; + } public ConversationContentMedia(final String url) { this.url = url; @@ -24,10 +30,19 @@ public void setUrl(final String url) { this.url = url; } + public String getCaption() { + return caption; + } + + public void setCaption(String caption) { + this.caption = caption; + } + @Override public String toString() { return "ConversationContentMedia{" + "url='" + url + '\'' + + ", caption='" + caption + '\'' + '}'; } } diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationContentType.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentType.java index 93f6bb4f..3dec398a 100644 --- a/api/src/main/java/com/messagebird/objects/conversations/ConversationContentType.java +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationContentType.java @@ -15,7 +15,8 @@ public enum ConversationContentType { IMAGE("image"), LOCATION("location"), TEXT("text"), - VIDEO("video"); + VIDEO("video"), + EMAIL("email"); private final String type; diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailAttachment.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailAttachment.java new file mode 100644 index 00000000..82e1a48a --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailAttachment.java @@ -0,0 +1,60 @@ +package com.messagebird.objects.conversations; + +public class ConversationEmailAttachment { + private String id; + private String name; + private String type; + private String URL; + private String length; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getURL() { + return URL; + } + + public void setURL(String URL) { + this.URL = URL; + } + + public String getLength() { + return length; + } + + public void setLength(String length) { + this.length = length; + } + + @Override + public String toString() { + return "ConversationEmailAttachment{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", type='" + type + '\'' + + ", URL='" + URL + '\'' + + ", length='" + length + '\'' + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailContent.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailContent.java new file mode 100644 index 00000000..7127b104 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailContent.java @@ -0,0 +1,30 @@ +package com.messagebird.objects.conversations; + +public class ConversationEmailContent { + private String html; + private String text; + + public String getHtml() { + return html; + } + + public void setHtml(String html) { + this.html = html; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + @Override + public String toString() { + return "ConversationEmailContent{" + + "html='" + html + '\'' + + ", text='" + text + '\'' + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailInlineImage.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailInlineImage.java new file mode 100644 index 00000000..a7318216 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailInlineImage.java @@ -0,0 +1,70 @@ +package com.messagebird.objects.conversations; + +public class ConversationEmailInlineImage { + private String id; + private String name; + private String type; + private String URL; + private int length; + private String contentId; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getURL() { + return URL; + } + + public void setURL(String URL) { + this.URL = URL; + } + + public int getLength() { + return length; + } + + public void setLength(int length) { + this.length = length; + } + + public String getContentId() { + return contentId; + } + + public void setContentId(String contentId) { + this.contentId = contentId; + } + + @Override + public String toString() { + return "ConversationEmailInlineImage{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", type='" + type + '\'' + + ", URL='" + URL + '\'' + + ", length=" + length + + ", contentId='" + contentId + '\'' + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailRecipient.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailRecipient.java new file mode 100644 index 00000000..f5bbdfb9 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailRecipient.java @@ -0,0 +1,42 @@ +package com.messagebird.objects.conversations; + +import java.util.Map; + +public class ConversationEmailRecipient { + private String address; + private String name; + private Map variables; + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map getVariables() { + return variables; + } + + public void setVariables(Map variables) { + this.variables = variables; + } + + @Override + public String toString() { + return "ConversationEmailRecipient{" + + "address='" + address + '\'' + + ", name='" + name + '\'' + + ", variables=" + variables + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailTracking.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailTracking.java new file mode 100644 index 00000000..b8861d14 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationEmailTracking.java @@ -0,0 +1,30 @@ +package com.messagebird.objects.conversations; + +public class ConversationEmailTracking { + private boolean open; + private boolean click; + + public boolean isOpen() { + return open; + } + + public void setOpen(boolean open) { + this.open = open; + } + + public boolean isClick() { + return click; + } + + public void setClick(boolean click) { + this.click = click; + } + + @Override + public String toString() { + return "ConversationEmailTracking{" + + "open=" + open + + ", click=" + click + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationFallbackOption.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationFallbackOption.java new file mode 100644 index 00000000..0048de12 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationFallbackOption.java @@ -0,0 +1,38 @@ +package com.messagebird.objects.conversations; + +public class ConversationFallbackOption { + private String from; + private String after; + + public ConversationFallbackOption() { + } + + public ConversationFallbackOption(String from, String after) { + this.from = from; + this.after = after; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getAfter() { + return after; + } + + public void setAfter(String after) { + this.after = after; + } + + @Override + public String toString() { + return "ConversationFallbackOption{" + + "from='" + from + '\'' + + ", after='" + after + '\'' + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLocalizableParameterCurrency.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLocalizableParameterCurrency.java index b17ecbce..708938ee 100644 --- a/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLocalizableParameterCurrency.java +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationHsmLocalizableParameterCurrency.java @@ -11,10 +11,10 @@ public class ConversationHsmLocalizableParameterCurrency { /** * Instantiates a localizable parameter for currencies. * - * @param code ISO 4217 compliant currency code. + * @param currencyCode ISO 4217 compliant currency code. * @param amount Amount multiplied by 1000. E.g. 12.34 becomes 12340. */ - public ConversationHsmLocalizableParameterCurrency(final String code, final int amount) { + public ConversationHsmLocalizableParameterCurrency(final String currencyCode, final int amount) { this.currencyCode = currencyCode; this.amount = amount; } diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessage.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessage.java index 43b2c275..1dea8280 100644 --- a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessage.java +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessage.java @@ -1,6 +1,7 @@ package com.messagebird.objects.conversations; import java.util.Date; +import java.util.Map; /** * Response object that represents a conversation's message. Messages can be @@ -12,12 +13,20 @@ public class ConversationMessage { private String id; private String conversationId; private String channelId; + private String trackId; private ConversationMessageDirection direction; private ConversationMessageStatus status; private ConversationContentType type; private ConversationContent content; private Date createdDatetime; private Date updatedDatetime; + private Map source; + private ConversationMessageTag tag; + private ConversationMessageMetadata metadata; + /** + * See: {@link ConversationPlatformConstants} + */ + private String platform; public String getId() { return id; @@ -91,6 +100,46 @@ public void setUpdatedDatetime(Date updatedDatetime) { this.updatedDatetime = updatedDatetime; } + public Map getSource() { + return source; + } + + public void setSource(Map source) { + this.source = source; + } + + public ConversationMessageTag getTag() { + return tag; + } + + public void setTag(ConversationMessageTag tag) { + this.tag = tag; + } + + public ConversationMessageMetadata getMetadata() { + return metadata; + } + + public void setMetadata(ConversationMessageMetadata metadata) { + this.metadata = metadata; + } + + public String getPlatform() { + return platform; + } + + public void setPlatform(String platform) { + this.platform = platform; + } + + public String getTrackId() { + return trackId; + } + + public void setTrackId(String trackId) { + this.trackId = trackId; + } + @Override public String toString() { return "ConversationMessage{" + @@ -100,9 +149,14 @@ public String toString() { ", direction=" + direction + ", status=" + status + ", type=" + type + + ", trackID=" + trackId + ", content=" + content + ", createdDatetime=" + createdDatetime + ", updatedDatetime=" + updatedDatetime + + ", source=" + source + + ", tag=" + tag + + ", metadata=" + metadata + + ", platform='" + platform + '\'' + '}'; } -} +} \ No newline at end of file diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageMetadata.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageMetadata.java new file mode 100644 index 00000000..c41e814b --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageMetadata.java @@ -0,0 +1,42 @@ +package com.messagebird.objects.conversations; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import java.util.Date; + +/** + * Inner metadata attached to a conversation message. Present on both incoming + * messages and status webhook payloads. {@code sender.userId} always contains + * the BSUID when Meta provides one. When both identifiers exist, the phone + * number appears in the parent {@code from} field, not in this object. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class ConversationMessageMetadata { + + private ConversationSenderMetadata sender; + private Date receivedAt; + + public ConversationSenderMetadata getSender() { + return sender; + } + + public void setSender(ConversationSenderMetadata sender) { + this.sender = sender; + } + + public Date getReceivedAt() { + return receivedAt; + } + + public void setReceivedAt(Date receivedAt) { + this.receivedAt = receivedAt; + } + + @Override + public String toString() { + return "ConversationMessageMetadata{" + + "sender=" + sender + + ", receivedAt=" + receivedAt + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageRequest.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageRequest.java index 5607c147..420213e3 100644 --- a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageRequest.java +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageRequest.java @@ -1,5 +1,7 @@ package com.messagebird.objects.conversations; +import java.util.Map; + /** * Request object that is used to send new messages over a channel. */ @@ -8,6 +10,10 @@ public class ConversationMessageRequest { private ConversationContentType type; private ConversationContent content; private String channelId; + private String reportUrl; + private String trackId; + private String ttl; + private Map source; public ConversationContentType getType() { return type; @@ -33,12 +39,47 @@ public void setChannelId(String channelId) { this.channelId = channelId; } + public String getReportUrl() { + return reportUrl; + } + + public void setReportUrl(String reportUrl) { + this.reportUrl = reportUrl; + } + + public Map getSource() { + return source; + } + + public void setSource(Map source) { + this.source = source; + } + + public String getTrackId() { + return trackId; + } + + public void setTrackId(String trackId) { + this.trackId = trackId; + } + public String getTtl() { + return ttl; + } + + public void setTtl(String ttl) { + this.ttl = ttl; + } + @Override public String toString() { return "ConversationMessageRequest{" + "type=" + type + ", content=" + content + ", channelId='" + channelId + '\'' + + ", reportUrl='" + reportUrl + '\'' + + ", source=" + source + + ", trackID=" + trackId + + ", ttl=" + ttl + '}'; } -} +} \ No newline at end of file diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageStatus.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageStatus.java index 475c7b74..aee5c5f5 100644 --- a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageStatus.java +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageStatus.java @@ -15,7 +15,25 @@ public enum ConversationMessageStatus { READ("read"), RECEIVED("received"), SENT("sent"), - UNSUPPORTED("unsupported"); + UNSUPPORTED("unsupported"), + ACCEPTED("accepted"), + REJECTED("rejected"), + UNKNOWN("unknown"), + //WA specific statuses + TRANSMITTED("transmitted"), + //SMS specific statuses + DELIVERY_FAILED("delivery_failed"), + BUFFERED("buffered"), + EXPIRED("expired"), + //Email specific statuses + CLICKED("clicked"), + OPENED("opened"), + BOUNCE("bounce"), + SPAM_COMPLAINT("spam_complaint"), + OUT_OF_BOUNDED("out_of_bounded"), + DELAYED("delayed"), + LIST_UNSUBSCRIBE("list_unsubscribe"), + DISPATCHED("dispatched"); private final String status; diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageTag.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageTag.java new file mode 100644 index 00000000..49314b3c --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationMessageTag.java @@ -0,0 +1,41 @@ +package com.messagebird.objects.conversations; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * These allow tagging a message based on Facebook tags. + * For more information visit: https://developers.facebook.com/docs/messenger-platform/send-messages/message-tags/ + */ +public enum ConversationMessageTag { + EventUpdate("event.update"), + PurchaseUpdate("purchase.update"), + AccountUpdate("account.update"), + HumanAgent("human_agent"); + + @JsonValue + private final String tag; + + ConversationMessageTag(String tag) { + this.tag = tag; + } + + @JsonCreator + public static ConversationMessageTag forValue(final String value) { + for (ConversationMessageTag tag : ConversationMessageTag.values()) { + if (tag.getTag().equals(value)) { + return tag; + } + } + return null; + } + + public String getTag() { + return tag; + } + + @Override + public String toString() { + return tag; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationPlatformConstants.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationPlatformConstants.java new file mode 100644 index 00000000..b9c02f49 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationPlatformConstants.java @@ -0,0 +1,33 @@ +package com.messagebird.objects.conversations; + +/** + * Platforms are communication channels that a conversation can communicate through. + */ +public class ConversationPlatformConstants { + // PlatformSMS identifies the MessageBird SMS platform. + public static final String SMS = "sms"; + + // PlatformWhatsApp identifies the WhatsApp platform. + public static final String WHATSAPP = "whatsapp"; + + // PlatformFacebook identifies the Facebook platform. + public static final String FACEBOOK = "facebook"; + + // PlatformTelegram identifies the Telegram platform. + public static final String TELEGRAM = "telegram"; + + // PlatformLine identifies the LINE platform. + public static final String LINE = "line"; + + // PlatformWeChat identifies the WeChat platform. + public static final String WECHAT = "wechat"; + + // PlatformEmail identifies the Email platform. + public static final String EMAIL = "email"; + + // PlatformEvents identifies the Events platform + public static final String EVENTS = "events"; + + // PlatformWhatsAppSandbox identified the WhatsApp sandbox platform. + public static final String WHATSAPP_SANDBOX = "whatsapp_sandbox"; +} \ No newline at end of file diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationRecipientMetadata.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationRecipientMetadata.java new file mode 100644 index 00000000..7537da85 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationRecipientMetadata.java @@ -0,0 +1,50 @@ +package com.messagebird.objects.conversations; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * Identifies the recipient of an outbound WhatsApp message, as reported back on + * status webhook payloads under {@code status.metadata.recipient}. Mirrors + * {@link ConversationSenderMetadata} on the inbound side. + * + *

{@code userId} is the recipient's BSUID (e.g. "US.13491208655302741918"); + * {@code parentUserId} is the parent business-scoped user ID of the enterprise + * that owns the business portfolio it was scoped against (e.g. + * "US.ENT.11815799212886844830"). + * + *

Either field may be {@code null}: Meta only supplies them for accounts + * enrolled in the BSUID rollout, and the enclosing {@code recipient} object is + * omitted entirely when neither is present. This is the only place a status + * payload carries the recipient's own identity — {@code messageMetadata.to} is + * an echo of the address the message was addressed to. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class ConversationRecipientMetadata { + + private String userId; + private String parentUserId; + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getParentUserId() { + return parentUserId; + } + + public void setParentUserId(String parentUserId) { + this.parentUserId = parentUserId; + } + + @Override + public String toString() { + return "ConversationRecipientMetadata{" + + "userId='" + userId + '\'' + + ", parentUserId='" + parentUserId + '\'' + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationSendRequest.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationSendRequest.java new file mode 100644 index 00000000..40ace8d7 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationSendRequest.java @@ -0,0 +1,125 @@ +package com.messagebird.objects.conversations; + +import java.util.Map; + +public class ConversationSendRequest { + private String to; + private ConversationContentType type; + private ConversationContent content; + private String from; + private String reportUrl; + private String trackId; + private String ttl; + private ConversationFallbackOption fallback; + private Map source; + private ConversationMessageTag tag; + + public ConversationSendRequest(String to, ConversationContentType type, ConversationContent content, String from, String reportUrl, ConversationFallbackOption fallback, Map source, ConversationMessageTag tag) { + this.to = to; + this.type = type; + this.content = content; + this.from = from; + this.reportUrl = reportUrl; + this.fallback = fallback; + this.source = source; + this.tag = tag; + } + + public ConversationSendRequest() { + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + public ConversationContentType getType() { + return type; + } + + public void setType(ConversationContentType type) { + this.type = type; + } + + public ConversationContent getContent() { + return content; + } + + public void setContent(ConversationContent content) { + this.content = content; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getReportUrl() { + return reportUrl; + } + + public void setReportUrl(String reportUrl) { + this.reportUrl = reportUrl; + } + + public ConversationFallbackOption getFallback() { + return fallback; + } + + public void setFallback(ConversationFallbackOption fallback) { + this.fallback = fallback; + } + + public Map getSource() { + return source; + } + + public void setSource(Map source) { + this.source = source; + } + + public ConversationMessageTag getTag() { + return tag; + } + + public void setTag(ConversationMessageTag tag) { + this.tag = tag; + } + + public void setTrackId(String trackId) { + this.trackId = trackId; + } + + public String getTrackId() { + return trackId; + } + + public String getTtl() { + return ttl; + } + + public void setTtl(String ttl) { + this.ttl = ttl; + } + @Override + public String toString() { + return "ConversationSendRequest{" + + "to='" + to + '\'' + + ", type=" + type + + ", content=" + content + + ", from='" + from + '\'' + + ", reportUrl='" + reportUrl + '\'' + + ", trackId='" + trackId + '\'' + + ", ttl='" + ttl + '\'' + + ", fallback=" + fallback + '\'' + + ", tags=" + tag + + ", source='" + source + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationSendResponse.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationSendResponse.java new file mode 100644 index 00000000..8d928df6 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationSendResponse.java @@ -0,0 +1,60 @@ +package com.messagebird.objects.conversations; + +public class ConversationSendResponse { + private String id; //messageID + private String status; + private FallbackOptionResponse fallback; + + public static class FallbackOptionResponse{ + private String id; + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public String toString() { + return "FallbackOptionResponse{" + + "id='" + id + '\'' + + '}'; + } + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public FallbackOptionResponse getFallback() { + return fallback; + } + + public void setFallback(FallbackOptionResponse fallback) { + this.fallback = fallback; + } + + @Override + public String toString() { + return "ConversationSendResponse{" + + "id='" + id + '\'' + + ", status='" + status + '\'' + + ", fallback=" + fallback + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationSenderMetadata.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationSenderMetadata.java new file mode 100644 index 00000000..b7fdcbb0 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationSenderMetadata.java @@ -0,0 +1,66 @@ +package com.messagebird.objects.conversations; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * Metadata about the sender of a WhatsApp message. {@code userId} always + * contains the BSUID (e.g. "US.13491208655302741918") when Meta supplies one. + * When both a phone number and a BSUID are available, the phone number appears + * in the parent message's {@code from} field — not here. + * + *

{@code parentUserId} carries the sender's parent business-scoped user ID + * (e.g. "US.ENT.11815799212886844830"), which identifies the enterprise that + * owns the business portfolio the {@code userId} was scoped against. It is only + * present for accounts enrolled in Meta's parent-BSUID rollout; for everyone + * else it stays {@code null}. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class ConversationSenderMetadata { + + private String userId; + private String parentUserId; + private String username; + private String displayName; + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getParentUserId() { + return parentUserId; + } + + public void setParentUserId(String parentUserId) { + this.parentUserId = parentUserId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + @Override + public String toString() { + return "ConversationSenderMetadata{" + + "userId='" + userId + '\'' + + ", parentUserId='" + parentUserId + '\'' + + ", username='" + username + '\'' + + ", displayName='" + displayName + '\'' + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationStartRequest.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationStartRequest.java index 9779bf5c..6f7c84fe 100644 --- a/api/src/main/java/com/messagebird/objects/conversations/ConversationStartRequest.java +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationStartRequest.java @@ -1,5 +1,7 @@ package com.messagebird.objects.conversations; +import java.util.Map; + /** * Request object used for starting a conversation. */ @@ -8,18 +10,27 @@ public class ConversationStartRequest { private String to; private ConversationContentType type; private ConversationContent content; + private Map source; + private ConversationMessageTag tag; private String channelId; + private String reportUrl; + private String trackId; + private String ttl; public ConversationStartRequest( final String to, final ConversationContentType type, final ConversationContent content, - final String channelId + final String channelId, + final Map source, + final ConversationMessageTag tag ) { this.to = to; this.type = type; this.content = content; this.channelId = channelId; + this.source = source; + this.tag = tag; } public ConversationStartRequest() { @@ -58,13 +69,50 @@ public void setChannelId(final String channelId) { this.channelId = channelId; } + public String getReportUrl() { + return reportUrl; + } + + public void setReportUrl(final String reportUrl) { + this.reportUrl = reportUrl; + } + + public Map getSource() { + return source; + } + + public void setSource(Map source) { + this.source = source; + } + + public ConversationMessageTag getTag() { + return tag; + } + + public void setTag(ConversationMessageTag tag) { + this.tag = tag; + } + + public String getTrackId() { + return trackId; + } + + public void setTrackId(String trackId) { + this.trackId = trackId; + } + @Override public String toString() { return "ConversationStartRequest{" + "to='" + to + '\'' + ", type=" + type + ", content=" + content + + ", source=" + source + + ", tag=" + tag + ", channelId='" + channelId + '\'' + + ", reportUrl='" + reportUrl + '\'' + + ", trackId='" + trackId + '\'' + + ", ttl='" + ttl + '\'' + '}'; } -} +} \ No newline at end of file diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationStatusMessageMetadata.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationStatusMessageMetadata.java new file mode 100644 index 00000000..47a4a8d1 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationStatusMessageMetadata.java @@ -0,0 +1,92 @@ +package com.messagebird.objects.conversations; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * The {@code messageMetadata} block delivered inside status webhook payloads + * (e.g. {@code statusSent}, {@code statusDelivered}). Reflects the original + * message that triggered the status update. + * + *

This class is not produced by any SDK request — it is a standalone POJO + * intended for consumers who deserialize incoming webhook payloads in their + * own HTTP handlers. Use it via {@code ObjectMapper.readValue(body, ...)}. + * + *

Both {@code from} and {@code to} accept either a phone number or a + * WhatsApp Business-Scoped User ID (BSUID, e.g. "US.13491208655302741918"). + * The BSUID is also available via {@code metadata.sender.userId}. + * + *

{@code to} echoes back the address the message was originally addressed + * to, so it is not a reliable source of the recipient's BSUID. That identity + * lives alongside this block, under {@code status.metadata.recipient} — see + * {@link ConversationStatusMetadata}. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class ConversationStatusMessageMetadata { + + private String id; + private String from; + private String to; + private String type; + private ConversationContent content; + private ConversationMessageMetadata metadata; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public ConversationContent getContent() { + return content; + } + + public void setContent(ConversationContent content) { + this.content = content; + } + + public ConversationMessageMetadata getMetadata() { + return metadata; + } + + public void setMetadata(ConversationMessageMetadata metadata) { + this.metadata = metadata; + } + + @Override + public String toString() { + return "ConversationStatusMessageMetadata{" + + "id='" + id + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", type='" + type + '\'' + + ", content=" + content + + ", metadata=" + metadata + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationStatusMetadata.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationStatusMetadata.java new file mode 100644 index 00000000..0b4d5f7f --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationStatusMetadata.java @@ -0,0 +1,85 @@ +package com.messagebird.objects.conversations; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * The {@code status.metadata} block delivered inside status webhook payloads + * (e.g. {@code statusSent}, {@code statusDelivered}). + * + *

This class is not produced by any SDK request — it is a standalone POJO + * intended for consumers who deserialize incoming webhook payloads in their own + * HTTP handlers. Use it via {@code ObjectMapper.readValue(body, ...)}. + * + *

Note the mixed casing of this object. {@code pricing} and + * {@code conversation} are near-verbatim passthroughs of Meta's own objects and + * so keep their snake_case keys ({@code pricing_model}, {@code category}, …); + * they are exposed here as raw maps rather than modelled types, because their + * contents track Meta's schema rather than ours. {@code recipient} is ours and + * follows the camelCase convention used everywhere else in the API. Any other + * key present on the payload — for example {@code biz_opaque_callback_data} — + * is collected into {@link #getAdditionalProperties()} rather than dropped. + * + *

{@code recipient} is absent from payloads for accounts that never receive + * BSUIDs, in which case {@link #getRecipient()} returns {@code null}. + */ +public class ConversationStatusMetadata { + + private Map pricing; + private Map conversation; + private ConversationRecipientMetadata recipient; + private final Map additionalProperties = new LinkedHashMap<>(); + + public Map getPricing() { + return pricing; + } + + public void setPricing(Map pricing) { + this.pricing = pricing; + } + + public Map getConversation() { + return conversation; + } + + public void setConversation(Map conversation) { + this.conversation = conversation; + } + + public ConversationRecipientMetadata getRecipient() { + return recipient; + } + + public void setRecipient(ConversationRecipientMetadata recipient) { + this.recipient = recipient; + } + + /** + * Every key on the payload that has no dedicated accessor above, in the + * order it was encountered. Empty when the payload holds nothing else. + * + * @return the unmodelled remainder of the metadata object + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + @JsonAnySetter + public void setAdditionalProperty(String name, Object value) { + additionalProperties.put(name, value); + } + + @Override + public String toString() { + return "ConversationStatusMetadata{" + + "pricing=" + pricing + + ", conversation=" + conversation + + ", recipient=" + recipient + + ", additionalProperties=" + additionalProperties + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationUpdateRequest.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationUpdateRequest.java new file mode 100644 index 00000000..9a67a516 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationUpdateRequest.java @@ -0,0 +1,29 @@ +package com.messagebird.objects.conversations; + +import java.util.Objects; + +public class ConversationUpdateRequest { + + private final ConversationStatus status; + + public ConversationUpdateRequest(ConversationStatus status) { + this.status = status; + } + + public ConversationStatus getStatus() { + return status; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ConversationUpdateRequest that = (ConversationUpdateRequest) o; + return status == that.status; + } + + @Override + public int hashCode() { + return Objects.hash(status); + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookCreateRequest.java b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookCreateRequest.java index 1b7c3864..24cf7f9a 100644 --- a/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookCreateRequest.java +++ b/api/src/main/java/com/messagebird/objects/conversations/ConversationWebhookCreateRequest.java @@ -28,4 +28,8 @@ protected String getRequestName() { protected String getStringRepresentationOfExtraParameters() { return "channelId='" + channelId; } + + public String getChannelId() { + return channelId; + } } diff --git a/api/src/main/java/com/messagebird/objects/conversations/HSMCurrency.java b/api/src/main/java/com/messagebird/objects/conversations/HSMCurrency.java new file mode 100644 index 00000000..24ea4be6 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/HSMCurrency.java @@ -0,0 +1,31 @@ +package com.messagebird.objects.conversations; + +public class HSMCurrency { + + private String currencyCode; + private int amount; + + public String getCurrencyCode() { + return currencyCode; + } + + public void setCurrencyCode(String currencyCode) { + this.currencyCode = currencyCode; + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } + + @Override + public String toString() { + return "HSMCurrency{" + + "currencyCode='" + currencyCode + '\'' + + ", amount=" + amount + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/Media.java b/api/src/main/java/com/messagebird/objects/conversations/Media.java new file mode 100644 index 00000000..4a9bf4ea --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/Media.java @@ -0,0 +1,31 @@ +package com.messagebird.objects.conversations; + +public class Media { + + private String url; + private String caption; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getCaption() { + return caption; + } + + public void setCaption(String caption) { + this.caption = caption; + } + + @Override + public String toString() { + return "Media{" + + "url='" + url + '\'' + + ", caption='" + caption + '\'' + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/MessageComponent.java b/api/src/main/java/com/messagebird/objects/conversations/MessageComponent.java new file mode 100644 index 00000000..d3a7c20b --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/MessageComponent.java @@ -0,0 +1,82 @@ +package com.messagebird.objects.conversations; + +import java.util.List; + +public class MessageComponent { + + private MessageComponentType type; + private String sub_type; + private int index; + private List parameters; + private int card_index; + private List cards; + private List components; + + public void setType(MessageComponentType type) { + this.type = type; + } + + public MessageComponentType getType() { + return type; + } + + public String getSub_type() { + return sub_type; + } + + public void setSub_type(String sub_type) { + this.sub_type = sub_type; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public List getParameters() { + return parameters; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } + + public void setCards(List cards) { + this.cards = cards; + } + + public List getCards() { + return cards; + } + + public int getCard_index() { + return card_index; + } + + public void setCard_index(int card_index) { + this.card_index = card_index; + } + + public List getComponents() { + return components; + } + + public void setComponents(List components) { + this.components = components; + } + + @Override + public String toString() { + return "MessageComponent{" + + "type='" + type + '\'' + + ", sub_type='" + sub_type + '\'' + + ", index=" + index + '\'' + + ", parameters=" + parameters + '\'' + + ", components=" + components + '\'' + + ", cards=" + cards + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/MessageComponentType.java b/api/src/main/java/com/messagebird/objects/conversations/MessageComponentType.java new file mode 100644 index 00000000..9f9290cf --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/MessageComponentType.java @@ -0,0 +1,54 @@ +package com.messagebird.objects.conversations; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.*; + +public enum MessageComponentType { + + HEADER("header"), + BODY("body"), + FOOTER("footer"), + BUTTON("button"), + CARD("card"), + CAROUSEL("carousel"), + LIMITED_TIME_OFFER("limited_time_offer"), + COPY_CODE("copy_code"); + + private static final Map TYPE_MAP; + + static { + Map map = new HashMap<>(); + for (MessageComponentType componentType : MessageComponentType.values()) { + map.put(componentType.getType().toLowerCase(), componentType); + } + TYPE_MAP = Collections.unmodifiableMap(map); + } + + private final String type; + + MessageComponentType(final String type) { + this.type = type; + } + + @JsonCreator + public static MessageComponentType forValue(String value) { + Objects.requireNonNull(value, "Value cannot be null"); + return TYPE_MAP.get(value.toLowerCase(Locale.ROOT)); + } + + @JsonValue + public String toJson() { + return getType(); + } + + public String getType() { + return type; + } + + @Override + public String toString() { + return getType(); + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/MessageParam.java b/api/src/main/java/com/messagebird/objects/conversations/MessageParam.java new file mode 100644 index 00000000..6432369f --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/MessageParam.java @@ -0,0 +1,125 @@ +package com.messagebird.objects.conversations; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang3.StringUtils; + +public class MessageParam { + + private TemplateMediaType type; + private String text; + private String payload; + private HSMCurrency currency; + private String dateTime; + private Media document; + private Media image; + private Media video; + @JsonProperty("expiration_time") + private String expirationTime; + @JsonProperty("coupon_code") + private String couponCode; + + public TemplateMediaType getType() { + return type; + } + + public void setType(TemplateMediaType type) { + this.type = type; + } + + public String getText() { + return text; + } + + public void setText(String text) { + if (StringUtils.isBlank(text)) { + throw new IllegalArgumentException("Text cannot be null or empty"); + } + this.text = text; + } + + public String getPayload() { + return payload; + } + + public void setPayload(String payload) { + this.payload = payload; + } + + public HSMCurrency getCurrency() { + return currency; + } + + public void setCurrency(HSMCurrency currency) { + this.currency = currency; + } + + public String getDateTime() { + return dateTime; + } + + public void setDateTime(String dateTime) { + if (StringUtils.isBlank(dateTime)) { + throw new IllegalArgumentException("dateTime cannot be null or empty"); + } + this.dateTime = dateTime; + } + + public Media getDocument() { + return document; + } + + public void setDocument(Media document) { + this.document = document; + } + + public Media getImage() { + return image; + } + + public void setImage(Media image) { + this.image = image; + } + + public Media getVideo() { return video; } + + public void setVideo(Media video) { this.video = video; } + + public String getExpirationTime() { + return expirationTime; + } + + public void setExpirationTime(String expirationTime) { + if (StringUtils.isBlank(expirationTime)) { + throw new IllegalArgumentException("expirationTime cannot be null or empty"); + } + this.expirationTime = expirationTime; + } + + public String getCouponCode() { + return couponCode; + } + + public void setCouponCode(String couponCode) { + if (StringUtils.isBlank(couponCode)) { + throw new IllegalArgumentException("couponCode cannot be null or empty"); + } + this.couponCode = couponCode; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("MessageParam{"); + sb.append("type=").append(type) + .append(", text='").append(text).append('\'') + .append(", payload='").append(payload).append('\'') + .append(", currency=").append(currency) + .append(", dateTime='").append(dateTime).append('\'') + .append(", document=").append(document) + .append(", image=").append(image) + .append(", video=").append(video) + .append(", expirationTime='").append(expirationTime).append('\'') + .append(", couponCode='").append(couponCode).append('\'') + .append('}'); + return sb.toString(); + } +} diff --git a/api/src/main/java/com/messagebird/objects/conversations/TemplateMediaType.java b/api/src/main/java/com/messagebird/objects/conversations/TemplateMediaType.java new file mode 100644 index 00000000..580dcc97 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/conversations/TemplateMediaType.java @@ -0,0 +1,60 @@ +package com.messagebird.objects.conversations; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Map; +import java.util.HashMap; +import java.util.Collections; + +public enum TemplateMediaType { + + IMAGE("image"), + DOCUMENT("document"), + VIDEO("video"), + TEXT("text"), + CURRENCY("currency"), + DATETIME("date_time"), + PAYLOAD("payload"), + EXPIRATION_TIME("expiration_time"), + COUPON_CODE("coupon_code"); + + private static final Map TYPE_MAP; + + static { + Map map = new HashMap<>(); + for (TemplateMediaType templateMediaType : TemplateMediaType.values()) { + map.put(templateMediaType.getType().toLowerCase(), templateMediaType); + } + TYPE_MAP = Collections.unmodifiableMap(map); + } + + + private final String type; + + TemplateMediaType(final String type) { + this.type = type; + } + + @JsonCreator + public static TemplateMediaType forValue(String value) { + if (value == null) { + throw new IllegalArgumentException("Value cannot be null"); + } + return TYPE_MAP.get(value.toLowerCase()); + } + + @JsonValue + public String toJson() { + return getType(); + } + + public String getType() { + return type; + } + + @Override + public String toString() { + return getType(); + } + +} \ No newline at end of file diff --git a/api/src/main/java/com/messagebird/objects/integrations/HSMCategory.java b/api/src/main/java/com/messagebird/objects/integrations/HSMCategory.java new file mode 100644 index 00000000..3edb1db9 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/integrations/HSMCategory.java @@ -0,0 +1,48 @@ +package com.messagebird.objects.integrations; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * An enum for HSMComponentFormat + * + * @see HSMComponentFormat + * @author ssk910 + */ +public enum HSMCategory { + + AUTHENTICATION("AUTHENTICATION"), + UTILITY("UTILITY"), + MARKETING("MARKETING"); + + private final String category; + + HSMCategory(String category) { + this.category = category; + } + + @JsonCreator + public static HSMCategory forValue(String value) { + for (HSMCategory hsmCategory : HSMCategory.values()) { + if (hsmCategory.getCategory().equals(value)) { + return hsmCategory; + } + } + + return null; + } + + @JsonValue + public String toJson() { + return getCategory(); + } + + public String getCategory() { + return category; + } + + @Override + public String toString() { + return getCategory(); + } +} diff --git a/api/src/main/java/com/messagebird/objects/integrations/HSMComponent.java b/api/src/main/java/com/messagebird/objects/integrations/HSMComponent.java new file mode 100644 index 00000000..ed93847f --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/integrations/HSMComponent.java @@ -0,0 +1,196 @@ +package com.messagebird.objects.integrations; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; + +/** + * A class for HSMComponent object + * + * @see HSMComponent + * @author ssk910 + */ +public class HSMComponent { + + private HSMComponentType type; + private HSMComponentFormat format; + private String text; + @JsonProperty("add_security_recommendation") + private Boolean addSecurityRecommendation; + @JsonProperty("code_expiration_minutes") + private Integer codeExpirationMinutes; + private List buttons; + @JsonProperty("has_expiration") + private Boolean hasExpiration; + + private List cards; + + private HSMExample example; + + public HSMComponentType getType() { + return type; + } + + public void setType(HSMComponentType type) { + this.type = type; + } + + public HSMComponentFormat getFormat() { + return format; + } + + public void setFormat(HSMComponentFormat format) { + this.format = format; + } + + public String getText() { + return text; + } + + public void setText(String text) { + if (StringUtils.isBlank(text)) { + throw new IllegalArgumentException("Text cannot be null or empty"); + } + this.text = text; + } + + public List getButtons() { + return buttons; + } + + public void setButtons(List buttons) { + this.buttons = buttons; + } + + public List getCards() { + return cards; + } + + public void setCards(List cards) { + this.cards = cards; + } + + public HSMExample getExample() { + return example; + } + + public void setExample(HSMExample example) { + this.example = example; + } + + public Boolean getAddSecurityRecommendation() { + return addSecurityRecommendation; + } + + public void setAddSecurityRecommendation(Boolean addSecurityRecommendation) { + this.addSecurityRecommendation = addSecurityRecommendation; + } + + public Integer getCodeExpirationMinutes() { + return codeExpirationMinutes; + } + + public void setCodeExpirationMinutes(Integer codeExpirationMinutes) { + this.codeExpirationMinutes = codeExpirationMinutes; + } + + public Boolean getHasExpiration() { + return hasExpiration; + } + + public void setHasExpiration(Boolean hasExpiration) { + this.hasExpiration = hasExpiration; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("HSMComponent{"); + sb.append("type=").append(type) + .append(", format=").append(format) + .append(", text='").append(text).append('\'') + .append(", addSecurityRecommendation=").append(addSecurityRecommendation) + .append(", codeExpirationMinutes=").append(codeExpirationMinutes) + .append(", buttons=").append(buttons) + .append(", hasExpiration=").append(hasExpiration) + .append(", cards=").append(cards) + .append(", example=").append(example) + .append('}'); + return sb.toString(); + } + + /** + * Check if this component is valid. + * + * @throws IllegalArgumentException Occurs when validation is not passed. + */ + public void validateComponent() throws IllegalArgumentException { + try { + this.validateButtons(); + this.validateComponentExample(); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Component validation failed: " + e.getMessage(), e); + } + } + + /** + * Check if button list is valid. + * + * @throws IllegalArgumentException Occurs when validation is not passed. + */ + private void validateButtons() throws IllegalArgumentException { + if (this.buttons == null) { + return; + } + + for (final HSMComponentButton button : this.buttons) { + button.validateButtonExample(); + } + } + + /** + * Check for header_text and header_url. + * + * @throws IllegalArgumentException Occurs when {@code header_text} or {@code header_url} is not able to use. + */ + private void validateComponentExample() throws IllegalArgumentException { + final boolean isExampleNotNull = this.example != null; + final boolean isHeaderTextNotEmpty = + isExampleNotNull && !(this.example.getHeader_text() == null || this.example.getHeader_text() + .isEmpty()); + final boolean isHeaderUrlNotEmpty = + isExampleNotNull && !(this.example.getHeader_url() == null || this.example.getHeader_url() + .isEmpty()); + + if (isHeaderTextNotEmpty) { + this.checkHeaderText(); + } + + if (isHeaderUrlNotEmpty) { + this.checkHeaderUrl(); + } + } + + /** + * Check if header_text is able to use. + * + * @throws IllegalArgumentException Occurs when type is not {@code HEADER} and format is not {@code TEXT}. + */ + private void checkHeaderText() throws IllegalArgumentException { + if (!(HSMComponentType.HEADER.equals(type) && HSMComponentFormat.TEXT.equals(format))) { + throw new IllegalArgumentException("\"header_text\" is available for only HEADER type and TEXT format."); + } + } + + /** + * Check if header_url is able to use. + * + * @throws IllegalArgumentException Occurs when type is not {@code HEADER} and format is not {@code IMAGE}. + */ + private void checkHeaderUrl() throws IllegalArgumentException { + if (!(HSMComponentType.HEADER.equals(type) && + (HSMComponentFormat.IMAGE.equals(format) || HSMComponentFormat.VIDEO.equals(format) || HSMComponentFormat.DOCUMENT.equals(format)))) { + throw new IllegalArgumentException("\"header_url\" is available for only HEADER type and IMAGE, VIDEO, or DOCUMENT formats."); + } + } +} diff --git a/api/src/main/java/com/messagebird/objects/integrations/HSMComponentButton.java b/api/src/main/java/com/messagebird/objects/integrations/HSMComponentButton.java new file mode 100644 index 00000000..75204df0 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/integrations/HSMComponentButton.java @@ -0,0 +1,132 @@ +package com.messagebird.objects.integrations; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** + * HSMComponentButton + * + * @see HSMComponentButton + * @author ssk910 + */ +public class HSMComponentButton { + + private HSMComponentButtonType type; + private String text; + private String url; + private String phone_number; + private List example; + + //fields used by the authentification template + @JsonProperty("otp_type") + private HSMOTPButtonType otpType; + @JsonProperty("autofill_text") + private String autofillText; + @JsonProperty("package_name") + private String packageName; + @JsonProperty("signature_hash") + private String signatureHash; + + public HSMComponentButtonType getType() { + return type; + } + + public void setType(HSMComponentButtonType type) { + this.type = type; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getPhone_number() { + return phone_number; + } + + public void setPhone_number(String phone_number) { + this.phone_number = phone_number; + } + + public List getExample() { + return example; + } + + public void setExample(List example) { + this.example = example; + } + public HSMOTPButtonType getOtpType() { + return otpType; + } + + public void setOtpType(HSMOTPButtonType otpType) { + this.otpType = otpType; + } + + public String getAutofillText() { + return autofillText; + } + + public void setAutofillText(String autofillText) { + this.autofillText = autofillText; + } + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + public String getSignatureHash() { + return signatureHash; + } + + public void setSignatureHash(String signatureHash) { + this.signatureHash = signatureHash; + } + @Override + public String toString() { + return "HSMComponentButton{" + + "type=" + type + + ", text='" + text + '\'' + + ", url='" + url + '\'' + + ", phone_number='" + phone_number + '\'' + + ", example=" + example + + '}'; + } + + /** + * Check if example field is able to use. + * + * @throws IllegalArgumentException Occurs if button type is not {@code URL} or {@code QUICK_REPLY}. + */ + public void validateButtonExample() throws IllegalArgumentException { + final boolean isExampleEmpty = this.example == null || this.example.isEmpty(); + final boolean isNotProperType = !(this.type.equals(HSMComponentButtonType.URL) + || this.type.equals(HSMComponentButtonType.QUICK_REPLY) + || this.type.equals(HSMComponentButtonType.COPY_CODE) + ); + + if (isExampleEmpty) { + return; + } + + if (isNotProperType) { + throw new IllegalArgumentException("An example field in HSMComponentButton is available for only URL or QUICK_REPLY button types."); + } + } +} diff --git a/api/src/main/java/com/messagebird/objects/integrations/HSMComponentButtonType.java b/api/src/main/java/com/messagebird/objects/integrations/HSMComponentButtonType.java new file mode 100644 index 00000000..06e6eb91 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/integrations/HSMComponentButtonType.java @@ -0,0 +1,50 @@ +package com.messagebird.objects.integrations; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * HSMComponentButtonType + * + * @see HSMComponentButtonType + * @author ssk910 + */ +public enum HSMComponentButtonType { + + PHONE_NUMBER("PHONE_NUMBER"), + URL("URL"), + QUICK_REPLY("QUICK_REPLY"), + OTP("OTP"), + COPY_CODE("COPY_CODE"); + + private final String type; + + HSMComponentButtonType(String type) { + this.type = type; + } + + @JsonCreator + public static HSMComponentButtonType forValue(String value) { + for (HSMComponentButtonType hsmComponentButtonType : HSMComponentButtonType.values()) { + if (hsmComponentButtonType.getType().equals(value)) { + return hsmComponentButtonType; + } + } + + return null; + } + + @JsonValue + public String toJson() { + return getType(); + } + + public String getType() { + return type; + } + + @Override + public String toString() { + return getType(); + } +} diff --git a/api/src/main/java/com/messagebird/objects/integrations/HSMComponentCard.java b/api/src/main/java/com/messagebird/objects/integrations/HSMComponentCard.java new file mode 100644 index 00000000..23411941 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/integrations/HSMComponentCard.java @@ -0,0 +1,21 @@ +package com.messagebird.objects.integrations; + +import java.util.List; + +/** + * HSMComponentCard + * + * @author AlexL-mb + * @see HSMComponentCard + */ +public class HSMComponentCard { + private List components; + + public List getComponents() { + return components; + } + + public void setComponents(List components) { + this.components = components; + } +} \ No newline at end of file diff --git a/api/src/main/java/com/messagebird/objects/integrations/HSMComponentFormat.java b/api/src/main/java/com/messagebird/objects/integrations/HSMComponentFormat.java new file mode 100644 index 00000000..ac7d199d --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/integrations/HSMComponentFormat.java @@ -0,0 +1,49 @@ +package com.messagebird.objects.integrations; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * An enum for HSMComponentFormat + * + * @see HSMComponentFormat + * @author ssk910 + */ +public enum HSMComponentFormat { + + TEXT("TEXT"), + IMAGE("IMAGE"), + DOCUMENT("DOCUMENT"), + VIDEO("VIDEO"); + + private final String format; + + HSMComponentFormat(String format) { + this.format = format; + } + + @JsonCreator + public static HSMComponentFormat forValue(String value) { + for (HSMComponentFormat hsmComponentFormat : HSMComponentFormat.values()) { + if (hsmComponentFormat.getFormat().equals(value)) { + return hsmComponentFormat; + } + } + + return null; + } + + @JsonValue + public String toJson() { + return getFormat(); + } + + public String getFormat() { + return format; + } + + @Override + public String toString() { + return getFormat(); + } +} diff --git a/api/src/main/java/com/messagebird/objects/integrations/HSMComponentType.java b/api/src/main/java/com/messagebird/objects/integrations/HSMComponentType.java new file mode 100644 index 00000000..8610146a --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/integrations/HSMComponentType.java @@ -0,0 +1,59 @@ +package com.messagebird.objects.integrations; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Map; +import java.util.HashMap; +import java.util.Collections; +import java.util.Locale; +import java.util.Objects; + +/** + * An enum for HSMComponentType + * + * @see HSMComponentType + */ +public enum HSMComponentType { + BODY("BODY"), + HEADER("HEADER"), + FOOTER("FOOTER"), + BUTTONS("BUTTONS"), + CAROUSEL("CAROUSEL"), + LIMITED_TIME_OFFER("LIMITED_TIME_OFFER"); + + private static final Map TYPE_MAP; + + static { + Map map = new HashMap<>(); + for (HSMComponentType hsmComponentType : HSMComponentType.values()) { + map.put(hsmComponentType.getType().toLowerCase(), hsmComponentType); + } + TYPE_MAP = Collections.unmodifiableMap(map); + } + + private final String type; + + HSMComponentType(String type) { + this.type = type; + } + + @JsonCreator + public static HSMComponentType forValue(String value) { + Objects.requireNonNull(value, "Value cannot be null"); + return TYPE_MAP.get(value.toLowerCase(Locale.ROOT)); + } + + @JsonValue + public String toJson() { + return getType(); + } + + public String getType() { + return type; + } + + @Override + public String toString() { + return getType(); + } +} diff --git a/api/src/main/java/com/messagebird/objects/integrations/HSMExample.java b/api/src/main/java/com/messagebird/objects/integrations/HSMExample.java new file mode 100644 index 00000000..a28fcc38 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/integrations/HSMExample.java @@ -0,0 +1,54 @@ +package com.messagebird.objects.integrations; + +import java.util.List; + +/** + * HSMExample object + * + * @see HSMExample object + * @author ssk910 + */ +public class HSMExample { + + /* Example values for HEADER type components, TEXT format */ + private List header_text; + + /* Example set of values for the body text variables */ + private List> body_text; + + /* Example values for HEADER type components, IMAGE format */ + private List header_url; + + public List getHeader_text() { + return header_text; + } + + public void setHeader_text(List header_text) { + this.header_text = header_text; + } + + public List> getBody_text() { + return body_text; + } + + public void setBody_text(List> body_text) { + this.body_text = body_text; + } + + public List getHeader_url() { + return header_url; + } + + public void setHeader_url(List header_url) { + this.header_url = header_url; + } + + @Override + public String toString() { + return "HSMExample{" + + "header_text=" + header_text + + ", body_text=" + body_text + + ", header_url=" + header_url + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/integrations/HSMOTPButtonType.java b/api/src/main/java/com/messagebird/objects/integrations/HSMOTPButtonType.java new file mode 100644 index 00000000..499ef1f3 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/integrations/HSMOTPButtonType.java @@ -0,0 +1,38 @@ +package com.messagebird.objects.integrations; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public enum HSMOTPButtonType { + ONE_TAP("ONE_TAP"), + COPY_CODE("COPY_CODE"); + + private final String type; + + HSMOTPButtonType(String type) { + this.type = type; + } + @JsonCreator + public static HSMOTPButtonType forValue(String value) { + for (HSMOTPButtonType OTPButtonType : HSMOTPButtonType.values()) { + if (OTPButtonType.getType().equals(value)) { + return OTPButtonType; + } + } + return null; + } + + @JsonValue + public String toJson() { + return getType(); + } + + public String getType() { + return type; + } + + @Override + public String toString() { + return getType(); + } +} diff --git a/api/src/main/java/com/messagebird/objects/integrations/HSMQualityScore.java b/api/src/main/java/com/messagebird/objects/integrations/HSMQualityScore.java new file mode 100644 index 00000000..112492fd --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/integrations/HSMQualityScore.java @@ -0,0 +1,42 @@ +package com.messagebird.objects.integrations; + +import java.util.List; + +public class HSMQualityScore { + private String score; + private long date; + private List reasons; + + public String getScore() { + return score; + } + + public void setScore(String score) { + this.score = score; + } + + public long getDate() { + return date; + } + + public void setDate(long date) { + this.date = date; + } + + public List getReasons() { + return reasons; + } + + public void setReasons(List reasons) { + this.reasons = reasons; + } + + @Override + public String toString() { + return "HSMQualityScore{" + + "score='" + score + '\'' + + ", date=" + date + + ", reasons=" + reasons + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/integrations/HSMStatus.java b/api/src/main/java/com/messagebird/objects/integrations/HSMStatus.java new file mode 100644 index 00000000..1b1980f1 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/integrations/HSMStatus.java @@ -0,0 +1,53 @@ +package com.messagebird.objects.integrations; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * An enum for HSMStatus object + * + * @see HSMStatus object + * @author ssk910 + */ +public enum HSMStatus { + + NEW("NEW"), + APPROVED("APPROVED"), + PENDING("PENDING"), + REJECTED("REJECTED"), + PENDING_DELETION("PENDING_DELETION"), + DELETED("DELETED"), + DISABLED("DISABLED"), + PAUSED("PAUSED"); + + private final String status; + + HSMStatus(String status) { + this.status = status; + } + + @JsonCreator + public static HSMStatus forValue(String value) { + for (HSMStatus hsmStatus : HSMStatus.values()) { + if (hsmStatus.getStatus().equals(value)) { + return hsmStatus; + } + } + + return null; + } + + @JsonValue + public String toJson() { + return getStatus(); + } + + public String getStatus() { + return status; + } + + @Override + public String toString() { + return getStatus(); + } +} diff --git a/api/src/main/java/com/messagebird/objects/integrations/Template.java b/api/src/main/java/com/messagebird/objects/integrations/Template.java new file mode 100644 index 00000000..f6a83e93 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/integrations/Template.java @@ -0,0 +1,173 @@ +package com.messagebird.objects.integrations; + +import java.util.List; + +/** + * Template Object as integrations API request. + * + * @see Integrations API + * @author ssk910 + */ +public class Template { + + private String name; + private String language; + private String wabaID; + private List components; + private HSMCategory category; + private boolean ctaURLLinkTrackingOptedOut; + + public Template() { + } + + + public Template(String name, String language, String wabaID, + List components, HSMCategory category, boolean ctaURLLinkTrackingOptedOut) { + this.name = name; + this.language = language; + this.wabaID = wabaID; + this.components = components; + this.category = category; + this.ctaURLLinkTrackingOptedOut = ctaURLLinkTrackingOptedOut; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public String getWABAID() { + return wabaID; + } + + public void setWABAID(String wabaID) { + this.wabaID = wabaID; + } + + public List getComponents() { + return components; + } + + public void setComponents(List components) { + this.components = components; + } + + public HSMCategory getCategory() { + return category; + } + + public void setCategory(HSMCategory category) { + this.category = category; + } + + public void setCtaURLLinkTrackingOptedOut (boolean ctaURLLinkTrackingOptedOut) { + this.ctaURLLinkTrackingOptedOut = ctaURLLinkTrackingOptedOut; + } + + public boolean getCtaURLLinkTrackingOptedOut () { + return ctaURLLinkTrackingOptedOut; + } + + @Override + public String toString() { + return "WhatsAppTemplate{" + + "name='" + name + '\'' + + ", language='" + language + '\'' + + ", wabaID='" + wabaID + '\'' + + ", components=" + components + + ", category='" + category + '\'' + + ", ctaURLLinkTrackingOptedOut='" + ctaURLLinkTrackingOptedOut + '\'' + + '}'; + } + + /** + * Validate required fields: components, name, language, category + * + * @throws IllegalArgumentException if required fields are invalid. + */ + public void validate() throws IllegalArgumentException { + this.validateComponents(); + this.validateName(); + this.validateLanguage(); + this.validateWABAID(); + this.validateCategory(); + } + + /** + * Check if components field is valid. + * + * @throws IllegalArgumentException If components field is null or empty list. + */ + private void validateComponents() throws IllegalArgumentException { + final boolean componentsNotEmpty = !(this.components == null || this.components.isEmpty()); + + if (componentsNotEmpty) { + for (final HSMComponent component : this.components) { + component.validateComponent(); + } + } else { + throw new IllegalArgumentException("A \"components\" field is required and should not be empty list."); + } + } + + /** + * Check if name field is valid. + * + * @throws IllegalArgumentException If name field is null or empty string. + */ + private void validateName() { + if (this.name == null) { + throw new IllegalArgumentException("A \"name\" field is required."); + } else if (this.name.length() == 0) { + throw new IllegalArgumentException("A \"name\" field can not be an empty string."); + } + } + + /** + * Check if language field is valid. + * + * @throws IllegalArgumentException If language field is null or empty string. + */ + private void validateLanguage() { + if (this.language == null) { + throw new IllegalArgumentException("A \"language\" field is required."); + } else if (this.language.length() == 0) { + throw new IllegalArgumentException("A \"language\" field can not be an empty string."); + } + } + + /** + * Check if wabaID field is valid. + * + * @throws IllegalArgumentException If wabaID field is null or empty string. + */ + private void validateWABAID() { + if (this.wabaID == null) { + throw new IllegalArgumentException("A \"wabaID\" field is required."); + } else if (this.wabaID.length() == 0) { + throw new IllegalArgumentException("A \"wabaID\" field can not be an empty string."); + } + } + + /** + * Check if category field is valid. + * + * @throws IllegalArgumentException If category field is null. + */ + private void validateCategory() { + if (this.category == null) { + throw new IllegalArgumentException("A \"category\" field is required."); + } + } +} diff --git a/api/src/main/java/com/messagebird/objects/integrations/TemplateList.java b/api/src/main/java/com/messagebird/objects/integrations/TemplateList.java new file mode 100644 index 00000000..5cea4023 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/integrations/TemplateList.java @@ -0,0 +1,12 @@ +package com.messagebird.objects.integrations; + +import com.messagebird.objects.ListBase; + +/** + * Response object representing the Template list type. + * + * @author ssk910 + */ +public class TemplateList extends ListBase { + +} diff --git a/api/src/main/java/com/messagebird/objects/integrations/TemplateResponse.java b/api/src/main/java/com/messagebird/objects/integrations/TemplateResponse.java new file mode 100644 index 00000000..3c75fabe --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/integrations/TemplateResponse.java @@ -0,0 +1,149 @@ +package com.messagebird.objects.integrations; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * Template response using integrations API. + * + * @author ssk910 + */ +public class TemplateResponse implements Serializable { + + private static final long serialVersionUID = 7154209824478715861L; + private String name; + private String language; + private HSMCategory category; + private List components; + private HSMStatus status; + private String rejectedReason; + private String wabaID; + private String namespace; + + private boolean ctaURLLinkTrackingOptedOut; + + private HSMQualityScore qualityScore; + + private Date createdAt; + private Date updatedAt; + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public HSMCategory getCategory() { + return category; + } + + public void setCategory(HSMCategory category) { + this.category = category; + } + + public List getComponents() { + return components; + } + + public void setComponents(List components) { + this.components = components; + } + + public HSMStatus getStatus() { + return status; + } + + public void setStatus(HSMStatus status) { + this.status = status; + } + + public String getRejectedReason() { + return rejectedReason; + } + + public void setRejectedReason(String rejectedReason) { + this.rejectedReason = rejectedReason; + } + + public String getWabaID() { + return wabaID; + } + + public void setWabaID(String wabaID) { + this.wabaID = wabaID; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public Date getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + } + + public Date getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(Date updatedAt) { + this.updatedAt = updatedAt; + } + + public boolean isCtaURLLinkTrackingOptedOut() { + return ctaURLLinkTrackingOptedOut; + } + + public void setCtaURLLinkTrackingOptedOut(boolean ctaURLLinkTrackingOptedOut) { + this.ctaURLLinkTrackingOptedOut = ctaURLLinkTrackingOptedOut; + } + + public HSMQualityScore getQualityScore() { + return qualityScore; + } + + public void setQualityScore(HSMQualityScore qualityScore) { + this.qualityScore = qualityScore; + } + + @Override + public String toString() { + return "WhatsAppTemplateResponse{" + + "name='" + name + '\'' + + ", language='" + language + '\'' + + ", category='" + category + '\'' + + ", components=" + components + + ", status='" + status + '\'' + + ", rejectedReason='" + rejectedReason + '\'' + + ", wabaID='" + wabaID + '\'' + + ", namespace='" + namespace + '\'' + + ", ctaURLLinkTrackingOptedOut='" + ctaURLLinkTrackingOptedOut + '\'' + + ", qualityScore='" + qualityScore + '\'' + + ", createdAt=" + createdAt + + ", updatedAt=" + updatedAt + + '}'; + } + +} diff --git a/api/src/main/java/com/messagebird/objects/voicecalls/SipResponseCode.java b/api/src/main/java/com/messagebird/objects/voicecalls/SipResponseCode.java new file mode 100644 index 00000000..7061c8fe --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/voicecalls/SipResponseCode.java @@ -0,0 +1,73 @@ +package com.messagebird.objects.voicecalls; + +import com.fasterxml.jackson.annotation.JsonCreator; + + +/** + * More details, including additional descriptions and common caused can be found here: https://developers.messagebird.com/api/voice-calling/#sip-status-codes + * @author leandropinto + * + */ +public enum SipResponseCode { + //Successful + OK, + //The server understood the request, but is refusing to fulfill it. + FORBIDDEN, + //The server has definitive information that the user does not exist at the domain specified in the Request-URI. + NOT_FOUND, + //Couldn't find the user in time. + REQUEST_TIMEOUT, + //The user existed once, but is not available here any more. + GONE, + //Callee currently unavailable. + TEMPORARILY_UNAVAILABLE, + //Request-URI incomplete. + ADDRESS_INCOMPLETE, + //Callee is busy. + BUSY_HERE, + //Some aspect of the session description or the Request-URI is not acceptable. + NOT_ACCEPTABLE_HERE, + //The server could not fulfill the request due to some unexpected condition. + INTERNAL_SERVER_ERROR, + //The server does not have the ability to fulfill the request, such as because it does not recognize the request method. + NOT_IMPLEMENTED, + //The server is acting as a gateway or proxy, and received an invalid response from a downstream server while attempting to fulfill the request. + BAD_GATEWAY, + //The server is undergoing maintenance or is temporarily overloaded and so cannot process the request. + SERVICE_UNAVAILABLE; + + @JsonCreator + public static SipResponseCode forValue(Integer value) { + switch (value) { + case 200: + return OK; + case 403: + return FORBIDDEN; + case 404: + return NOT_FOUND; + case 408: + return REQUEST_TIMEOUT; + case 410: + return GONE; + case 480: + return TEMPORARILY_UNAVAILABLE; + case 484: + return ADDRESS_INCOMPLETE; + case 486: + return BUSY_HERE; + case 488: + return NOT_ACCEPTABLE_HERE; + case 500: + return INTERNAL_SERVER_ERROR; + case 501: + return NOT_IMPLEMENTED; + case 502: + return BAD_GATEWAY; + case 503: + return SERVICE_UNAVAILABLE; + + default: + throw new IllegalArgumentException("Unknown sip response code: " + value); + } + } +} \ No newline at end of file diff --git a/api/src/main/java/com/messagebird/objects/voicecalls/Transcription.java b/api/src/main/java/com/messagebird/objects/voicecalls/Transcription.java index 3c6c665e..3a43a98a 100644 --- a/api/src/main/java/com/messagebird/objects/voicecalls/Transcription.java +++ b/api/src/main/java/com/messagebird/objects/voicecalls/Transcription.java @@ -12,6 +12,8 @@ public class Transcription implements Serializable { private String id; private String recordingId; private String error; + private String status; + private String legId; private Date createdAt; private Date updatedAt; @JsonProperty("_links") @@ -45,6 +47,10 @@ public Date getCreatedAt() { return createdAt; } + public static long getSerialVersionUID() { + return serialVersionUID; + } + public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } @@ -65,15 +71,33 @@ public void setLinks(Map links) { this.links = links; } + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getLegId() { + return legId; + } + + public void setLegId(String legId) { + this.legId = legId; + } + @Override public String toString() { return "Transcription{" + "id='" + id + '\'' + ", recordingId='" + recordingId + '\'' + ", error='" + error + '\'' + + ", status='" + status + '\'' + + ", legId='" + legId + '\'' + ", createdAt=" + createdAt + ", updatedAt=" + updatedAt + - ", links='" + links + '\'' + + ", links=" + links + '}'; } } diff --git a/api/src/main/java/com/messagebird/objects/voicecalls/TranscriptionResponse.java b/api/src/main/java/com/messagebird/objects/voicecalls/TranscriptionResponse.java index a760d915..442c44da 100644 --- a/api/src/main/java/com/messagebird/objects/voicecalls/TranscriptionResponse.java +++ b/api/src/main/java/com/messagebird/objects/voicecalls/TranscriptionResponse.java @@ -1,13 +1,28 @@ package com.messagebird.objects.voicecalls; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.io.Serializable; import java.util.List; +import java.util.Map; public class TranscriptionResponse implements Serializable { private static final long serialVersionUID = -25064223639161201L; private List data; + @JsonProperty("_links") + private Map links; + private Pagination pagination; + + public TranscriptionResponse() {} + + public TranscriptionResponse(List data, Map links, Pagination pagination) { + this.data = data; + this.links = links; + this.pagination = pagination; + } + public List getData() { return data; } @@ -16,10 +31,32 @@ public void setData(List data) { this.data = data; } + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public Map getLinks() { + return links; + } + + public void setLinks(Map links) { + this.links = links; + } + + public Pagination getPagination() { + return pagination; + } + + public void setPagination(Pagination pagination) { + this.pagination = pagination; + } + @Override public String toString() { return "TranscriptionResponse{" + "data=" + data + + ", links=" + links + + ", pagination=" + pagination + '}'; } } diff --git a/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCall.java b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCall.java index ea7bff21..573b42be 100644 --- a/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCall.java +++ b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCall.java @@ -1,5 +1,6 @@ package com.messagebird.objects.voicecalls; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.messagebird.objects.MessageBase; import java.io.Serializable; @@ -12,9 +13,7 @@ public class VoiceCall implements MessageBase, Serializable { private String source; private String destination; private VoiceCallFlow callFlow; - - private String webhookUrl; - private String webhookToken; + private Webhook webhook = new Webhook(); @Override public String getBody() { @@ -60,20 +59,43 @@ public void setCallFlow(VoiceCallFlow callFlow) { this.callFlow = callFlow; } + public Webhook getWebhook() { + return webhook; + } + + @JsonIgnore + public void setWebhook(String url) { + this.setWebhook(url, null); + } + + @JsonIgnore + public void setWebhook(String url, String token) { + this.webhook.setUrl(url); + this.webhook.setToken(token); + } + + @JsonIgnore + @Deprecated public String getWebhookUrl() { - return webhookUrl; + return webhook.getUrl(); } + @JsonIgnore + @Deprecated public void setWebhookUrl(String webhookUrl) { - this.webhookUrl = webhookUrl; + this.webhook.setUrl(webhookUrl); } + @JsonIgnore + @Deprecated public String getWebhookToken() { - return webhookToken; + return webhook.getToken(); } + @JsonIgnore + @Deprecated public void setWebhookToken(String webhookToken) { - this.webhookToken = webhookToken; + this.webhook.setToken(webhookToken); } @Override @@ -82,8 +104,7 @@ public String toString() { "source='" + source + '\'' + ", destination='" + destination + '\'' + ", callFlow=" + callFlow + - ", webhookUrl='" + webhookUrl + '\'' + - ", webhookToken='" + webhookToken + '\'' + + ", webhook=" + webhook + '}'; } } diff --git a/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallCondition.java b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallCondition.java new file mode 100644 index 00000000..de99a1b9 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallCondition.java @@ -0,0 +1,34 @@ +package com.messagebird.objects.voicecalls; + +public class VoiceCallCondition { + private String variable; + private String operator; + private String value; + + public VoiceCallCondition() { + } + + public String getVariable() { + return variable; + } + + public void setVariable(String variable) { + this.variable = variable; + } + + public String getOperator() { + return operator; + } + + public void setOperator(String operator) { + this.operator = operator; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallFlow.java b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallFlow.java index fc638cf6..53130c77 100644 --- a/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallFlow.java +++ b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallFlow.java @@ -1,10 +1,12 @@ package com.messagebird.objects.voicecalls; import com.messagebird.objects.VoiceStep; +import com.fasterxml.jackson.annotation.JsonProperty; import java.io.Serializable; import java.util.Date; import java.util.List; +import java.util.Map; public class VoiceCallFlow implements Serializable { @@ -14,10 +16,22 @@ public class VoiceCallFlow implements Serializable { private String title; private boolean record; private List steps; + + /* + * default is reserved name in JAVA so we use alternate name + */ + @JsonProperty("default") private boolean defaultCall; + + @JsonProperty("maxDuration") + private Integer maxDuration; + private Date createdAt; private Date updatedAt; + @JsonProperty("_links") + private Map links; + public String getId() { return id; } @@ -26,10 +40,12 @@ public void setId(String id) { this.id = id; } + @Deprecated public String getTitle() { return title; } + @Deprecated public void setTitle(String title) { this.title = title; } @@ -58,6 +74,10 @@ public void setDefaultCall(boolean defaultCall) { this.defaultCall = defaultCall; } + public Integer getMaxDuration() { return maxDuration; } + + public void setMaxDuration(Integer maxDuration) { this.maxDuration = maxDuration; } + public Date getCreatedAt() { return createdAt; } @@ -74,6 +94,14 @@ public void setUpdatedAt(Date updatedAt) { this.updatedAt = updatedAt; } + public Map getLinks() { + return links; + } + + public void setLinks(Map links) { + this.links = links; + } + @Override public String toString() { return "VoiceCallFlow{" + @@ -81,7 +109,8 @@ public String toString() { ", title='" + title + '\'' + ", record=" + record + ", steps=" + steps + - ", defaultCall=" + defaultCall + + ", default=" + defaultCall + + ", maxDuration=" + maxDuration + ", createdAt=" + createdAt + ", updatedAt=" + updatedAt + '}'; diff --git a/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallFlowList.java b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallFlowList.java new file mode 100644 index 00000000..dc2ad000 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallFlowList.java @@ -0,0 +1,63 @@ +package com.messagebird.objects.voicecalls; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a listing of VoiceCallFlow objects, along with pagination details. + * @TODO needs a little polishing (reorganise methods, rename properties, add + * missing properties) + */ +public class VoiceCallFlowList implements Serializable { + + @JsonProperty("_links") + private Map links; + + private Pagination pagination; + + private List items; + + @JsonCreator + public VoiceCallFlowList(@JsonProperty("data") List data) { + this.items = data; + } + + @Override + public String toString() { + return pagination.toString(); + } + + public void setPagination(Pagination pagination) { + this.pagination = pagination; + } + + public Integer getTotalCount() { + return this.pagination.getTotalCount(); + } + + public Integer getPageCount() { + return this.pagination.getPageCount(); + } + + public Integer getCurrentPage() { + return this.pagination.getCurrentPage(); + } + + public Integer getPerPage() { + return this.pagination.getPerPage(); + } + + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } +} + + diff --git a/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallFlowRequest.java b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallFlowRequest.java new file mode 100644 index 00000000..e447d9d9 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallFlowRequest.java @@ -0,0 +1,82 @@ +package com.messagebird.objects.voicecalls; + +import java.util.List; +import java.util.Date; +import com.messagebird.objects.*; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Contains writable values for VoiceCallFlow objects. + */ +public class VoiceCallFlowRequest { + + + private String id; + private String title; + private boolean record; + private List steps; + + @JsonProperty("default") + private boolean defaultCall; + + public VoiceCallFlowRequest(String id) + { + this.id = id; + } + + public VoiceCallFlowRequest() + { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Deprecated + public String getTitle() { + return title; + } + + @Deprecated + public void setTitle(String title) { + this.title = title; + } + + public boolean isRecord() { + return record; + } + + public void setRecord(boolean record) { + this.record = record; + } + + public List getSteps() { + return steps; + } + + public void setSteps(List steps) { + this.steps = steps; + } + + public boolean isDefaultCall() { + return defaultCall; + } + + public void setDefaultCall(boolean defaultCall) { + this.defaultCall = defaultCall; + } + + @Override + public String toString() { + return "VoiceCallFlowRequest{" + + "title='" + title + '\'' + + ", record=" + record + + ", steps=" + steps + + ", default=" + defaultCall + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallFlowResponse.java b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallFlowResponse.java new file mode 100644 index 00000000..ec4da8d0 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallFlowResponse.java @@ -0,0 +1,40 @@ +package com.messagebird.objects.voicecalls; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +public class VoiceCallFlowResponse implements Serializable { + + private static final long serialVersionUID = -3429781513863789117L; + + private List data; + @JsonProperty("_links") + private Map links; + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public Map getLinks() { + return links; + } + + public void setLinks(Map links) { + this.links = links; + } + + @Override + public String toString() { + return "VoiceCallResponse{" + + "data=" + data + + ", links=" + links + + '}'; + } +} diff --git a/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallLeg.java b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallLeg.java index 93dc0fa2..d09b7bdf 100644 --- a/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallLeg.java +++ b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallLeg.java @@ -28,6 +28,7 @@ public class VoiceCallLeg { public final Date updatedAt; public final Date answeredAt; public final Date endedAt; + public final SipResponseCode sipResponseCode; @JsonCreator @@ -44,7 +45,8 @@ public VoiceCallLeg( @JsonProperty("createdAt") Date createdAt, @JsonProperty("updatedAt") Date updatedAt, @JsonProperty("answeredAt") Date answeredAt, - @JsonProperty("endedAt") Date endedAt + @JsonProperty("endedAt") Date endedAt, + @JsonProperty("sipResponseCode") SipResponseCode sipResponseCode ) { this.id = id; this.callID = callID; @@ -59,6 +61,7 @@ public VoiceCallLeg( this.updatedAt = updatedAt; this.answeredAt = answeredAt; this.endedAt = endedAt; + this.sipResponseCode = sipResponseCode; } @Override @@ -77,6 +80,7 @@ public String toString() { ", updatedAt='" + updatedAt + '\'' + ", answeredAt='" + answeredAt + '\'' + ", endedAt='" + endedAt + '\'' + + ", sipResponseCode='" + sipResponseCode + '\'' + '}'; } @@ -97,11 +101,12 @@ public boolean equals(Object o) { Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt) && Objects.equals(answeredAt, that.answeredAt) && - Objects.equals(endedAt, that.endedAt); + Objects.equals(endedAt, that.endedAt) && + Objects.equals(sipResponseCode, that.sipResponseCode); } @Override public int hashCode() { - return Objects.hash(id, callID, source, destination, status, direction, cost, currency, duration, createdAt, updatedAt, answeredAt, endedAt); + return Objects.hash(id, callID, source, destination, status, direction, cost, currency, duration, createdAt, updatedAt, answeredAt, endedAt, sipResponseCode); } } diff --git a/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallStatus.java b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallStatus.java index e46c32d9..6806b8e7 100644 --- a/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallStatus.java +++ b/api/src/main/java/com/messagebird/objects/voicecalls/VoiceCallStatus.java @@ -5,7 +5,10 @@ public enum VoiceCallStatus { queued("queued"), starting("starting"), ongoing("ongoing"), - ended("ended"); + ended("ended"), + failed("failed"), + busy("busy"), + no_answer("no_answer"); final String value; diff --git a/api/src/main/java/com/messagebird/objects/voicecalls/Webhook.java b/api/src/main/java/com/messagebird/objects/voicecalls/Webhook.java index 2ac11c01..ce236cb3 100644 --- a/api/src/main/java/com/messagebird/objects/voicecalls/Webhook.java +++ b/api/src/main/java/com/messagebird/objects/voicecalls/Webhook.java @@ -6,18 +6,9 @@ public class Webhook implements Serializable { private static final long serialVersionUID = 727746356185518354L; - private String title; private String url; private String token; - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - public String getUrl() { return url; } @@ -37,8 +28,7 @@ public void setToken(String token) { @Override public String toString() { return "Webhook{" + - "title='" + title + '\'' + - ", url='" + url + '\'' + + "url='" + url + '\'' + ", token='" + token + '\'' + '}'; } diff --git a/api/src/main/java/com/messagebird/objects/voicecalls/WebhookList.java b/api/src/main/java/com/messagebird/objects/voicecalls/WebhookList.java new file mode 100644 index 00000000..da39f342 --- /dev/null +++ b/api/src/main/java/com/messagebird/objects/voicecalls/WebhookList.java @@ -0,0 +1,50 @@ +package com.messagebird.objects.voicecalls; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +public class WebhookList implements Serializable { + + private static final long serialVersionUID = -5524142916135114801L; + + private List data; + @JsonProperty("_list") + private Map links; + private Pagination pagination; + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public Map getLinks() { + return links; + } + + public void setLinks(Map links) { + this.links = links; + } + + public Pagination getPagination() { + return pagination; + } + + public void setPagination(Pagination pagination) { + this.pagination = pagination; + } + + @Override + public String toString() { + return "WebhookList{" + + "data=" + data + + ", links=" + links + + ", pagination=" + pagination + + '}'; + } +} diff --git a/api/src/test/java/com/messagebird/ContactTest.java b/api/src/test/java/com/messagebird/ContactTest.java index d289a4fe..5dfdea38 100644 --- a/api/src/test/java/com/messagebird/ContactTest.java +++ b/api/src/test/java/com/messagebird/ContactTest.java @@ -8,8 +8,13 @@ import org.mockito.Mockito; import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import static org.junit.Assume.assumeNotNull; import static org.unitils.reflectionassert.ReflectionAssert.assertReflectionEquals; +/** + * @deprecated - This is an integration test, not a unit test and it should + * be refactored to use mocks instead of LIVE API + */ public class ContactTest { private static MessageBirdServiceImpl messageBirdService; @@ -26,6 +31,7 @@ public class ContactTest { @BeforeClass public static void setUpClass() throws UnauthorizedException, GeneralException { String accessKey = System.getProperty("messageBirdAccessKey"); + assumeNotNull("Integration test skipped: set -DmessageBirdAccessKey to run", accessKey); msisdn = generateMsisdn(); diff --git a/api/src/test/java/com/messagebird/ConversationMessagesTest.java b/api/src/test/java/com/messagebird/ConversationMessagesTest.java index dab6e41f..beac543a 100644 --- a/api/src/test/java/com/messagebird/ConversationMessagesTest.java +++ b/api/src/test/java/com/messagebird/ConversationMessagesTest.java @@ -1,5 +1,6 @@ package com.messagebird; +import com.fasterxml.jackson.databind.ObjectMapper; import com.messagebird.exceptions.GeneralException; import com.messagebird.exceptions.NotFoundException; import com.messagebird.exceptions.UnauthorizedException; @@ -22,6 +23,20 @@ public class ConversationMessagesTest { private static final String JSON_CONVERSATION_MESSAGE_LOCATION = "{\"id\": \"mesid\",\"conversationId\": \"convid\",\"channelId\": \"chanid\",\"status\": \"received\",\"type\": \"location\",\"direction\": \"received\",\"content\": {\"location\": { \"latitude\": 52.344263, \"longitude\": 4.911627 } },\"createdDatetime\": \"2018-08-29T11:49:16Z\",\"updatedDatetime\": \"2018-08-29T11:49:16Z\"}"; private static final String JSON_CONVERSATION_MESSAGE_TEXT = "{\"id\": \"mesid\",\"conversationId\": \"convid\",\"channelId\": \"chanid\",\"status\": \"received\",\"type\": \"text\",\"direction\": \"received\",\"content\": {\"text\": \"Hello\"},\"createdDatetime\": \"2018-08-29T11:49:16Z\",\"updatedDatetime\": \"2018-08-29T11:49:16Z\"}"; private static final String JSON_CONVERSATION_MESSAGE_VIDEO = "{\"id\": \"mesid\",\"conversationId\": \"convid\",\"channelId\": \"chanid\",\"status\": \"received\",\"type\": \"video\",\"direction\": \"received\",\"content\": {\"video\": { \"url\": \"https://example.com/video.mp4\" } },\"createdDatetime\": \"2018-08-29T11:49:16Z\",\"updatedDatetime\": \"2018-08-29T11:49:16Z\"}"; + private static final String JSON_CONVERSATION_SEND_MESSAGE_RESPONSE = "{\"id\":\"mesid\",\"status\":\"accepted\",\"fallback\":{\"id\":\"mesid\"}}"; + private static final String JSON_CONVERSATION_MESSAGE_BSUID = "{\"id\": \"mesid\",\"conversationId\": \"convid\",\"channelId\": \"chanid\",\"status\": \"received\",\"type\": \"text\",\"direction\": \"received\",\"content\": {\"text\": \"Hello\"},\"metadata\": {\"sender\": {\"displayName\": \"Alice\",\"username\": \"alice_shop\",\"userId\": \"US.13491208655302741918\",\"parentUserId\": \"US.ENT.11815799212886844830\"},\"receivedAt\": \"2025-04-15T16:00:00Z\"},\"createdDatetime\": \"2025-04-15T16:00:00Z\",\"updatedDatetime\": \"2025-04-15T16:00:00Z\"}"; + private static final String JSON_STATUS_MESSAGE_METADATA = "{\"id\": \"e5f6a7b8-c9d0-1234-ef01-23456789abcd\",\"from\": \"15551234567\",\"to\": \"US.13491208655302741918\",\"type\": \"text\",\"content\": {\"text\": \"Hello! Your order has been shipped.\"},\"metadata\": {\"sender\": {\"userId\": \"US.13491208655302741918\"},\"receivedAt\": \"0001-01-01T00:00:00Z\"}}"; + + private static final String JSON_STATUS_METADATA_WITH_RECIPIENT = "{\"pricing\": {\"billable\": true,\"pricing_model\": \"CBP\",\"category\": \"utility\"},\"conversation\": {\"id\": \"a1b2c3d4\",\"origin\": {\"type\": \"utility\"}},\"biz_opaque_callback_data\": \"order-1234\",\"recipient\": {\"userId\": \"US.13491208655302741918\",\"parentUserId\": \"US.ENT.11815799212886844830\"}}"; + private static final String JSON_STATUS_METADATA_WITHOUT_RECIPIENT = "{\"pricing\": {\"billable\": true,\"pricing_model\": \"CBP\",\"category\": \"utility\"},\"conversation\": {\"id\": \"a1b2c3d4\",\"origin\": {\"type\": \"utility\"}}}"; + private static final String JSON_STATUS_METADATA_PARENT_ONLY = "{\"recipient\": {\"parentUserId\": \"US.ENT.11815799212886844830\"}}"; + + /** + * The same payload as JSON_STATUS_MESSAGE_METADATA with an unrecognised key + * added at every nesting level, standing in for fields the platform adds + * after this SDK version ships. + */ + private static final String JSON_STATUS_MESSAGE_METADATA_UNKNOWN_FIELDS = "{\"id\": \"e5f6a7b8-c9d0-1234-ef01-23456789abcd\",\"from\": \"15551234567\",\"to\": \"US.13491208655302741918\",\"type\": \"text\",\"futureTopLevelField\": \"ignored\",\"content\": {\"text\": \"Hello! Your order has been shipped.\",\"futureContentField\": \"ignored\"},\"metadata\": {\"sender\": {\"userId\": \"US.13491208655302741918\",\"futureSenderField\": \"ignored\"},\"receivedAt\": \"0001-01-01T00:00:00Z\",\"futureMetadataField\": \"ignored\"}}"; /** * Epsilon to use when checking two latitudes or longitudes for equality. @@ -52,6 +67,7 @@ public void testSendConversationMessage() throws GeneralException, UnauthorizedE conversationMessageRequest.setChannelId("aChannelIdentifier"); conversationMessageRequest.setType(ConversationContentType.VIDEO); conversationMessageRequest.setContent(conversationContent); + conversationMessageRequest.setReportUrl("https://example.com/reportUrl"); MessageBirdService messageBirdService = SpyService .expects("POST", "conversations/convid/messages", conversationMessageRequest) @@ -65,6 +81,31 @@ public void testSendConversationMessage() throws GeneralException, UnauthorizedE assertEquals(ConversationContentType.VIDEO, conversationMessage.getType()); } + @Test + public void testSendMessage() throws GeneralException, UnauthorizedException { + ConversationContent conversationContent = new ConversationContent(); + conversationContent.setText("test"); + + ConversationSendRequest sendRequest = new ConversationSendRequest(); + sendRequest.setFrom("aChannelIdentifier"); + sendRequest.setType(ConversationContentType.TEXT); + sendRequest.setContent(conversationContent); + sendRequest.setReportUrl("https://example.com/reportUrl"); + + MessageBirdService messageBirdService = SpyService + .expects("POST", "send", sendRequest) + .withConversationsAPIBaseURL() + .andReturns(new APIResponse(JSON_CONVERSATION_SEND_MESSAGE_RESPONSE)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + + ConversationSendResponse conversationMessage + = messageBirdClient.sendMessage(sendRequest); + + assertEquals("mesid", conversationMessage.getId()); + assertEquals("accepted", conversationMessage.getStatus()); + assertEquals("mesid", conversationMessage.getFallback().getId()); + } + @Test public void testViewConversationMessageAudio() throws GeneralException, NotFoundException, UnauthorizedException { MessageBirdService messageBirdService = SpyService @@ -156,6 +197,96 @@ public void testViewConversationMessageLocation() throws GeneralException, NotFo assertEquals(4.911627, location.getLongitude(), EPSILON_LOCATION_EQUALITY); } + @Test + public void testViewConversationMessageWithBsuidMetadata() throws GeneralException, NotFoundException, UnauthorizedException { + MessageBirdService messageBirdService = SpyService + .expects("GET", "messages/mesid") + .withConversationsAPIBaseURL() + .andReturns(new APIResponse(JSON_CONVERSATION_MESSAGE_BSUID)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + + ConversationMessage message = messageBirdClient.viewConversationMessage("mesid"); + + ConversationMessageMetadata metadata = message.getMetadata(); + assertNotNull(metadata); + assertNotNull(metadata.getReceivedAt()); + ConversationSenderMetadata sender = metadata.getSender(); + assertEquals("Alice", sender.getDisplayName()); + assertEquals("alice_shop", sender.getUsername()); + assertEquals("US.13491208655302741918", sender.getUserId()); + assertEquals("US.ENT.11815799212886844830", sender.getParentUserId()); + } + + @Test + public void testStatusMessageMetadataToleratesUnknownFields() throws Exception { + // A plain mapper, and unknown keys at every level: webhook payload POJOs + // must not force consumers to disable FAIL_ON_UNKNOWN_PROPERTIES, and + // must survive fields the platform adds after this version ships. + ConversationStatusMessageMetadata md = new ObjectMapper().readValue( + JSON_STATUS_MESSAGE_METADATA_UNKNOWN_FIELDS, ConversationStatusMessageMetadata.class); + + assertEquals("e5f6a7b8-c9d0-1234-ef01-23456789abcd", md.getId()); + assertEquals("Hello! Your order has been shipped.", md.getContent().getText()); + assertEquals("US.13491208655302741918", md.getMetadata().getSender().getUserId()); + assertNotNull(md.getMetadata().getReceivedAt()); + } + + @Test + public void testStatusMetadataDeserializesRecipient() throws Exception { + // A plain mapper: these payload POJOs must not require the caller to + // disable FAIL_ON_UNKNOWN_PROPERTIES. + ConversationStatusMetadata metadata = new ObjectMapper().readValue( + JSON_STATUS_METADATA_WITH_RECIPIENT, ConversationStatusMetadata.class); + + ConversationRecipientMetadata recipient = metadata.getRecipient(); + assertNotNull(recipient); + assertEquals("US.13491208655302741918", recipient.getUserId()); + assertEquals("US.ENT.11815799212886844830", recipient.getParentUserId()); + + // Meta's own objects pass through untouched, snake_case keys and all. + assertEquals("CBP", metadata.getPricing().get("pricing_model")); + assertEquals("a1b2c3d4", metadata.getConversation().get("id")); + + // Anything else on the payload is kept rather than dropped. + assertEquals("order-1234", metadata.getAdditionalProperties().get("biz_opaque_callback_data")); + } + + @Test + public void testStatusMetadataWithoutRecipientIsNull() throws Exception { + ConversationStatusMetadata metadata = new ObjectMapper().readValue( + JSON_STATUS_METADATA_WITHOUT_RECIPIENT, ConversationStatusMetadata.class); + + // Accounts that never receive BSUIDs get payloads with no recipient key + // at all — the rest of the metadata must still parse. + assertNull(metadata.getRecipient()); + assertEquals("CBP", metadata.getPricing().get("pricing_model")); + assertTrue(metadata.getAdditionalProperties().isEmpty()); + } + + @Test + public void testStatusMetadataRecipientWithParentOnly() throws Exception { + ConversationStatusMetadata metadata = new ObjectMapper().readValue( + JSON_STATUS_METADATA_PARENT_ONLY, ConversationStatusMetadata.class); + + ConversationRecipientMetadata recipient = metadata.getRecipient(); + assertNotNull(recipient); + assertNull(recipient.getUserId()); + assertEquals("US.ENT.11815799212886844830", recipient.getParentUserId()); + } + + @Test + public void testStatusMessageMetadataDeserializes() throws Exception { + ConversationStatusMessageMetadata md = new ObjectMapper().readValue( + JSON_STATUS_MESSAGE_METADATA, ConversationStatusMessageMetadata.class); + + assertEquals("e5f6a7b8-c9d0-1234-ef01-23456789abcd", md.getId()); + assertEquals("15551234567", md.getFrom()); + assertEquals("US.13491208655302741918", md.getTo()); + assertEquals("text", md.getType()); + assertEquals("Hello! Your order has been shipped.", md.getContent().getText()); + assertEquals("US.13491208655302741918", md.getMetadata().getSender().getUserId()); + } + @Test public void testViewConversationMessageText() throws GeneralException, NotFoundException, UnauthorizedException { MessageBirdService messageBirdService = SpyService diff --git a/api/src/test/java/com/messagebird/ConversationsTest.java b/api/src/test/java/com/messagebird/ConversationsTest.java index 2f3834d1..4e7624e8 100644 --- a/api/src/test/java/com/messagebird/ConversationsTest.java +++ b/api/src/test/java/com/messagebird/ConversationsTest.java @@ -6,6 +6,10 @@ import com.messagebird.objects.conversations.*; import org.junit.Test; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + import static org.junit.Assert.*; public class ConversationsTest { @@ -44,12 +48,19 @@ public void testStartConversation() throws GeneralException, UnauthorizedExcepti ConversationContent conversationContent = new ConversationContent(); conversationContent.setText("Hello world"); + Map source = new HashMap<>(); + source.put("agentId", "abc123"); + source.put("userId", Arrays.asList(1, 2, 3)); + ConversationStartRequest request = new ConversationStartRequest( "31612345678", ConversationContentType.TEXT, conversationContent, - "chanid" + "chanid", + source, + ConversationMessageTag.AccountUpdate ); + request.setReportUrl("https://example.com/reportUrl"); MessageBirdService messageBirdService = SpyService .expects("POST", "conversations/start", request) @@ -65,7 +76,7 @@ public void testStartConversation() throws GeneralException, UnauthorizedExcepti @Test public void testUpdateConversation() throws GeneralException, UnauthorizedException { MessageBirdService messageBirdService = SpyService - .expects("PATCH", "conversations/convid", ConversationStatus.ARCHIVED) + .expects("PATCH", "conversations/convid", new ConversationUpdateRequest(ConversationStatus.ARCHIVED)) .withConversationsAPIBaseURL() .andReturns(new APIResponse(JSON_CONVERSATION)); MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); diff --git a/api/src/test/java/com/messagebird/MessageBirdClientTest.java b/api/src/test/java/com/messagebird/MessageBirdClientTest.java index 9b22afbb..b11c2f54 100644 --- a/api/src/test/java/com/messagebird/MessageBirdClientTest.java +++ b/api/src/test/java/com/messagebird/MessageBirdClientTest.java @@ -4,21 +4,33 @@ import com.messagebird.exceptions.NotFoundException; import com.messagebird.exceptions.UnauthorizedException; import com.messagebird.objects.*; +import com.messagebird.objects.conversations.*; +import com.messagebird.objects.integrations.Template; +import com.messagebird.objects.integrations.TemplateList; +import com.messagebird.objects.integrations.TemplateResponse; import com.messagebird.objects.voicecalls.*; +import java.util.ArrayList; + +import org.jetbrains.annotations.NotNull; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mockito; +import static org.junit.Assume.assumeNotNull; -import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import static com.messagebird.MessageBirdClient.*; +import static com.messagebird.TestUtil.*; +import static com.messagebird.TestUtil.createChildAccountDetailedResponse; import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import static org.unitils.reflectionassert.ReflectionAssert.assertReflectionEquals; /** * Created by rvt on 1/8/15. @@ -33,7 +45,10 @@ public class MessageBirdClientTest { @BeforeClass public static void setUpClass() { messageBirdAccessKey = System.getProperty("messageBirdAccessKey"); - messageBirdMSISDN = new BigInteger(System.getProperty("messageBirdMSISDN")); + String msisdn = System.getProperty("messageBirdMSISDN"); + assumeNotNull("Integration test skipped: set -DmessageBirdAccessKey and -DmessageBirdMSISDN to run", + messageBirdAccessKey, msisdn); + messageBirdMSISDN = new BigInteger(msisdn); } @Before @@ -104,6 +119,32 @@ public void testDeleteMessage() throws Exception { messageBirdClient.deleteMessage("Foo"); } + @Test + public void testListScheduledMessages() throws Exception { + final MessageList mockedResponse = new MessageList(); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientMock = new MessageBirdClient(messageBirdServiceMock); + + Map filters = new LinkedHashMap<>(); + filters.put("status", "scheduled"); + + when(messageBirdServiceMock.requestList("/messages", filters, null, null, MessageList.class)) + .thenReturn(mockedResponse); + + final MessageList response = messageBirdClientMock.listMessagesFiltered(null, null, filters); + assertNotNull(response); + assertEquals(response, mockedResponse); + } + + @Test(expected = IllegalArgumentException.class) + public void testListScheduledMessagesWrongFilter() throws Exception { + Map filters = new LinkedHashMap<>(); + filters.put("does not exist", null); + + messageBirdClient.listMessagesFiltered(null, null, filters); + } + /*********************************************************************/ /** Test message system **/ /*********************************************************************/ @@ -118,7 +159,7 @@ public void testSendDeleteMessage() throws Exception { assertNotNull(mr.getId()); assertEquals(mr.getReference(), reference); assertEquals(mr.getBody(), body); - assertEquals(mr.getDatacoding(), DataCodingType.plain); + assertEquals(DataCodingType.plain,mr.getDatacoding()); // Deleting of a message is not yet supported in test mode // Thread.sleep(1000); @@ -188,8 +229,8 @@ public void testSendDeleteFlashMessage() throws Exception { final String body = "Body test message Über € " + messageBirdMSISDN; final MessageResponse mr = messageBirdClient.sendFlashMessage("originator", body, Collections.singletonList(messageBirdMSISDN)); assertNotNull(mr.getId()); - assertSame(mr.getType(), MsgType.flash); - assertSame(mr.getMclass(), MClassType.flash); + assertSame(MsgType.flash,mr.getType() ); + assertSame(MClassType.flash, mr.getMclass()); // Deleting of a message is not yet supported in test mode // Thread.sleep(1000); @@ -260,8 +301,8 @@ public void testSendVoiceMessage() throws Exception { final VoiceMessageResponse mr = messageBirdClient.sendVoiceMessage(vm); assertNotNull(mr.getId()); assertEquals(mr.getBody(), body); - assertSame(mr.getIfMachine(), IfMachineType.hangup); - assertSame(mr.getVoice(), VoiceType.male); + assertSame(IfMachineType.hangup, mr.getIfMachine() ); + assertSame(VoiceType.male, mr.getVoice()); // Deleting of a message is not yet supported in test mode // Thread.sleep(1000); @@ -288,17 +329,6 @@ public void testSendVoiceMessage2() throws Exception { assertNotNull(mr.getId()); assertEquals(mr.getBody(), body); assertEquals(mr.getReference(), reference); - - Thread.sleep(500); - // Viewing of a message is not yet supported in test mode - // final VoiceMessageResponse mr2 = messageBirdClient.viewVoiceMessage(mr.getId()); - // assertTrue(mr2.getId() != null); - // assertTrue(mr2.getBody().equals(body)); - // assertTrue(mr2.getReference().equals(reference)); - - // Deleting of a message is not yet supported in test mode - // Thread.sleep(1000); - // Gives 404 messageBirdClient.deleteVoiceMessage(mr.getId()); } /** @@ -333,7 +363,7 @@ public void testSendVerifyToken1() throws UnauthorizedException, GeneralExceptio } @Test - public void testSendVerifyTokenAndGetVerifyObject() throws UnauthorizedException, GeneralException, NotFoundException { + public void testSendVerifyTokenAndGetVerifyObject() throws UnauthorizedException, GeneralException { Verify verify = messageBirdClient.sendVerifyToken(messageBirdMSISDN.toString()); assertFalse("href is empty", verify.getHref().isEmpty()); assertFalse("id is empty", verify.getId().isEmpty()); @@ -347,7 +377,7 @@ public void testSendVerifyTokenAndGetVerifyObject() throws UnauthorizedException } @Test - public void testVerifyToken() throws UnauthorizedException, GeneralException, UnsupportedEncodingException { + public void testVerifyToken() throws UnauthorizedException, GeneralException { Verify verify = messageBirdClient.sendVerifyToken(messageBirdMSISDN.toString()); assertFalse("href is empty", verify.getHref().isEmpty()); @@ -363,7 +393,7 @@ public void testVerifyToken() throws UnauthorizedException, GeneralException, Un } @Test - public void testDeleteVerifyToken() throws UnauthorizedException, GeneralException, NotFoundException, UnsupportedEncodingException { + public void testDeleteVerifyToken() throws UnauthorizedException, GeneralException { Verify verify = messageBirdClient.sendVerifyToken(messageBirdMSISDN.toString()); assertFalse("href is empty", verify.getHref().isEmpty()); try { @@ -401,7 +431,6 @@ public void shouldThrowIllegalArgumentExceptionWhenSourceOfVoiceCallIsMissing() voiceCall.setDestination("ANY_DESTINATION"); final VoiceCallFlow voiceCallFlow = new VoiceCallFlow(); - voiceCallFlow.setTitle("Test title"); VoiceStep voiceStep = new VoiceStep(); voiceStep.setAction("say"); @@ -417,13 +446,11 @@ public void shouldThrowIllegalArgumentExceptionWhenSourceOfVoiceCallIsMissing() } @Test(expected = IllegalArgumentException.class) - public void shouldThrowIllegalArgumentExceptionWhenDestinationOfVoiceCallIsMissing() throws UnauthorizedException, - GeneralException { + public void shouldThrowIllegalArgumentExceptionWhenDestinationOfVoiceCallIsMissing() throws UnauthorizedException, GeneralException { final VoiceCall voiceCall = new VoiceCall(); voiceCall.setSource("ANY_SOURCE"); final VoiceCallFlow voiceCallFlow = new VoiceCallFlow(); - voiceCallFlow.setTitle("Test title"); VoiceStep voiceStep = new VoiceStep(); voiceStep.setAction("say"); @@ -470,8 +497,7 @@ public void testViewVoiceCall() throws UnauthorizedException, GeneralException, } @Test - public void testDeleteVoiceCall() throws UnauthorizedException, - GeneralException, NotFoundException { + public void testDeleteVoiceCall() throws UnauthorizedException, GeneralException, NotFoundException { MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); @@ -565,45 +591,129 @@ public void testCreateTranscription() throws UnauthorizedException, GeneralExcep assertEquals(response.getData().get(0).getCreatedAt(), transcriptionResponse.getData().get(0).getCreatedAt()); } + @Test + public void testListRecordings() throws UnauthorizedException, GeneralException { + final RecordingResponse recordings = TestUtil.createRecordingResponseList(); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s/%s%s/%s%s", + VOICE_CALLS_BASE_URL, + VOICECALLSPATH, + "ANY_CALL_ID", + LEGSPATH, + "ANY_LEG_ID", + RECORDINGPATH + ); + when(messageBirdServiceMock.requestList(url, 0, 0, RecordingResponse.class)) + .thenReturn(recordings); + + final RecordingResponse response = messageBirdClientInjectMock + .listRecordings("ANY_CALL_ID", "ANY_LEG_ID", 0, 0); + verify(messageBirdServiceMock, times(1)).requestList(url, 0, 0, RecordingResponse.class); + assertNotNull(response); + for(int i = 0; i < response.getData().size() ; i++) { + assertReflectionEquals(response.getData().get(i), recordings.getData().get(i)); + } + } + + @Test + public void testDownloadRecording() throws NotFoundException, GeneralException, UnauthorizedException { + String recordId = "123123123"; + String basePath = "test"; + String fileName = String.format("%s%s", recordId, RECORDING_DOWNLOAD_FORMAT); + final String downloadPath = TestUtil.createDownloadPath(recordId, basePath); + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s/%s%s/%s%s/%s", + VOICE_CALLS_BASE_URL, + VOICECALLSPATH, + "ANY_CALL_ID", + LEGSPATH, + "ANY_LEG_ID", + RECORDINGPATH, + fileName + ); + when(messageBirdServiceMock.getBinaryData(url, basePath, fileName)) + .thenReturn(downloadPath); + final String response = messageBirdClientInjectMock + .downloadRecording("ANY_CALL_ID", "ANY_LEG_ID", recordId, basePath); + verify(messageBirdServiceMock, times(1)).getBinaryData(url, basePath, fileName); + assertNotNull(response); + assertEquals(downloadPath, response); + } + + @Test + public void testDownloadTranscription() throws NotFoundException, GeneralException, UnauthorizedException { + String transcriptionId = "123123123"; + String basePath = "test"; + String fileName = String.format("%s%s", transcriptionId, TRANSCRIPTION_DOWNLOAD_FORMAT); + final String downloadPath = TestUtil.createDownloadPath(transcriptionId, basePath); + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s/%s%s/%s%s/%s%s/%s", + VOICE_CALLS_BASE_URL, + VOICECALLSPATH, + "ANY_CALL_ID", + LEGSPATH, + "ANY_LEG_ID", + RECORDINGPATH, + "ANY_RECORDING_ID", + TRANSCRIPTIONPATH, + fileName + ); + when(messageBirdServiceMock.getBinaryData(url, basePath, fileName)) + .thenReturn(downloadPath); + final String response = messageBirdClientInjectMock + .downloadTranscription("ANY_CALL_ID", "ANY_LEG_ID", "ANY_RECORDING_ID", transcriptionId, basePath); + verify(messageBirdServiceMock, times(1)).getBinaryData(url, basePath, fileName); + assertNotNull(response); + assertEquals(downloadPath, response); + } + @Test(expected = IllegalArgumentException.class) public void shouldThrowIllegalArgumentExceptionWhenLanguageIsNotSupported() throws UnauthorizedException, GeneralException { messageBirdClient.createTranscription("ANY_CALL_ID", "ANY_LEG_ID", "ANY_ID", "tr-TR"); } @Test - public void testViewTranscription() throws UnauthorizedException, GeneralException { + public void testViewTranscription() throws UnauthorizedException, GeneralException, NotFoundException { final TranscriptionResponse transcriptionResponse = TestUtil.createTranscriptionResponse(); MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); String url = String.format( - "%s%s/%s%s/%s%s/%s", + "%s%s/%s%s/%s%s/%s%s", VOICE_CALLS_BASE_URL, VOICECALLSPATH, "ANY_CALL_ID", LEGSPATH, "ANY_LEG_ID", RECORDINGPATH, - "ANY_ID"); + "ANY_ID", + TRANSCRIPTIONPATH); - when(messageBirdServiceMock.requestList(Mockito.eq(url), Mockito.isA(PagedPaging.class), Mockito.eq(TranscriptionResponse.class))) + when(messageBirdServiceMock.requestByID(Mockito.eq(url), Mockito.eq("ANY_TRANSCRIPTION_ID"), Mockito.eq(TranscriptionResponse.class))) .thenReturn(transcriptionResponse); final TranscriptionResponse response = messageBirdClientInjectMock - .viewTranscription("ANY_CALL_ID", "ANY_LEG_ID", "ANY_ID", 1, 2); + .viewTranscription("ANY_CALL_ID", "ANY_LEG_ID", "ANY_ID", "ANY_TRANSCRIPTION_ID"); verify(messageBirdServiceMock, times(1)) - .requestList(Mockito.eq(url), Mockito.isA(PagedPaging.class), Mockito.eq(TranscriptionResponse.class)); + .requestByID(Mockito.eq(url), Mockito.eq("ANY_TRANSCRIPTION_ID"), Mockito.eq(TranscriptionResponse.class)); assertNotNull(response); - assertEquals(response.getData().get(0).getId(), transcriptionResponse.getData().get(0).getId()); - assertEquals(response.getData().get(0).getRecordingId(), transcriptionResponse.getData().get(0).getRecordingId()); - assertEquals(response.getData().get(0).getCreatedAt(), transcriptionResponse.getData().get(0).getCreatedAt()); - + assertReflectionEquals(response.getData().get(0), transcriptionResponse.getData().get(0)); } @Test public void testCreateWebhook() throws UnauthorizedException, GeneralException { - final Webhook webhook = TestUtil.createWebHook(); + final Webhook webhook = TestUtil.createWebhook(); final WebhookResponseData webhookResponseData = TestUtil.createWebhookResponseData(); MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); @@ -611,26 +721,16 @@ public void testCreateWebhook() throws UnauthorizedException, GeneralException { when(messageBirdServiceMock.sendPayLoad(VOICE_CALLS_BASE_URL + WEBHOOKS, webhook, WebhookResponseData.class)) .thenReturn(webhookResponseData); - final WebhookResponseData response = messageBirdClientInjectMock.createWebHook(webhook); + final WebhookResponseData response = messageBirdClientInjectMock.createWebhook(webhook); verify(messageBirdServiceMock, times(1)).sendPayLoad(VOICE_CALLS_BASE_URL + WEBHOOKS, webhook, WebhookResponseData.class); assertNotNull(response); assertEquals(response.getData().get(0).getId(), webhookResponseData.getData().get(0).getId()); } - @Test(expected = IllegalArgumentException.class) - public void shouldThrowIllegalArgumentExceptionWhenCreateWebhookWithMissingTitle() throws UnauthorizedException, GeneralException { - final Webhook webhook = new Webhook(); - webhook.setUrl("ANY_URL"); - messageBirdClient.createWebHook(webhook); - - } - @Test(expected = IllegalArgumentException.class) public void shouldThrowIllegalArgumentExceptionWhenCreateWebhookWithMissingUrl() throws UnauthorizedException, GeneralException { final Webhook webhook = new Webhook(); - webhook.setTitle("ANY_TITLE"); - messageBirdClient.createWebHook(webhook); - + messageBirdClient.createWebhook(webhook); } @Test @@ -642,11 +742,879 @@ public void testViewWebhook() throws UnauthorizedException, GeneralException, No when(messageBirdServiceMock.requestByID(VOICE_CALLS_BASE_URL + WEBHOOKS, "ANY_ID", WebhookResponseData.class)) .thenReturn(webhookResponseData); - final WebhookResponseData response = messageBirdClientInjectMock.viewWebHook("ANY_ID"); + final WebhookResponseData response = messageBirdClientInjectMock.viewWebhook("ANY_ID"); verify(messageBirdServiceMock, times(1)).requestByID(VOICE_CALLS_BASE_URL + WEBHOOKS, "ANY_ID", WebhookResponseData.class); assertNotNull(response); assertEquals(response.getData().get(0).getId(), webhookResponseData.getData().get(0).getId()); assertEquals(response.getData().get(0).getUrl(), webhookResponseData.getData().get(0).getUrl()); } + + @Test + public void testListWebhooks() throws UnauthorizedException, GeneralException { + final WebhookList webhookList = TestUtil.createWebhookList(); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientMock = new MessageBirdClient(messageBirdServiceMock); + + when(messageBirdServiceMock.requestList(anyString(), anyInt(), anyInt(), eq(WebhookList.class))) + .thenReturn(webhookList); + final WebhookList response = messageBirdClientMock.listWebhooks(0, 0); + verify(messageBirdServiceMock, times(1)) + .requestList(VOICE_CALLS_BASE_URL + WEBHOOKS, 0, 0, WebhookList.class); + assertNotNull(response); + assertEquals(response.getData().get(0).getId(), webhookList.getData().get(0).getId()); + assertEquals(response.getData().get(0).getUrl(), webhookList.getData().get(0).getUrl()); + } + + @Test + public void testUpdateWebhook() throws UnauthorizedException, GeneralException { + final Webhook webhook = TestUtil.createWebhook(); + final WebhookResponseData webhookResponseData = TestUtil.createWebhookResponseData(); + final String id = webhookResponseData.getData().get(0).getId(); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format("%s%s/%s", VOICE_CALLS_BASE_URL, WEBHOOKS, id); + when(messageBirdServiceMock.sendPayLoad(anyString(), anyString(), eq(webhook), eq(WebhookResponseData.class))) + .thenReturn(webhookResponseData); + final WebhookResponseData response = messageBirdClientMock.updateWebhook(id, webhook); + verify(messageBirdServiceMock, times(1)) + .sendPayLoad("PUT", url, webhook, WebhookResponseData.class); + assertNotNull(response); + assertEquals(response.getData().get(0).getUrl(), webhookResponseData.getData().get(0).getUrl()); + } + + @Test + public void testDeleteWebhook() throws NotFoundException, GeneralException, UnauthorizedException { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientMock = new MessageBirdClient(messageBirdServiceMock); + + messageBirdClientMock.deleteWebhook("id"); + verify(messageBirdServiceMock, times(1)).deleteByID(VOICE_CALLS_BASE_URL + WEBHOOKS, "id"); + } + + @Test + public void testListNumbersForPurchase() throws IllegalArgumentException, GeneralException, UnauthorizedException, NotFoundException { + final String url = String.format("%s/available-phone-numbers", NUMBERS_CALLS_BASE_URL); + final PhoneNumbersResponse mockedResponse = new PhoneNumbersResponse(); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientMock = new MessageBirdClient(messageBirdServiceMock); + + when(messageBirdServiceMock.requestByID(url, "NL", PhoneNumbersResponse.class)) + .thenReturn(mockedResponse); + + final PhoneNumbersResponse response = messageBirdClientMock.listNumbersForPurchase("NL"); + verify(messageBirdServiceMock, times(1)).requestByID(url, "NL", PhoneNumbersResponse.class); + + assertNotNull(response); + assertEquals(response, mockedResponse); + } + + @Test + public void testListNumbersForPurchaseWithParams() throws IllegalArgumentException, GeneralException, UnauthorizedException, NotFoundException { + final String url = String.format("%s/available-phone-numbers", NUMBERS_CALLS_BASE_URL); + final PhoneNumbersResponse mockedResponse = new PhoneNumbersResponse(); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientMock = new MessageBirdClient(messageBirdServiceMock); + + PhoneNumbersLookup options = new PhoneNumbersLookup(); + options.setFeatures(PhoneNumberFeature.VOICE, PhoneNumberFeature.SMS); + options.setType(PhoneNumberType.MOBILE); + options.setLimit(1); + options.setNumber(562); + options.setSearchPattern(PhoneNumberSearchPattern.START); + + when(messageBirdServiceMock.requestByID(url, "US", options.toHashMap(), PhoneNumbersResponse.class)) + .thenReturn(mockedResponse); + + final PhoneNumbersResponse response = messageBirdClientMock.listNumbersForPurchase("US", options); + verify(messageBirdServiceMock, times(1)).requestByID(url, "US", options.toHashMap(), PhoneNumbersResponse.class); + assertNotNull(response); + assertEquals(response, mockedResponse); + } + + @Test + public void testPurchaseNumber() throws UnauthorizedException, GeneralException { + final String url = String.format("%s/phone-numbers", NUMBERS_CALLS_BASE_URL); + + PurchasedNumberCreatedResponse purchasedNumberMockData = new PurchasedNumberCreatedResponse(); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientMock = new MessageBirdClient(messageBirdServiceMock); + + final Map payload = new LinkedHashMap(); + payload.put("number", "15625267429"); + payload.put("countryCode", "US"); + payload.put("billingIntervalMonths", 1); + + when(messageBirdServiceMock.sendPayLoad(url, payload, PurchasedNumberCreatedResponse.class)) + .thenReturn(purchasedNumberMockData); + final PurchasedNumberCreatedResponse response = messageBirdClientMock.purchaseNumber("15625267429", "US", 1); + verify(messageBirdServiceMock, times(1)).sendPayLoad(url, payload, PurchasedNumberCreatedResponse.class); + assertNotNull(response); + assertEquals(response, purchasedNumberMockData); + } + + @Test + public void testListPurchasedNumbers() throws UnauthorizedException, GeneralException, NotFoundException { + final String url = String.format("%s/phone-numbers", NUMBERS_CALLS_BASE_URL); + + PurchasedNumbersResponse purchasedNumbersMockData = new PurchasedNumbersResponse(); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientMock = new MessageBirdClient(messageBirdServiceMock); + + PurchasedNumbersFilter filter = new PurchasedNumbersFilter(); + filter.setLimit(1); + filter.addFeature(PhoneNumberFeature.SMS); + filter.setType(PhoneNumberType.MOBILE); + filter.addTag("tag"); + + when(messageBirdServiceMock.requestByID(url, null, filter.toHashMap(), PurchasedNumbersResponse.class)) + .thenReturn(purchasedNumbersMockData); + + final PurchasedNumbersResponse response = messageBirdClientMock.listPurchasedNumbers(filter); + + verify(messageBirdServiceMock, times(1)).requestByID(url, null, filter.toHashMap(), PurchasedNumbersResponse.class); + assertNotNull(response); + assertEquals(response, purchasedNumbersMockData); + } + + @Test + public void testViewPurchasedNumber() throws UnauthorizedException, GeneralException, NotFoundException { + final String url = String.format("%s/phone-numbers", NUMBERS_CALLS_BASE_URL); + + PurchasedNumber purchasedNumberMockData = new PurchasedNumber(); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientMock = new MessageBirdClient(messageBirdServiceMock); + when(messageBirdServiceMock.requestByID(url, "15625267429", PurchasedNumber.class)) + .thenReturn(purchasedNumberMockData); + final PurchasedNumber response = messageBirdClientMock.viewPurchasedNumber("15625267429"); + + verify(messageBirdServiceMock, times(1)).requestByID(url, "15625267429", PurchasedNumber.class); + assertNotNull(response); + assertEquals(response, purchasedNumberMockData); + } + + @Test + public void updatePurchasedNumber() throws UnauthorizedException, GeneralException { + final String phoneNumber = "15625267429"; + final String url = String.format("%s/phone-numbers/%s", NUMBERS_CALLS_BASE_URL, phoneNumber); + + PurchasedNumber updatedNumberMock = new PurchasedNumber(); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientMock = new MessageBirdClient(messageBirdServiceMock); + + final Map> payload = new HashMap>(); + payload.put("tags", Collections.singletonList("tag")); + + when(messageBirdServiceMock.sendPayLoad("PATCH", url, payload, PurchasedNumber.class)) + .thenReturn(updatedNumberMock); + final PurchasedNumber response = messageBirdClientMock.updateNumber(phoneNumber, "tag"); + verify(messageBirdServiceMock, times(1)).sendPayLoad("PATCH", url, payload, PurchasedNumber.class); + assertNotNull(response); + assertEquals(response, updatedNumberMock); + } + + @Test + public void deletePurchasedNumber() throws UnauthorizedException, GeneralException, NotFoundException { + final String phoneNumber = "15625267429"; + final String url = String.format("%s/phone-numbers", NUMBERS_CALLS_BASE_URL); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientMock = new MessageBirdClient(messageBirdServiceMock); + + messageBirdClientMock.cancelNumber(phoneNumber); + verify(messageBirdServiceMock, times(1)).deleteByID(url, phoneNumber); + } + + @Test + public void testDeleteRecording() throws NotFoundException, GeneralException, UnauthorizedException { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientMock = new MessageBirdClient(messageBirdServiceMock); + String url = String.format( + "%s%s/%s%s/%s%s", + VOICE_CALLS_BASE_URL, + VOICECALLSPATH, + "ANY_CALL_ID", + LEGSPATH, + "ANY_LEG_ID", + RECORDINGPATH + ); + messageBirdClientMock.deleteRecording("ANY_CALL_ID", "ANY_LEG_ID","recordingID"); + verify(messageBirdServiceMock, times(1)).deleteByID(url , "recordingID"); + } + + @Test + public void testMockUploadFile() throws GeneralException, UnauthorizedException { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdServiceMock); + byte[] binary = {1, 2, 3, 4, 5, 6}; + String contentType = "image/png"; + String filename = "filename.png"; + messageBirdClient.uploadFile(binary, contentType, filename); + String url = MESSAGING_BASE_URL + FILES_PATH; + final Map headers = new HashMap<>(); + headers.put("Content-Type", contentType); + headers.put("filename", filename); + verify(messageBirdServiceMock, times(1)).sendPayLoad("POST", url, headers, binary, FileUploadResponse.class); + } + + @Test + public void testUploadFileWithNullBinary() { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdServiceMock); + String contentType = "image/png"; + String filename = "filename.png"; + assertThrows(IllegalArgumentException.class, () -> messageBirdClient.uploadFile(null, contentType, filename)); + } + + @Test + public void testUploadFileWithNullContentType() { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdServiceMock); + byte[] binary = {1, 2, 3, 4, 5, 6}; + String filename = "filename.png"; + assertThrows(IllegalArgumentException.class, () -> messageBirdClient.uploadFile(binary, null, filename)); + } + + @Test + public void testUploadFileWithNullFilename() throws GeneralException, UnauthorizedException { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdServiceMock); + byte[] binary = {1, 2, 3, 4, 5, 6}; + String contentType = "image/png"; + messageBirdClient.uploadFile(binary, contentType, null); + String url = MESSAGING_BASE_URL + FILES_PATH; + final Map headers = new HashMap<>(); + headers.put("Content-Type", contentType); + verify(messageBirdServiceMock, times(1)).sendPayLoad("POST", url, headers, binary, FileUploadResponse.class); + } + + @Test + public void testMockDownloadFile() throws GeneralException, UnauthorizedException, NotFoundException { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdServiceMock); + String id = "8144d3bf-6228-4b0e-903f-022d8917f297"; + String filename = "file.png"; + String basePath = "/base/path"; + messageBirdClient.downloadFile(id, filename, basePath); + String url = MESSAGING_BASE_URL + FILES_PATH + "/" + id; + verify(messageBirdServiceMock, times(1)).getBinaryData(url, basePath, filename); + } + + @Test + public void testDownloadFileWithNullId() { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdServiceMock); + String filename = "file.png"; + String basePath = "/base/path"; + assertThrows(IllegalArgumentException.class, () -> messageBirdClient.downloadFile(null, filename, basePath)); + } + + @Test + public void testDownloadFileWithNullFilename() throws GeneralException, UnauthorizedException, NotFoundException { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdServiceMock); + String id = "8144d3bf-6228-4b0e-903f-022d8917f297"; + String basePath = "/base/path"; + messageBirdClient.downloadFile(id, null, basePath); + String url = MESSAGING_BASE_URL + FILES_PATH + "/" + id; + verify(messageBirdServiceMock, times(1)).getBinaryData(url, basePath, id); + } + + @Test + public void testDownloadFileWithNullBasePath() throws GeneralException, UnauthorizedException, NotFoundException { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdServiceMock); + String id = "8144d3bf-6228-4b0e-903f-022d8917f297"; + String filename = "file.png"; + messageBirdClient.downloadFile(id, filename, null); + String url = MESSAGING_BASE_URL + FILES_PATH + "/" + id; + verify(messageBirdServiceMock, times(1)).getBinaryData(url, null, filename); + } + + /****************************************************************************************************/ + /** Testing WhatsApp Templates **/ + /****************************************************************************************************/ + + @Test + public void testCreateWhatsAppTemplate() throws UnauthorizedException, GeneralException { + final TemplateResponse templateResponse = TestUtil.createWhatsAppTemplateResponse("sample_template_name", "ko"); + final Template template = TestUtil.createWhatsAppTemplate("sample_template_name", "ko"); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH + ); + + when(messageBirdServiceMock.sendPayLoad(url, template, TemplateResponse.class)) + .thenReturn(templateResponse); + + final TemplateResponse response = messageBirdClientInjectMock.createWhatsAppTemplate(template); + + verify(messageBirdServiceMock, times(1)).sendPayLoad(url, template, TemplateResponse.class); + assertNotNull(response); + assertEquals(response.getName(), templateResponse.getName()); + assertEquals(response.getLanguage(), templateResponse.getLanguage()); + assertEquals(response.getCategory(), templateResponse.getCategory()); + assertEquals(response.getStatus(), templateResponse.getStatus()); + assertEquals(response.getWabaID(), templateResponse.getWabaID()); + assertEquals(response.getCreatedAt(), templateResponse.getCreatedAt()); + assertEquals(response.getUpdatedAt(), templateResponse.getUpdatedAt()); + + /* verify components */ + for (int i = 0; i < response.getComponents().size(); i++) { + assertEquals(response.getComponents().get(i).getType(), templateResponse.getComponents().get(i).getType()); + assertEquals(response.getComponents().get(i).getFormat(), templateResponse.getComponents().get(i).getFormat()); + assertEquals(response.getComponents().get(i).getText(), templateResponse.getComponents().get(i).getText()); + } + } + @Test + public void testCreateWhatsAppCarouselTemplate() throws UnauthorizedException, GeneralException { + final TemplateResponse templateResponse = TestUtil.createWhatsAppCarouselTemplateResponse("sample_template_name", "ko"); + final Template template = TestUtil.createWhatsAppCarouselTemplate("sample_template_name", "ko"); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH + ); + + when(messageBirdServiceMock.sendPayLoad(url, template, TemplateResponse.class)) + .thenReturn(templateResponse); + + final TemplateResponse response = messageBirdClientInjectMock.createWhatsAppTemplate(template); + + verify(messageBirdServiceMock, times(1)).sendPayLoad(url, template, TemplateResponse.class); + assertNotNull(response); + assertEquals(response.getName(), templateResponse.getName()); + assertEquals(response.getLanguage(), templateResponse.getLanguage()); + assertEquals(response.getCategory(), templateResponse.getCategory()); + assertEquals(response.getStatus(), templateResponse.getStatus()); + assertEquals(response.getWabaID(), templateResponse.getWabaID()); + assertEquals(response.getCreatedAt(), templateResponse.getCreatedAt()); + assertEquals(response.getUpdatedAt(), templateResponse.getUpdatedAt()); + + /* verify components */ + for (int i = 0; i < response.getComponents().size(); i++) { + assertEquals(response.getComponents().get(i).getType(), templateResponse.getComponents().get(i).getType()); + assertEquals(response.getComponents().get(i).getFormat(), templateResponse.getComponents().get(i).getFormat()); + assertEquals(response.getComponents().get(i).getText(), templateResponse.getComponents().get(i).getText()); + } + } + + @Test + public void testUpdateWhatsAppTemplate() throws UnauthorizedException, GeneralException { + final TemplateResponse templateResponse = TestUtil.createWhatsAppTemplateResponse("sample_template_name", "ko"); + final Template template = TestUtil.createWhatsAppTemplate("sample_template_name", "ko"); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s%s/%s/%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH, + "sample_template_name", + "ko" + ); + + when(messageBirdServiceMock.sendPayLoad("PUT",url, template, TemplateResponse.class)) + .thenReturn(templateResponse); + + final TemplateResponse response = messageBirdClientInjectMock.updateWhatsAppTemplate(template,"sample_template_name","ko"); + verify(messageBirdServiceMock, times(1)).sendPayLoad("PUT",url, template, TemplateResponse.class); + assertNotNull(response); + assertEquals(response.getName(), templateResponse.getName()); + assertEquals(response.getLanguage(), templateResponse.getLanguage()); + assertEquals(response.getCategory(), templateResponse.getCategory()); + assertEquals(response.getStatus(), templateResponse.getStatus()); + assertEquals(response.getWabaID(), templateResponse.getWabaID()); + assertEquals(response.getCreatedAt(), templateResponse.getCreatedAt()); + assertEquals(response.getUpdatedAt(), templateResponse.getUpdatedAt()); + + /* verify components */ + for (int i = 0; i < response.getComponents().size(); i++) { + assertEquals(response.getComponents().get(i).getType(), templateResponse.getComponents().get(i).getType()); + assertEquals(response.getComponents().get(i).getFormat(), templateResponse.getComponents().get(i).getFormat()); + assertEquals(response.getComponents().get(i).getText(), templateResponse.getComponents().get(i).getText()); + } + } + + @Test + public void testListWhatsAppTemplates() throws UnauthorizedException, GeneralException { + final TemplateList templateList = TestUtil.createWhatsAppTemplateList("sample_template_name"); + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s%s", + INTEGRATIONS_BASE_URL_V3, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH + ); + + when(messageBirdServiceMock.requestList(url, 0, 0, TemplateList.class)) + .thenReturn(templateList); + + final TemplateList response = messageBirdClientInjectMock.listWhatsAppTemplates(0, 0); + verify(messageBirdServiceMock, times(1)).requestList(url, 0, 0, TemplateList.class); + assertNotNull(response); + for(int i = 0; i < response.getItems().size() ; i++) { + assertReflectionEquals(response.getItems().get(i), templateList.getItems().get(i)); + } + } + + @Test + public void testListWhatsAppTemplatesDefault() throws UnauthorizedException, GeneralException { + final TemplateList templateList = TestUtil.createWhatsAppTemplateList("sample_template_name"); + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s%s", + INTEGRATIONS_BASE_URL_V3, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH + ); + + when(messageBirdServiceMock.requestList(url, 0, 10, TemplateList.class)) + .thenReturn(templateList); + + final TemplateList response = messageBirdClientInjectMock.listWhatsAppTemplates(); + verify(messageBirdServiceMock, times(1)).requestList(url, 0, 10, TemplateList.class); + assertNotNull(response); + for(int i = 0; i < response.getItems().size() ; i++) { + assertReflectionEquals(response.getItems().get(i), templateList.getItems().get(i)); + } + } + + @Test + public void testListWhatsAppTemplatesByWABAID() throws UnauthorizedException, GeneralException { + final TemplateList templateList = TestUtil.createWhatsAppTemplateList("sample_template_name"); + final String wabaID = "testWABAID"; + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s%s", + INTEGRATIONS_BASE_URL_V3, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH + ); + + Map map = new LinkedHashMap<>(); + map.put("wabaId", wabaID); + + when(messageBirdServiceMock.requestList(url, map, 0, 0, TemplateList.class)) + .thenReturn(templateList); + + final TemplateList response = messageBirdClientInjectMock.listWhatsAppTemplates(0, 0, wabaID, null); + verify(messageBirdServiceMock, times(1)).requestList(url, map, 0, 0, TemplateList.class); + assertNotNull(response); + for(int i = 0; i < response.getItems().size() ; i++) { + assertReflectionEquals(response.getItems().get(i), templateList.getItems().get(i)); + } + } + + @Test + public void testListWhatsAppTemplatesByChannelID() throws UnauthorizedException, GeneralException { + final TemplateList templateList = TestUtil.createWhatsAppTemplateList("sample_template_name"); + final String channelID = "channel-id"; + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s%s", + INTEGRATIONS_BASE_URL_V3, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH + ); + + Map map = new LinkedHashMap<>(); + map.put("channelId", channelID); + + when(messageBirdServiceMock.requestList(url, map, 0, 0, TemplateList.class)) + .thenReturn(templateList); + + final TemplateList response = messageBirdClientInjectMock.listWhatsAppTemplates(0, 0, null, channelID); + verify(messageBirdServiceMock, times(1)).requestList(url, map, 0, 0, TemplateList.class); + assertNotNull(response); + for(int i = 0; i < response.getItems().size() ; i++) { + assertReflectionEquals(response.getItems().get(i), templateList.getItems().get(i)); + } + } + + @Test + public void testGetWhatsAppTemplatesBy() throws GeneralException, UnauthorizedException, NotFoundException { + final String templateName = "sample_template_name"; + final TemplateResponse templateResponse1 = TestUtil.createWhatsAppTemplateResponse(templateName, "en_US"); + final TemplateResponse templateResponse2 = TestUtil.createWhatsAppTemplateResponse("another_template", "en_US"); + final List templateList = new ArrayList<>(); + templateList.add(templateResponse1); + templateList.add(templateResponse2); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH + ); + + when(messageBirdServiceMock.requestByIdAsList(url, templateName, TemplateResponse.class)) + .thenReturn(templateList); + + final List response = messageBirdClientInjectMock.getWhatsAppTemplatesBy(templateName); + verify(messageBirdServiceMock, times(1)).requestByIdAsList(url, templateName, TemplateResponse.class); + assertNotNull(response); + assertEquals(response.size(), templateList.size()); + for(int i = 0; i < response.size() ; i++) { + assertReflectionEquals(response.get(i), templateList.get(i)); + } + } + + @Test + public void testGetWhatsAppTemplatesByNameAndWABAID() throws GeneralException, UnauthorizedException, NotFoundException { + final String templateName = "sample_template_name"; + final String wabaID = "testWABAID"; + final TemplateResponse templateResponse1 = TestUtil.createWhatsAppTemplateResponse(templateName, "en_US"); + final TemplateResponse templateResponse2 = TestUtil.createWhatsAppTemplateResponse("another_template", "en_US"); + final List templateList = new ArrayList<>(); + templateList.add(templateResponse1); + templateList.add(templateResponse2); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH + ); + String id = String.format( + "%s?wabaId=%s", + templateName, + wabaID + ); + + when(messageBirdServiceMock.requestByIdAsList(url, id, TemplateResponse.class)) + .thenReturn(templateList); + + final List response = messageBirdClientInjectMock.getWhatsAppTemplatesBy(templateName, wabaID, null); + verify(messageBirdServiceMock, times(1)).requestByIdAsList(url, id, TemplateResponse.class); + assertNotNull(response); + assertEquals(response.size(), templateList.size()); + for(int i = 0; i < response.size() ; i++) { + assertReflectionEquals(response.get(i), templateList.get(i)); + } + } + + @Test + public void testGetWhatsAppTemplatesByNameForChannelID() throws GeneralException, UnauthorizedException, NotFoundException { + final String templateName = "sample_template_name"; + final String channelID = "channel-id"; + final TemplateResponse templateResponse1 = TestUtil.createWhatsAppTemplateResponse(templateName, "en_US"); + final TemplateResponse templateResponse2 = TestUtil.createWhatsAppTemplateResponse("another_template", "en_US"); + final List templateList = new ArrayList<>(); + templateList.add(templateResponse1); + templateList.add(templateResponse2); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH + ); + String id = String.format( + "%s?channelId=%s", + templateName, + channelID + ); + + when(messageBirdServiceMock.requestByIdAsList(url, id, TemplateResponse.class)) + .thenReturn(templateList); + + final List response = messageBirdClientInjectMock.getWhatsAppTemplatesBy(templateName, null, channelID); + verify(messageBirdServiceMock, times(1)).requestByIdAsList(url, id, TemplateResponse.class); + assertNotNull(response); + assertEquals(response.size(), templateList.size()); + for(int i = 0; i < response.size() ; i++) { + assertReflectionEquals(response.get(i), templateList.get(i)); + } + } + + @Test + public void testFetchWhatsAppTemplateBy() throws UnauthorizedException, GeneralException, NotFoundException { + final String templateName = "sample_template_name"; + final String language = "ko"; + final TemplateResponse template = TestUtil.createWhatsAppTemplateResponse(templateName, language); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s%s/%s/%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH, + templateName, + language + ); + + when(messageBirdServiceMock.request(url, TemplateResponse.class)) + .thenReturn(template); + + final TemplateResponse response = messageBirdClientInjectMock.fetchWhatsAppTemplateBy(templateName, language); + verify(messageBirdServiceMock, times(1)).request(url, TemplateResponse.class); + assertNotNull(response); + assertEquals(response.getName(), template.getName()); + assertEquals(response.getLanguage(), template.getLanguage()); + assertEquals(response.getStatus(), template.getStatus()); + assertReflectionEquals(response.getComponents(), template.getComponents()); + } + + @Test + public void testFetchWhatsAppTemplateByNameAndLanguageAndWABAID() throws UnauthorizedException, GeneralException, NotFoundException { + final String templateName = "sample_template_name"; + final String language = "ko"; + final String wabaID = "testWABAID"; + final TemplateResponse template = TestUtil.createWhatsAppTemplateResponse(templateName, language); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s%s/%s/%s?wabaId=%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH, + templateName, + language, + wabaID + ); + + when(messageBirdServiceMock.request(url, TemplateResponse.class)) + .thenReturn(template); + + final TemplateResponse response = messageBirdClientInjectMock.fetchWhatsAppTemplateBy(templateName, language, wabaID, null); + verify(messageBirdServiceMock, times(1)).request(url, TemplateResponse.class); + assertNotNull(response); + assertEquals(response.getName(), template.getName()); + assertEquals(response.getLanguage(), template.getLanguage()); + assertEquals(response.getStatus(), template.getStatus()); + assertReflectionEquals(response.getComponents(), template.getComponents()); + } + + @Test + public void testFetchWhatsAppTemplateByNameAndLanguageForChannelID() throws UnauthorizedException, GeneralException, NotFoundException { + final String templateName = "sample_template_name"; + final String language = "ko"; + final String channelID = "channel-id"; + final TemplateResponse template = TestUtil.createWhatsAppTemplateResponse(templateName, language); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s%s/%s/%s?channelId=%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH, + templateName, + language, + channelID + ); + + when(messageBirdServiceMock.request(url, TemplateResponse.class)) + .thenReturn(template); + + final TemplateResponse response = messageBirdClientInjectMock.fetchWhatsAppTemplateBy(templateName, language, null, channelID); + verify(messageBirdServiceMock, times(1)).request(url, TemplateResponse.class); + assertNotNull(response); + assertEquals(response.getName(), template.getName()); + assertEquals(response.getLanguage(), template.getLanguage()); + assertEquals(response.getStatus(), template.getStatus()); + assertReflectionEquals(response.getComponents(), template.getComponents()); + } + + @Test + public void testDeleteTemplatesByName() throws UnauthorizedException, GeneralException, NotFoundException { + final String templateName = "sample_template_name"; + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + String url = String.format( + "%s%s%s/%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH, + templateName + ); + + when(messageBirdServiceMock.delete(url, null)).thenReturn(null); + messageBirdClientInjectMock.deleteTemplatesBy(templateName); + verify(messageBirdServiceMock).delete(url, null); + } + + @Test + public void testCreateChildAccounts() throws GeneralException, UnauthorizedException { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + ChildAccountCreateResponse childAccountCreateResponse = createChildAccountCreateResponse(); + ChildAccountRequest childAccountRequest = new ChildAccountRequest(); + childAccountRequest.setName("name"); + when(messageBirdServiceMock.sendPayLoad(PARTNER_ACCOUNTS_BASE_URL + "/child-accounts" , childAccountRequest, ChildAccountCreateResponse.class)) + .thenReturn(childAccountCreateResponse); + final ChildAccountCreateResponse response = messageBirdClientInjectMock.createChildAccount(childAccountRequest); + + verify(messageBirdServiceMock, times(1)) + .sendPayLoad(PARTNER_ACCOUNTS_BASE_URL + "/child-accounts" , childAccountRequest, ChildAccountCreateResponse.class); + assertNotNull(response); + assertEquals(response.getId(), childAccountCreateResponse.getId()); + assertEquals(response.getName(), childAccountCreateResponse.getName()); + assertEquals(response.getInvoiceAggregation(), childAccountCreateResponse.getInvoiceAggregation()); + assertEquals(response.getSigningKey(), childAccountCreateResponse.getSigningKey()); + assertEquals(response.getAccessKeys().get(0).getId(), childAccountCreateResponse.getAccessKeys().get(0).getId()); + assertEquals(response.getAccessKeys().get(0).getAccessKey(), childAccountCreateResponse.getAccessKeys().get(0).getAccessKey()); + assertEquals(response.getAccessKeys().get(0).getMode(), childAccountCreateResponse.getAccessKeys().get(0).getMode()); + } + + @Test + public void testGetChildAccount() throws GeneralException, UnauthorizedException, NotFoundException { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + ChildAccountDetailedResponse childAccountDetailedResponse = createChildAccountDetailedResponse(); + when(messageBirdServiceMock.requestByID(PARTNER_ACCOUNTS_BASE_URL + "/child-accounts", "ANY_ID", ChildAccountDetailedResponse.class)) + .thenReturn(childAccountDetailedResponse); + ChildAccountDetailedResponse response = messageBirdClientInjectMock.getChildAccountById("ANY_ID"); + + verify(messageBirdServiceMock, times(1)) + .requestByID(PARTNER_ACCOUNTS_BASE_URL + "/child-accounts", "ANY_ID", ChildAccountDetailedResponse.class); + assertNotNull(response); + assertEquals(response.getId(), childAccountDetailedResponse.getId()); + assertEquals(response.getName(), childAccountDetailedResponse.getName()); + assertEquals(response.getInvoiceAggregation(), childAccountDetailedResponse.getInvoiceAggregation()); + } + + @Test + public void testGetChildAccounts() throws GeneralException, UnauthorizedException { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + List childAccountResponses = Collections.singletonList(createChildAccountResponse()); + when(messageBirdServiceMock.requestList(PARTNER_ACCOUNTS_BASE_URL + "/child-accounts", 1, 10, List.class)) + .thenReturn(childAccountResponses); + + List response = messageBirdClientInjectMock.getChildAccounts(1, 10); + + verify(messageBirdServiceMock, times(1)) + .requestList(PARTNER_ACCOUNTS_BASE_URL + "/child-accounts", 1, 10, List.class); + assertNotNull(response); + assertEquals(response.get(0).getId(), childAccountResponses.get(0).getId()); + assertEquals(response.get(0).getName(), childAccountResponses.get(0).getName()); + } + + @Test + public void testUpdateChildAccount() throws GeneralException, UnauthorizedException { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + ChildAccountResponse childAccountResponse = createChildAccountResponse(); + when(messageBirdServiceMock.sendPayLoad(any(), any(), any(), any())) + .thenReturn(childAccountResponse); + ChildAccountResponse response = messageBirdClientInjectMock.updateChildAccount("ANY_NAME", "ANY_ID"); + + verify(messageBirdServiceMock, times(1)) + .sendPayLoad(any(), any(), any(), any()); + assertNotNull(response); + assertEquals(response.getId(), childAccountResponse.getId()); + assertEquals(response.getName(), childAccountResponse.getName()); + } + + @Test + public void testDeleteChildAccount() throws GeneralException, UnauthorizedException, NotFoundException { + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + String url = String.format("%s/child-accounts/%s", PARTNER_ACCOUNTS_BASE_URL, "ANY_ID"); + doNothing().when(messageBirdServiceMock).deleteByID(url, "ANY_ID"); + messageBirdClientInjectMock.deleteChildAccount("id"); + } + + @Test + public void testConversationMessage() throws Exception { + ConversationSendRequest request = createDummyConversationRequest(); + ConversationSendResponse conversationSendResponse = new ConversationSendResponse(); + conversationSendResponse.setStatus("ACCEPTED"); + conversationSendResponse.setId("1234"); + + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + + when(messageBirdServiceMock.sendPayLoad( CONVERSATIONS_BASE_URL + CONVERSATION_SEND_PATH, request, ConversationSendResponse.class)) + .thenReturn(conversationSendResponse); + ConversationSendResponse response = messageBirdClientInjectMock.sendMessage(request); + + verify(messageBirdServiceMock, times(1)) + .sendPayLoad(CONVERSATIONS_BASE_URL + CONVERSATION_SEND_PATH, request, ConversationSendResponse.class); + assertNotNull(response.getId()); + assertNotNull(response.getStatus()); + } + + private ConversationSendRequest createDummyConversationRequest() { + ConversationContent conversationContent = new ConversationContent(); + conversationContent.setText("test"); + ConversationSendRequest request = new ConversationSendRequest(); + request.setFrom("channelid"); + request.setTo("+34123123123"); + request.setTtl("15s"); + request.setType(ConversationContentType.TEXT); + request.setContent(conversationContent); + request.setTrackId("mycampaign"); + return request; + } + + @Test + public void testUnpauseTemplatesByTemplateName_Success() throws UnauthorizedException, GeneralException { + final Template template = TestUtil.createWhatsAppTemplate("sample_template_name", "ko"); + MessageBirdService messageBirdServiceMock = mock(MessageBirdService.class); + MessageBirdClient messageBirdClientInjectMock = new MessageBirdClient(messageBirdServiceMock); + String url = String.format( + "%s%s%s%s/%s", + INTEGRATIONS_BASE_URL_V2, + INTEGRATIONS_WHATSAPP_PATH, + TEMPLATES_PATH, + UNPAUSE_TEMAPLATE_PATH, + "sample_template_name" + ); + messageBirdClientInjectMock.unpauseTemplatesByTemplateName("sample_template_name"); + verify(messageBirdServiceMock).sendPayLoad("POST", url, "", null); + } + + @Test(expected = GeneralException.class) + public void testUnpauseTemplatesByTemplateName_NotFound() throws UnauthorizedException, GeneralException { + messageBirdClient.unpauseTemplatesByTemplateName("foo"); + } } diff --git a/api/src/test/java/com/messagebird/OutboundSmsPricesTest.java b/api/src/test/java/com/messagebird/OutboundSmsPricesTest.java new file mode 100644 index 00000000..665dba71 --- /dev/null +++ b/api/src/test/java/com/messagebird/OutboundSmsPricesTest.java @@ -0,0 +1,83 @@ +package com.messagebird; + +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.OutboundSmsPriceResponse; +import com.messagebird.util.Resources; +import org.junit.Test; + +import java.math.BigDecimal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; + +public class OutboundSmsPricesTest { + + @Test + public void testGetOutboundSmsPrices() throws GeneralException, UnauthorizedException, NotFoundException { + String responseFixture = Resources.readResourceText("/fixtures/outbound_sms_prices.json"); + + MessageBirdService messageBirdService = SpyService + .expects("GET", "pricing/sms/outbound") + .withRestAPIBaseURL() + .andReturns(new APIResponse(responseFixture, 200)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + + assertReceivedExpectedResponse(messageBirdClient.getOutboundSmsPrices()); + } + + @Test + public void testGetOutboundSmsPricesSmppUsername() throws GeneralException, UnauthorizedException, NotFoundException { + String responseFixture = Resources.readResourceText("/fixtures/outbound_sms_prices.json"); + + MessageBirdService messageBirdService = SpyService + .expects("GET", "pricing/sms/outbound/smpp/test-smpp-user") + .withRestAPIBaseURL() + .andReturns(new APIResponse(responseFixture, 200)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + + assertReceivedExpectedResponse(messageBirdClient.getOutboundSmsPrices("test-smpp-user")); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetOutboundSmsPricesSmppUsernameNull() throws GeneralException, UnauthorizedException, NotFoundException { + new MessageBirdClient(mock(MessageBirdService.class)).getOutboundSmsPrices(null); + } + + private static void assertReceivedExpectedResponse(OutboundSmsPriceResponse outboundSmsPriceResponse) { + assertEquals(10, outboundSmsPriceResponse.getGateway()); + assertEquals("EUR", outboundSmsPriceResponse.getCurrencyCode()); + assertEquals(3, outboundSmsPriceResponse.getTotalCount()); + + assertEquals(3, outboundSmsPriceResponse.getPrices().size()); + + assertEquals(new BigDecimal("0.060000"), outboundSmsPriceResponse.getPrices().get(0).getPrice()); + assertEquals("EUR", outboundSmsPriceResponse.getPrices().get(0).getCurrencyCode()); + assertEquals("0", outboundSmsPriceResponse.getPrices().get(0).getMccmnc()); + assertEquals("0", outboundSmsPriceResponse.getPrices().get(0).getMcc()); + assertNull(outboundSmsPriceResponse.getPrices().get(0).getMnc()); + assertEquals("Default Rate", outboundSmsPriceResponse.getPrices().get(0).getCountryName()); + assertEquals("XX", outboundSmsPriceResponse.getPrices().get(0).getCountryIsoCode()); + assertEquals("Default Rate", outboundSmsPriceResponse.getPrices().get(0).getOperatorName()); + + assertEquals(new BigDecimal("0.047000"), outboundSmsPriceResponse.getPrices().get(1).getPrice()); + assertEquals("EUR", outboundSmsPriceResponse.getPrices().get(1).getCurrencyCode()); + assertEquals("202", outboundSmsPriceResponse.getPrices().get(1).getMccmnc()); + assertEquals("202", outboundSmsPriceResponse.getPrices().get(1).getMcc()); + assertNull(outboundSmsPriceResponse.getPrices().get(1).getMnc()); + assertEquals("Greece", outboundSmsPriceResponse.getPrices().get(1).getCountryName()); + assertEquals("GR", outboundSmsPriceResponse.getPrices().get(1).getCountryIsoCode()); + assertNull(outboundSmsPriceResponse.getPrices().get(1).getOperatorName()); + + assertEquals(new BigDecimal("0.045000"), outboundSmsPriceResponse.getPrices().get(2).getPrice()); + assertEquals("EUR", outboundSmsPriceResponse.getPrices().get(2).getCurrencyCode()); + assertEquals("20205", outboundSmsPriceResponse.getPrices().get(2).getMccmnc()); + assertEquals("202", outboundSmsPriceResponse.getPrices().get(2).getMcc()); + assertEquals("05", outboundSmsPriceResponse.getPrices().get(2).getMnc()); + assertEquals("Greece", outboundSmsPriceResponse.getPrices().get(2).getCountryName()); + assertEquals("GR", outboundSmsPriceResponse.getPrices().get(2).getCountryIsoCode()); + assertEquals("Vodafone", outboundSmsPriceResponse.getPrices().get(2).getOperatorName()); + } +} diff --git a/api/src/test/java/com/messagebird/PurchasedNumbersFilterTest.java b/api/src/test/java/com/messagebird/PurchasedNumbersFilterTest.java new file mode 100644 index 00000000..0a70c9ba --- /dev/null +++ b/api/src/test/java/com/messagebird/PurchasedNumbersFilterTest.java @@ -0,0 +1,211 @@ +package com.messagebird; + +import com.messagebird.exceptions.GeneralException; +import com.messagebird.objects.*; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.*; +import static org.junit.Assert.assertNull; + +public class PurchasedNumbersFilterTest { + + @Test + public void testDefaults() { + // Setup + PurchasedNumbersFilter filter = new PurchasedNumbersFilter(); + + // Test + assertEquals(10, filter.getLimit()); + assertEquals(0, filter.getOffset()); + assertEquals(0, filter.getFeatures().size()); + assertEquals(0, filter.getTags().size()); + assertNull(filter.getNumber()); + assertNull(filter.getRegion()); + assertNull(filter.getLocality()); + assertNull(filter.getType()); + } + + @Test + public void testAddingFeatures() { + // Setup + PurchasedNumbersFilter filter = new PurchasedNumbersFilter(); + + assertEquals(EnumSet.noneOf(PhoneNumberFeature.class), filter.getFeatures()); + + // Test can add a feature + filter.addFeature(PhoneNumberFeature.SMS); + assertEquals(EnumSet.of(PhoneNumberFeature.SMS), filter.getFeatures()); + + // Test can have multiple features + filter.addFeature(PhoneNumberFeature.VOICE); + assertEquals(EnumSet.of(PhoneNumberFeature.SMS, PhoneNumberFeature.VOICE), filter.getFeatures()); + + // Test shouldn't have more than one of each + filter.addFeature(PhoneNumberFeature.SMS); + assertEquals(EnumSet.of(PhoneNumberFeature.SMS, PhoneNumberFeature.VOICE), filter.getFeatures()); + + filter.addFeature(PhoneNumberFeature.MMS); + assertEquals(EnumSet.of(PhoneNumberFeature.SMS, PhoneNumberFeature.VOICE, PhoneNumberFeature.MMS), filter.getFeatures()); + } + + @Test + public void testAddingMultipleFeatures() { + // Setup + PurchasedNumbersFilter filter = new PurchasedNumbersFilter(); + + assertEquals(EnumSet.noneOf(PhoneNumberFeature.class), filter.getFeatures()); + + // Test + filter.addFeature(PhoneNumberFeature.SMS, PhoneNumberFeature.VOICE); + assertEquals(EnumSet.of(PhoneNumberFeature.SMS, PhoneNumberFeature.VOICE), filter.getFeatures()); + } + + @Test + public void testRemovingFeatures() { + // Setup + PurchasedNumbersFilter filter = new PurchasedNumbersFilter(); + + // Test + filter.addFeature(PhoneNumberFeature.SMS, PhoneNumberFeature.MMS, PhoneNumberFeature.VOICE); + assertEquals(EnumSet.of(PhoneNumberFeature.SMS, PhoneNumberFeature.VOICE, PhoneNumberFeature.MMS), filter.getFeatures()); + + filter.removeFeature(PhoneNumberFeature.VOICE); + assertEquals(EnumSet.of(PhoneNumberFeature.SMS, PhoneNumberFeature.MMS), filter.getFeatures()); + + filter.removeFeature(PhoneNumberFeature.VOICE); + assertEquals(EnumSet.of(PhoneNumberFeature.SMS, PhoneNumberFeature.MMS), filter.getFeatures()); + + filter.removeFeature(PhoneNumberFeature.SMS, PhoneNumberFeature.MMS); + assertEquals(EnumSet.noneOf(PhoneNumberFeature.class), filter.getFeatures()); + } + + @Test + public void testAddingTags() { + // Setup + PurchasedNumbersFilter filter = new PurchasedNumbersFilter(); + + assertArrayEquals(new String[]{}, filter.getTags().toArray()); + + // Test + filter.addTag("TEST_TAG"); + assertArrayEquals(new String[]{"TEST_TAG"}, filter.getTags().toArray()); + + filter.addTag("Another test tag"); + assertArrayEquals(new String[]{"TEST_TAG", "Another test tag"}, filter.getTags().toArray()); + + filter.addTag("a", "b", "c"); + assertArrayEquals(new String[]{"TEST_TAG", "Another test tag", "a", "b", "c"}, filter.getTags().toArray()); + } + + @Test + public void testAddingDuplicateTags() { + // Setup + PurchasedNumbersFilter filter = new PurchasedNumbersFilter(); + + filter.addTag("a"); + filter.addTag("a"); + filter.addTag("a", "b", "b", "c", "b"); + + assertArrayEquals(new String[]{"a", "b", "c"}, filter.getTags().toArray()); + } + + @Test + public void testRemoveTags() { + // Setup + PurchasedNumbersFilter filter = new PurchasedNumbersFilter(); + + filter.addTag("a", "b", "c", "d"); + assertArrayEquals(new String[]{"a", "b", "c", "d"}, filter.getTags().toArray()); + + // Test + filter.removeTag("b"); + assertArrayEquals(new String[]{"a", "c", "d"}, filter.getTags().toArray()); + + filter.removeTag("d"); + assertArrayEquals(new String[]{"a", "c"}, filter.getTags().toArray()); + + filter.removeTag("b", "c", "d"); + assertArrayEquals(new String[]{"a"}, filter.getTags().toArray()); + } + + @Test + public void testClearTags() { + // Setup + PurchasedNumbersFilter filter = new PurchasedNumbersFilter(); + + filter.addTag("a", "b", "c", "d"); + assertArrayEquals(new String[]{"a", "b", "c", "d"}, filter.getTags().toArray()); + + // Test + filter.clearTags(); + assertArrayEquals(new String[]{}, filter.getTags().toArray()); + } + + @Test + public void testBasicSetters() { + // Setup + PurchasedNumbersFilter filter = new PurchasedNumbersFilter(); + + assertNull(filter.getNumber()); + assertNull(filter.getRegion()); + assertNull(filter.getLocality()); + assertNull(filter.getType()); + + // Test + filter.setLimit(23); + filter.setOffset(5); + filter.setNumber("TEST_NUMBER"); + filter.setRegion("TEST_REGION"); + filter.setLocality("TEST_LOCALITY"); + filter.setType(PhoneNumberType.MOBILE); + + assertEquals(23, filter.getLimit()); + assertEquals(5, filter.getOffset()); + assertEquals("TEST_NUMBER", filter.getNumber()); + assertEquals("TEST_REGION", filter.getRegion()); + assertEquals("TEST_LOCALITY", filter.getLocality()); + assertEquals(PhoneNumberType.MOBILE, filter.getType()); + } + + @Test + public void testToHashMapWithDefaultValues() throws GeneralException { + // Setup + PurchasedNumbersFilter filter = new PurchasedNumbersFilter(); + + HashMap map = filter.toHashMap(); + + assertEquals(10, map.get("limit")); + assertEquals(0, map.get("offset")); + assertEquals(EnumSet.noneOf(PhoneNumberFeature.class), map.get("features")); + assertEquals(new ArrayList(), map.get("tags")); + } + + @Test + public void testToHashMapWithAllValues() throws GeneralException { + // Setup + PurchasedNumbersFilter filter = new PurchasedNumbersFilter(); + + filter.setLimit(42); + filter.setOffset(24); + filter.setNumber("1234567890"); + filter.setRegion("My Region"); + filter.setLocality("My Locality"); + filter.setType(PhoneNumberType.MOBILE); + filter.addFeature(PhoneNumberFeature.SMS, PhoneNumberFeature.VOICE); + filter.addTag("h", "e", "l", "l", "o"); + + HashMap map = filter.toHashMap(); + + assertEquals(42, map.get("limit")); + assertEquals(24, map.get("offset")); + assertEquals("1234567890", map.get("number")); + assertEquals("My Region", map.get("region")); + assertEquals("My Locality", map.get("locality")); + assertEquals("mobile", map.get("type").toString()); + assertArrayEquals(new PhoneNumberFeature[]{PhoneNumberFeature.SMS, PhoneNumberFeature.VOICE}, ((Collection) map.get("features")).toArray()); + + assertArrayEquals(new String[]{"h", "e", "l", "o"}, ((Collection) map.get("tags")).toArray()); + } +} diff --git a/api/src/test/java/com/messagebird/RequestSignerTest.java b/api/src/test/java/com/messagebird/RequestSignerTest.java index 435ef1ce..17adc0dc 100644 --- a/api/src/test/java/com/messagebird/RequestSignerTest.java +++ b/api/src/test/java/com/messagebird/RequestSignerTest.java @@ -6,6 +6,10 @@ import static org.junit.Assert.*; +/** + * @deprecated This class is being deprecated together with {@link RequestSigner} + */ +@Deprecated public class RequestSignerTest { /** @@ -87,24 +91,24 @@ public void testWithRealSignature() { byte[] spoiledBody = getBytes("get shit spoiled"); assertTrue( - "Definitely valid signature is threaten as invalid", - requestSigner.isMatch(requestSignature, new Request(requestTimestamp, requestParams, requestBody)) + "Definitely valid signature is threaten as invalid", + requestSigner.isMatch(requestSignature, new Request(requestTimestamp, requestParams, requestBody)) ); assertFalse( - "Invalid signature is threaten as invalid", - requestSigner.isMatch(spoiledSignature, new Request(requestTimestamp, requestParams, requestBody)) + "Invalid signature is threaten as invalid", + requestSigner.isMatch(spoiledSignature, new Request(requestTimestamp, requestParams, requestBody)) ); assertFalse( - "Signature is still valid with replaced timestamp", - requestSigner.isMatch(requestSignature, new Request(spoiledTimestamp, requestParams, requestBody)) + "Signature is still valid with replaced timestamp", + requestSigner.isMatch(requestSignature, new Request(spoiledTimestamp, requestParams, requestBody)) ); assertFalse( - "Signature is still valid with replaced params", - requestSigner.isMatch(requestSignature, new Request(requestTimestamp, spoiledParams, requestBody)) + "Signature is still valid with replaced params", + requestSigner.isMatch(requestSignature, new Request(requestTimestamp, spoiledParams, requestBody)) ); assertFalse( - "Signature is still valid with replaced body", - requestSigner.isMatch(requestSignature, new Request(requestTimestamp, requestParams, spoiledBody)) + "Signature is still valid with replaced body", + requestSigner.isMatch(requestSignature, new Request(requestTimestamp, requestParams, spoiledBody)) ); } } diff --git a/api/src/test/java/com/messagebird/RequestValidatorTest.java b/api/src/test/java/com/messagebird/RequestValidatorTest.java new file mode 100644 index 00000000..23c64ef0 --- /dev/null +++ b/api/src/test/java/com/messagebird/RequestValidatorTest.java @@ -0,0 +1,102 @@ +package com.messagebird; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.messagebird.exceptions.RequestValidationException; +import com.messagebird.util.Resources; +import org.junit.Test; +import org.junit.function.ThrowingRunnable; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.time.Clock; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.util.*; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.*; + +@RunWith(Parameterized.class) +public class RequestValidatorTest { + /** + * Error Map that maps test data expected outcome to actual error message. + */ + private static final Map ERROR_MAP = new HashMap() { + { + put("invalid jwt: claim nbf is in the future", "The Token can't be used before"); + put("invalid jwt: claim exp is in the past", "The Token has expired on"); + put("invalid jwt: claim url_hash is invalid", "The Claim 'url_hash' value doesn't match the required one."); + put("invalid jwt: claim payload_hash is invalid", + "The Claim 'payload_hash' value doesn't match the required one."); + put("invalid jwt: signature is invalid", "Signature is invalid."); + put("invalid jwt: claim payload_hash is set but actual payload is missing", + "The Claim 'payload_hash' is set but actual payload is missing."); + put("invalid jwt: claim payload_hash is not set but payload is present", + "The Claim 'payload_hash' is not set but payload is present."); + put("invalid jwt: signing method none is invalid", "The signing method 'none' is invalid."); + + } + }; + private final WebhookSignatureTestCase testCase; + + public RequestValidatorTest(String testName, WebhookSignatureTestCase testCase) { + this.testCase = testCase; + } + + @Parameters(name = "{0}") + public static Collection data() throws IOException { + List testCases = new ObjectMapper().readValue( + Resources.readResourceText("/webhook_test_data.json"), + new TypeReference>() { + }); + + return testCases.stream() + .map(tc -> new Object[]{tc.name, tc}) + .collect(Collectors.toList()); + } + + @Test + public void testWebhookSignature() throws Throwable { + RequestValidator validator = new RequestValidator(testCase.secret != null ? testCase.secret : ""); + + Clock clock = Clock.fixed(OffsetDateTime.parse(testCase.timestamp).toInstant(), ZoneId.systemDefault()); + ThrowingRunnable runnable = () -> validator.validateSignature(clock, testCase.token, testCase.url, + (testCase.payload == null) ? null : testCase.payload.getBytes(StandardCharsets.UTF_8)); + + if (testCase.valid) { + runnable.run(); + return; + } + + assertTrue(String.format("Expected error message mapping for '%s' but it was not found.", testCase.reason), + ERROR_MAP.containsKey(testCase.reason)); + + String expectedError = ERROR_MAP.get(testCase.reason); + + RequestValidationException err = assertThrows(RequestValidationException.class, runnable); + assertTrue(String.format("Expected error message containing: %s (originally %s) but was: %s", expectedError, + testCase.reason, err.getMessage()), err.getMessage().contains(expectedError)); + } + + /** + * WebhookSignatureTestCase + */ + public static class WebhookSignatureTestCase { + public String name; + public String method; + public String secret; + public String url; + public String payload; + public String timestamp; + public String token; + public Boolean valid; + public String reason; + } +} diff --git a/api/src/test/java/com/messagebird/SpyService.java b/api/src/test/java/com/messagebird/SpyService.java index ec7a03fe..2922b1b4 100644 --- a/api/src/test/java/com/messagebird/SpyService.java +++ b/api/src/test/java/com/messagebird/SpyService.java @@ -2,6 +2,8 @@ import com.messagebird.exceptions.GeneralException; +import java.util.HashMap; + import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @@ -68,7 +70,7 @@ static

SpyService expects(final String method, final String url, final P pay service.method = method; service.url = url; service.payload = payload; - + return service; } @@ -147,7 +149,7 @@ MessageBirdService andReturns(final APIResponse apiResponse) throws GeneralExcep } MessageBirdServiceImpl messageBirdService = spy(new MessageBirdServiceImpl(getAccessKey())); - doReturn(apiResponse).when(messageBirdService).doRequest(method, url, payload); + doReturn(apiResponse).when(messageBirdService).doRequest(method, url, new HashMap<>(), payload); return messageBirdService; } diff --git a/api/src/test/java/com/messagebird/TestUtil.java b/api/src/test/java/com/messagebird/TestUtil.java index 584b6834..3e9c5a33 100644 --- a/api/src/test/java/com/messagebird/TestUtil.java +++ b/api/src/test/java/com/messagebird/TestUtil.java @@ -2,6 +2,18 @@ import com.messagebird.objects.*; import com.messagebird.objects.conversations.*; +import com.messagebird.objects.integrations.HSMCategory; +import com.messagebird.objects.integrations.HSMComponent; +import com.messagebird.objects.integrations.HSMComponentButton; +import com.messagebird.objects.integrations.HSMComponentButtonType; +import com.messagebird.objects.integrations.HSMComponentCard; +import com.messagebird.objects.integrations.HSMComponentFormat; +import com.messagebird.objects.integrations.HSMComponentType; +import com.messagebird.objects.integrations.HSMExample; +import com.messagebird.objects.integrations.HSMStatus; +import com.messagebird.objects.integrations.Template; +import com.messagebird.objects.integrations.TemplateList; +import com.messagebird.objects.integrations.TemplateResponse; import com.messagebird.objects.voicecalls.*; import java.util.*; @@ -17,9 +29,9 @@ static VoiceCall createVoiceCall(String destination) { voiceCall.setDestination(destination); final VoiceCallFlow voiceCallFlow = new VoiceCallFlow(); - voiceCallFlow.setTitle("Test title"); VoiceStep voiceStep = new VoiceStep(); voiceStep.setAction("say"); + voiceCallFlow.setMaxDuration(28800); final VoiceStepOption voiceStepOption = new VoiceStepOption(); voiceStepOption.setPayload("This is a journey into sound. Good bye!"); @@ -83,6 +95,20 @@ static RecordingResponse createRecordingResponse(){ return new RecordingResponse(Collections.singletonList(createRecording()), links, new Pagination()); } + static RecordingResponse createRecordingResponseList() { + Map links = new LinkedHashMap<>(); + links.put("self", "ANY_SELF"); + links.put("file", "ANY_FILE"); + List recordingList = new ArrayList<>(); + recordingList.add(createRecording()); + recordingList.add(createRecording()); + return new RecordingResponse(recordingList, links, new Pagination()); + } + + static String createDownloadPath(String recordId , String basePath) { + return String.format("%s/%s%s",basePath, recordId, MessageBirdClient.RECORDING_DOWNLOAD_FORMAT); + } + static TranscriptionResponse createTranscriptionResponse() { final TranscriptionResponse transcriptionResponse = new TranscriptionResponse(); final Transcription transcription = new Transcription(); @@ -93,9 +119,8 @@ static TranscriptionResponse createTranscriptionResponse() { return transcriptionResponse; } - static Webhook createWebHook() { + static Webhook createWebhook() { final Webhook webhook = new Webhook(); - webhook.setTitle("ANY_TITLE"); webhook.setUrl("ANY_URL"); return webhook; } @@ -114,6 +139,13 @@ static WebhookResponseData createWebhookResponseData() { return webhookResponseData; } + static WebhookList createWebhookList() { + final WebhookList webhookList = new WebhookList(); + webhookList.setData(Collections.singletonList(createWebhookResponse())); + webhookList.setLinks(Collections.singletonMap("self", "ANY_ID")); + return webhookList; + } + private static Contact createContact(){ final CustomDetails customDetails = new CustomDetails(); customDetails.setCustom1("ANY_DETAIL"); @@ -146,6 +178,47 @@ static ContactList createContactList() { return contactList; } + private static VoiceStepOption createVoiceStepOption() + { + final VoiceStepOption voiceStepOption = new VoiceStepOption(); + voiceStepOption.setDestination("123"); + voiceStepOption.setPayload("Test payload Update"); + voiceStepOption.setLanguage("en-US"); + voiceStepOption.setVoice("female"); + voiceStepOption.setRepeat(5); + voiceStepOption.setMedia("test.wav"); + voiceStepOption.setLength(10); + voiceStepOption.setMaxLength(20); + voiceStepOption.setTimeout(30); + voiceStepOption.setFinishOnKey("#"); + voiceStepOption.setTranscribe(true); + voiceStepOption.setTranscribeLanguage("en-US"); + voiceStepOption.setRecord("in"); + voiceStepOption.setUrl("http://www."); + voiceStepOption.setIfMachine("machine1"); + voiceStepOption.setMachineTimeout(2000); + voiceStepOption.setOnFinish("http://www."); + voiceStepOption.setMask(false); + return voiceStepOption; + } + + public static VoiceStep createVoiceStep() { + final VoiceStep voiceStep = new VoiceStep(); + voiceStep.setId("a8e44a38-b935-482f-b17f-ed3472c6292c"); + voiceStep.setAction("transfer"); + voiceStep.setOptions(createVoiceStepOption()); + return voiceStep; + } + + public static VoiceCallFlowRequest createVoiceCallFlowRequest() { + final VoiceCallFlowRequest voiceCallFlow = new VoiceCallFlowRequest(); + voiceCallFlow.setRecord(true); + voiceCallFlow.setSteps(Collections.singletonList(createVoiceStep())); + voiceCallFlow.setDefaultCall(true); + + return voiceCallFlow; + } + static ConversationWebhook createConversationWebhook() { ConversationWebhook conversationWebhookResponse = new ConversationWebhook(); conversationWebhookResponse.setId("whid"); @@ -178,4 +251,194 @@ static ConversationWebhookCreateRequest createConversationWebhookRequest() { ) ); } + + private static HSMComponent createHSMComponentHeader() { + final HSMComponent headerComponent = new HSMComponent(); + final HSMExample headerExample = new HSMExample(); + headerExample.setHeader_url(Arrays.asList("https://www.mysample.com/sample.img")); + + headerComponent.setType(HSMComponentType.HEADER); + headerComponent.setFormat(HSMComponentFormat.IMAGE); + headerComponent.setExample(headerExample); + + return headerComponent; + } + + private static HSMComponent createHSMComponentBody() { + final HSMComponent bodyComponent = new HSMComponent(); + final HSMExample bodyExample = new HSMExample(); + final List> bodyText = new ArrayList<>(); + bodyText.add(Arrays.asList("John")); + bodyText.add(Arrays.asList("Anna")); + bodyExample.setBody_text(bodyText); + + bodyComponent.setType(HSMComponentType.BODY); + bodyComponent.setText("Hey {{1}}! This is a sample template from Java."); + bodyComponent.setExample(bodyExample); + + return bodyComponent; + } + + private static HSMComponent createHSMComponentFooter() { + final HSMComponent footerComponent = new HSMComponent(); + footerComponent.setType(HSMComponentType.FOOTER); + footerComponent.setText("This is a sample footer"); + + return footerComponent; + } + + private static HSMComponent createHSMComponentButton() { + final HSMComponent buttonComponent = new HSMComponent(); + final List buttons = new ArrayList<>(); + final HSMComponentButton button = new HSMComponentButton(); + button.setType(HSMComponentButtonType.URL); + button.setText("Touch it"); + button.setUrl("https://www.messagebird.com"); + button.setExample(Arrays.asList("https://developers.messagebird.com")); + buttons.add(button); + buttonComponent.setType(HSMComponentType.BUTTONS); + buttonComponent.setButtons(buttons); + + return buttonComponent; + } + + private static HSMComponent createHSMComponentCarousel() { + final HSMComponent carouselComponent = new HSMComponent(); + carouselComponent.setType(HSMComponentType.CAROUSEL); + + final List cards = new ArrayList<>(); + final HSMComponentCard card = new HSMComponentCard(); + final List cardComponents = new ArrayList<>(); + cardComponents.add(createHSMComponentHeader()); + cardComponents.add(createHSMComponentBody()); + cardComponents.add(createHSMComponentButton()); + card.setComponents(cardComponents); + cards.add(card); + + carouselComponent.setCards(cards); + + return carouselComponent; + } + + public static TemplateResponse createWhatsAppTemplateResponse(final String templateName, final String language) { + final TemplateResponse templateResponse = new TemplateResponse(); + templateResponse.setName(templateName); + templateResponse.setLanguage(language); + templateResponse.setCategory(HSMCategory.AUTHENTICATION); + templateResponse.setStatus(HSMStatus.NEW); + templateResponse.setCtaURLLinkTrackingOptedOut(true); + templateResponse.setCreatedAt(new Date()); + templateResponse.setUpdatedAt(new Date()); + + final List components = new ArrayList<>(); + components.add(createHSMComponentHeader()); + components.add(createHSMComponentBody()); + components.add(createHSMComponentFooter()); + components.add(createHSMComponentButton()); + templateResponse.setComponents(components); + + templateResponse.setWabaID("testWABAID"); + templateResponse.setNamespace("testNamespace"); + + return templateResponse; + } + + public static Template createWhatsAppTemplate(final String templateName, final String language) { + final Template template = new Template(); + template.setName(templateName); + template.setLanguage(language); + template.setCategory(HSMCategory.AUTHENTICATION); + + final List components = new ArrayList<>(); + components.add(createHSMComponentHeader()); + components.add(createHSMComponentBody()); + components.add(createHSMComponentFooter()); + components.add(createHSMComponentButton()); + template.setComponents(components); + + template.setWABAID("testWABAID"); + + return template; + } + + public static TemplateResponse createWhatsAppCarouselTemplateResponse(final String templateName, final String language) { + final TemplateResponse templateResponse = new TemplateResponse(); + templateResponse.setName(templateName); + templateResponse.setLanguage(language); + templateResponse.setCategory(HSMCategory.MARKETING); + templateResponse.setStatus(HSMStatus.NEW); + templateResponse.setCreatedAt(new Date()); + templateResponse.setUpdatedAt(new Date()); + + final List components = new ArrayList<>(); + components.add(createHSMComponentBody()); + components.add(createHSMComponentCarousel()); + templateResponse.setComponents(components); + + templateResponse.setWabaID("testWABAID"); + templateResponse.setNamespace("testNamespace"); + + return templateResponse; + } + + public static Template createWhatsAppCarouselTemplate(final String templateName, final String language) { + final Template template = new Template(); + template.setName(templateName); + template.setLanguage(language); + template.setCategory(HSMCategory.MARKETING); + + final List components = new ArrayList<>(); + components.add(createHSMComponentBody()); + components.add(createHSMComponentCarousel()); + template.setComponents(components); + + template.setWABAID("testWABAID"); + + return template; + } + + + public static TemplateList createWhatsAppTemplateList(final String templateName) { + final TemplateResponse template1 = TestUtil.createWhatsAppTemplateResponse(templateName, "en_US"); + final TemplateResponse template2 = TestUtil.createWhatsAppTemplateResponse(templateName, "ko"); + final TemplateList templateList = new TemplateList(); + + List templateResponseList = new ArrayList<>(); + templateResponseList.add(template1); + templateResponseList.add(template2); + + templateList.setItems(templateResponseList); + return templateList; + } + + public static ChildAccountCreateResponse createChildAccountCreateResponse() { + final AccessKey accessKey = new AccessKey(); + accessKey.setId("ANY_ID"); + accessKey.setAccessKey("ANY_KEY"); + accessKey.setMode("ANY_MOD"); + + final ChildAccountCreateResponse childAccountCreateResponse = new ChildAccountCreateResponse(); + childAccountCreateResponse.setId("ANY_ID"); + childAccountCreateResponse.setName("ANY_NAME"); + childAccountCreateResponse.setAccessKeys(Collections.singletonList(accessKey)); + childAccountCreateResponse.setInvoiceAggregation("ANY_INVOICE_AGGREGATION"); + childAccountCreateResponse.setSigningKey("ANY_SIGNING_KEY"); + + return childAccountCreateResponse; + } + + public static ChildAccountDetailedResponse createChildAccountDetailedResponse(){ + final ChildAccountDetailedResponse childAccountDetailedResponse = new ChildAccountDetailedResponse(); + childAccountDetailedResponse.setId("ANY_ID"); + childAccountDetailedResponse.setName("ANY_NAME"); + childAccountDetailedResponse.setInvoiceAggregation("ANY_INVOICE_AGGREGATION"); + return childAccountDetailedResponse; + } + + public static ChildAccountResponse createChildAccountResponse(){ + final ChildAccountResponse childAccountResponse = new ChildAccountResponse(); + childAccountResponse.setId("ANY_ID"); + childAccountResponse.setName("ANY_NAME"); + return childAccountResponse; + } } diff --git a/api/src/test/java/com/messagebird/VerifyTest.java b/api/src/test/java/com/messagebird/VerifyTest.java index 6a3d1a80..7ec8565e 100644 --- a/api/src/test/java/com/messagebird/VerifyTest.java +++ b/api/src/test/java/com/messagebird/VerifyTest.java @@ -12,8 +12,9 @@ public class VerifyTest { - private static final String VERIFY_SMS_RESPONSE = "{\"id\": \"verify-id-sms\",\"href\": \"https://rest.messagebird.com/verify/verify-id-sms\",\"recipient\": 31612345678,\"reference\": null,\"messages\": {\"href\": \"https://rest.messagebird.com/messages/5958c0d5e2df41de8154e5e88bfeb5bc\"},\"status\": \"sent\",\"createdDatetime\": \"2018-09-25T14:38:12+00:00\",\"validUntilDatetime\": \"2018-09-25T14:38:42+00:00\"}"; - private static final String VERIFY_TTS_RESPONSE = "{\"id\": \"verify-id-tts\",\"href\": \"https://rest.messagebird.com/verify/verify-id-tts\",\"recipient\": 31612345678,\"reference\": null,\"messages\": {\"href\": \"https://rest.messagebird.com/voicemessages/c043ab473f8e4f2590ab9a16d25f2899\"},\"status\": \"sent\",\"createdDatetime\": \"2018-09-25T14:35:10+00:00\",\"validUntilDatetime\": \"2018-09-25T14:35:40+00:00\"}"; + private static final String VERIFY_SMS_RESPONSE = "{\"id\": \"verify-id-sms\",\"href\": \"https://rest.messagebird.com/verify/verify-id-sms\",\"recipient\": 31612345678,\"reference\": null,\"messages\": {\"href\": \"https://rest.messagebird.com/messages/5958c0d5e2df41de8154e5e88bfeb5bc\"},\"status\": \"sent\",\"createdDatetime\": \"2018-09-25T14:38:12+00:00\",\"validUntilDatetime\": \"2018-09-25T14:38:42+00:00\"}"; + private static final String VERIFY_TTS_RESPONSE = "{\"id\": \"verify-id-tts\",\"href\": \"https://rest.messagebird.com/verify/verify-id-tts\",\"recipient\": 31612345678,\"reference\": null,\"messages\": {\"href\": \"https://rest.messagebird.com/voicemessages/c043ab473f8e4f2590ab9a16d25f2899\"},\"status\": \"sent\",\"createdDatetime\": \"2018-09-25T14:35:10+00:00\",\"validUntilDatetime\": \"2018-09-25T14:35:40+00:00\"}"; + private static final String VERIFY_EMAIL_RESPONSE = "{\"id\": \"verify-id-email\",\"href\": \"https://rest.messagebird.com/verify/verify-id-email\",\"recipient\": \"test@mb.com\",\"reference\": null,\"messages\": {\"href\": \"https://rest.messagebird.com/verify/messages/email/c043ab473f8e4f2590ab9a16d25f2899\"},\"status\": \"sent\",\"createdDatetime\": \"2018-09-25T14:35:10+00:00\",\"validUntilDatetime\": \"2018-09-25T14:35:40+00:00\"}"; @Test public void testSendVerifyTokenSms() throws GeneralException, UnauthorizedException { @@ -47,11 +48,28 @@ public void testSendVerifyTokenTts() throws GeneralException, UnauthorizedExcept assertEquals("verify-id-tts", verify.getId()); } + @Test + public void testSendVerifyTokenEmail() throws GeneralException, UnauthorizedException { + VerifyRequest verifyRequest = new VerifyRequest("rec@mb.com"); + verifyRequest.setType(VerifyType.EMAIL); + + MessageBirdService messageBirdService = SpyService + .expects("POST", "verify", verifyRequest) + .withRestAPIBaseURL() + .andReturns(new APIResponse(VERIFY_EMAIL_RESPONSE, 200)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + + Verify verify = messageBirdClient.sendVerifyToken(verifyRequest); + + assertEquals("verify-id-email", verify.getId()); + } + @Test public void testVerifyTypeValue() { // Important for generating proper JSON payloads... assertEquals("flash", VerifyType.FLASH.getValue()); assertEquals("sms", VerifyType.SMS.getValue()); assertEquals("tts", VerifyType.TTS.getValue()); + assertEquals("email", VerifyType.EMAIL.getValue()); } } diff --git a/api/src/test/java/com/messagebird/VoiceCallFlowTest.java b/api/src/test/java/com/messagebird/VoiceCallFlowTest.java new file mode 100644 index 00000000..c38c0c7f --- /dev/null +++ b/api/src/test/java/com/messagebird/VoiceCallFlowTest.java @@ -0,0 +1,273 @@ +package com.messagebird; + +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.*; +import com.messagebird.objects.voicecalls.*; +import com.messagebird.util.Resources; + +import org.junit.*; +import org.mockito.Mockito; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static java.net.HttpURLConnection.HTTP_NO_CONTENT; +import static java.net.HttpURLConnection.HTTP_NOT_FOUND; +import java.util.*; + +/* + * Class used for covering all the CallFlows functionality with tests + * The purpose is solely on the individual CallFlow + * and not on the associated callFlows to voiceCall + */ +public class VoiceCallFlowTest { + + private static MessageBirdServiceImpl messageBirdService; + private static MessageBirdClient messageBirdClient; + private static String NOT_FOUND_ERROR = "{\"data\":null,\"errors\":[{\"message\":\"No call flow found for ID `1`.\",\"code\":13}]}"; + + @BeforeClass + public static void setUpClass() { + TimeZone.setDefault(TimeZone.getTimeZone("Europe/Amsterdam")); + } + + /* + * We define a fixture and we test against the fixture all the setters and getters + * as well as the transformation of the JSON retrieved + */ + @Test + public void testCreate() throws GeneralException, UnauthorizedException { + VoiceCallFlowRequest voiceCallFlowRequest = TestUtil.createVoiceCallFlowRequest(); + String responseFixture = Resources.readResourceText("/fixtures/call_flows_post.json"); + + MessageBirdService messageBirdService = SpyService + .expects("POST", "call-flows", voiceCallFlowRequest) + .withVoiceCallAPIBaseURL() + .andReturns(new APIResponse(responseFixture)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + VoiceCallFlow voiceCallFlow = messageBirdClient.sendVoiceCallFlow(voiceCallFlowRequest).getData().get(0); + this.testVoiceCallFlowAgainstFixture(voiceCallFlow); + } + + @Test + public void testView() throws GeneralException, UnauthorizedException, NotFoundException { + String responseFixture = Resources.readResourceText("/fixtures/call_flow_view.json"); + MessageBirdService messageBirdService = SpyService + .expects("GET", "call-flows/e781a76f-14ad-45b0-8490-409300244e20") + .withVoiceCallAPIBaseURL() + .andReturns(new APIResponse(responseFixture)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + VoiceCallFlow voiceCallFlow = messageBirdClient + .viewVoiceCallFlow("e781a76f-14ad-45b0-8490-409300244e20") + .getData() + .get(0); + this.testVoiceCallFlowAgainstFixture(voiceCallFlow); + } + + @Test + public void testViewTitlePresent() throws GeneralException, UnauthorizedException, NotFoundException { + String responseFixture = Resources.readResourceText("/fixtures/call_flow_view_title.json"); + MessageBirdService messageBirdService = SpyService + .expects("GET", "call-flows/e781a76f-14ad-45b0-8490-409300244e20") + .withVoiceCallAPIBaseURL() + .andReturns(new APIResponse(responseFixture)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + VoiceCallFlow voiceCallFlow = messageBirdClient + .viewVoiceCallFlow("e781a76f-14ad-45b0-8490-409300244e20") + .getData() + .get(0); + this.testVoiceCallFlowAgainstFixture(voiceCallFlow, true); + } + + @Test (expected = IllegalArgumentException.class) + public void testViewShouldThrowInvalidArgumentException() throws NotFoundException, GeneralException, UnauthorizedException { + String responseFixture = Resources.readResourceText("/fixtures/call_flow_view.json"); + MessageBirdService messageBirdService = SpyService + .expects("GET", "call-flows/e781a76f-14ad-45b0-8490-409300244e20") + .withVoiceCallAPIBaseURL() + .andReturns(new APIResponse(responseFixture)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + messageBirdClient + .viewVoiceCallFlow(null); + } + + @Test + public void testUpdate() throws GeneralException, UnauthorizedException, NotFoundException { + String responseFixture = Resources.readResourceText("/fixtures/call_flow_update_response.json"); + + VoiceCallFlowRequest voiceCallFlowRequest = new VoiceCallFlowRequest("e781a76f-14ad-45b0-8490-409300244e20"); + voiceCallFlowRequest.setDefaultCall(true); + voiceCallFlowRequest.setRecord(true); + voiceCallFlowRequest.setSteps( + Collections.singletonList( + TestUtil.createVoiceStep() + ) + ); + + MessageBirdService messageBirdService = SpyService + .expects("PUT", "call-flows/e781a76f-14ad-45b0-8490-409300244e20", voiceCallFlowRequest) + .withVoiceCallAPIBaseURL() + .andReturns(new APIResponse(responseFixture)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + + VoiceCallFlow voiceCallFlow = messageBirdClient + .updateVoiceCallFlow("e781a76f-14ad-45b0-8490-409300244e20", voiceCallFlowRequest) + .getData() + .get(0); + this.testVoiceCallFlowAgainstFixture(voiceCallFlow); + } + + @Test (expected = GeneralException.class) + public void testUpdateGeneralException() throws GeneralException, UnauthorizedException { + VoiceCallFlowRequest voiceCallFlowRequest = new VoiceCallFlowRequest("123"); + MessageBirdService messageBirdService = SpyService + .expects("PUT", "call-flows/123", voiceCallFlowRequest) + .withVoiceCallAPIBaseURL() + .andReturns(new APIResponse(NOT_FOUND_ERROR, HTTP_NOT_FOUND)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + messageBirdClient + .updateVoiceCallFlow("123", voiceCallFlowRequest); + } + + @Test (expected = IllegalArgumentException.class) + public void testUpdateIllegalArgumentException() throws GeneralException, UnauthorizedException { + VoiceCallFlowRequest voiceCallFlowRequest = new VoiceCallFlowRequest("123"); + MessageBirdService messageBirdService = SpyService + .expects("PUT", "call-flows/123", voiceCallFlowRequest) + .withVoiceCallAPIBaseURL() + .andReturns(new APIResponse(NOT_FOUND_ERROR, HTTP_NOT_FOUND)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + messageBirdClient + .updateVoiceCallFlow(null, voiceCallFlowRequest); + } + + @Test (expected = NotFoundException.class) + public void testViewNotFoundException() throws NotFoundException, GeneralException, UnauthorizedException { + String responseFixture = Resources.readResourceText("/fixtures/call_flow_view.json"); + MessageBirdService messageBirdService = SpyService + .expects("GET", "call-flows/e781a76f-14ad-45b0-8490-409300244e20") + .withVoiceCallAPIBaseURL() + .andReturns(new APIResponse(NOT_FOUND_ERROR, HTTP_NOT_FOUND)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + VoiceCallFlow voiceCallFlow = messageBirdClient + .viewVoiceCallFlow("e781a76f-14ad-45b0-8490-409300244e20") + .getData() + .get(0); + + } + + @Test + public void testList() throws GeneralException, UnauthorizedException { + String responseFixture = Resources.readResourceText("/fixtures/call_flows_list.json"); + MessageBirdService messageBirdService = SpyService + .expects("GET", "call-flows?offset=0&limit=0") + .withVoiceCallAPIBaseURL() + .andReturns(new APIResponse(responseFixture)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + VoiceCallFlowList voiceCallFlowList = messageBirdClient.listVoiceCallFlows(0, 0); + assertEquals((int) voiceCallFlowList.getItems().size(), 1); + assertEquals((int) voiceCallFlowList.getTotalCount(), 10); + assertEquals((int) voiceCallFlowList.getPageCount(), 3); + assertEquals((int) voiceCallFlowList.getCurrentPage(), 2); + assertEquals((int) voiceCallFlowList.getPerPage(), 12); + VoiceCallFlow voiceCallFlow = voiceCallFlowList.getItems().get(0); + this.testVoiceCallFlowAgainstFixture(voiceCallFlow); + } + + @Test (expected = IllegalArgumentException.class) + public void testListShouldThrowInvalidArgumentException() + throws GeneralException, UnauthorizedException { + String responseFixture = Resources.readResourceText("/fixtures/call_flows_list.json"); + MessageBirdService messageBirdService = SpyService + .expects("GET", "call-flows?offset=0&limit=0") + .withVoiceCallAPIBaseURL() + .andReturns(new APIResponse(responseFixture)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + VoiceCallFlowList voiceCallFlowList = messageBirdClient.listVoiceCallFlows(-1, 0); + } + + @Test (expected = IllegalArgumentException.class) + public void testListShouldThrowInvalidArgumentExceptionForLimit() + throws GeneralException, UnauthorizedException { + String responseFixture = Resources.readResourceText("/fixtures/call_flows_list.json"); + MessageBirdService messageBirdService = SpyService + .expects("GET", "call-flows?offset=0&limit=0") + .withVoiceCallAPIBaseURL() + .andReturns(new APIResponse(responseFixture)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + VoiceCallFlowList voiceCallFlowList = messageBirdClient.listVoiceCallFlows(0, -1); + } + + @Test + public void testDelete() throws GeneralException, UnauthorizedException, NotFoundException { + MessageBirdService messageBirdService = SpyService + .expects("DELETE", "call-flows/123abc") + .withVoiceCallAPIBaseURL() + .andReturns(new APIResponse("", HTTP_NO_CONTENT)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + messageBirdClient.deleteVoiceCallFlow("123abc"); + } + + @Test (expected = NotFoundException.class) + public void testDeleteNotFoundException() throws NotFoundException, GeneralException, UnauthorizedException { + // test not found exception + MessageBirdService messageBirdService = SpyService + .expects("DELETE", "call-flows/123abc") + .withVoiceCallAPIBaseURL() + .andReturns(new APIResponse("", HTTP_NOT_FOUND)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + messageBirdClient.deleteVoiceCallFlow("123abc"); + } + + @Test (expected = IllegalArgumentException.class) + public void testDeleteIllegalArgumentException() throws NotFoundException, GeneralException, UnauthorizedException { + // test not found exception + MessageBirdService messageBirdService = SpyService + .expects("DELETE", "call-flows/123abc") + .withVoiceCallAPIBaseURL() + .andReturns(new APIResponse("", HTTP_NOT_FOUND)); + MessageBirdClient messageBirdClient = new MessageBirdClient(messageBirdService); + messageBirdClient.deleteVoiceCallFlow(null); + } + + /** + * In order to reuse this method for further tests you need to make sure that the fixtures + * match the date in this test. See call_flows_post.json + */ + + private void testVoiceCallFlowAgainstFixture(VoiceCallFlow voiceCallFlow) { + testVoiceCallFlowAgainstFixture(voiceCallFlow, false); + } + + private void testVoiceCallFlowAgainstFixture(VoiceCallFlow voiceCallFlow, boolean hasTitle) { + assertEquals(voiceCallFlow.getId(), "e781a76f-14ad-45b0-8490-409300244e20"); + if (!hasTitle) assertEquals(voiceCallFlow.getTitle(), null); + if (hasTitle) assertNotEquals(voiceCallFlow.getTitle(), null); + assertEquals(voiceCallFlow.isRecord(), true); + assertEquals(voiceCallFlow.isDefaultCall(), false); + assertEquals(voiceCallFlow.getCreatedAt().toString(), "Tue Aug 06 16:13:06 CEST 2019"); + assertEquals(voiceCallFlow.getUpdatedAt().toString(), "Tue Aug 06 16:13:06 CEST 2019"); + assertEquals(voiceCallFlow.getSteps().size(), 1); + VoiceStep voiceStep = voiceCallFlow.getSteps().get(0); + assertEquals(voiceStep.getId(), "a8e44a38-b935-482f-b17f-ed3472c6292c"); + assertEquals(voiceStep.getAction(), "transfer"); + VoiceStepOption voiceStepOption = voiceStep.getOptions(); + assertEquals(voiceStepOption.getDestination(), "31612345678"); + assertEquals(voiceStepOption.getPayload(), "Test payload"); + assertEquals(voiceStepOption.getLanguage(), "en-GB"); + assertEquals(voiceStepOption.getVoice(), "male"); + assertEquals(voiceStepOption.getRepeat(), 1); + assertEquals(voiceStepOption.getMedia(), "test.mp3"); + assertEquals(voiceStepOption.getFinishOnKey(), "1"); + assertEquals(voiceStepOption.getTranscribeLanguage(), "en-GB"); + assertEquals(voiceStepOption.getRecord(), "both"); + assertEquals(voiceStepOption.getUrl(), "http://"); + assertEquals(voiceStepOption.getIfMachine(), "ifMachine"); + assertEquals(voiceStepOption.getMachineTimeout(), 200); + assertEquals(voiceStepOption.getOnFinish(), "http://"); + assertEquals(voiceStepOption.getLength(), 1); + assertEquals(voiceStepOption.getTimeout(), 3); + assertEquals(voiceStepOption.getMaxLength(), 2); + assertEquals(voiceStepOption.isTranscribe(), false); + } +} diff --git a/api/src/test/java/com/messagebird/VoiceCallingTest.java b/api/src/test/java/com/messagebird/VoiceCallingTest.java index 778f9b1c..cc8ae90e 100644 --- a/api/src/test/java/com/messagebird/VoiceCallingTest.java +++ b/api/src/test/java/com/messagebird/VoiceCallingTest.java @@ -3,6 +3,7 @@ import com.messagebird.exceptions.GeneralException; import com.messagebird.exceptions.NotFoundException; import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.voicecalls.SipResponseCode; import com.messagebird.objects.voicecalls.VoiceCall; import com.messagebird.objects.voicecalls.VoiceLegDirection; import com.messagebird.objects.voicecalls.VoiceLegStatus; @@ -100,7 +101,7 @@ public void testGetLeg() throws IOException, GeneralException, UnauthorizedExcep VoiceLegStatus.Hangup, VoiceLegDirection.Outgoing, new BigDecimal("0.001519"), "EUR", 7, parseDate("2019-01-10T16:12:54Z"), parseDate("2019-01-10T16:13:54Z"), - parseDate("2019-01-10T16:13:24Z"), parseDate("2019-01-10T16:13:30Z") + parseDate("2019-01-10T16:13:24Z"), parseDate("2019-01-10T16:13:30Z"), SipResponseCode.OK ); private static Date parseDate(String input) { diff --git a/api/src/test/java/com/messagebird/objects/conversations/MessageComponentTypeTest.java b/api/src/test/java/com/messagebird/objects/conversations/MessageComponentTypeTest.java new file mode 100644 index 00000000..385b1652 --- /dev/null +++ b/api/src/test/java/com/messagebird/objects/conversations/MessageComponentTypeTest.java @@ -0,0 +1,28 @@ +package com.messagebird.objects.conversations; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class MessageComponentTypeTest { + @Test + public void testMessageComponentTypeForValueValid() { + assertEquals(MessageComponentType.HEADER, MessageComponentType.forValue("header")); + assertEquals(MessageComponentType.BUTTON, MessageComponentType.forValue("button")); + } + + @Test(expected = NullPointerException.class) + public void testMessageComponentTypeForValueNull() { + MessageComponentType.forValue(null); + } + + @Test + public void testMessageComponentTypeForValueInvalid() { + assertNull(MessageComponentType.forValue("invalid_type")); + } + + @Test + public void testMessageComponentTypeToString() { + assertEquals("header", MessageComponentType.HEADER.toString()); + } +} diff --git a/api/src/test/java/com/messagebird/objects/conversations/MessageParamTest.java b/api/src/test/java/com/messagebird/objects/conversations/MessageParamTest.java new file mode 100644 index 00000000..f1147d2f --- /dev/null +++ b/api/src/test/java/com/messagebird/objects/conversations/MessageParamTest.java @@ -0,0 +1,30 @@ +package com.messagebird.objects.conversations; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +public class MessageParamTest { + @Test + public void testMessageParamToString() { + MessageParam param = new MessageParam(); + param.setType(TemplateMediaType.IMAGE); + param.setText("Sample text"); + param.setPayload("Sample payload"); + + String expected = "MessageParam{type=image, text='Sample text', payload='Sample payload', currency=null, dateTime='null', document=null, image=null, video=null, expirationTime='null', couponCode='null'}"; + assertEquals(expected, param.toString()); + } + + @Test(expected = IllegalArgumentException.class) + public void testMessageParamSetTextInvalid() { + MessageParam param = new MessageParam(); + param.setText(""); + } + + @Test + public void testMessageParamSetTextValid() { + MessageParam param = new MessageParam(); + param.setText("Valid text"); + assertEquals("Valid text", param.getText()); + } +} diff --git a/api/src/test/java/com/messagebird/objects/conversations/TemplateMediaTypeTest.java b/api/src/test/java/com/messagebird/objects/conversations/TemplateMediaTypeTest.java new file mode 100644 index 00000000..d30bde28 --- /dev/null +++ b/api/src/test/java/com/messagebird/objects/conversations/TemplateMediaTypeTest.java @@ -0,0 +1,27 @@ +package com.messagebird.objects.conversations; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class TemplateMediaTypeTest { + @Test + public void testTemplateMediaTypeForValueValid() { + assertEquals(TemplateMediaType.VIDEO, TemplateMediaType.forValue("video")); + } + + @Test(expected = IllegalArgumentException.class) + public void testTemplateMediaTypeForValueNull() { + TemplateMediaType.forValue(null); + } + + @Test + public void testTemplateMediaTypeForValueInvalid() { + assertNull(TemplateMediaType.forValue("non_existing_value")); + } + + @Test + public void testTemplateMediaTypeToString() { + assertEquals("video", TemplateMediaType.VIDEO.toString()); + } +} diff --git a/api/src/test/java/com/messagebird/objects/integrations/HSMComponentTest.java b/api/src/test/java/com/messagebird/objects/integrations/HSMComponentTest.java new file mode 100644 index 00000000..9af4bb64 --- /dev/null +++ b/api/src/test/java/com/messagebird/objects/integrations/HSMComponentTest.java @@ -0,0 +1,26 @@ +package com.messagebird.objects.integrations; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +public class HSMComponentTest { + @Test + public void testToString() { + HSMComponent component = new HSMComponent(); + component.setType(HSMComponentType.BODY); + component.setFormat(HSMComponentFormat.TEXT); + component.setText("Test text"); + component.setAddSecurityRecommendation(true); + component.setCodeExpirationMinutes(10); + component.setHasExpiration(true); + + String expected = "HSMComponent{type=BODY, format=TEXT, text='Test text', addSecurityRecommendation=true, codeExpirationMinutes=10, buttons=null, hasExpiration=true, cards=null, example=null}"; + assertEquals(expected, component.toString()); + } + + @Test(expected = IllegalArgumentException.class) + public void testSetTextInvalid() { + HSMComponent component = new HSMComponent(); + component.setText(""); + } +} diff --git a/api/src/test/java/com/messagebird/objects/integrations/HSMComponentTypeTest.java b/api/src/test/java/com/messagebird/objects/integrations/HSMComponentTypeTest.java new file mode 100644 index 00000000..47053509 --- /dev/null +++ b/api/src/test/java/com/messagebird/objects/integrations/HSMComponentTypeTest.java @@ -0,0 +1,23 @@ +package com.messagebird.objects.integrations; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class HSMComponentTypeTest { + @Test + public void testForValueValid() { + assertEquals(HSMComponentType.BODY, HSMComponentType.forValue("BODY")); + assertEquals(HSMComponentType.BODY, HSMComponentType.forValue("body")); + } + + @Test(expected = NullPointerException.class) + public void testForValueNull() { + HSMComponentType.forValue(null); + } + + @Test + public void testForValueInvalid() { + assertNull(HSMComponentType.forValue("INVALID")); + } +} diff --git a/api/src/test/resources/fixtures/call_flow_update_response.json b/api/src/test/resources/fixtures/call_flow_update_response.json new file mode 100644 index 00000000..1a7c1f21 --- /dev/null +++ b/api/src/test/resources/fixtures/call_flow_update_response.json @@ -0,0 +1,40 @@ +{ + "data": [ + { + "id": "e781a76f-14ad-45b0-8490-409300244e20", + "steps": [ + { + "id": "a8e44a38-b935-482f-b17f-ed3472c6292c", + "action": "transfer", + "options": { + "destination" : "31612345678", + "payload" : "Test payload", + "language" : "en-GB", + "voice" : "male", + "repeat" : 1, + "media" : "test.mp3", + "length" : 1, + "maxLength" : 2, + "timeout" : 3, + "finishOnKey" : "1", + "transcribe" : "false", + "transcribeLanguage" : "en-GB", + "record" : "both", + "url" : "http://", + "ifMachine" : "ifMachine", + "machineTimeout" : 200, + "onFinish" : "http://", + "mask" : false + } + } + ], + "record": true, + "default": false, + "createdAt": "2019-08-06T14:13:06Z", + "updatedAt": "2019-08-06T14:13:06Z" + } + ], + "_links": { + "self": "/call-flows/e781a76f-14ad-45b0-8490-409300244e20" + } +} \ No newline at end of file diff --git a/api/src/test/resources/fixtures/call_flow_view.json b/api/src/test/resources/fixtures/call_flow_view.json new file mode 100644 index 00000000..7546da2b --- /dev/null +++ b/api/src/test/resources/fixtures/call_flow_view.json @@ -0,0 +1,40 @@ +{ + "data": [ + { + "id": "e781a76f-14ad-45b0-8490-409300244e20", + "steps": [ + { + "id": "a8e44a38-b935-482f-b17f-ed3472c6292c", + "action": "transfer", + "options": { + "destination" : "31612345678", + "payload" : "Test payload", + "language" : "en-GB", + "voice" : "male", + "repeat" : 1, + "media" : "test.mp3", + "length" : 1, + "maxLength" : 2, + "timeout" : 3, + "finishOnKey" : "1", + "transcribe" : "false", + "transcribeLanguage" : "en-GB", + "record" : "both", + "url" : "http://", + "ifMachine" : "ifMachine", + "machineTimeout" : 200, + "onFinish" : "http://", + "mask" : false + } + } + ], + "record": true, + "default": false, + "createdAt": "2019-08-06T14:13:06Z", + "updatedAt": "2019-08-06T14:13:06Z" + } + ], + "_links": { + "self": "/call-flows/70fbdda2-4f1f-44ce-8792-75af32cf598c" + } +} \ No newline at end of file diff --git a/api/src/test/resources/fixtures/call_flow_view_title.json b/api/src/test/resources/fixtures/call_flow_view_title.json new file mode 100644 index 00000000..1640484d --- /dev/null +++ b/api/src/test/resources/fixtures/call_flow_view_title.json @@ -0,0 +1,41 @@ +{ + "data": [ + { + "title": "Call title", + "id": "e781a76f-14ad-45b0-8490-409300244e20", + "steps": [ + { + "id": "a8e44a38-b935-482f-b17f-ed3472c6292c", + "action": "transfer", + "options": { + "destination" : "31612345678", + "payload" : "Test payload", + "language" : "en-GB", + "voice" : "male", + "repeat" : 1, + "media" : "test.mp3", + "length" : 1, + "maxLength" : 2, + "timeout" : 3, + "finishOnKey" : "1", + "transcribe" : "false", + "transcribeLanguage" : "en-GB", + "record" : "both", + "url" : "http://", + "ifMachine" : "ifMachine", + "machineTimeout" : 200, + "onFinish" : "http://", + "mask" : false + } + } + ], + "record": true, + "default": false, + "createdAt": "2019-08-06T14:13:06Z", + "updatedAt": "2019-08-06T14:13:06Z" + } + ], + "_links": { + "self": "/call-flows/70fbdda2-4f1f-44ce-8792-75af32cf598c" + } +} \ No newline at end of file diff --git a/api/src/test/resources/fixtures/call_flows_list.json b/api/src/test/resources/fixtures/call_flows_list.json new file mode 100644 index 00000000..0820526d --- /dev/null +++ b/api/src/test/resources/fixtures/call_flows_list.json @@ -0,0 +1,49 @@ +{ + "data": [ + { + "id": "e781a76f-14ad-45b0-8490-409300244e20", + "steps": [ + { + "id": "a8e44a38-b935-482f-b17f-ed3472c6292c", + "action": "transfer", + "options": { + "destination" : "31612345678", + "payload" : "Test payload", + "language" : "en-GB", + "voice" : "male", + "repeat" : 1, + "media" : "test.mp3", + "length" : 1, + "maxLength" : 2, + "timeout" : 3, + "finishOnKey" : "1", + "transcribe" : "false", + "transcribeLanguage" : "en-GB", + "record" : "both", + "url" : "http://", + "ifMachine" : "ifMachine", + "machineTimeout" : 200, + "onFinish" : "http://", + "mask" : false + } + } + ], + "record": true, + "default": false, + "createdAt": "2019-08-06T14:13:06Z", + "updatedAt": "2019-08-06T14:13:06Z", + "_links": { + "self": "/call-flows/70fbdda2-4f1f-44ce-8792-75af32cf598c" + } + } + ], + "_links": { + "self": "/call-flows?page=1" + }, + "pagination": { + "totalCount": 10, + "pageCount": 3, + "currentPage": 2, + "perPage": 12 + } +} \ No newline at end of file diff --git a/api/src/test/resources/fixtures/call_flows_post.json b/api/src/test/resources/fixtures/call_flows_post.json new file mode 100644 index 00000000..1a7c1f21 --- /dev/null +++ b/api/src/test/resources/fixtures/call_flows_post.json @@ -0,0 +1,40 @@ +{ + "data": [ + { + "id": "e781a76f-14ad-45b0-8490-409300244e20", + "steps": [ + { + "id": "a8e44a38-b935-482f-b17f-ed3472c6292c", + "action": "transfer", + "options": { + "destination" : "31612345678", + "payload" : "Test payload", + "language" : "en-GB", + "voice" : "male", + "repeat" : 1, + "media" : "test.mp3", + "length" : 1, + "maxLength" : 2, + "timeout" : 3, + "finishOnKey" : "1", + "transcribe" : "false", + "transcribeLanguage" : "en-GB", + "record" : "both", + "url" : "http://", + "ifMachine" : "ifMachine", + "machineTimeout" : 200, + "onFinish" : "http://", + "mask" : false + } + } + ], + "record": true, + "default": false, + "createdAt": "2019-08-06T14:13:06Z", + "updatedAt": "2019-08-06T14:13:06Z" + } + ], + "_links": { + "self": "/call-flows/e781a76f-14ad-45b0-8490-409300244e20" + } +} \ No newline at end of file diff --git a/api/src/test/resources/fixtures/call_legs_get.json b/api/src/test/resources/fixtures/call_legs_get.json index 64d5f65b..23670a0a 100644 --- a/api/src/test/resources/fixtures/call_legs_get.json +++ b/api/src/test/resources/fixtures/call_legs_get.json @@ -13,7 +13,8 @@ "createdAt": "2019-01-10T16:12:54Z", "updatedAt": "2019-01-10T16:13:54Z", "answeredAt": "2019-01-10T16:13:24Z", - "endedAt": "2019-01-10T16:13:30Z" + "endedAt": "2019-01-10T16:13:30Z", + "sipResponseCode": 200 } ], "_links": { diff --git a/api/src/test/resources/fixtures/call_legs_list.json b/api/src/test/resources/fixtures/call_legs_list.json index 53a20f71..0ea233f6 100644 --- a/api/src/test/resources/fixtures/call_legs_list.json +++ b/api/src/test/resources/fixtures/call_legs_list.json @@ -14,6 +14,7 @@ "updatedAt":"2019-01-10T16:13:54Z", "answeredAt":"2019-01-10T16:13:24Z", "endedAt":"2019-01-10T16:13:30Z", + "sipResponseCode": 200, "_links":{ "self":"/calls/unforgiven-call/legs/first-leg-of-unforgiven-call" } diff --git a/api/src/test/resources/fixtures/outbound_sms_prices.json b/api/src/test/resources/fixtures/outbound_sms_prices.json new file mode 100644 index 00000000..261401d3 --- /dev/null +++ b/api/src/test/resources/fixtures/outbound_sms_prices.json @@ -0,0 +1,37 @@ +{ + "gateway": 10, + "currencyCode": "EUR", + "totalCount": 3, + "prices": [ + { + "price": "0.060000", + "currencyCode": "EUR", + "mccmnc": "0", + "mcc": "0", + "mnc": null, + "countryName": "Default Rate", + "countryIsoCode": "XX", + "operatorName": "Default Rate" + }, + { + "price": "0.047000", + "currencyCode": "EUR", + "mccmnc": "202", + "mcc": "202", + "mnc": null, + "countryName": "Greece", + "countryIsoCode": "GR", + "operatorName": null + }, + { + "price": "0.045000", + "currencyCode": "EUR", + "mccmnc": "20205", + "mcc": "202", + "mnc": "05", + "countryName": "Greece", + "countryIsoCode": "GR", + "operatorName": "Vodafone" + } + ] +} diff --git a/api/src/test/resources/webhook_test_data.json b/api/src/test/resources/webhook_test_data.json new file mode 100644 index 00000000..da8cb6c2 --- /dev/null +++ b/api/src/test/resources/webhook_test_data.json @@ -0,0 +1,433 @@ +[ + { + "name": "Valid JWT with no URL parameters or payload - DELETE", + "method": "DELETE", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.6Fp0rVOsRr1fj9S2GidXAfkmPVofKr8_RTffC6G6r2E", + "valid": true + }, + { + "name": "Valid JWT with no URL parameters or payload - GET", + "method": "GET", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.6Fp0rVOsRr1fj9S2GidXAfkmPVofKr8_RTffC6G6r2E", + "valid": true + }, + { + "name": "Valid JWT with no URL parameters or payload - PATCH", + "method": "PATCH", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.6Fp0rVOsRr1fj9S2GidXAfkmPVofKr8_RTffC6G6r2E", + "valid": true + }, + { + "name": "Valid JWT with no URL parameters or payload - POST", + "method": "POST", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.6Fp0rVOsRr1fj9S2GidXAfkmPVofKr8_RTffC6G6r2E", + "valid": true + }, + { + "name": "Valid JWT with no URL parameters or payload - PUT", + "method": "PUT", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.6Fp0rVOsRr1fj9S2GidXAfkmPVofKr8_RTffC6G6r2E", + "valid": true + }, + { + "name": "Valid JWT with URL parameters and no payload - DELETE", + "method": "DELETE", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/path?bar=1\u0026foo=2", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiJjOTQ2YWY3Ny1lMTgyLTRlYWEtYjJmZi0xYTU0NWI1ZTk5MWEiLCJ1cmxfaGFzaCI6IjQxZjA1ZjBkZGQwYTIyYWIyMDlhYzQ2ZjQ3YzQ1NzJkOWNlZmEyNTdlZDc0YjI0MDA0YmFlNzUzZWNlNmMyNjAifQ.huo2ou6JDoDc7sV25d75UMWeYhBeavQlLsqIibSZuac", + "valid": true + }, + { + "name": "Valid JWT with URL parameters and no payload - GET", + "method": "GET", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/path?bar=1\u0026foo=2", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiJjOTQ2YWY3Ny1lMTgyLTRlYWEtYjJmZi0xYTU0NWI1ZTk5MWEiLCJ1cmxfaGFzaCI6IjQxZjA1ZjBkZGQwYTIyYWIyMDlhYzQ2ZjQ3YzQ1NzJkOWNlZmEyNTdlZDc0YjI0MDA0YmFlNzUzZWNlNmMyNjAifQ.huo2ou6JDoDc7sV25d75UMWeYhBeavQlLsqIibSZuac", + "valid": true + }, + { + "name": "Valid JWT with URL parameters and no payload - PATCH", + "method": "PATCH", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/path?bar=1\u0026foo=2", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiJjOTQ2YWY3Ny1lMTgyLTRlYWEtYjJmZi0xYTU0NWI1ZTk5MWEiLCJ1cmxfaGFzaCI6IjQxZjA1ZjBkZGQwYTIyYWIyMDlhYzQ2ZjQ3YzQ1NzJkOWNlZmEyNTdlZDc0YjI0MDA0YmFlNzUzZWNlNmMyNjAifQ.huo2ou6JDoDc7sV25d75UMWeYhBeavQlLsqIibSZuac", + "valid": true + }, + { + "name": "Valid JWT with URL parameters and no payload - POST", + "method": "POST", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/path?bar=1\u0026foo=2", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiJjOTQ2YWY3Ny1lMTgyLTRlYWEtYjJmZi0xYTU0NWI1ZTk5MWEiLCJ1cmxfaGFzaCI6IjQxZjA1ZjBkZGQwYTIyYWIyMDlhYzQ2ZjQ3YzQ1NzJkOWNlZmEyNTdlZDc0YjI0MDA0YmFlNzUzZWNlNmMyNjAifQ.huo2ou6JDoDc7sV25d75UMWeYhBeavQlLsqIibSZuac", + "valid": true + }, + { + "name": "Valid JWT with URL parameters and no payload - PUT", + "method": "PUT", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/path?bar=1\u0026foo=2", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiJjOTQ2YWY3Ny1lMTgyLTRlYWEtYjJmZi0xYTU0NWI1ZTk5MWEiLCJ1cmxfaGFzaCI6IjQxZjA1ZjBkZGQwYTIyYWIyMDlhYzQ2ZjQ3YzQ1NzJkOWNlZmEyNTdlZDc0YjI0MDA0YmFlNzUzZWNlNmMyNjAifQ.huo2ou6JDoDc7sV25d75UMWeYhBeavQlLsqIibSZuac", + "valid": true + }, + { + "name": "Valid JWT with URL parameters and payload - DELETE", + "method": "DELETE", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/path?bar=1\u0026foo=2", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI5M2U1NTAwNi1hMGU4LTQ1MjYtYTE5MC1mYTVmZjAwZWExMTYiLCJ1cmxfaGFzaCI6IjQxZjA1ZjBkZGQwYTIyYWIyMDlhYzQ2ZjQ3YzQ1NzJkOWNlZmEyNTdlZDc0YjI0MDA0YmFlNzUzZWNlNmMyNjAiLCJwYXlsb2FkX2hhc2giOiJkZmZkNjAyMWJiMmJkNWIwYWY2NzYyOTA4MDllYzNhNTMxOTFkZDgxYzdmNzBhNGIyODY4OGEzNjIxODI5ODZmIn0._H--TOuYFLpeEH39-rg5E3IHVkjHozBcaKVWPRC5m9I", + "valid": true + }, + { + "name": "Valid JWT with URL parameters and payload - GET", + "method": "GET", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/path?bar=1\u0026foo=2", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI5M2U1NTAwNi1hMGU4LTQ1MjYtYTE5MC1mYTVmZjAwZWExMTYiLCJ1cmxfaGFzaCI6IjQxZjA1ZjBkZGQwYTIyYWIyMDlhYzQ2ZjQ3YzQ1NzJkOWNlZmEyNTdlZDc0YjI0MDA0YmFlNzUzZWNlNmMyNjAiLCJwYXlsb2FkX2hhc2giOiJkZmZkNjAyMWJiMmJkNWIwYWY2NzYyOTA4MDllYzNhNTMxOTFkZDgxYzdmNzBhNGIyODY4OGEzNjIxODI5ODZmIn0._H--TOuYFLpeEH39-rg5E3IHVkjHozBcaKVWPRC5m9I", + "valid": true + }, + { + "name": "Valid JWT with URL parameters and payload - PATCH", + "method": "PATCH", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/path?bar=1\u0026foo=2", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI5M2U1NTAwNi1hMGU4LTQ1MjYtYTE5MC1mYTVmZjAwZWExMTYiLCJ1cmxfaGFzaCI6IjQxZjA1ZjBkZGQwYTIyYWIyMDlhYzQ2ZjQ3YzQ1NzJkOWNlZmEyNTdlZDc0YjI0MDA0YmFlNzUzZWNlNmMyNjAiLCJwYXlsb2FkX2hhc2giOiJkZmZkNjAyMWJiMmJkNWIwYWY2NzYyOTA4MDllYzNhNTMxOTFkZDgxYzdmNzBhNGIyODY4OGEzNjIxODI5ODZmIn0._H--TOuYFLpeEH39-rg5E3IHVkjHozBcaKVWPRC5m9I", + "valid": true + }, + { + "name": "Valid JWT with URL parameters and payload - POST", + "method": "POST", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/path?bar=1\u0026foo=2", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI5M2U1NTAwNi1hMGU4LTQ1MjYtYTE5MC1mYTVmZjAwZWExMTYiLCJ1cmxfaGFzaCI6IjQxZjA1ZjBkZGQwYTIyYWIyMDlhYzQ2ZjQ3YzQ1NzJkOWNlZmEyNTdlZDc0YjI0MDA0YmFlNzUzZWNlNmMyNjAiLCJwYXlsb2FkX2hhc2giOiJkZmZkNjAyMWJiMmJkNWIwYWY2NzYyOTA4MDllYzNhNTMxOTFkZDgxYzdmNzBhNGIyODY4OGEzNjIxODI5ODZmIn0._H--TOuYFLpeEH39-rg5E3IHVkjHozBcaKVWPRC5m9I", + "valid": true + }, + { + "name": "Valid JWT with URL parameters and payload - PUT", + "method": "PUT", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/path?bar=1\u0026foo=2", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI5M2U1NTAwNi1hMGU4LTQ1MjYtYTE5MC1mYTVmZjAwZWExMTYiLCJ1cmxfaGFzaCI6IjQxZjA1ZjBkZGQwYTIyYWIyMDlhYzQ2ZjQ3YzQ1NzJkOWNlZmEyNTdlZDc0YjI0MDA0YmFlNzUzZWNlNmMyNjAiLCJwYXlsb2FkX2hhc2giOiJkZmZkNjAyMWJiMmJkNWIwYWY2NzYyOTA4MDllYzNhNTMxOTFkZDgxYzdmNzBhNGIyODY4OGEzNjIxODI5ODZmIn0._H--TOuYFLpeEH39-rg5E3IHVkjHozBcaKVWPRC5m9I", + "valid": true + }, + { + "name": "Token received before it was issued - GET", + "method": "GET", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ4MjgwMCwiZXhwIjoxNjI1NDgyODYwLCJqdGkiOiJmOWY4YzM4Mi0yNDQ5LTQzMTEtYjcyYi0xZGY3MTY4NzkzMWUiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.ZELgDFNGhjZH9CffQKcq3sytBe2I0KciLxpBhcfstHQ", + "valid": false, + "reason": "invalid jwt: claim nbf is in the future" + }, + { + "name": "Token received after it was expired - GET", + "method": "GET", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3NTYwMCwiZXhwIjoxNjI1NDc1NjYwLCJqdGkiOiI1ZjAyZjUyMi02MDMwLTQ2YzgtYjVhMy0wMTI0NjQ3OGQ4YmMiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.45MSST3B_2PsjNUeiuW54_vUQgVw4rBXrdWrOUEz3lM", + "valid": false, + "reason": "invalid jwt: claim exp is in the past" + }, + { + "name": "Token received on different URL (parameters were sorted out of order) - GET", + "method": "GET", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/path?foo=1\u0026bar=2", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiJhNzVjOTA5Ni1lODIzLTQ0MmItODVmMi03ZDNjOWQ5YjcyNmIiLCJ1cmxfaGFzaCI6IjZhMDIyZDgzNGMxODMzMGJhYWQ4YTJiZDYxMzUxNzNlZDIxNjEwYTQ1NDUxNmJlMGRmM2YxY2MwMTUxNjEwZWEifQ.z6Sw1XQIM0wuEQGBhXBdawDIIrtMg2XnmA_bpDq53pE", + "valid": false, + "reason": "invalid jwt: claim url_hash is invalid" + }, + { + "name": "Payload does not match - DELETE", + "method": "DELETE", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiIxNDUwMTUzMi05NmYyLTQ2ODQtOTgzMi02OGYwOTUxYWUzNDIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiJmYjYyZjAyYWNkYTdkNzQxNzdhNzAxYTFjZTAwNmU2YmFjZDkwYzdkNGQ3YWI0ODE2OTJjMWRhNDdjODEwNzZiIn0.m79RCwO6dGa9pvzsQypkVuXQ6eM0CkRl6_U7MfWg6TM", + "valid": false, + "reason": "invalid jwt: claim payload_hash is invalid" + }, + { + "name": "Payload does not match - GET", + "method": "GET", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiIxNDUwMTUzMi05NmYyLTQ2ODQtOTgzMi02OGYwOTUxYWUzNDIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiJmYjYyZjAyYWNkYTdkNzQxNzdhNzAxYTFjZTAwNmU2YmFjZDkwYzdkNGQ3YWI0ODE2OTJjMWRhNDdjODEwNzZiIn0.m79RCwO6dGa9pvzsQypkVuXQ6eM0CkRl6_U7MfWg6TM", + "valid": false, + "reason": "invalid jwt: claim payload_hash is invalid" + }, + { + "name": "Payload does not match - PATCH", + "method": "PATCH", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiIxNDUwMTUzMi05NmYyLTQ2ODQtOTgzMi02OGYwOTUxYWUzNDIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiJmYjYyZjAyYWNkYTdkNzQxNzdhNzAxYTFjZTAwNmU2YmFjZDkwYzdkNGQ3YWI0ODE2OTJjMWRhNDdjODEwNzZiIn0.m79RCwO6dGa9pvzsQypkVuXQ6eM0CkRl6_U7MfWg6TM", + "valid": false, + "reason": "invalid jwt: claim payload_hash is invalid" + }, + { + "name": "Payload does not match - POST", + "method": "POST", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiIxNDUwMTUzMi05NmYyLTQ2ODQtOTgzMi02OGYwOTUxYWUzNDIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiJmYjYyZjAyYWNkYTdkNzQxNzdhNzAxYTFjZTAwNmU2YmFjZDkwYzdkNGQ3YWI0ODE2OTJjMWRhNDdjODEwNzZiIn0.m79RCwO6dGa9pvzsQypkVuXQ6eM0CkRl6_U7MfWg6TM", + "valid": false, + "reason": "invalid jwt: claim payload_hash is invalid" + }, + { + "name": "Payload does not match - PUT", + "method": "PUT", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiIxNDUwMTUzMi05NmYyLTQ2ODQtOTgzMi02OGYwOTUxYWUzNDIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiJmYjYyZjAyYWNkYTdkNzQxNzdhNzAxYTFjZTAwNmU2YmFjZDkwYzdkNGQ3YWI0ODE2OTJjMWRhNDdjODEwNzZiIn0.m79RCwO6dGa9pvzsQypkVuXQ6eM0CkRl6_U7MfWg6TM", + "valid": false, + "reason": "invalid jwt: claim payload_hash is invalid" + }, + { + "name": "Different secret - GET", + "method": "GET", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiIyNDNjMjdhZS0yZjAyLTQ2YTAtODg1Mi1jNjZmMzdlYTlmNDYiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.JgSeYyOtlEKAXk8bz30iJI4tXgf4lxknoiezawuVhb4", + "valid": false, + "reason": "invalid jwt: signature is invalid" + }, + { + "name": "payload was removed in transit - DELETE", + "method": "DELETE", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiIxNDUwMTUzMi05NmYyLTQ2ODQtOTgzMi02OGYwOTUxYWUzNDIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiJkZmZkNjAyMWJiMmJkNWIwYWY2NzYyOTA4MDllYzNhNTMxOTFkZDgxYzdmNzBhNGIyODY4OGEzNjIxODI5ODZmIn0.bHfsjj3tljfzTufoG3EU3p_yFrQh9CufyzRSaRMd4ss", + "valid": false, + "reason": "invalid jwt: claim payload_hash is set but actual payload is missing" + }, + { + "name": "payload was removed in transit - GET", + "method": "GET", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiIxNDUwMTUzMi05NmYyLTQ2ODQtOTgzMi02OGYwOTUxYWUzNDIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiJkZmZkNjAyMWJiMmJkNWIwYWY2NzYyOTA4MDllYzNhNTMxOTFkZDgxYzdmNzBhNGIyODY4OGEzNjIxODI5ODZmIn0.bHfsjj3tljfzTufoG3EU3p_yFrQh9CufyzRSaRMd4ss", + "valid": false, + "reason": "invalid jwt: claim payload_hash is set but actual payload is missing" + }, + { + "name": "payload was removed in transit - PATCH", + "method": "PATCH", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiIxNDUwMTUzMi05NmYyLTQ2ODQtOTgzMi02OGYwOTUxYWUzNDIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiJkZmZkNjAyMWJiMmJkNWIwYWY2NzYyOTA4MDllYzNhNTMxOTFkZDgxYzdmNzBhNGIyODY4OGEzNjIxODI5ODZmIn0.bHfsjj3tljfzTufoG3EU3p_yFrQh9CufyzRSaRMd4ss", + "valid": false, + "reason": "invalid jwt: claim payload_hash is set but actual payload is missing" + }, + { + "name": "payload was removed in transit - POST", + "method": "POST", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiIxNDUwMTUzMi05NmYyLTQ2ODQtOTgzMi02OGYwOTUxYWUzNDIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiJkZmZkNjAyMWJiMmJkNWIwYWY2NzYyOTA4MDllYzNhNTMxOTFkZDgxYzdmNzBhNGIyODY4OGEzNjIxODI5ODZmIn0.bHfsjj3tljfzTufoG3EU3p_yFrQh9CufyzRSaRMd4ss", + "valid": false, + "reason": "invalid jwt: claim payload_hash is set but actual payload is missing" + }, + { + "name": "payload was removed in transit - PUT", + "method": "PUT", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiIxNDUwMTUzMi05NmYyLTQ2ODQtOTgzMi02OGYwOTUxYWUzNDIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiJkZmZkNjAyMWJiMmJkNWIwYWY2NzYyOTA4MDllYzNhNTMxOTFkZDgxYzdmNzBhNGIyODY4OGEzNjIxODI5ODZmIn0.bHfsjj3tljfzTufoG3EU3p_yFrQh9CufyzRSaRMd4ss", + "valid": false, + "reason": "invalid jwt: claim payload_hash is set but actual payload is missing" + }, + { + "name": "payload was added in transit - DELETE", + "method": "DELETE", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.6Fp0rVOsRr1fj9S2GidXAfkmPVofKr8_RTffC6G6r2E", + "valid": false, + "reason": "invalid jwt: claim payload_hash is not set but payload is present" + }, + { + "name": "payload was added in transit - GET", + "method": "GET", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.6Fp0rVOsRr1fj9S2GidXAfkmPVofKr8_RTffC6G6r2E", + "valid": false, + "reason": "invalid jwt: claim payload_hash is not set but payload is present" + }, + { + "name": "payload was added in transit - PATCH", + "method": "PATCH", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.6Fp0rVOsRr1fj9S2GidXAfkmPVofKr8_RTffC6G6r2E", + "valid": false, + "reason": "invalid jwt: claim payload_hash is not set but payload is present" + }, + { + "name": "payload was added in transit - POST", + "method": "POST", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.6Fp0rVOsRr1fj9S2GidXAfkmPVofKr8_RTffC6G6r2E", + "valid": false, + "reason": "invalid jwt: claim payload_hash is not set but payload is present" + }, + { + "name": "payload was added in transit - PUT", + "method": "PUT", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Hello, World!", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.6Fp0rVOsRr1fj9S2GidXAfkmPVofKr8_RTffC6G6r2E", + "valid": false, + "reason": "invalid jwt: claim payload_hash is not set but payload is present" + }, + { + "name": "Special characters in URL - GET", + "method": "GET", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/?obj%5Bfield1%5D=val1\u0026obj%5Bfield2%5D=val2\u0026param=value+with+spaces", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjhhZmYzNzQ4ZDc4OWM0MzMzNTczZTFjMzFmZTViMDA0OTQwY2I1ZmM0NzQ2MGVkM2QwZTY1NGY3ZmM0ZTVmYWUifQ.mJBCMfEjKmN3IMuzqPXPHktWETrcu0iNdF3agE8PDyI", + "valid": true + }, + { + "name": "Special characters in the payload - DELETE", + "method": "DELETE", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Some text containg \u0026 \u003c \u003e, \u0026 and \\u0026", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiIyNDMxYmQ5Y2NiNDhlZDAxNmZhZDU2N2IwNzcxMjAzOGI1Y2RkOTQ5YWM5ZDQxZmU0NjkzZDVhMzg0NWNmN2U4In0.IQiKzeEPaO3A48lhKmBDSmxZyCESPCGxdSKFdi0dEJs", + "valid": true + }, + { + "name": "Special characters in the payload - GET", + "method": "GET", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Some text containg \u0026 \u003c \u003e, \u0026 and \\u0026", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiIyNDMxYmQ5Y2NiNDhlZDAxNmZhZDU2N2IwNzcxMjAzOGI1Y2RkOTQ5YWM5ZDQxZmU0NjkzZDVhMzg0NWNmN2U4In0.IQiKzeEPaO3A48lhKmBDSmxZyCESPCGxdSKFdi0dEJs", + "valid": true + }, + { + "name": "Special characters in the payload - PATCH", + "method": "PATCH", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Some text containg \u0026 \u003c \u003e, \u0026 and \\u0026", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiIyNDMxYmQ5Y2NiNDhlZDAxNmZhZDU2N2IwNzcxMjAzOGI1Y2RkOTQ5YWM5ZDQxZmU0NjkzZDVhMzg0NWNmN2U4In0.IQiKzeEPaO3A48lhKmBDSmxZyCESPCGxdSKFdi0dEJs", + "valid": true + }, + { + "name": "Special characters in the payload - POST", + "method": "POST", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Some text containg \u0026 \u003c \u003e, \u0026 and \\u0026", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiIyNDMxYmQ5Y2NiNDhlZDAxNmZhZDU2N2IwNzcxMjAzOGI1Y2RkOTQ5YWM5ZDQxZmU0NjkzZDVhMzg0NWNmN2U4In0.IQiKzeEPaO3A48lhKmBDSmxZyCESPCGxdSKFdi0dEJs", + "valid": true + }, + { + "name": "Special characters in the payload - PUT", + "method": "PUT", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "payload": "Some text containg \u0026 \u003c \u003e, \u0026 and \\u0026", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDciLCJwYXlsb2FkX2hhc2giOiIyNDMxYmQ5Y2NiNDhlZDAxNmZhZDU2N2IwNzcxMjAzOGI1Y2RkOTQ5YWM5ZDQxZmU0NjkzZDVhMzg0NWNmN2U4In0.IQiKzeEPaO3A48lhKmBDSmxZyCESPCGxdSKFdi0dEJs", + "valid": true + }, + { + "name": "HS384 alg - GET", + "method": "GET", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.KNMW-29X77lZeuPmThmHWc_RUAvTkaDkpxIZK6mqE08v8mWKiU9Edh4QXwAJO2nv", + "valid": true + }, + { + "name": "HS512 alg - GET", + "method": "GET", + "secret": "36efdd1aace2e26cd490f0d951138253bef2f7c6d34d18981da781555cc4cebb", + "url": "https://example.com/", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJNZXNzYWdlQmlyZCIsIm5iZiI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.rd6r0iMyNGnVPCwurETphE3Y8rpAyvvnUK0S8WvGUkt2E1QSRAZ7NZJZBHw1Y_Wb5W-sK9HJr_PRL2vz4jRT3Q", + "valid": true + }, + { + "name": "none alg - GET", + "method": "GET", + "url": "https://example.com/", + "timestamp": "2021-07-05T12:00:00+02:00", + "token": "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJNZXNzYWdlQmlyZCIsImlhdCI6MTYyNTQ3OTIwMCwiZXhwIjoxNjI1NDc5MjYwLCJqdGkiOiI1OWEyNDRkYy1lOWFkLTRlMjMtOTc3OC0zNzFmYWEyMzhmNzIiLCJ1cmxfaGFzaCI6IjBmMTE1ZGIwNjJiN2MwZGQwMzBiMTY4NzhjOTlkZWE1YzM1NGI0OWRjMzdiMzhlYjg4NDYxNzljNzc4M2U5ZDcifQ.", + "valid": false, + "reason": "invalid jwt: signing method none is invalid" + } +] diff --git a/examples/pom.xml b/examples/pom.xml index 88d378fd..cb9b7489 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -6,7 +6,7 @@ com.messagebird examples - 3.0.0 + 6.4.0 @@ -20,7 +20,7 @@ com.messagebird messagebird-api - 3.0.0 + 6.4.0 compile @@ -50,8 +50,8 @@ maven-compiler-plugin 3.1 - 11 - 11 + 1.8 + 1.8 diff --git a/examples/src/main/java/ExampleCancelNumber.java b/examples/src/main/java/ExampleCancelNumber.java new file mode 100644 index 00000000..eba1d3d4 --- /dev/null +++ b/examples/src/main/java/ExampleCancelNumber.java @@ -0,0 +1,30 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.exceptions.NotFoundException; + +public class ExampleCancelNumber { + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key & the number you wish to delete."); + return; + } + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + messageBirdClient.cancelNumber(args[1]); + System.out.println("Number Deleted!"); + } catch (UnauthorizedException | GeneralException | NotFoundException exception) { + if (exception.getErrors() != null) { + System.out.println(exception.getErrors().toString()); + } + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleConversationSendCarouselTemplate.java b/examples/src/main/java/ExampleConversationSendCarouselTemplate.java new file mode 100644 index 00000000..f386917d --- /dev/null +++ b/examples/src/main/java/ExampleConversationSendCarouselTemplate.java @@ -0,0 +1,116 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.conversations.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ExampleConversationSendCarouselTemplate { + + public static void main(String[] args) { + + if (args.length < 3) { + System.out.println("Please at least specify your access key, the channel id and destination address.\n" + + "Usage : java -jar test_accesskey(Required) channel_id(Required) to(Required)"); + return; + } + + //First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + //Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + ConversationContent conversationContent = new ConversationContent(); + ConversationContentHsm conversationContentHsm = new ConversationContentHsm(); + conversationContentHsm.setNamespace("c663a566_a4de_492f_86b2_028cbf612345"); + conversationContentHsm.setTemplateName("carousel_template_test_hello"); + ConversationHsmLanguage language = new ConversationHsmLanguage(); + language.setCode("en_US"); + conversationContentHsm.setLanguage(language); + + + +// card0 components + List card0Components = new ArrayList<>(); +// card0 header + MessageComponent card0HeaderComponent = new MessageComponent(); + card0HeaderComponent.setType(MessageComponentType.HEADER); + MessageParam imageParam = new MessageParam(); + imageParam.setType(TemplateMediaType.IMAGE); + Media media = new Media(); + media.setUrl("https://upload.wikimedia.org/wikipedia/commons/f/f9/Phoenicopterus_ruber_in_S%C3%A3o_Paulo_Zoo.jpg"); + imageParam.setImage(media); + card0HeaderComponent.setParameters(Collections.singletonList(imageParam)); + card0Components.add(card0HeaderComponent); +// card0 body + MessageComponent card0BodyComponent = new MessageComponent(); + card0BodyComponent.setType(MessageComponentType.BODY); + MessageParam textParam = new MessageParam(); + textParam.setType(TemplateMediaType.TEXT); + textParam.setText("dummy text"); + card0BodyComponent.setParameters(Collections.singletonList(textParam)); + card0Components.add(card0BodyComponent); +// card0 button + MessageComponent card0ButtonComponent = new MessageComponent(); + card0ButtonComponent.setType(MessageComponentType.BUTTON); + card0ButtonComponent.setSub_type("quick_reply"); + card0ButtonComponent.setIndex(0); + MessageParam buttonParam = new MessageParam(); + buttonParam.setType(TemplateMediaType.PAYLOAD); + buttonParam.setPayload("dummy button"); + card0ButtonComponent.setParameters(Collections.singletonList(buttonParam)); + card0Components.add(card0ButtonComponent); + +// card0 + MessageComponent card0 = new MessageComponent(); + card0.setType(MessageComponentType.CARD); + card0.setCard_index(0); + card0.setComponents(card0Components); + + +// cards list + List cards = new ArrayList<>(); + cards.add(card0); + +// carousel component + MessageComponent carousel = new MessageComponent(); + carousel.setType(MessageComponentType.CAROUSEL); + carousel.setCards(cards); +// body component + MessageComponent body = new MessageComponent(); + body.setType(MessageComponentType.BODY); + MessageParam bodyParam = new MessageParam(); + bodyParam.setType(TemplateMediaType.TEXT); + bodyParam.setText("Jackson"); + body.setParameters(Collections.singletonList(bodyParam)); + +// set components to message + List messageComponents = new ArrayList<>(); + messageComponents.add(body); + messageComponents.add(carousel); + conversationContentHsm.setComponents(messageComponents); + + conversationContent.setHsm(conversationContentHsm); + ConversationSendRequest request = new ConversationSendRequest( + args[2], + ConversationContentType.HSM, + conversationContent, + args[1], + "", + null, + null, + null); + + try { + ConversationSendResponse sendResponse = messageBirdClient.sendMessage(request); + System.out.println(sendResponse.toString()); + + } catch (GeneralException | UnauthorizedException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleConversationSendEmailMessage.java b/examples/src/main/java/ExampleConversationSendEmailMessage.java new file mode 100644 index 00000000..63734bb8 --- /dev/null +++ b/examples/src/main/java/ExampleConversationSendEmailMessage.java @@ -0,0 +1,75 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.conversations.ConversationContent; +import com.messagebird.objects.conversations.ConversationContentEmail; +import com.messagebird.objects.conversations.ConversationContentType; +import com.messagebird.objects.conversations.ConversationEmailContent; +import com.messagebird.objects.conversations.ConversationEmailRecipient; +import com.messagebird.objects.conversations.ConversationSendRequest; +import com.messagebird.objects.conversations.ConversationSendResponse; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class ExampleConversationSendEmailMessage { + + public static void main(String[] args) { + + if (args.length < 4) { + System.out.println("Please at least specify your access key, the channel id and destination address.\n" + + "Usage : java -jar test_accesskey(Required) channel_id(Required) from(Required) to(Required)"); + return; + } + + //First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + //Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + ConversationEmailRecipient fromRecipient = new ConversationEmailRecipient(); + fromRecipient.setAddress(args[2]); + ConversationEmailRecipient toRecipient = new ConversationEmailRecipient(); + toRecipient.setAddress(args[3]); + ConversationEmailContent content = new ConversationEmailContent(); + content.setHtml("

HTML Ipsum Presents

\n" + + "\n" + + "

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. " + + "Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget " + + "tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

\n" + + "\n" + + "

Header Level 2

"); + ConversationContentEmail emailContent = new ConversationContentEmail(); + emailContent.setContent(content); + emailContent.setFrom(fromRecipient); + emailContent.setTo(Arrays.asList(toRecipient)); + emailContent.setSubject("Greetings From Messagebird"); + ConversationContent conversationContent = new ConversationContent(); + conversationContent.setEmail(emailContent); + + // Optional source parameter, that identifies the actor making the request. + Map source = new HashMap<>(); + source.put("Salesman", "Sir. John Doe"); + + ConversationSendRequest request = new ConversationSendRequest( + args[2], + ConversationContentType.EMAIL, + conversationContent, + args[1], + "", + null, + source, + null); + + try { + ConversationSendResponse sendResponse = messageBirdClient.sendMessage(request); + System.out.println(sendResponse.toString()); + + } catch (GeneralException | UnauthorizedException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleConversationSendHSMCopyCodeTemplate.java b/examples/src/main/java/ExampleConversationSendHSMCopyCodeTemplate.java new file mode 100644 index 00000000..4d5dcd3f --- /dev/null +++ b/examples/src/main/java/ExampleConversationSendHSMCopyCodeTemplate.java @@ -0,0 +1,92 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.conversations.*; + +import java.util.ArrayList; +import java.util.List; + +public class ExampleConversationSendHSMCopyCodeTemplate { + + public static void main(String[] args) { + if (args.length < 6) { + System.out.println("Please at least specify your access key, the channel id and destination address.\n" + + "Usage : java -jar test_accesskey(Required) channel_id(Required) from(Required) destination(Required) templateName(Required) namespace(Required) couponCodeInput(Required)"); + return; + } + + final String accessKey = args[0]; + final String from = args[1]; + final String destination = args[2]; + final String templateName = args[3]; + final String namespace = args[4]; + final String couponCodeInput = args[5]; + + //First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(accessKey); + //Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + ConversationContent conversationContent = new ConversationContent(); + ConversationContentHsm conversationContentHsm = new ConversationContentHsm(); + conversationContentHsm.setNamespace(namespace); + conversationContentHsm.setTemplateName(templateName); + ConversationHsmLanguage language = new ConversationHsmLanguage(); + language.setCode("en"); + conversationContentHsm.setLanguage(language); + List messageComponents = new ArrayList<>(); + + // Add LTO component + MessageComponent messageCopyCodeComponent = new MessageComponent(); + messageCopyCodeComponent.setType(MessageComponentType.BUTTON); + messageCopyCodeComponent.setSub_type(MessageComponentType.COPY_CODE.toString()); + List messageCCParams = new ArrayList<>(); + + MessageParam couponCodeParam = new MessageParam(); + couponCodeParam.setType(TemplateMediaType.COUPON_CODE); + couponCodeParam.setCouponCode(couponCodeInput); + messageCCParams.add(couponCodeParam); + + messageCopyCodeComponent.setParameters(messageCCParams); + + // Add body component + MessageComponent messageBodyComponent = new MessageComponent(); + messageBodyComponent.setType(MessageComponentType.BODY); + List messageBodyParams = new ArrayList<>(); + + MessageParam text = new MessageParam(); + text.setType(TemplateMediaType.TEXT); + text.setText("Bob"); + messageBodyParams.add(text); + + messageBodyComponent.setParameters(messageBodyParams); + + messageComponents.add(messageCopyCodeComponent); + messageComponents.add(messageBodyComponent); + conversationContentHsm.setComponents(messageComponents); + conversationContent.setHsm(conversationContentHsm); + ConversationSendRequest request = new ConversationSendRequest( + destination, + ConversationContentType.HSM, + conversationContent, + from, + "", + null, + null, + null); + + try { + System.out.println(request.toString()); + ConversationSendResponse sendResponse = messageBirdClient.sendMessage(request); + System.out.println(sendResponse.toString()); + + } catch (UnauthorizedException e) { + System.err.println("Authorization failed. Please check your access key: " + e.getMessage()); + } catch (GeneralException e) { + System.err.println("An error occurred while sending the message: " + e.getMessage()); + } + } + +} diff --git a/examples/src/main/java/ExampleConversationSendHSMLimitedTimeOfferTemplate.java b/examples/src/main/java/ExampleConversationSendHSMLimitedTimeOfferTemplate.java new file mode 100644 index 00000000..65b959a8 --- /dev/null +++ b/examples/src/main/java/ExampleConversationSendHSMLimitedTimeOfferTemplate.java @@ -0,0 +1,90 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.conversations.*; + +import java.util.ArrayList; +import java.util.List; + +public class ExampleConversationSendHSMLimitedTimeOfferTemplate { + + public static void main(String[] args) { + if (args.length < 6) { + System.out.println("Please at least specify your access key, the channel id and destination address.\n" + + "Usage : java -jar test_accesskey(Required) channel_id(Required) from(Required) destination(Required) templateName(Required) namespace(Required) expirationTimeInput(Required)"); + return; + } + + final String accessKey = args[0]; + final String from = args[1]; + final String destination = args[2]; + final String templateName = args[3]; + final String namespace = args[4]; + final String expirationTimeInput = args[5]; + + //First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(accessKey); + //Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + ConversationContent conversationContent = new ConversationContent(); + ConversationContentHsm conversationContentHsm = new ConversationContentHsm(); + conversationContentHsm.setNamespace(namespace); + conversationContentHsm.setTemplateName(templateName); + ConversationHsmLanguage language = new ConversationHsmLanguage(); + language.setCode("en"); + conversationContentHsm.setLanguage(language); + List messageComponents = new ArrayList<>(); + + // Add LTO component + MessageComponent messageLTOComponent = new MessageComponent(); + messageLTOComponent.setType(MessageComponentType.LIMITED_TIME_OFFER); + List messageLTOParams = new ArrayList<>(); + + MessageParam expirationTime = new MessageParam(); + expirationTime.setType(TemplateMediaType.EXPIRATION_TIME); + expirationTime.setExpirationTime(expirationTimeInput); + messageLTOParams.add(expirationTime); + + messageLTOComponent.setParameters(messageLTOParams); + + // Add body component + MessageComponent messageBodyComponent = new MessageComponent(); + messageBodyComponent.setType(MessageComponentType.BODY); + List messageBodyParams = new ArrayList<>(); + + MessageParam text = new MessageParam(); + text.setType(TemplateMediaType.TEXT); + text.setText("Bob"); + messageBodyParams.add(text); + + messageBodyComponent.setParameters(messageBodyParams); + + messageComponents.add(messageLTOComponent); + messageComponents.add(messageBodyComponent); + conversationContentHsm.setComponents(messageComponents); + conversationContent.setHsm(conversationContentHsm); + ConversationSendRequest request = new ConversationSendRequest( + destination, + ConversationContentType.HSM, + conversationContent, + from, + "", + null, + null, + null); + + try { + ConversationSendResponse sendResponse = messageBirdClient.sendMessage(request); + System.out.println(sendResponse.toString()); + + } catch (UnauthorizedException e) { + System.err.println("Authorization failed. Please check your access key: " + e.getMessage()); + } catch (GeneralException e) { + System.err.println("An error occurred while sending the message: " + e.getMessage()); + } + } + +} diff --git a/examples/src/main/java/ExampleConversationSendHSMMediaTemplate.java b/examples/src/main/java/ExampleConversationSendHSMMediaTemplate.java new file mode 100644 index 00000000..3f495aee --- /dev/null +++ b/examples/src/main/java/ExampleConversationSendHSMMediaTemplate.java @@ -0,0 +1,103 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.conversations.ConversationContent; +import com.messagebird.objects.conversations.ConversationContentHsm; +import com.messagebird.objects.conversations.ConversationContentType; +import com.messagebird.objects.conversations.ConversationHsmLanguage; +import com.messagebird.objects.conversations.ConversationSendRequest; +import com.messagebird.objects.conversations.ConversationSendResponse; +import com.messagebird.objects.conversations.Media; +import com.messagebird.objects.conversations.MessageComponent; +import com.messagebird.objects.conversations.MessageComponentType; +import com.messagebird.objects.conversations.MessageParam; +import com.messagebird.objects.conversations.TemplateMediaType; +import java.util.ArrayList; +import java.util.List; + +public class ExampleConversationSendHSMMediaTemplate { + + // Reference Example: https://developers.messagebird.com/quickstarts/whatsapp/send-media-template-message/ + public static void main(String[] args) { + + if (args.length < 4) { + System.out.println("Please at least specify your access key, the channel id and destination address.\n" + + "Usage : java -jar test_accesskey(Required) channel_id(Required) from(Required) to(Required)"); + return; + } + + //First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + //Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + + ConversationContent conversationContent = new ConversationContent(); + ConversationContentHsm conversationContentHsm = new ConversationContentHsm(); + conversationContentHsm.setNamespace("20332cd4_f095_b080_d255_35677159aaff"); + conversationContentHsm.setTemplateName("33172012024_ship_img_but_1"); + ConversationHsmLanguage language = new ConversationHsmLanguage(); + language.setCode("en"); + conversationContentHsm.setLanguage(language); + List messageComponents = new ArrayList<>(); + //Define header component with image + MessageComponent messageHeaderComponent = new MessageComponent(); + messageHeaderComponent.setType(MessageComponentType.HEADER); + MessageParam imageParam = new MessageParam(); + Media media = new Media(); + media.setUrl("https://i.ytimg.com/vi/3fDoOw4lIeU/maxresdefault.jpg"); + imageParam.setImage(media); + imageParam.setType(TemplateMediaType.IMAGE); + List messageHeaderParams = new ArrayList<>(); + messageHeaderParams.add(imageParam); + messageHeaderComponent.setParameters(messageHeaderParams); + //Define body component with texts + MessageComponent messageBodyComponent = new MessageComponent(); + messageBodyComponent.setType(MessageComponentType.BODY); + List messageBodyParams = new ArrayList<>(); + messageBodyComponent.setParameters(messageBodyParams); + MessageParam firstText = new MessageParam(); + firstText.setType(TemplateMediaType.TEXT); + firstText.setText("John"); + messageBodyParams.add(firstText); + + MessageParam secondText = new MessageParam(); + secondText.setType(TemplateMediaType.TEXT); + secondText.setText("MB93824"); + messageBodyParams.add(secondText); + + MessageParam thirdText = new MessageParam(); + thirdText.setType(TemplateMediaType.TEXT); + thirdText.setText("2 days"); + messageBodyParams.add(thirdText); + + MessageParam fourthText = new MessageParam(); + fourthText.setType(TemplateMediaType.TEXT); + fourthText.setText("MessageBird"); + messageBodyParams.add(fourthText); + + messageComponents.add(messageHeaderComponent); + messageComponents.add(messageBodyComponent); + conversationContentHsm.setComponents(messageComponents); + conversationContent.setHsm(conversationContentHsm); + ConversationSendRequest request = new ConversationSendRequest( + args[2], + ConversationContentType.HSM, + conversationContent, + args[1], + "", + null, + null, + null); + + try { + ConversationSendResponse sendResponse = messageBirdClient.sendMessage(request); + System.out.println(sendResponse.toString()); + + } catch (GeneralException | UnauthorizedException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleConversationSendHSMTemplateWithButtons.java b/examples/src/main/java/ExampleConversationSendHSMTemplateWithButtons.java new file mode 100644 index 00000000..40202a26 --- /dev/null +++ b/examples/src/main/java/ExampleConversationSendHSMTemplateWithButtons.java @@ -0,0 +1,63 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.conversations.*; + +import java.util.Collections; + +public class ExampleConversationSendHSMTemplateWithButtons { + + // Reference Example: https://developers.messagebird.com/quickstarts/whatsapp/send-message-with-buttons/ + public static void main(String[] args) { + + if (args.length < 4) { + System.out.println("Please at least specify your access key, the channel id and destination address.\n" + + "Usage : java -jar test_accesskey(Required) channel_id(Required) from(Required) to(Required)"); + return; + } + + //First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + //Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + ConversationContent conversationContent = new ConversationContent(); + ConversationContentHsm conversationContentHsm = new ConversationContentHsm(); + conversationContentHsm.setNamespace("20332cd4_f095_b080_d255_35677159aaff"); + conversationContentHsm.setTemplateName("33172012024_ship_img_but_1"); + ConversationHsmLanguage language = new ConversationHsmLanguage(); + language.setCode("en"); + conversationContentHsm.setLanguage(language); + + //Define component with button + MessageComponent messageButtonComponent = new MessageComponent(); + messageButtonComponent.setType(MessageComponentType.BUTTON); + messageButtonComponent.setSub_type("url"); + MessageParam textParam = new MessageParam(); + textParam.setType(TemplateMediaType.TEXT); + textParam.setText("23493282245"); + + messageButtonComponent.setParameters(Collections.singletonList(textParam)); + conversationContentHsm.setComponents(Collections.singletonList(messageButtonComponent)); + conversationContent.setHsm(conversationContentHsm); + ConversationSendRequest request = new ConversationSendRequest( + args[2], + ConversationContentType.HSM, + conversationContent, + args[1], + "", + null, + null, + null); + + try { + ConversationSendResponse sendResponse = messageBirdClient.sendMessage(request); + System.out.println(sendResponse.toString()); + + } catch (GeneralException | UnauthorizedException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleConversationSendMessage.java b/examples/src/main/java/ExampleConversationSendMessage.java new file mode 100644 index 00000000..01d91dc7 --- /dev/null +++ b/examples/src/main/java/ExampleConversationSendMessage.java @@ -0,0 +1,60 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.conversations.ConversationContent; +import com.messagebird.objects.conversations.ConversationContentType; +import com.messagebird.objects.conversations.ConversationFallbackOption; +import com.messagebird.objects.conversations.ConversationSendRequest; +import com.messagebird.objects.conversations.ConversationSendResponse; + +import java.util.HashMap; +import java.util.Map; + +public class ExampleConversationSendMessage { + + public static void main(String[] args) { + if (args.length < 3) { + System.out.println("Please at least specify your access key, the channel id and destination address.\n" + + "Usage : java -jar test_accesskey(Required) channel_id(Required) to(Required) fallback_channel_id(optional)"); + return; + } + + //First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + //Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + + + ConversationFallbackOption fallbackOption = null; + if (args.length == 4) { + fallbackOption = new ConversationFallbackOption(args[3], "5m"); + } + ConversationContent conversationContent = new ConversationContent(); + conversationContent.setText("Hello world from java sdk"); + + // Optional source parameter, that identifies the actor making the request. + Map source = new HashMap<>(); + source.put("Salesman", "Sir. John Doe"); + + ConversationSendRequest request = new ConversationSendRequest( + args[2], + ConversationContentType.TEXT, + conversationContent, + args[1], + "", + fallbackOption, + source, + null); + + try { + ConversationSendResponse sendResponse = messageBirdClient.sendMessage(request); + System.out.println(sendResponse.toString()); + + } catch (GeneralException | UnauthorizedException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleCreateAuthTemplate.java b/examples/src/main/java/ExampleCreateAuthTemplate.java new file mode 100644 index 00000000..31b5f415 --- /dev/null +++ b/examples/src/main/java/ExampleCreateAuthTemplate.java @@ -0,0 +1,69 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.integrations.*; + +import java.util.ArrayList; +import java.util.List; + +public class ExampleCreateAuthTemplate { + public static void main(String[] args) { + + if (args.length < 3) { + System.out.println("Please specify your access key and a template name and WABA ID example : java -jar test_accesskey \"My template name\" \"WABA ID\""); + return; + } + + // First create your service object + MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + /* body */ + HSMComponent bodyComponent = new HSMComponent(); + bodyComponent.setType(HSMComponentType.BODY); + bodyComponent.setAddSecurityRecommendation(true); + + /* footer */ + HSMComponent footerComponent = new HSMComponent(); + footerComponent.setType(HSMComponentType.FOOTER); + footerComponent.setCodeExpirationMinutes(8); + + /* button */ + HSMComponent buttonComponent = new HSMComponent(); + List buttons = new ArrayList<>(); + HSMComponentButton otpButton = new HSMComponentButton(); + otpButton.setOtpType(HSMOTPButtonType.ONE_TAP); + otpButton.setText("Copy code"); + otpButton.setAutofillText("Autofill"); + otpButton.setPackageName("com.example.luckyshrub"); + otpButton.setSignatureHash("K8a%2FAINcGX7"); + + buttons.add(otpButton); + buttonComponent.setType(HSMComponentType.BUTTONS); + buttonComponent.setButtons(buttons); + + /* set components */ + Template template = new Template(); + List components = new ArrayList<>(); + components.add(bodyComponent); + components.add(footerComponent); + components.add(buttonComponent); + + template.setName(args[1]); + template.setLanguage("en_US"); + template.setWABAID(args[2]); + template.setComponents(components); + template.setCategory(HSMCategory.AUTHENTICATION); + + try { + TemplateResponse response = messageBirdClient.createWhatsAppTemplate(template); + System.out.println(response.toString()); + } catch (GeneralException | UnauthorizedException | IllegalArgumentException exception) { + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleCreateCarouselTemplate.java b/examples/src/main/java/ExampleCreateCarouselTemplate.java new file mode 100644 index 00000000..54bea0dd --- /dev/null +++ b/examples/src/main/java/ExampleCreateCarouselTemplate.java @@ -0,0 +1,113 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.integrations.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Create template. + * + * @see Doc - Create template + * @author AlexL-mb + */ +public class ExampleCreateCarouselTemplate { + + public static void main(String[] args) { + if (args.length < 3) { + System.out.println("Please specify your access key and a template name and WABA ID example : java -jar test_accesskey \"My template name\" \"WABA ID\""); + return; + } + + // First create your service object + MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + /* body */ + HSMComponent bodyComponent = new HSMComponent(); + final HSMExample bodyExample = new HSMExample(); + final List> bodyText = new ArrayList<>(); + bodyText.add(Arrays.asList("John")); + bodyExample.setBody_text(bodyText); + bodyComponent.setType(HSMComponentType.BODY); + bodyComponent.setText("Hey {{1}}! This is a sample template from Java."); + bodyComponent.setExample(bodyExample); + + /* carousel */ + final HSMComponent carouselComponent = new HSMComponent(); + carouselComponent.setType(HSMComponentType.CAROUSEL); + + /* cards */ + final List cards = new ArrayList<>(); + /* card 0 */ + final HSMComponentCard card = new HSMComponentCard(); + final List cardComponents = new ArrayList<>(); + + /* card header */ + final HSMComponent headerComponent = new HSMComponent(); + final HSMExample headerExample = new HSMExample(); + headerExample.setHeader_url(Arrays.asList("https://images.freeimages.com/images/small-previews/c5a/colourful-paper-rip-1-1195879.jpg")); + + headerComponent.setType(HSMComponentType.HEADER); + headerComponent.setFormat(HSMComponentFormat.IMAGE); + headerComponent.setExample(headerExample); + cardComponents.add(headerComponent); + + /* card body */ + final HSMComponent cardBodyComponent = new HSMComponent(); + final HSMExample cardBodyExample = new HSMExample(); + final List> cardBodyText = new ArrayList<>(); + cardBodyText.add(Arrays.asList("John")); + cardBodyExample.setBody_text(cardBodyText); + + cardBodyComponent.setType(HSMComponentType.BODY); + cardBodyComponent.setText("Hey {{1}}! This is a sample template from Java."); + cardBodyComponent.setExample(cardBodyExample); + cardComponents.add(cardBodyComponent); + + /* card buttons */ + final HSMComponent buttonComponent = new HSMComponent(); + final List buttons = new ArrayList<>(); + final HSMComponentButton button = new HSMComponentButton(); + button.setType(HSMComponentButtonType.URL); + button.setText("Touch it"); + button.setUrl("https://www.messagebird.com"); + button.setExample(Arrays.asList("https://developers.messagebird.com")); + buttons.add(button); + buttonComponent.setType(HSMComponentType.BUTTONS); + buttonComponent.setButtons(buttons); + cardComponents.add(buttonComponent); + + card.setComponents(cardComponents); + + cards.add(card); + + carouselComponent.setCards(cards); + + /* set components */ + Template template = new Template(); + List components = new ArrayList<>(); + components.add(bodyComponent); + components.add(carouselComponent); + + template.setName(args[1]); + template.setLanguage("en_US"); + template.setWABAID(args[2]); + template.setComponents(components); + template.setCategory(HSMCategory.MARKETING); + + try { + TemplateResponse response = messageBirdClient.createWhatsAppTemplate(template); + System.out.println(response.toString()); + } catch (GeneralException | UnauthorizedException | IllegalArgumentException exception) { + exception.printStackTrace(); + } + + } +} diff --git a/examples/src/main/java/ExampleCreateChildAccount.java b/examples/src/main/java/ExampleCreateChildAccount.java new file mode 100644 index 00000000..2c71ae51 --- /dev/null +++ b/examples/src/main/java/ExampleCreateChildAccount.java @@ -0,0 +1,30 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.ChildAccountRequest; +import com.messagebird.objects.ChildAccountCreateResponse; + +public class ExampleCreateChildAccount { + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key and name of child account arguments"); + return; + } + + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + System.out.println("Creating a child account of partner accounts"); + final ChildAccountRequest childAccountRequest = new ChildAccountRequest(); + childAccountRequest.setName(args[1]); + ChildAccountCreateResponse childAccount = messageBirdClient.createChildAccount(childAccountRequest); + System.out.println("Child account is created: " + childAccount.toString()); + + } catch (GeneralException | UnauthorizedException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleCreateCouponTemplate.java b/examples/src/main/java/ExampleCreateCouponTemplate.java new file mode 100644 index 00000000..f5480434 --- /dev/null +++ b/examples/src/main/java/ExampleCreateCouponTemplate.java @@ -0,0 +1,81 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.integrations.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Create template. + * + * @see Doc - Create template + * @author AlexL-mb + */ +public class ExampleCreateCouponTemplate { + + public static void main(String[] args) { + if (args.length < 3) { + System.out.println("Please specify your access key and a template name and WABA ID example : java -jar test_accesskey \"My template name\" \"WABA ID\""); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + /* header */ + final HSMComponent headerComponent = new HSMComponent(); + headerComponent.setType(HSMComponentType.HEADER); + headerComponent.setFormat(HSMComponentFormat.TEXT); + headerComponent.setText("Our Fall Sale is on!"); + + /* body */ + final HSMComponent bodyComponent = new HSMComponent(); + final HSMExample bodyExample = new HSMExample(); + final List> bodyText = new ArrayList<>(); + bodyText.add(Arrays.asList("25OFF", "25%")); + bodyExample.setBody_text(bodyText); + + bodyComponent.setType(HSMComponentType.BODY); + bodyComponent.setText("Shop now through November and use code {{1}} to get {{2}} off of all merchandise!"); + bodyComponent.setExample(bodyExample); + + + /* button */ + final HSMComponent buttonComponent = new HSMComponent(); + final List buttons = new ArrayList<>(); + final HSMComponentButton button = new HSMComponentButton(); + button.setType(HSMComponentButtonType.COPY_CODE); + button.setExample(Arrays.asList("CODE25")); + buttons.add(button); + buttonComponent.setType(HSMComponentType.BUTTONS); + buttonComponent.setButtons(buttons); + + /* set components */ + final Template template = new Template(); + final List components = new ArrayList<>(); + components.add(headerComponent); + components.add(bodyComponent); + components.add(buttonComponent); + + template.setName(args[1]); + template.setLanguage("en_US"); + template.setWABAID(args[2]); + template.setComponents(components); + template.setCategory(HSMCategory.MARKETING); + template.setCtaURLLinkTrackingOptedOut(true); + + try { + TemplateResponse response = messageBirdClient.createWhatsAppTemplate(template); + System.out.println(response.toString()); + } catch (GeneralException | UnauthorizedException | IllegalArgumentException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleCreateLimitedTimeOfferTemplate.java b/examples/src/main/java/ExampleCreateLimitedTimeOfferTemplate.java new file mode 100644 index 00000000..dc721f9f --- /dev/null +++ b/examples/src/main/java/ExampleCreateLimitedTimeOfferTemplate.java @@ -0,0 +1,95 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.integrations.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ExampleCreateLimitedTimeOfferTemplate { + + public static void main(String[] args) { + if (args.length < 4) { + System.out.println("Please specify your access key and a template name and WABA ID example : java -jar test_accesskey(Required) templateName(Required) wabaID(required)"); + return; + } + + final String accessKey = args[0]; + final String templateName = args[1]; + final String wabaID = args[2]; + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(accessKey); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + // header + final HSMComponent headerComponent = new HSMComponent(); + final HSMExample headerExample = new HSMExample(); + headerExample.setHeader_url(Arrays.asList("https://images.freeimages.com/images/small-previews/c5a/colourful-paper-rip-1-1195879.jpg")); + + headerComponent.setType(HSMComponentType.HEADER); + headerComponent.setFormat(HSMComponentFormat.IMAGE); + headerComponent.setExample(headerExample); + + // limited time offer + final HSMComponent ltoComponent = new HSMComponent(); + ltoComponent.setType(HSMComponentType.LIMITED_TIME_OFFER); + ltoComponent.setText("Expiring offer!"); + ltoComponent.setHasExpiration(true); + + // body + final HSMComponent bodyComponent = new HSMComponent(); + final HSMExample bodyExample = new HSMExample(); + final List> bodyText = new ArrayList<>(); + bodyText.add(Arrays.asList("John", "CARIBE25")); + bodyExample.setBody_text(bodyText); + + bodyComponent.setType(HSMComponentType.BODY); + bodyComponent.setText("Good news, {{1}}! Use code {{2}} to get 0% off all packages!"); + bodyComponent.setExample(bodyExample); + + // buttons + final HSMComponent buttonComponent = new HSMComponent(); + final List buttons = new ArrayList<>(); + + final HSMComponentButton buttonCopyCode = new HSMComponentButton(); + buttonCopyCode.setType(HSMComponentButtonType.COPY_CODE); + buttonCopyCode.setExample(Arrays.asList("CARIBE25")); + + final HSMComponentButton buttonBookNow = new HSMComponentButton(); + buttonBookNow.setType(HSMComponentButtonType.URL); + buttonBookNow.setText("Book now!"); + buttonBookNow.setUrl("https://www.bird.com?code={{1}}"); + buttonBookNow.setExample(Arrays.asList("https://www.bird.com?code=CARIBE25")); + + buttons.addAll(Arrays.asList(buttonCopyCode, buttonBookNow)); + buttonComponent.setType(HSMComponentType.BUTTONS); + buttonComponent.setButtons(buttons); + + // set components + final Template template = new Template(); + final List components = new ArrayList<>(); + components.addAll(Arrays.asList(headerComponent, ltoComponent, bodyComponent, buttonComponent)); + + template.setName(templateName); + template.setLanguage("en_US"); + template.setWABAID(wabaID); + template.setComponents(components); + template.setCategory(HSMCategory.MARKETING); + + try { + TemplateResponse response = messageBirdClient.createWhatsAppTemplate(template); + System.out.println(response.toString()); + } catch (UnauthorizedException e) { + System.err.println("Authorization failed. Please check your access key: " + e.getMessage()); + } catch (GeneralException e) { + System.err.println("An error occurred while sending the message: " + e.getMessage()); + } + } + +} diff --git a/examples/src/main/java/ExampleCreateTemplate.java b/examples/src/main/java/ExampleCreateTemplate.java new file mode 100644 index 00000000..336340a6 --- /dev/null +++ b/examples/src/main/java/ExampleCreateTemplate.java @@ -0,0 +1,98 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.integrations.HSMCategory; +import com.messagebird.objects.integrations.HSMComponent; +import com.messagebird.objects.integrations.HSMComponentButton; +import com.messagebird.objects.integrations.HSMComponentButtonType; +import com.messagebird.objects.integrations.HSMComponentFormat; +import com.messagebird.objects.integrations.HSMComponentType; +import com.messagebird.objects.integrations.HSMExample; +import com.messagebird.objects.integrations.Template; +import com.messagebird.objects.integrations.TemplateResponse; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Create template. + * + * @see Doc - Create template + * @author ssk910 + */ +public class ExampleCreateTemplate { + + public static void main(String[] args) { + if (args.length < 3) { + System.out.println("Please specify your access key and a template name and WABA ID example : java -jar test_accesskey \"My template name\" \"WABA ID\""); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + /* header */ + final HSMComponent headerComponent = new HSMComponent(); + final HSMExample headerExample = new HSMExample(); + headerExample.setHeader_url(Arrays.asList("https://images.freeimages.com/images/small-previews/c5a/colourful-paper-rip-1-1195879.jpg")); + + headerComponent.setType(HSMComponentType.HEADER); + headerComponent.setFormat(HSMComponentFormat.IMAGE); + headerComponent.setExample(headerExample); + + /* body */ + final HSMComponent bodyComponent = new HSMComponent(); + final HSMExample bodyExample = new HSMExample(); + final List> bodyText = new ArrayList<>(); + bodyText.add(Arrays.asList("John")); + bodyText.add(Arrays.asList("Anna")); + bodyExample.setBody_text(bodyText); + + bodyComponent.setType(HSMComponentType.BODY); + bodyComponent.setText("Hey {{1}}! This is a sample template from Java."); + bodyComponent.setExample(bodyExample); + + /* footer */ + final HSMComponent footerComponent = new HSMComponent(); + footerComponent.setType(HSMComponentType.FOOTER); + footerComponent.setText("This is a sample footer"); + + /* button */ + final HSMComponent buttonComponent = new HSMComponent(); + final List buttons = new ArrayList<>(); + final HSMComponentButton button = new HSMComponentButton(); + button.setType(HSMComponentButtonType.URL); + button.setText("Touch it"); + button.setUrl("https://www.messagebird.com"); + button.setExample(Arrays.asList("https://developers.messagebird.com")); + buttons.add(button); + buttonComponent.setType(HSMComponentType.BUTTONS); + buttonComponent.setButtons(buttons); + + /* set components */ + final Template template = new Template(); + final List components = new ArrayList<>(); + components.add(headerComponent); + components.add(bodyComponent); + components.add(footerComponent); + components.add(buttonComponent); + + template.setName(args[1]); + template.setLanguage("en_US"); + template.setWABAID(args[2]); + template.setComponents(components); + template.setCategory(HSMCategory.AUTHENTICATION); + + try { + TemplateResponse response = messageBirdClient.createWhatsAppTemplate(template); + System.out.println(response.toString()); + } catch (GeneralException | UnauthorizedException | IllegalArgumentException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleCreateVoiceCallFlow.java b/examples/src/main/java/ExampleCreateVoiceCallFlow.java new file mode 100644 index 00000000..83f265b1 --- /dev/null +++ b/examples/src/main/java/ExampleCreateVoiceCallFlow.java @@ -0,0 +1,42 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.voicecalls.VoiceCallFlowRequest; +import com.messagebird.objects.VoiceStep; + +import java.util.Collections; + +public class ExampleCreateVoiceCallFlow { + + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key and voice call flow arguments"); + return; + } + + //First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + //Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + final VoiceCallFlowRequest voiceCallFlowRequest = new VoiceCallFlowRequest(); + + voiceCallFlowRequest.setRecord(true); // Can be false as well, see docs + VoiceStep voiceStep = new VoiceStep(); + voiceCallFlowRequest.setSteps(Collections.singletonList(voiceStep)); // VoiceStep Object + voiceCallFlowRequest.setDefaultCall(true); // Can be false as well, see docs + + try { + //Creating voice call by id + System.out.println("Creting a Voice Call Flow"); + messageBirdClient.sendVoiceCallFlow(voiceCallFlowRequest); + System.out.println("Voice call flow created"); + + } catch (GeneralException | UnauthorizedException exception) { + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleDeleteChildAccount.java b/examples/src/main/java/ExampleDeleteChildAccount.java new file mode 100644 index 00000000..2724a59a --- /dev/null +++ b/examples/src/main/java/ExampleDeleteChildAccount.java @@ -0,0 +1,27 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; + +public class ExampleDeleteChildAccount { + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key and id of child account arguments"); + return; + } + + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + System.out.println("Deleting a child account of partner accounts"); + messageBirdClient.deleteChildAccount(args[1]); + System.out.println("Child account is deleted"); + + } catch (GeneralException | UnauthorizedException | NotFoundException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleVerifyToken.java b/examples/src/main/java/ExampleDeleteRecording.java similarity index 59% rename from examples/src/main/java/ExampleVerifyToken.java rename to examples/src/main/java/ExampleDeleteRecording.java index 41eb28f8..39ff9dc6 100644 --- a/examples/src/main/java/ExampleVerifyToken.java +++ b/examples/src/main/java/ExampleDeleteRecording.java @@ -4,16 +4,12 @@ import com.messagebird.exceptions.GeneralException; import com.messagebird.exceptions.NotFoundException; import com.messagebird.exceptions.UnauthorizedException; -import com.messagebird.objects.Verify; - -/** - * Created by faizan on 10/12/15. - */ -public class ExampleVerifyToken { +public class ExampleDeleteRecording { public static void main(String[] args) { + if (args.length < 3) { - System.out.println("Please specify your access key, verifyId and a token : java -jar test_accessKey verifyId token"); + System.out.println("Please specify your access key and a call_id, leg_id, and recording_id to delete: java -jar " ); return; } @@ -23,17 +19,12 @@ public static void main(String[] args) { // Add the service to the client final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); - try { - // Send verify token - final String verifyId = args[1]; - final String token = args[2]; + // Deleting message by id + System.out.println("Delete recording:"); + messageBirdClient.deleteRecording(args[1],args[2],args[3]); + System.out.println("Recording ID ["+args[3]+"] deleted."); - System.out.println("verifying token request: " + token); - //Sending token to verify - final Verify verify = messageBirdClient.verifyToken(verifyId, token); - //Display result - System.out.println(verify.toString()); } catch (UnauthorizedException | GeneralException | NotFoundException exception) { if (exception.getErrors() != null) { System.out.println(exception.getErrors().toString()); diff --git a/examples/src/main/java/ExampleDeleteTemplatesByName.java b/examples/src/main/java/ExampleDeleteTemplatesByName.java new file mode 100644 index 00000000..c8d529f3 --- /dev/null +++ b/examples/src/main/java/ExampleDeleteTemplatesByName.java @@ -0,0 +1,39 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; + +/** + * List templates by name + * + * @see List templates by name + * @author ssk910 + */ +public class ExampleDeleteTemplatesByName { + + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key and a template name example : java -jar test_accesskey \"My template name\""); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + // template name from input + final String templateName = args[1]; + + try { + System.out.println("Deleting WhatsApp Templates by name : " + templateName); + messageBirdClient.deleteTemplatesBy(templateName); + System.out.println("Template [" + templateName + "] deleted."); + } catch (GeneralException | UnauthorizedException | NotFoundException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleDeleteVoiceCall.java b/examples/src/main/java/ExampleDeleteVoiceCall.java index 65983ff8..bbdb13ea 100644 --- a/examples/src/main/java/ExampleDeleteVoiceCall.java +++ b/examples/src/main/java/ExampleDeleteVoiceCall.java @@ -2,8 +2,8 @@ import com.messagebird.MessageBirdService; import com.messagebird.MessageBirdServiceImpl; import com.messagebird.exceptions.GeneralException; -import com.messagebird.exceptions.NotFoundException; import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.exceptions.NotFoundException; public class ExampleDeleteVoiceCall { diff --git a/examples/src/main/java/ExampleDeleteVoiceCallFlow.java b/examples/src/main/java/ExampleDeleteVoiceCallFlow.java new file mode 100644 index 00000000..af5e3be8 --- /dev/null +++ b/examples/src/main/java/ExampleDeleteVoiceCallFlow.java @@ -0,0 +1,32 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.exceptions.NotFoundException; + +public class ExampleDeleteVoiceCallFlow { + + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key and a voice call flow ID"); + return; + } + + //First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + //Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + //Deleting voice call by id + System.out.println("Deleting a Voice Call Flow"); + messageBirdClient.deleteVoiceCallFlow(args[1]); + System.out.println("Voice call flow deleted "); + + } catch (GeneralException | NotFoundException | UnauthorizedException exception) { + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleDownloadRecording.java b/examples/src/main/java/ExampleDownloadRecording.java new file mode 100644 index 00000000..b880967a --- /dev/null +++ b/examples/src/main/java/ExampleDownloadRecording.java @@ -0,0 +1,41 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; + +public class ExampleDownloadRecording { + public static void main(String[] args) { + if (args.length < 4) { + System.out.println("Please specify your access key and call id and leg id and recording id and base path(optional) example :" + + " java -jar test_accesskey e8077d803532c0b5937c639b60216938 e8077d803532c0b5937c639b60216938 e8077d803532c0b5937c639b60216938 /users/{user}/test"); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + System.out.println("Getting a recording"); + final String callId = args[1]; + final String legId = args[2]; + final String recordingId = args[3]; + String basePath = null; + if (args.length > 4) { + basePath = args[4]; + } + //Sending call id and leg id and recording id parameters to client + final String filePath = messageBirdClient.downloadRecording(callId, legId, recordingId, basePath); + if (filePath != null) { + System.out.println("Record file is downloaded to "+filePath); + } + + } catch (GeneralException | NotFoundException | UnauthorizedException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleDownloadTranscription.java b/examples/src/main/java/ExampleDownloadTranscription.java new file mode 100644 index 00000000..ec672fbf --- /dev/null +++ b/examples/src/main/java/ExampleDownloadTranscription.java @@ -0,0 +1,43 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; + +public class ExampleDownloadTranscription { + public static void main(String[] args) { + if (args.length < 5) { + System.out.println("Please specify your access key and call id and leg id and recording id and transcription Id and basePath(optional) example :" + + " java -jar test_accesskey e8077d803532c0b5937c639b60216938 e8077d803532c0b5937c639b60216938 e8077d803532c0b5937c639b60216938" + + " e8077d803532c0b5937c639b60216938 /users/{user}/test"); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + System.out.println("Getting a transcription file"); + final String callId = args[1]; + final String legId = args[2]; + final String recordingId = args[3]; + final String transcriptionId = args[4]; + String basePath = null; + if (args.length > 5) { + basePath = args[5]; + } + //Sending call id and leg id and recording and transcription id parameters to client + final String filePath = messageBirdClient.downloadTranscription(callId, legId, recordingId, transcriptionId, basePath); + if (filePath != null) { + System.out.println("Transcription file is downloaded to "+filePath); + } + + } catch (GeneralException | NotFoundException | UnauthorizedException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleFetchTemplateByNameAndLanguage.java b/examples/src/main/java/ExampleFetchTemplateByNameAndLanguage.java new file mode 100644 index 00000000..c28d124c --- /dev/null +++ b/examples/src/main/java/ExampleFetchTemplateByNameAndLanguage.java @@ -0,0 +1,41 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.integrations.TemplateResponse; + +/** + * Fetch template by name and language + * + * @see Fetch template by name and language + * @author ssk910 + */ +public class ExampleFetchTemplateByNameAndLanguage { + + public static void main(String[] args) { + if (args.length < 3) { + System.out.println("Please specify your access key and a template name example : java -jar test_accesskey \"My template name\" \"Template language\""); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + // template name and language from input + final String templateName = args[1]; + final String language = args[2]; + + try { + System.out.println("Fetching WhatsApp Template list by {name: " + templateName + ", language: " + language + "}"); + final TemplateResponse template = messageBirdClient.fetchWhatsAppTemplateBy(templateName, language); + System.out.println(template.toString()); + } catch (GeneralException | UnauthorizedException | NotFoundException | IllegalArgumentException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleFetchTemplateByNameAndLanguageAndWABAID.java b/examples/src/main/java/ExampleFetchTemplateByNameAndLanguageAndWABAID.java new file mode 100644 index 00000000..deb12bf8 --- /dev/null +++ b/examples/src/main/java/ExampleFetchTemplateByNameAndLanguageAndWABAID.java @@ -0,0 +1,42 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.integrations.TemplateResponse; + +/** + * Fetch template by name and language by WABA ID + * + * @see Fetch template by name and language by WABA ID + * @author ssk910 + */ +public class ExampleFetchTemplateByNameAndLanguageAndWABAID { + + public static void main(String[] args) { + if (args.length < 4) { + System.out.println("Please specify your access key, template name, language and WABA ID example : java -jar test_accesskey \"My template name\" \"Template language\" \"WABA ID\""); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + // template name, language and waba ID from input + final String templateName = args[1]; + final String language = args[2]; + final String wabaID = args[3]; + + try { + System.out.println("Fetching WhatsApp Template list by {name: " + templateName + ", language: " + language + ", wabaID: " + wabaID + "}"); + final TemplateResponse template = messageBirdClient.fetchWhatsAppTemplateBy(templateName, language, wabaID, null); + System.out.println(template.toString()); + } catch (GeneralException | UnauthorizedException | NotFoundException | IllegalArgumentException exception) { + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleFetchTemplateByNameAndLanguageForChannelID.java b/examples/src/main/java/ExampleFetchTemplateByNameAndLanguageForChannelID.java new file mode 100644 index 00000000..639370e2 --- /dev/null +++ b/examples/src/main/java/ExampleFetchTemplateByNameAndLanguageForChannelID.java @@ -0,0 +1,43 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.integrations.TemplateResponse; + +/** + * Fetch template by name and language for Channel ID + * + * @see Fetch template by name and language for Channel ID + * @author ssk910 + */ +public class ExampleFetchTemplateByNameAndLanguageForChannelID { + + public static void main(String[] args) { + if (args.length < 4) { + System.out.println("Please specify your access key, template name, language and Channel ID example : java -jar test_accesskey \"My template name\" \"Template language\" \"Channel ID\""); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + // template name, language and the channel ID from input + final String templateName = args[1]; + final String language = args[2]; + final String channelID = args[3]; + + // Will return a template only if the channel belongs to the same WABA that the template belongs to. + try { + System.out.println("Fetching WhatsApp Template list by {name: " + templateName + ", language: " + language + ", channelID: " + channelID + "}"); + final TemplateResponse template = messageBirdClient.fetchWhatsAppTemplateBy(templateName, language, null, channelID); + System.out.println(template.toString()); + } catch (GeneralException | UnauthorizedException | NotFoundException | IllegalArgumentException exception) { + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleGetChildAccountById.java b/examples/src/main/java/ExampleGetChildAccountById.java new file mode 100644 index 00000000..2e42fd6e --- /dev/null +++ b/examples/src/main/java/ExampleGetChildAccountById.java @@ -0,0 +1,27 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.ChildAccountDetailedResponse; + +public class ExampleGetChildAccountById { + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key and id of child account arguments"); + return; + } + + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + System.out.println("Get a child account by id"); + ChildAccountDetailedResponse response = messageBirdClient.getChildAccountById(args[1]); + System.out.println("Response: " + response.toString()); + } catch (GeneralException | UnauthorizedException | NotFoundException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleGetChildAccounts.java b/examples/src/main/java/ExampleGetChildAccounts.java new file mode 100644 index 00000000..79dd17cb --- /dev/null +++ b/examples/src/main/java/ExampleGetChildAccounts.java @@ -0,0 +1,28 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.ChildAccountResponse; + +import java.util.List; + +public class ExampleGetChildAccounts { + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key, offset, limit arguments"); + return; + } + + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + System.out.println("Get a child accounts of partner account"); + List response = messageBirdClient.getChildAccounts(Integer.valueOf(args[1]), Integer.valueOf(args[2])); + System.out.println("response: " + response); + } catch (GeneralException | UnauthorizedException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleGetOutboundSmsPrices.java b/examples/src/main/java/ExampleGetOutboundSmsPrices.java new file mode 100644 index 00000000..e90d5151 --- /dev/null +++ b/examples/src/main/java/ExampleGetOutboundSmsPrices.java @@ -0,0 +1,33 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.MessageBirdException; +import com.messagebird.objects.OutboundSmsPriceResponse; + +public class ExampleGetOutboundSmsPrices { + + public static void main(String[] args) { + if (args.length != 1 && args.length != 2) { + System.out.println("Please specify your access key and (optionally) SMPP username"); + return; + } + + final MessageBirdClient messageBirdClient = new MessageBirdClient(new MessageBirdServiceImpl(args[0])); + + try { + System.out.println("Get a list of outbound SMS prices"); + + final OutboundSmsPriceResponse outboundSmsPriceResponse; + + if (args.length == 2) { + final String smppUsername = args[1]; + outboundSmsPriceResponse = messageBirdClient.getOutboundSmsPrices(smppUsername); + } else { + outboundSmsPriceResponse = messageBirdClient.getOutboundSmsPrices(); + } + + System.out.println("response: " + outboundSmsPriceResponse); + } catch (MessageBirdException e) { + e.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleListConversationMessagesWithQueryParam.java b/examples/src/main/java/ExampleListConversationMessagesWithQueryParam.java new file mode 100644 index 00000000..5ab13cf5 --- /dev/null +++ b/examples/src/main/java/ExampleListConversationMessagesWithQueryParam.java @@ -0,0 +1,43 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.conversations.ConversationMessageList; +import java.util.HashMap; + +public class ExampleListConversationMessagesWithQueryParam { + public static void main(String[] args) { + + if (args.length == 0) { + System.out.println("Please specify your access key example : java -jar test_accesskey"); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + // Get list of conversation messages with query param + System.out.println("Retrieving message list"); + final ConversationMessageList conversationMessageList = messageBirdClient.listConversationMessagesWithQueryParam( + new HashMap() { + { + put("ids", "9f0b413e79e24d76b01b895381b12a6d,d46054ee0f7245bcbc7ba586878d0ab4"); + } + }); + + // Display conversation Message list + System.out.println(conversationMessageList.toString()); + } catch (UnauthorizedException | GeneralException | NotFoundException exception) { + if (exception.getErrors() != null) { + System.out.println(exception.getErrors().toString()); + } + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleListMessagesFiltered.java b/examples/src/main/java/ExampleListMessagesFiltered.java new file mode 100644 index 00000000..4a6c94ea --- /dev/null +++ b/examples/src/main/java/ExampleListMessagesFiltered.java @@ -0,0 +1,44 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.MessageList; +import java.util.LinkedHashMap; +import java.util.Map; + + +public class ExampleListMessagesFiltered { + public static void main(String[] args) { + + if (args.length == 0) { + System.out.println("Please specify your access key example : java -jar test_accesskey"); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + // Get list of messages with offset and limit + System.out.println("Retrieving message list"); + + // Create filters + Map filters = new LinkedHashMap<>(); + filters.put("status", "scheduled"); + + final MessageList messageList = messageBirdClient.listMessagesFiltered(3, null, filters); + + // Display messages + System.out.println(messageList.toString()); + } catch (UnauthorizedException | GeneralException exception) { + if (exception.getErrors() != null) { + System.out.println(exception.getErrors().toString()); + } + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleListNumbersForPurchase.java b/examples/src/main/java/ExampleListNumbersForPurchase.java new file mode 100644 index 00000000..6c289b5a --- /dev/null +++ b/examples/src/main/java/ExampleListNumbersForPurchase.java @@ -0,0 +1,44 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.PhoneNumberFeature; +import com.messagebird.objects.PhoneNumberType; +import com.messagebird.objects.PhoneNumberSearchPattern; +import com.messagebird.objects.PhoneNumbersLookup; + +public class ExampleListNumbersForPurchase { + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key and a country code to test."); + return; + } + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + if (args.length > 2) { + PhoneNumbersLookup options = new PhoneNumbersLookup(); + options.setFeatures(PhoneNumberFeature.VOICE, PhoneNumberFeature.SMS); + options.setType(PhoneNumberType.MOBILE); + options.setLimit(10); + options.setNumber(562); + options.setSearchPattern(PhoneNumberSearchPattern.START); + System.out.print(options.toString()); + System.out.println(String.format("Request Made With Params: %s", messageBirdClient.listNumbersForPurchase("US", options))); + } else { + System.out.println(String.format("Request Made Without Params: %s", messageBirdClient.listNumbersForPurchase(args[1]))); + } + } catch (UnauthorizedException | GeneralException | NotFoundException exception) { + if (exception.getErrors() != null) { + System.out.println(exception.getErrors().toString()); + } + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleListOfRecording.java b/examples/src/main/java/ExampleListOfRecording.java new file mode 100644 index 00000000..ed1fa5a0 --- /dev/null +++ b/examples/src/main/java/ExampleListOfRecording.java @@ -0,0 +1,42 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.voicecalls.RecordingResponse; + +public class ExampleListOfRecording { + public static void main(String[] args) { + if (args.length < 3) { + System.out.println("Please specify your access key and call id and leg id example :" + + " java -jar test_accesskey e8077d803532c0b5937c639b60216938 e8077d803532c0b5937c639b60216938"); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + System.out.println("Getting a recording"); + final String callId = args[1]; + final String legId = args[2]; + //Sending call id and leg id and recording id parameters to client + final RecordingResponse recordings = messageBirdClient.listRecordings(callId, legId, 0, 0); + if (recordings.getData() == null) { + System.out.println("No record data found"); + } + //Display recording responses + for(int i = 0; i< recordings.getData().size(); i++) { + System.out.println(recordings.getData().get(i).toString()); + System.out.println(); + } + + } catch (GeneralException | UnauthorizedException exception) { + exception.printStackTrace(); + } + + } +} diff --git a/examples/src/main/java/ExampleListPurchasedNumbers.java b/examples/src/main/java/ExampleListPurchasedNumbers.java new file mode 100644 index 00000000..a065ba88 --- /dev/null +++ b/examples/src/main/java/ExampleListPurchasedNumbers.java @@ -0,0 +1,34 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.PhoneNumberFeature; +import com.messagebird.objects.PurchasedNumbersFilter; + +public class ExampleListPurchasedNumbers { + public static void main(String[] args) { + if (args.length < 1) { + System.out.println("Please specify your access key."); + return; + } + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + PurchasedNumbersFilter filter = new PurchasedNumbersFilter(); + filter.setLimit(25); + + try { + System.out.println(messageBirdClient.listPurchasedNumbers(filter)); + } catch (UnauthorizedException | NotFoundException | GeneralException exception) { + if (exception.getErrors() != null) { + System.out.println(exception.getErrors().toString()); + } + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleListTemplates.java b/examples/src/main/java/ExampleListTemplates.java new file mode 100644 index 00000000..b91b2ec2 --- /dev/null +++ b/examples/src/main/java/ExampleListTemplates.java @@ -0,0 +1,36 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.integrations.TemplateList; + +/** + * List templates + * + * @see List templates + * @author ssk910 + */ +public class ExampleListTemplates { + + public static void main(String[] args) { + if (args.length == 0) { + System.out.println("Please specify your access key example : java -jar test_accesskey"); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + System.out.println("Retrieving WhatsApp Template list"); + final TemplateList templateList = messageBirdClient.listWhatsAppTemplates(); + System.out.println(templateList.toString()); + } catch (GeneralException | UnauthorizedException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleListTemplatesByName.java b/examples/src/main/java/ExampleListTemplatesByName.java new file mode 100644 index 00000000..33241ca6 --- /dev/null +++ b/examples/src/main/java/ExampleListTemplatesByName.java @@ -0,0 +1,41 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.integrations.TemplateResponse; +import java.util.List; + +/** + * List templates by name + * + * @see List templates by name + * @author ssk910 + */ +public class ExampleListTemplatesByName { + + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key and a template name example : java -jar test_accesskey \"My template name\""); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + // template name from input + final String templateName = args[1]; + + try { + System.out.println("Retrieving WhatsApp Template list by name : " + templateName); + final List templateList = messageBirdClient.getWhatsAppTemplatesBy(templateName); + System.out.println(templateList.toString()); + } catch (GeneralException | UnauthorizedException | NotFoundException | IllegalArgumentException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleListTemplatesByNameAndForChannelID.java b/examples/src/main/java/ExampleListTemplatesByNameAndForChannelID.java new file mode 100644 index 00000000..02f2377b --- /dev/null +++ b/examples/src/main/java/ExampleListTemplatesByNameAndForChannelID.java @@ -0,0 +1,45 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.integrations.TemplateResponse; +import java.util.List; + +/** + * List templates by name and for channel ID + * + * @see List templates by name and for channel ID + * @author ssk910 + */ +public class ExampleListTemplatesByNameAndForChannelID { + + public static void main(String[] args) { + if (args.length < 3) { + System.out.println("Please specify your access key, template name and channel ID example : java -jar test_accesskey \"My template name\" \"Channel ID\""); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + // template name from input + final String templateName = args[1]; + + // Channel ID from input + final String channelID = args[2]; + + // Will return templates only if the channel belongs to the same WABA that the templates belongs to. + try { + System.out.println("Retrieving WhatsApp Template list by name '" + templateName + "' and channel ID '" + channelID + "'"); + final List templateList = messageBirdClient.getWhatsAppTemplatesBy(templateName, null, channelID); + System.out.println(templateList.toString()); + } catch (GeneralException | UnauthorizedException | NotFoundException | IllegalArgumentException exception) { + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleListTemplatesByNameAndWABAID.java b/examples/src/main/java/ExampleListTemplatesByNameAndWABAID.java new file mode 100644 index 00000000..6bb8fd3f --- /dev/null +++ b/examples/src/main/java/ExampleListTemplatesByNameAndWABAID.java @@ -0,0 +1,44 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.integrations.TemplateResponse; +import java.util.List; + +/** + * List templates by name and WABA ID + * + * @see List templates by name and WABA ID + * @author ssk910 + */ +public class ExampleListTemplatesByNameAndWABAID { + + public static void main(String[] args) { + if (args.length < 3) { + System.out.println("Please specify your access key, template name and WABA ID example : java -jar test_accesskey \"My template name\" \"WABA ID\""); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + // template name from input + final String templateName = args[1]; + + // WABA ID from input + final String wabaID = args[2]; + + try { + System.out.println("Retrieving WhatsApp Template list by name '" + templateName + "' and WABA ID '" + wabaID + "'"); + final List templateList = messageBirdClient.getWhatsAppTemplatesBy(templateName, wabaID, null); + System.out.println(templateList.toString()); + } catch (GeneralException | UnauthorizedException | NotFoundException | IllegalArgumentException exception) { + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleListTemplatesByWABAID.java b/examples/src/main/java/ExampleListTemplatesByWABAID.java new file mode 100644 index 00000000..a11ed37f --- /dev/null +++ b/examples/src/main/java/ExampleListTemplatesByWABAID.java @@ -0,0 +1,36 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.integrations.TemplateList; + +/** + * List templates by WABA ID + * + * @see List templates by WABA ID + * @author ssk910 + */ +public class ExampleListTemplatesByWABAID { + + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key and WABA ID example : java -jar test_accesskey \"WABA ID\""); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + System.out.println("Retrieving WhatsApp Template list by WABA"); + final TemplateList templateList = messageBirdClient.listWhatsAppTemplates(0, 25, args[1], null); + System.out.println(templateList.toString()); + } catch (GeneralException | UnauthorizedException | IllegalArgumentException exception) { + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleListTemplatesForChannelID.java b/examples/src/main/java/ExampleListTemplatesForChannelID.java new file mode 100644 index 00000000..4a2bdda9 --- /dev/null +++ b/examples/src/main/java/ExampleListTemplatesForChannelID.java @@ -0,0 +1,36 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.integrations.TemplateList; + +/** + * List templates for Channel ID + * + * @see List templates for Channel ID + * @author ssk910 + */ +public class ExampleListTemplatesForChannelID { + + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key and channel ID example : java -jar test_accesskey"); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + System.out.println("Retrieving WhatsApp Template list for a channel"); + final TemplateList templateList = messageBirdClient.listWhatsAppTemplates(0, 25, null, args[1]); + System.out.println(templateList.toString()); + } catch (GeneralException | UnauthorizedException | IllegalArgumentException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleListTranscriptions.java b/examples/src/main/java/ExampleListTranscriptions.java new file mode 100644 index 00000000..9aee955b --- /dev/null +++ b/examples/src/main/java/ExampleListTranscriptions.java @@ -0,0 +1,44 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.voicecalls.Transcription; +import com.messagebird.objects.voicecalls.TranscriptionResponse; + +public class ExampleListTranscriptions { + public static void main(String[] args) { + if (args.length < 6) { + System.out.println("Please specify your access key and call id and leg id and recording id and page and page size example :" + + " java -jar test_accesskey e8077d803532c0b5937c639b60216938 e8077d803532c0b5937c639b60216938 e8077d803532c0b5937c639b60216938 1 10"); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + System.out.println("Getting transcriptions"); + final String callId = args[1]; + final String legId = args[2]; + final String recordingId = args[3]; + final Integer page = Integer.valueOf(args[4]); + final Integer pageSize = Integer.valueOf(args[5]); + TranscriptionResponse transcriptions = messageBirdClient.listTranscriptions(callId, legId, recordingId, page, pageSize); + if(transcriptions.getData() == null) { + System.out.println("no transcriptions found"); + return; + } + for (Transcription transcription: transcriptions.getData()) { + System.out.println(transcription.toString()); + System.out.println(); + } + + } catch (GeneralException | UnauthorizedException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleListVoiceCallFlow.java b/examples/src/main/java/ExampleListVoiceCallFlow.java new file mode 100644 index 00000000..12ed0982 --- /dev/null +++ b/examples/src/main/java/ExampleListVoiceCallFlow.java @@ -0,0 +1,33 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.voicecalls.VoiceCallFlowList; + +public class ExampleListVoiceCallFlow { + + public static void main(String[] args) { + if (args.length < 1) { + System.out.println("Please specify your access key, example : java -jar test_accesskey"); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the cligient + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + // Get list of call flows with offset and limit + System.out.println("Retrieving call flows list"); + VoiceCallFlowList voiceCallFlowList = messageBirdClient.listVoiceCallFlows(0, 0); + + // Display balance + System.out.println(voiceCallFlowList.toString()); + } catch (UnauthorizedException | GeneralException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExamplePurchaseNumber.java b/examples/src/main/java/ExamplePurchaseNumber.java new file mode 100644 index 00000000..3c71017c --- /dev/null +++ b/examples/src/main/java/ExamplePurchaseNumber.java @@ -0,0 +1,31 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.PurchasedNumberCreatedResponse; + +public class ExamplePurchaseNumber { + public static void main(String[] args) { + if (args.length < 4) { + System.out.println("Please specify your access key, number, country code and billing interval months, eg: ExamplePurchaseNumber test_accesskey 3197010240563 NL 1"); + return; + } + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0], "https://numbers.messagebird.com"); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + PurchasedNumberCreatedResponse purchasedNumberCreatedResponse = messageBirdClient.purchaseNumber(args[1], args[2], Integer.parseInt(args[3])); + + System.out.println(purchasedNumberCreatedResponse); + } catch (UnauthorizedException | GeneralException exception) { + if (exception.getErrors() != null) { + System.out.println(exception.getErrors().toString()); + } + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleRequestSignatureValidation.java b/examples/src/main/java/ExampleRequestSignatureValidation.java index 37ee8af6..e7b92566 100644 --- a/examples/src/main/java/ExampleRequestSignatureValidation.java +++ b/examples/src/main/java/ExampleRequestSignatureValidation.java @@ -1,48 +1,47 @@ import com.messagebird.MessageBirdClient; import com.messagebird.MessageBirdService; import com.messagebird.MessageBirdServiceImpl; -import com.messagebird.RequestSigner; +import com.messagebird.RequestValidator; import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.RequestValidationException; import com.messagebird.exceptions.UnauthorizedException; import com.messagebird.objects.Message; -import com.messagebird.Request; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import util.HttpHandlerHelpers; -import java.io.*; +import java.io.IOException; import java.net.InetSocketAddress; +import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.Map; /** * Created by hasselbach * - * Complete example of MessageBird webhook signature verification - * @see com.messagebird.RequestSignerTest for simplified examples - * * For exposing your application for external calls (webhooks from MessageBird) - * you can use serveo.net: `ssh -R 80:localhost:3000 serveo.net` + * you can use localtunnel (requires NodeJS). + * + * Here is example of usage: * - * Here is example of usage + * Install localtunnel globally: + * npm install -g localtunnel * * Select a free port: 3000, for example * export MBEXAMPLEPORT=3000 * * Run: - * ssh -R 80:localhost:$MBEXAMPLEPORT serveo.net + * lt --port $MBEXAMPLEPORT * * It will show you something like this: - * Hi there - * Forwarding HTTP traffic from https://blabla.serveo.net - * Press g to start a GUI session and ctrl-c to quit. + * your url is: https://loud-yak-31.loca.lt * * * NOTE * you should not terminate this process, so next operations should be done in the other terminal session * * Remember the address from output: - * export FORWARDING_URL=https://blabla.serveo.net + * export FORWARDING_URL=https://loud-yak-31.loca.lt * * Take your access and secret key from Dashboard: * secret key from https://dashboard.messagebird.com/en/developers/settings @@ -50,7 +49,7 @@ * *NOTE* you should use `Live` key for receiving webhooks * * Run this example as: - * java -jar $MBEXAMPLEPORT test_accesskey test_secret $FORWARDING_URL/webhook + * java -jar $MBEXAMPLEPORT test_accesskey test_secret $FORWARDING_URL * * Now you able to play: * send an SMS: @@ -59,8 +58,8 @@ * and then you will see in example app output: * New request: * GET /webhook?id=ee2d02749a6fb78a572bd7ce9118dff&mccmnc=20409&ported=0&recipient=XXXXXX&reference=example-server&status=delivered&statusDatetime=2019-01-10T09%3A23%3A03%2B00%3A00 - * Request has valid signature * Message for XXXXXX is delivered + * Request has valid signature * * Description of webhook parameters can be found on * https://developers.messagebird.com/docs/sms-messaging#handle-a-status-report @@ -83,16 +82,17 @@ public static void main(String[] args) { int serverPort = Integer.parseInt(args[0]); String apiKey = args[1]; String apiSecret = args[2]; - URL reportURL = new URL(args[3]); + String forwardURL = args[3]; + URL reportURL = new URL(forwardURL + "/webhook"); - RequestSigner reqSigner = new RequestSigner(apiSecret.getBytes()); + RequestValidator reqValidator = new RequestValidator(apiSecret); // Creating MessageBird client final MessageBirdService wsr = new MessageBirdServiceImpl(apiKey); final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); HttpServer httpServer = HttpServer.create(new InetSocketAddress(serverPort), 0); - httpServer.createContext("/webhook", new MessageBirdWebhookHandler(reqSigner)); + httpServer.createContext("/webhook", new MessageBirdWebhookHandler(reqValidator, forwardURL)); httpServer.createContext("/send", new MessageBirdSender(messageBirdClient, reportURL)); @@ -110,10 +110,12 @@ public static void main(String[] args) { */ static class MessageBirdWebhookHandler extends HttpHandlerHelpers implements HttpHandler { - private final RequestSigner reqSigner; + private final RequestValidator reqValidator; + private final String baseURL; - MessageBirdWebhookHandler(RequestSigner reqSigner) { - this.reqSigner = reqSigner; + MessageBirdWebhookHandler(RequestValidator reqSigner, String baseURL) { + this.reqValidator = reqSigner; + this.baseURL = baseURL; } @Override @@ -121,33 +123,29 @@ public void handle(HttpExchange he) throws IOException { System.out.println("New request:"); try { - String requestSignature = he.getRequestHeaders().getFirst("MessageBird-Signature"); - String requestTimestamp = he.getRequestHeaders().getFirst("MessageBird-Request-Timestamp"); - String requestParams = he.getRequestURI().getRawQuery(); + String requestSignature = he.getRequestHeaders().getFirst(RequestValidator.SIGNATURE_HEADER); + String requestURL = URI.create(baseURL).resolve(he.getRequestURI()).toString(); byte[] requestBody = readAllBytes(he.getRequestBody()); - Request request = new Request(requestTimestamp, requestParams, requestBody); - printRequest( he.getRequestMethod(), he.getRequestURI().toString(), new String(requestBody, StandardCharsets.UTF_8) ); - if (reqSigner.isMatch(requestSignature, request)) { - // then only if signature is valid we can look at what is sent - // for SMS status parameters can be found on https://developers.messagebird.com/docs/sms-messaging#handle-a-status-report - reportStatus(parseQuery(he.getRequestURI().getQuery())); + reqValidator.validateSignature(requestSignature, requestURL, requestBody); - // MessageBird expects for `200 OK` status - // otherwise, MessageBird will retry this request limited times - sendResponse(he, 200, "Ok"); - System.out.println("Request has valid signature"); + // then only if signature is valid we can look at what is sent + // for SMS status parameters can be found on https://developers.messagebird.com/docs/sms-messaging#handle-a-status-report + reportStatus(parseQuery(he.getRequestURI().getQuery())); - } else { - sendResponse(he, 401, "Signature is invalid"); - System.out.println("Request has invalid signature"); - } + // MessageBird expects for `200 OK` status + // otherwise, MessageBird will retry this request limited times + sendResponse(he, 200, "Ok"); + System.out.println("Request has valid signature"); + } catch (RequestValidationException e) { + sendResponse(he, 401, "Signature is invalid"); + System.out.println("Request has invalid signature: " + e.getMessage()); } catch (Exception e) { sendResponse(he, 500, e.getMessage()); } @@ -163,6 +161,7 @@ private void reportStatus(Map queryParams) { /** * Simple endpoint for sending SMS-messages via MessageBird + * * @see ExampleSendMessage simplified example for message sending */ static class MessageBirdSender extends HttpHandlerHelpers implements HttpHandler { @@ -192,7 +191,7 @@ public void handle(HttpExchange he) throws IOException { try { messageBirdClient.sendMessage(message); - sendResponse(he,201, "Message sent"); + sendResponse(he, 201, "Message sent"); } catch (GeneralException | UnauthorizedException e) { sendResponse(he, 500, e.getMessage()); throw new IOException(e); diff --git a/examples/src/main/java/ExampleSendConversationMessage.java b/examples/src/main/java/ExampleSendConversationMessage.java new file mode 100644 index 00000000..549bf897 --- /dev/null +++ b/examples/src/main/java/ExampleSendConversationMessage.java @@ -0,0 +1,46 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.conversations.ConversationContent; +import com.messagebird.objects.conversations.ConversationContentMedia; +import com.messagebird.objects.conversations.ConversationContentType; +import com.messagebird.objects.conversations.ConversationMessage; +import com.messagebird.objects.conversations.ConversationMessageRequest; + +/** + * Created by olimpias on 24/3/20. + */ +public class ExampleSendConversationMessage { + + public static void main(String[] args) { + if (args.length < 3) { + System.out.println("Please specify your access key, one ore more phone numbers and a message body example : java -jar test_accesskey 31612345678,3161112233 \"My message to be send\""); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + ConversationMessageRequest request = new ConversationMessageRequest(); + request.setChannelId(args[1]); + ConversationContent content = new ConversationContent(); + ConversationContentMedia media = new ConversationContentMedia("https://example.com/photo.png", "example"); + content.setImage(media); + request.setContent(content); + request.setType(ConversationContentType.IMAGE); + try { + final ConversationMessage response = messageBirdClient.sendConversationMessage(args[2], request); + //Display message response + System.out.println(response.toString()); + } catch (UnauthorizedException | GeneralException exception) { + if (exception.getErrors() != null) { + System.out.println(exception.getErrors().toString()); + } + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleSendVoiceCall.java b/examples/src/main/java/ExampleSendVoiceCall.java index d327a78c..13381ca9 100644 --- a/examples/src/main/java/ExampleSendVoiceCall.java +++ b/examples/src/main/java/ExampleSendVoiceCall.java @@ -31,10 +31,9 @@ public static void main(String[] args) { final VoiceCall voiceCall = new VoiceCall(); voiceCall.setSource("31644556677"); voiceCall.setDestination(args[1]); + voiceCall.setWebhook("https://example.com/","foobar"); - //Title and steps are required fields for creating callFlow final VoiceCallFlow voiceCallFlow = new VoiceCallFlow(); - voiceCallFlow.setTitle("Test title"); //action is required VoiceStep voiceStep = new VoiceStep(); voiceStep.setAction("say"); @@ -47,6 +46,7 @@ public static void main(String[] args) { voiceStep.setOptions(voiceStepOption); voiceCallFlow.setSteps(Collections.singletonList(voiceStep)); + voiceCallFlow.setMaxDuration(28800); voiceCall.setCallFlow(voiceCallFlow); //Sending request to client final VoiceCallResponse response = messageBirdClient.sendVoiceCall(voiceCall); diff --git a/examples/src/main/java/ExampleSendWebhook.java b/examples/src/main/java/ExampleSendWebhook.java index a1ec27a5..7eab5a6d 100644 --- a/examples/src/main/java/ExampleSendWebhook.java +++ b/examples/src/main/java/ExampleSendWebhook.java @@ -10,8 +10,8 @@ public class ExampleSendWebhook { public static void main(String[] args) { if (args.length < 3) { - System.out.println("Please specify your access key, title of webhook and url of webhook :" + - " java -jar test_accesskey webhook_title webhook-url"); + System.out.println("Please specify your access key, url and token of webhook :" + + " java -jar test_accesskey webhook-url webhook-token"); return; } @@ -22,14 +22,14 @@ public static void main(String[] args) { final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); try { - //Creating webHook object to send client + //Creating webhook object to send client System.out.println("Creating new webhook.."); final Webhook webhook = new Webhook(); - webhook.setTitle(args[1]); - webhook.setUrl(args[2]); - //Sending webHook object to client - final WebhookResponseData webhookResponseDataList = messageBirdClient.createWebHook(webhook); - //Display webHook response + webhook.setUrl(args[1]); + webhook.setToken(args[2]); + //Sending webhook object to client + final WebhookResponseData webhookResponseDataList = messageBirdClient.createWebhook(webhook); + //Display webhook response System.out.println(webhookResponseDataList.toString()); } catch (GeneralException | UnauthorizedException exception) { exception.printStackTrace(); diff --git a/examples/src/main/java/ExampleStartConversationsWithWhatsAppSandbox.java b/examples/src/main/java/ExampleStartConversationsWithWhatsAppSandbox.java deleted file mode 100644 index 08e82f33..00000000 --- a/examples/src/main/java/ExampleStartConversationsWithWhatsAppSandbox.java +++ /dev/null @@ -1,42 +0,0 @@ -import com.messagebird.MessageBirdClient; -import com.messagebird.MessageBirdService; -import com.messagebird.MessageBirdServiceImpl; -import com.messagebird.exceptions.GeneralException; -import com.messagebird.exceptions.UnauthorizedException; -import com.messagebird.objects.conversations.*; -import java.util.List; - -public class ExampleStartConversationsWithWhatsAppSandbox { - - public static void main(String[] args) { - if (args.length != 3) { - System.out.println("Please at least specify your access key, the channel id and destination address.\n" + - "Usage : java -jar test_accesskey(Required) channel_id(Required) to(Required)"); - return; - } - - //First create your service object - final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); - - //Add the service to the client - final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr, List.of(MessageBirdClient.Feature.ENABLE_CONVERSATION_API_WHATSAPP_SANDBOX)); //Create client with WhatsApp Sandbox enabled - - ConversationContent conversationContent = new ConversationContent(); - conversationContent.setText("Hello world from java sdk"); - - ConversationStartRequest request = new ConversationStartRequest( - args[2], - ConversationContentType.TEXT, - conversationContent, - args[1] - ); - try { - Conversation conversation = messageBirdClient.startConversation(request); - // assertEquals("convid", conversation.getId()); - System.out.println(conversation.getId()); - - } catch (GeneralException | UnauthorizedException exception) { - exception.printStackTrace(); - } - } -} diff --git a/examples/src/main/java/ExampleUpdateChildAccount.java b/examples/src/main/java/ExampleUpdateChildAccount.java new file mode 100644 index 00000000..1976f6ab --- /dev/null +++ b/examples/src/main/java/ExampleUpdateChildAccount.java @@ -0,0 +1,26 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.ChildAccountResponse; + +public class ExampleUpdateChildAccount { + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key, name of child account, id of child account parameters"); + return; + } + + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + System.out.println("Updating a child account"); + ChildAccountResponse response = messageBirdClient.updateChildAccount(args[1], args[2]); + System.out.println("Child account is updated: " + response.toString()); + } catch (GeneralException | UnauthorizedException exception) { + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleUpdateConversation.java b/examples/src/main/java/ExampleUpdateConversation.java new file mode 100644 index 00000000..0dc58be3 --- /dev/null +++ b/examples/src/main/java/ExampleUpdateConversation.java @@ -0,0 +1,34 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.conversations.*; + +public class ExampleUpdateConversation { + + public static void main(String[] args) { + if (args.length < 3) { + System.out.println("Please specify your access key, the ID of a conversation and the status to update the conversation to." + + " Example : java -jar test_accesskey test_conversationId archived"); + return; + } + + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + final ConversationStatus newStatus = ConversationStatus.forValue(args[2]); + try { + final Conversation response = messageBirdClient.updateConversation(args[1], newStatus); + // Display message response + System.out.println(response.toString()); + } catch (UnauthorizedException | GeneralException exception) { + if (exception.getErrors() != null) { + System.out.println(exception.getErrors().toString()); + } + exception.printStackTrace(); + } + } +} diff --git a/examples/src/main/java/ExampleUpdateConversationWebhook.java b/examples/src/main/java/ExampleUpdateConversationWebhook.java index 37b46955..a4a659cd 100644 --- a/examples/src/main/java/ExampleUpdateConversationWebhook.java +++ b/examples/src/main/java/ExampleUpdateConversationWebhook.java @@ -28,16 +28,16 @@ public static void main(String[] args) { final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); try { - //Creating webHook object to send client + //Creating webhook object to send client System.out.println("Updating conversation webhook.."); ConversationWebhookUpdateRequest request = new ConversationWebhookUpdateRequest( getStatus(args) , getWebhookUrl(args), - getConversationWebHookEvents(args) + getConversationWebhookEvents(args) ); - //Sending ConversationWebHook update request + //Sending ConversationWebhook update request final ConversationWebhook conversationWebhookResponse = messageBirdClient.updateConversationWebhook(args[1], request); //Display conversationWebhook response System.out.println(conversationWebhookResponse); @@ -54,7 +54,7 @@ private static String getWebhookUrl(String[] args) { return args.length > 4 ? args[3] : "https://example-web-hook-url"; } - private static List parseConversationWebHookEvents(String[] args) { + private static List parseConversationWebhookEvents(String[] args) { List conversationWebhookEventList = new ArrayList<>(); for (String arg : args ) { @@ -71,13 +71,13 @@ private static List parseConversationWebHookEvents(Str return conversationWebhookEventList; } - private static List getConversationWebHookEvents(String[] args) { + private static List getConversationWebhookEvents(String[] args) { if (args.length < 5) return Arrays.asList(ConversationWebhookEvent.CONVERSATION_CREATED, ConversationWebhookEvent.MESSAGE_CREATED); String[] arrayOfEvents = new String[args.length - 4]; System.arraycopy(args, 4, arrayOfEvents, 0, args.length - 4); - return parseConversationWebHookEvents(arrayOfEvents); + return parseConversationWebhookEvents(arrayOfEvents); } } diff --git a/examples/src/main/java/ExampleUpdateNumber.java b/examples/src/main/java/ExampleUpdateNumber.java new file mode 100644 index 00000000..1c0ed142 --- /dev/null +++ b/examples/src/main/java/ExampleUpdateNumber.java @@ -0,0 +1,27 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; + +public class ExampleUpdateNumber { + public static void main(String[] args) { + if (args.length < 3) { + System.out.println("Please specify your access key, phone number, and the tags you wish to apply to it."); + return; + } + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + try { + System.out.println(messageBirdClient.updateNumber(args[1], args[2], args[3], args[4])); + } catch (UnauthorizedException | GeneralException exception) { + if (exception.getErrors() != null) { + System.out.println(exception.getErrors().toString()); + } + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleUpdateVoiceCallFlow.java b/examples/src/main/java/ExampleUpdateVoiceCallFlow.java new file mode 100644 index 00000000..4a6bcf1c --- /dev/null +++ b/examples/src/main/java/ExampleUpdateVoiceCallFlow.java @@ -0,0 +1,47 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.VoiceStep; +import com.messagebird.objects.voicecalls.VoiceCallFlowRequest; +import com.messagebird.objects.voicecalls.VoiceCallFlow; +import java.util.Collections; + +public class ExampleUpdateVoiceCallFlow { + + public static void main(String[] args) { + if (args.length < 3) { + System.out.println("Please specify your access key and voice call flow arguments"); + return; + } + + //First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + //Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + final VoiceCallFlowRequest voiceCallFlowRequest = new VoiceCallFlowRequest(); + + voiceCallFlowRequest.setRecord(true); // Can be false as well, see docs + VoiceStep voiceStep = new VoiceStep(); + voiceCallFlowRequest.setSteps(Collections.singletonList(voiceStep)); // VoiceStep Object + voiceCallFlowRequest.setDefaultCall(true); // Can be false as well, see docs + + try { + //Deleting voice call by id + System.out.println("Updating a Voice Call Flow"); + VoiceCallFlow voiceCallFlow = messageBirdClient + .updateVoiceCallFlow(args[1], voiceCallFlowRequest) + .getData() + .get(0); + System.out.println("Voice call flow updated"); + + } catch (GeneralException | UnauthorizedException exception) { + exception.printStackTrace(); + } + + System.out.print(voiceCallFlowRequest.toString()); + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleVerifyEmail.java b/examples/src/main/java/ExampleVerifyEmail.java new file mode 100644 index 00000000..b2ab99bf --- /dev/null +++ b/examples/src/main/java/ExampleVerifyEmail.java @@ -0,0 +1,51 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.objects.Verify; +import com.messagebird.objects.VerifyMessage; +import com.messagebird.objects.VerifyRequest; + +/** + * Created by leandro.pinto on 23/06/21. + */ +public class ExampleVerifyEmail { + + public static void main(String[] args) throws UnauthorizedException, GeneralException, NotFoundException { + + final String ACCESS_KEY = args[0]; + final String METHOD = args[1]; + + final MessageBirdService wsr = new MessageBirdServiceImpl(ACCESS_KEY); + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + Verify verify = null; + + switch (METHOD) { + case "send": + VerifyRequest request = new VerifyRequest(""); + request.setType("email"); + request.setOriginator(""); + request.setSubject(""); + request.setTimeout(300); + + verify = messageBirdClient.sendVerifyToken(request); + System.out.println(verify.toString()); + + break; + case "verify": + final String VERIFY_ID = args[2]; + final String TOKEN = args[3]; + + verify = messageBirdClient.verifyToken(VERIFY_ID, TOKEN); + System.out.println(verify.toString()); + break; + case "view": + final String MESSAGE_ID = args[2]; + VerifyMessage verifyMessage = messageBirdClient.getVerifyEmailMessage(MESSAGE_ID); + System.out.println(verifyMessage.toString()); + break; + } + } +} diff --git a/examples/src/main/java/ExampleViewPurchasedNumber.java b/examples/src/main/java/ExampleViewPurchasedNumber.java new file mode 100644 index 00000000..34af8301 --- /dev/null +++ b/examples/src/main/java/ExampleViewPurchasedNumber.java @@ -0,0 +1,29 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.exceptions.UnauthorizedException; + +public class ExampleViewPurchasedNumber { + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key and phone number."); + return; + } + // First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0], "https://numbers.messagebird.com"); + + // Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + System.out.println(messageBirdClient.viewPurchasedNumber(args[1])); + } catch (UnauthorizedException | NotFoundException | GeneralException exception) { + if (exception.getErrors() != null) { + System.out.println(exception.getErrors().toString()); + } + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleViewTranscription.java b/examples/src/main/java/ExampleViewTranscription.java index 778fcd42..e3dfe6db 100644 --- a/examples/src/main/java/ExampleViewTranscription.java +++ b/examples/src/main/java/ExampleViewTranscription.java @@ -2,15 +2,17 @@ import com.messagebird.MessageBirdService; import com.messagebird.MessageBirdServiceImpl; import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.NotFoundException; import com.messagebird.exceptions.UnauthorizedException; import com.messagebird.objects.voicecalls.TranscriptionResponse; public class ExampleViewTranscription { public static void main(String[] args) { - if (args.length < 6) { - System.out.println("Please specify your access key, call ID, leg ID, recording ID, page, page size :" + - " java -jar test_accesskey e8077d803532c0b5937c639b60216938 e8077d803532c0b5937c639b60216938 e8077d803532c0b5937c639b60216938"); + if (args.length < 5) { + System.out.println("Please specify your access key, call ID, leg ID, recording ID and transcriptionId :" + + " java -jar test_accesskey e8077d803532c0b5937c639b60216938 e8077d803532c0b5937c639b60216938" + + " e8077d803532c0b5937c639b60216938 e8077d803532c0b5937c639b60216938"); return; } @@ -25,14 +27,13 @@ public static void main(String[] args) { final String callId = args[1]; final String legId = args[2]; final String recordingId = args[3]; - final int page = Integer.valueOf(args[4]); - final int pageSize = Integer.valueOf(args[5]); + final String transactionId = args[4]; // Sending call ID, leg ID, recording ID, page, page size parameters to client - final TranscriptionResponse responseList = messageBirdClient.viewTranscription(callId, legId, recordingId, page, pageSize) ; + final TranscriptionResponse responseList = messageBirdClient.viewTranscription(callId, legId, recordingId, transactionId) ; //Display transcription response System.out.println(responseList.toString()); - } catch (GeneralException | UnauthorizedException exceptions) { + } catch (GeneralException | UnauthorizedException | NotFoundException exceptions) { exceptions.printStackTrace(); } diff --git a/examples/src/main/java/ExampleViewVoiceCallFlow.java b/examples/src/main/java/ExampleViewVoiceCallFlow.java new file mode 100644 index 00000000..69e73037 --- /dev/null +++ b/examples/src/main/java/ExampleViewVoiceCallFlow.java @@ -0,0 +1,34 @@ +import com.messagebird.MessageBirdClient; +import com.messagebird.MessageBirdService; +import com.messagebird.MessageBirdServiceImpl; +import com.messagebird.exceptions.GeneralException; +import com.messagebird.exceptions.UnauthorizedException; +import com.messagebird.exceptions.NotFoundException; +import com.messagebird.objects.voicecalls.VoiceCallFlowResponse; + +public class ExampleViewVoiceCallFlow { + + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Please specify your access key and a voice call flow ID"); + return; + } + + //First create your service object + final MessageBirdService wsr = new MessageBirdServiceImpl(args[0]); + + //Add the service to the client + final MessageBirdClient messageBirdClient = new MessageBirdClient(wsr); + + try { + //Deleting voice call by id + System.out.println("Requesting a Voice Call Flow"); + VoiceCallFlowResponse voiceCallFlowResponse = messageBirdClient + .viewVoiceCallFlow(args[1]); + System.out.println("Voice call flow retrieved "); + + } catch (GeneralException | UnauthorizedException | NotFoundException exception) { + exception.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/ExampleViewVoiceCallLegs.java b/examples/src/main/java/ExampleViewVoiceCallLegs.java index 00713686..8429d57c 100644 --- a/examples/src/main/java/ExampleViewVoiceCallLegs.java +++ b/examples/src/main/java/ExampleViewVoiceCallLegs.java @@ -34,7 +34,7 @@ public static void main(String[] args) { messageBirdClient.viewCallLegsByCallId(voiceCall.getId(), null, null); //Display voice call leg response object for (VoiceCallLeg callLeg : voiceCallLegResponse.getData()) { - System.out.printf("\t\t%s -> %s, %s [%s]\n", callLeg.source, callLeg.destination, callLeg.direction, callLeg.status); + System.out.printf("\t\t%s -> %s, %s [status: %s sipResponseCode: %s] \n", callLeg.source, callLeg.destination, callLeg.direction, callLeg.status, callLeg.sipResponseCode); } } diff --git a/examples/src/main/java/ExampleViewWebhook.java b/examples/src/main/java/ExampleViewWebhook.java index e0976ec6..f7eb2b7c 100644 --- a/examples/src/main/java/ExampleViewWebhook.java +++ b/examples/src/main/java/ExampleViewWebhook.java @@ -22,11 +22,11 @@ public static void main(String[] args) { try { System.out.println("Viewing webhook.."); - final String webHookId = args[1]; - //Viewing webHook by webHook id - final WebhookResponseData webHookResponseDataList = messageBirdClient.viewWebHook(webHookId); - //Display WebHook Response Data - System.out.println(webHookResponseDataList.toString()); + final String webhookId = args[1]; + //Viewing webhook by webhook id + final WebhookResponseData webhookResponseDataList = messageBirdClient.viewWebhook(webhookId); + //Display Webhook Response Data + System.out.println(webhookResponseDataList.toString()); } catch (GeneralException | UnauthorizedException | NotFoundException exception) { exception.printStackTrace(); }