Play time with Google – Google’s Libphonenumber library

Ever wonder if there was a tool capable of distinguishing phone numbers in a pool of text? Well wonder no more, because Google has the answer! Enter, Google’s libphonenumber library!

Libphonenumber is an open source project created by the famous company Google, and it’s a project that they (or other open source devs) are still contributing to to this day. Now you may be wondering “So what, it’s just a library that can read phone numbers, it’s no big deal.” To that, I say:

ohDKCIO

It is a big deal, and the reason for that is simply because the uses for such an application are virtually limitless. Picture this: You create an application that recognizes the text on an image from a photo (which I’m sure there is already a library for), you then sanitize that recognized text and pass it over to libphonenumber to find phone numbers, sort them and have it sent back to a user or do with it whatever you will. Wouldn’t that be cool?! Well I think it would be! Now though I didn’t do as I just mentioned – I know, bummer, I will however take you on a journey of what I did end up doing using this library, which is make an API!

The Task

I was tasked with using Google’s library in order to create an API that is capable of two things.

  1. Accept POST requests of a text file, parse it, find valid phone numbers, and return it to the user.
  2. Accept GET requests where the user would send any amount of text through the URL, which would then get parsed for any valid phone numbers and be returned.

Fairly simple, actually. However, since I’ve never written any API code, I really wasn’t too sure on where to begin. How does one even make an API? What languages are available for the tool? What language does it best? What language does it worse? After doing some digging on the internet, I found out that an API can easily be made using Node.js and Express.js! I love JavaScript, and I’ve been doing a bit of work on Node.js lately creating a bot (a future blog post topic?), and it just so happens that Google has a JavaScript version of their libphonenumber library. So everything was set, the path to success was laid out right there in front of me!

Writing of the code

In about an hour, I found success in the form of the following:

// Find phone numbers from given array
let findPhoneNumbers = async arrayOfNumbers => {
// Object to hold returned phone numbers
let numbersArray = {
validNumbers: [],
invalidNumbers: []
};
// Filter through the array looking for numbers
for (let i = 0; i < arrayOfNumbers.length; i++) {
let currentNumber;
let numWasFound = true;
try {
currentNumber = phoneUtil.parse(arrayOfNumbers[i], 'CA');
}
catch (error) {
numWasFound = false;
}
if (numWasFound) {
// Format the found phone number into (xxx) xxx-xxxx
let tempNumb = phoneUtil.format(currentNumber, PNF.NATIONAL);
// Note: Google's library will only properly format the number if it is a valid one
// Which is why I do this check and place the number into the correct array
if (tempNumb.includes('(')) {
numbersArray.validNumbers.push(tempNumb);
}
else {
numbersArray.invalidNumbers.push(tempNumb);
}
}
}
return numbersArray;
}
app.get(`/api/phonenumbers/parse/text/:givenText?`, async (request, response) => {
// Check if param was passed
if (request.params.givenText) {
// Replace anything that isn't a number or a comma with nothing
// split into array by splitting on commas
let userData = request.params.givenText.replace(/[^0-9,]/g, '').split(',');
let numbersFound = await findPhoneNumbers(userData);
response.json(numbersFound);
}
else {
response.status(400).json([]);
}
});

view raw
app-get-method.js
hosted with ❤ by GitHub

My API was well under way. It could now accept a GET request and send back a JavaScript object with the appropriate data. Pretty good for a “Mediocre coder at best”! But how do I get the upload part to work? I’ve never done this before, I’m lost, what do?! Off to the internet we go looking for answers to this question! Enter, multer
“Multer is a node.js middleware for handling multipart/form-data, which is primarily used for uploading files. It is written on top of busboy for maximum efficiency.” This was absolutely perfect, quite literally what I was looking for. With multer by my side, this was a piece of cake!

In just a few minutes, I was accepting file uploads left, right and center!

