How to Provide Number Lookup Information

Warning: Beta software This API is in beta stage and may be subject to change. Therefore, we do not recommend using this in production.

Interested in this feature? Please reach out to mobility-services-developer@cisco.com

This guide will show you how to become a Number Lookup Provider.

You should have an understanding of how this works before continuing, see How Does Number Lookup Work?.

Prerequisites

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

Required Scope

lookup.number:read

Limitations

Limitations for display name

The display name returned must be max 60 characters, and may only contain the following:

  • alphanumeric (a-z, A-Z, 0-9)
  •  (space), -, _
  • ., !, %, *, ', +, ~, `

Characters not matching the above will be stripped.

Code Dependencies

<dependency>
  <groupId>com.wgtwo.api.v0.grpc</groupId>
  <artifactId>lookup</artifactId>
  <version>0.3.0</version>
</dependency>

Code


#!/usr/bin/env bash

# We send data to grpcurl via a PIPE, and receive data via stdout.
PIPE="/tmp/provider-PIPE-$RANDOM"
mkfifo $PIPE
exec 3<> $PIPE

cleanup() {
    echo "Cleaning up..."
    rm -f $PIPE
    exec 3>&-
    exit
}
trap cleanup INT TERM EXIT

grpcurl \
   -d @ \
   sandbox.api.shamrock.wgtwo.com:443 \
   wgtwo.lookup.v0.NumberLookupService/NumberLookup <&3 \
| while IFS= read -r LINE; do
    # grpcurl sends output line by line, so we buffer until we have a valid JSON object.
    BUFFER+="$LINE"
    if ! jq . >/dev/null 2>&1 <<<"$BUFFER"; then continue; fi

    # Create a JSON response that includes the request and a dummy result.
    RESPONSE_JSON=$(jq -c -n --argjson incoming_content "$BUFFER" \
      '{
          number_lookup_request: $incoming_content,
          result: {
            name: "John Doe"
          }
      }'
    )

    # Send the response to grpcurl via the PIPE.
    echo "$RESPONSE_JSON" > $PIPE

    echo "Sent response:"
    echo "$RESPONSE_JSON" | jq .
    echo ""

    # Clear the buffer.
    BUFFER=""
done

package com.example.numberlookup

import com.google.protobuf.util.Durations
import com.wgtwo.api.v0.lookup.NumberLookupProto.NumberLookupRequest
import com.wgtwo.api.v0.lookup.NumberLookupProto.NumberLookupResponse
import com.wgtwo.api.v0.lookup.NumberLookupServiceGrpcKt
import com.wgtwo.api.v0.lookup.cacheControl
import com.wgtwo.api.v0.lookup.numberLookupResponse
import com.wgtwo.api.v0.lookup.result
import io.grpc.ManagedChannelBuilder
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.runBlocking
import java.util.concurrent.TimeUnit
import kotlin.time.Duration.Companion.seconds

suspend fun startStream() {
    val grpcChannel = ManagedChannelBuilder.forTarget("sandbox.api.shamrock.wgtwo.com:443")
        .keepAliveWithoutCalls(true)
        .keepAliveTime(1, TimeUnit.MINUTES)
        .keepAliveTimeout(10, TimeUnit.SECONDS)
        .idleTimeout(1, TimeUnit.HOURS)
        .build()

    val stub = NumberLookupServiceGrpcKt.NumberLookupServiceCoroutineStub(grpcChannel)

    val phonebook = mapOf(
        "+4799999999" to "John Doe",
        "+4799990000" to "Jane Doe",
        "+4799990001" to "Alice Doe",
        "+4799990002" to "Bob Doe",
    )

    val responseQueue = Channel<NumberLookupResponse>()

    // Check queue to see if we have any responses and send them over the wire
    val responseFlow = flow {
        for (response in responseQueue) {
            emit(response)
        }
    }
    stub.numberLookup(responseFlow)
        .onStart { println("Starting new stream") }
        .onEach { request: NumberLookupRequest ->
            println("Got request for ${request.number.e164}")
            val displayName = phonebook[request.number.e164] ?: ""
            val response = numberLookupResponse {
                numberLookupRequest = request
                result = result {
                    name = displayName
                }
                cacheControl = cacheControl {
                    maxAge = Durations.fromMinutes(60)
                }
            }
            println("Sending response for ${response.numberLookupRequest.number.e164}: \"${response.result.name}\"")
            responseQueue.send(response)
        }
        .catch { e ->
            println("Got error: ${e.message} - Will reconnect in 1s")
            delay(1.seconds)
        }
        .collect()
}

fun main() = runBlocking {
    // Run forever - if the stream fails we will reconnect
    while (true) {
        startStream()
    }
}

Example Result

{
  "number_lookup_request": {
    "id": "c996a774-0c8b-482d-8fe2-880978ff44db",
    "number": {
      "e164": "+4799990000"
    }
  },
  "result": {
    "name": "John Doe"
  }
}


Sent response:
{
  "number_lookup_request": {
    "id": "defdbc17-d2a1-4f85-91f3-1cc2a14a2087",
    "number": {
      "e164": "+4799990005"
    }
  },
  "result": {
    "name": "John Doe"
  }
}
Starting new stream
Got request for +4799990005
Sending response for +4799990005: ""
Got request for +4799990000
Sending response for +4799990000: "Jane Doe"
Got request for +4799990001
Sending response for +4799990001: "Alice Doe"

Cache

As part of the response, the provider can include a cache TTL.

This is the time in seconds that the display name should be cached in the core. If not set, a default cache TTL documented in the proto file will be used.

NumberLookupResponse {
  number_lookup_request { } # Fields omitted for brevity

  result {
    name: "John Doe"
  }

  # Optional, set to default if not set
  cache_control {
    max_age {
      seconds: 3600  # 1 hour, google.protobuf.Duration
    }
  }
}
Setting Value Description
keep-alive period 1 minute Must be ≥ 1 minute and ≤ 5 minutes
keep-alive timeout 10 seconds
permit keep-alive without calls true

Keep-Alive

The API Gateway hosting api.{region}.wgtwo.com (see environments for a list of regions) will silently drop connections if they have been idle for 350 seconds (5m 50s).

To avoid this, the provider should send a keep-alive message every minute.

Local Testing

For local testing, you can use our sandbox environment hosted at sandbox.api.shamrock.wgtwo.com.

In addition to that, we do provide a test application that can be used for testing which may be useful to see how your provider behaves under heavy load.

The test application is hosted at github.com/working-group-two/number-lookup-provider-tester.

By setting up a stream to this application, it will send number lookup requests at the configured rate. It will not do any flow control, but show number of in-flight messages (requests sent - responses received).

Docker

The test application is also available as a pre-built docker image. Usage:

docker run --network=host ghcr.io/working-group-two/number-lookup-provider-tester:latest \
    --address :8118 \
    --rps 5 \
    --numbers 4799990001,4799990002,4799990003 \
    --print-progress \
    --print-requests \
    --print-responses

Read More