Get handset change events

Prerequisites

  1. An OAuth 2.0 client
  2. A client access token

Required scope

subscription.handset_details:read

Code

TIP

You can test our APIs without authorization by targetting sandbox.api.wgtwo.com instead of api.wgtwo.com and removing any authorization from the request/code sample.

Download proto definitions
curl -sL 'https://github.com/working-group-two/wgtwoapis/blob/master/image.bin?raw=true' -o wgtwo.bin
1
export ACCESS_TOKEN="my_client_access_token"
grpcurl -protoset wgtwo.bin \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '
  {
    "stream_configuration": {
      "regular": {},
      "disable_explicit_ack": {}
    }
  }
  ' \
  api.wgtwo.com:443 \
  wgtwo.subscription.v1.SubscriptionEventService/StreamHandsetChangeEvents
1
2
3
4
5
6
7
8
9
10
11
12
13
About stream_configuration

For testing purposes, we include the config:

"stream_configuration": {
  "regular": {},              Reading position will not be stored in the server and load is not spread between your clients 
  "disable_explicit_ack": {}  Let events be automatically acked
}
1
2
3
4

By default, load will automatically be spread between all connections using the same OAuth 2.0 client and you will need to reply with a ack once your service has handled the message. This is also what we would recommend for real production usage.

See configuring event streaming for details.

Example result

{
  "metadata": {
    "timestamp": "2021-09-30T14:27:55.468Z",
    "identifier": {
      "subscriptionIdentifier": {
        "value": "ce78..."
      }
    },
    "ackInfo": {
      "value": "Ch5..."
    }
  },
  "handsetChangeEvent": {
    "previous": {
      "imeiSv": {
        "imei": "867...",
        "softwareVersion": "64"
      }
    },
    "current": {
      "imeiSv": {
        "imei": "867...",
        "softwareVersion": "65"
      }
    },
    "number": {
      "e164": "479..."
    },
    "imsi": {
      "value": "Rn..."
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Install dependencies

Maven

<dependency>
  <groupId>com.wgtwo.api.v1.grpc</groupId>
  <artifactId>subscription</artifactId>
  <version>1.10.1</version>
</dependency>
package com.example.handsetchange

import com.wgtwo.api.v1.subscription.SubscriptionEventServiceGrpc
import com.wgtwo.api.v1.subscription.SubscriptionEventsProto.AckHandsetChangeEventRequest
import com.wgtwo.api.v1.subscription.SubscriptionEventsProto.StreamHandsetChangeEventsRequest
import com.wgtwo.api.v1.subscription.SubscriptionEventsProto.StreamHandsetChangeEventsResponse
import com.wgtwo.auth.BearerTokenCallCredentials
import io.grpc.ManagedChannelBuilder
import io.grpc.StatusRuntimeException
import java.util.concurrent.Executors

private const val MAX_IN_FLIGHT = 10
private val environment = Environment.SANDBOX

private val endpoint = when (environment) {
    Environment.SANDBOX -> "sandbox.api.wgtwo.com"
    Environment.PRODUCTION -> "api.wgtwo.com"
}
private val channel = ManagedChannelBuilder.forAddress(endpoint, 443).build()
private val stub = SubscriptionEventServiceGrpc.newBlockingStub(channel).apply {
    /**
     * If you are not using the sandbox, you need to add credentials.
     * The BearerTokenCallCredentials class can be found in our auth library.
     */
    if (environment == Environment.PRODUCTION) {
        this.withCallCredentials(BearerTokenCallCredentials { "MY_CLIENT_ACCESS_TOKEN" })
    }
}
private val executor = Executors.newFixedThreadPool(MAX_IN_FLIGHT)

fun main() {
    while (!channel.isShutdown) {
        try {
            subscribe()
        } catch (e: StatusRuntimeException) {
            println("Got exception: ${e.status} - Reconnecting in 1 second")
            Thread.sleep(1000)
        }
    }
}

private fun subscribe() {
    println("Starting subscription")
    val request = StreamHandsetChangeEventsRequest.newBuilder().apply {
        streamConfigurationBuilder.maxInFlight = MAX_IN_FLIGHT
    }.build()

    stub.streamHandsetChangeEvents(request).forEach { response ->
        // Using an executor to handle up to MAX_IN_FLIGHT messages in parallel
        executor.submit {
            handleResponse(response)
            ack(response)
        }
    }
}

private fun handleResponse(response: StreamHandsetChangeEventsResponse) {
    println("Got response:\n$response")
}

private fun ack(response: StreamHandsetChangeEventsResponse) {
    val ackInfo = response.metadata.ackInfo
    val request = AckHandsetChangeEventRequest.newBuilder().setAckInfo(ackInfo).build()
    stub.ackHandsetChangeEvent(request)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

Example result

metadata {
  timestamp {
    seconds: 1635326429
    nanos: 93000000
  }
  identifier {
    subscription_identifier {
      value: "dummy:se:0"
    }
  }
}
handset_change_event {
  previous {
    imei_sv {
      imei: "359237066557492"
      software_version: "00"
    }
  }
  current {
    imei_sv {
      imei: "353752084447134"
      software_version: "00"
    }
  }
  number {
    e164: "479..."
  }
  imsi {
    value: "33"
  }
}

# Ack success
metadata {
  timestamp {
    seconds: 1635326429
    nanos: 193000000
  }
  identifier {
    subscription_identifier {
      value: "dummy:se:0"
    }
  }
}
handset_change_event {
  previous {
    imei_sv {
      imei: "353752084447134"
      software_version: "00"
    }
  }
  current {
    imei_sv {
      imei: "355330105977849"
      software_version: "00"
    }
  }
  number {
    e164: "479..."
  }
  imsi {
    value: "33"
  }
}

# Ack success
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

Read more