app.post(`/api/phonenumbers/parse/file/`, upload.single('myFile'), async (request, response) => {
if (request.file) {
// Read the contents of the uploaded file
let fileContents = fs.readFileSync(request.file.path);
// Convert it to base64 with ascii characters
let asciiContent = Buffer.from(fileContents, 'base64').toString('ascii');
// Replace anything that isn't a number or a comma with nothing
// split into array by splitting on commas
let fileArray = asciiContent.replace(/[^0-9,]/g, '').split(',');
let numbersFound = await findPhoneNumbers(fileArray);
response.json(numbersFound);
}
else {
response.status(400).json("No file recieved");
}
});

view raw
app-post-method.js
hosted with ❤ by GitHub

The task has been completed. My API can take GET and POST requests and handle them appropriately, job well done! But I needed to find a way to automate the testing of my code, hmmm…

Unit tests

Until I worked on this project, I didn’t even know that this was a thing. Having files that test your code for you wasn’t something I was aware of in the programming world (TIL!)

Through multiple YouTube videos and internet articles, I managed to learn of the existence of Mocha and Chai . “What’s a ‘describe’? What’s an ‘expect’? Chai? Is that tea? I hope it’s tea, I love tea!” – All of what came to my mind as I read and watched the information I found. Thankfully, all of them were very informative and taught me a lot about the different frameworks like Jest. I decided to stick with Mocha and Chai as they were the most simple (for me) and had just what I was looking for. In a matter of an hour, I was able to construct about four tests using Chai that successfully passed!

describe('API endpoint /', () => {
// GET – Root
it('Should return status 200', () => {
return chai.request(app)
.get('/')
.then((response) => {
expect(response).to.have.status(200);
expect(response.body).to.be.a('object');
});
});
});
describe('API GET request /api/phonenumbers/parse/text/nothing', () => {
// GET – No numbers
it(`Should return {"validNumbers":[],"invalidNumbers":[]}`, () => {
return chai.request(app)
.get('/api/phonenumbers/parse/text/nothing')
.then((response) => {
expect(response).to.have.status(200);
expect(response.body).to.containSubset({
"validNumbers": [],
"invalidNumbers": []
});
});
});
});
describe('API GET request /api/phonenumbers/parse/text/Seneca%20Phone%20Number%3A%20416-154-9036', () => {
// GET – Multiple Numbers
it(`Should return status 200. With {
"validNumbers":[
"(416) 154-9036",
"(905) 365-1864"
],
"invalidNumbers":[]
}`, () => {
return chai.request(app)
.get('/api/phonenumbers/parse/text/Seneca%20Phone%20Number%3A%20416-154-9036,9053651864')
.then((response) => {
expect(response).to.have.status(200);
expect(response.body).to.be.a('object');
expect(response.body).to.containSubset({
"validNumbers": [
"(416) 154-9036",
"(905) 365-1864"
],
"invalidNumbers": []
});
});
});
});
describe('API POST request /api/phonenumbers/parse/file', () => {
// POST – Multiple Numbers
it(`It should resturn status 200. With {
"validNumbers":[
"(416) 987-3546",
"(647) 315-9753",
"(905) 354-1587",
"(416) 987-3546"
],
"invalidNumbers":[]
}`, () => {
return chai.request(app)
.post('/api/phonenumbers/parse/file')
.set('Content-Type', 'text/plain;charset=base64')
.attach('myFile', fs.readFileSync('./phoneNums.txt'), ' ')
.then(function (response) {
expect(response).to.have.status(200);
expect(response.body).to.containSubset({
"validNumbers": [
"(416) 987-3546",
"(647) 315-9753",
"(905) 354-1587",
"(416) 987-3546"],
"invalidNumbers": []
});
});
});
});

view raw
app-test.js
hosted with ❤ by GitHub

Finally, I could mark this project as ‘temporarily done.’ Meaning I can now push it to Github and continue to work on it in order to add features of enhance the current ones, which is a really good feeling.

Bringing it all in

So to summarize my experience with this project, I would say that it was an amazing learning experience. In taking on what seemed like a very simple project at first, but turned out to be something more, I learnt quite a lot about request handling, uploading of files, and how to write tests. More importantly, I learnt how to write an API!

I’d like to thank you for taking the time to read this far. If you have any questions, please don’t hesitate to leave them in the comments section!

Tools used

You can find the finished product in its Github repo!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s