Статьи

Тестирование Ionic Push Webhooks с IBM Bluemix

Отказ от ответственности: Ionic Services в настоящее время в альфа-статусе. Хотя функция, о которой я говорю, наверняка будет существовать, когда сервис Push от Ionic станет золотым,  детали того,  о чем я расскажу сегодня, наверняка изменятся. Пожалуйста, имейте это в виду.

Я готовился к различным ионным службам, готовясь к  презентации на  следующей неделе. Одним из аспектов службы Push от Ionic, который я еще не использовал, являются  веб-зацепки . Ionic позволяет вам определить веб-крючок (действительно URL), который он будет вызывать всякий раз, когда кто-то регистрируется на push, отменяет регистрацию на push или просто имеет устройство, которое становится недействительным. Я решил создать быстрое приложение Node.js, чтобы протестировать эту функцию для себя. Для размещения этого приложения я буду использовать  Bluemix , наше PaaS-решение, которое делает хостинг Node.js довольно простым. Кроме того, Bluemix поддерживает Push сам по себе, и вы можете использовать его вместо службы Push от Ionic. Одна из приятных особенностей Bluemix — это возможность смешивать и сопоставлять услуги по своему усмотрению.

Для начала я создал простую демоверсию Push. Вы можете найти полный исходный код для этой демонстрации здесь: https://github.com/cfjedimaster/IonicServicesPresentation/tree/master/demos/push1_user . Я не собираюсь показывать код здесь в записи блога, поскольку он не обязательно уместен. Все, что он делает, это регистрирует приложение для push и связывает его с Ionic User. Это само по себе интересно, так как документы еще не показывают полный пример этого, но я оставлю это для записи в блоге позже на этой неделе. Важно то, что у меня есть приложение, которое я могу запустить на своем устройстве и выполнить реальные push-тесты.

Теперь давайте обратим наше внимание на   функцию webhook . Основная цель этой функции — дать вам возможность узнать, когда пользователь регистрируется (или отменяет регистрацию) для push. Вы можете использовать для этого  любую  серверную технологию, которая для меня означает Node.js. Я начал с перехода в Bluemix, авторизовался (кстати, бесплатно!) И создал новое приложение, используя стартер Node.js. Я скачал образец кода (который стал немного проще с тех пор, как я впервые начал использовать Bluemix) и побежал,  npm install чтобы подготовить вещи локально. Я уже говорил о том, как использовать Bluemix для Node.js, но в случае, если вам нужно обновить программу , ознакомьтесь с моей статьей здесь:  Размещение приложений Node.js в Bluemix .

Для своего приложения я решил, что он будет отвечать на звонки Ionic, сохраняя регистрационные данные в  Cloudant . Для этого есть отличный пакет Node.js  ,  поэтому я знал, что использовать его в приложении будет просто. Вы можете легко добавить службу Cloudant в приложение Bluemix из каталога:

Shot1

После того, как я добавил службу, я пошел к администратору и создал базу данных под названием «регистрации». Теперь я открыл свой код и начал писать. Хотите верьте, хотите нет, но я все написал ниже за один присест и не допустил ошибок. Шутки в сторону. (Хорошо, я могу быть отключен в десять раз или около того.) Вот код, который я использовал для обработки вызовов от webhook:

/*eslint-env node*/

var express = require('express');

// cfenv provides access to your Cloud Foundry environment
// for more info, see: https://www.npmjs.com/package/cfenv
var cfenv = require('cfenv');

var Cloudant = require('cloudant');

var cdMe = "";
var cdPassword = "";
if(process.env.VCAP_SERVICES) {
var info = JSON.parse(process.env.VCAP_SERVICES);
cdMe = info.cloudantNoSQLDB[0].credentials.username;
cdPassword = info.cloudantNoSQLDB[0].credentials.password;
} else {
cdMe = "the username from the Bluemix console";
cdPassword = "ditto";
}


var cloudant = Cloudant({account:cdMe, password:cdPassword});
var registrationDb = cloudant.db.use('registrations');

var app = express();

// serve the files out of ./public as our main files
app.use(express.static(__dirname + '/public'));

var jsonBody = require("body/json");

// get the app environment from Cloud Foundry
var appEnv = cfenv.getAppEnv();

function getRegistrationMode(body) {
if(body.token_invalid) return "invalid";
if(body.unregister && body.unregister == true) return "unregister";
return "register";
}

app.post('/register', function(req, res) {
console.log('running register');

jsonBody(req, function(err,body) {
console.log(body);

var tokens = [];

/*
There are 3 'modes' of the hook: register, invalid, unregister,
but there is no simple flag for this. We'll do the ugly code in a
function and when Ionic improves this, we can fix it there.
*/
var mode = getRegistrationMode(body);
console.log('registration mode is '+mode);

if(mode === 'register') {

if(body._push.android_tokens) {
body._push.android_tokens.forEach(function(token) {
tokens.push(token);
});
}
if(body._push.ios_tokens) {
body._push.ios_tokens.forEach(function(token) {
tokens.push(token);
});
}
console.log("Going to add tokens "+tokens);
tokens.forEach(function(token) {
//do a get to see if exists
registrationDb.get(token, function(err, dbBody) {
if(err) {
console.log('inserting '+token);
//console.log(arguments);
registrationDb.insert({_id:token,time:new Date().getTime()}, function(err, body, header) {
//console.log("cloudant db response: "+JSON.stringify(arguments));
});
} else {
console.log('updating '+token);
registrationDb.insert({_id:token,time:new Date().getTime(),_rev:dbBody._rev}, function(err, body, header) {
//console.log("cloudant db response: "+JSON.stringify(arguments));
});
}
});
});
} else {
//for both unregister, invalid we just delete, but how we get the tokens is different
if(mode === "unregister") {
if(body._push.android_tokens) {
body._push.android_tokens.forEach(function(token) {
tokens.push(token);
});
}
if(body._push.ios_tokens) {
body._push.ios_tokens.forEach(function(token) {
tokens.push(token);
});
}
} else {
if(body.android_token) tokens = [body.android_token];
if(body.ios_token) tokens = [body.ios_token];
}
console.log("Removing "+tokens);
tokens.forEach(function(token) {
//do a get to see if exists
registrationDb.get(token, function(err, dbBody) {
if(!err){
registrationDb.destroy(token,dbBody._rev, function(err, body, header) {
//console.log("cloudant db response: "+JSON.stringify(arguments));
});
}
});
});
}
});

res.send(1);
});

app.get('/list', function(req, res) {
var results = [];
registrationDb.list(function(err, body) {
if(!err) {
body.rows.forEach(function(doc) {
results.push(doc);
});
res.send(JSON.stringify(results));
}
});
});

// start server on the specified port and binding host
app.listen(appEnv.port, function() {
// print a message when the server starts listening
console.log("server starting on " + appEnv.url);
});

Прежде чем мы углубимся в это, позвольте мне пояснить, что я написал   здесь минимум, чтобы мои тесты работали. Этот код может быть организован немного лучше. Мне жаль.

простите

Давайте проработаем наш путь сверху вниз. По большей части первые несколько строк — просто простые операторы require. Обратите внимание, что я получаю свои учетные данные Cloudant либо через переменную среды, либо через жестко закодированное значение. Не забудьте, что вы можете получить свои учетные данные в консоли Bluemix, нажав на ссылку «Показать учетные данные» самой службы:

Shot2

Двигаясь вниз — следующий важный аспект  getRegistrationMode. Если вы прочтете документы о том, как Ionic попадет на ваш URL, вы заметите, что трудно отличить один «режим» от другого. Я говорил с разработчиком этого сервиса, и он согласен, что это может быть проще, но сейчас я написал простую функцию для просмотра данных и определения режима. На  /register маршруте мы затем используем эту функцию, чтобы выяснить, что, черт возьми, мы делаем.

Если мы добавляем, мы получаем токены и выясняем, существуют ли они уже в нашей базе данных. Если они делают, мы обновляем, если нет, мы вставляем. Я включаю отметку времени вместе с регистрацией. Я мог бы включить и другие вещи. Как «незарегистрированный», так и недействительный статус мы в итоге удаляем токен.

Наконец, я построил  /list маршрут, чтобы быстро увидеть, работает ли он. Вот и все. Как я уже сказал, я написал все это одним махом без каких-либо ошибок. (На самом деле я сделал что-то невероятно глупое — подробности читайте в ps внизу.)

Сначала я запустил это на своей локальной машине, и для тестирования я скопировал образцы пакетов JSON из документации Ionic и использовал приложение Postman Chrome  для тестирования. Почтальон существует уже много лет, но до вчерашнего дня я не играл с ним. Это превосходно. Вот оно в действии:

почтальон

Я действительно не могу похвалить это приложение достаточно. Это, конечно, не вещь типа «использовать каждый день», но это невероятно полезно. После того, как у меня было мое приложение работает нормально, я толкнул его до Bluemix:  cf push IonicPushRegistration. Я дождался его установки и запустил это в командной строке, чтобы сообщить Ionic о webhook: ionic push webhook_url http://ionicpushregistration.mybluemix.net/register

В то время, когда я пишу это, у вас нет возможности определить, использует ли приложение Ionic веб-хук. Опять же, я сообщил об этом Ионику, и они знают, что это проблема.

И это действительно так. Вне пакета данных от Ionic, нуждающегося в некоторой организации, эта функция работает, как и ожидалось. Вы можете открыть   URL-адрес / list и просмотреть проверенные мной токены.

Как только сервис Ionic достигнет 1.0, я рассмотрю возможность выпуска полного приложения Node.js для этого. Просто напомни мне!

ps Так что да, мой «эпик» облажался. Я пытался написать код, который гласил: «Если у меня есть переменная окружения для моей проверки подлинности Cloudant, используйте ее, в противном случае по умолчанию используйте эти значения». При этом я полностью сломал process.env, установив для него новый объект, который уничтожил все остальное. Это что-то напортачило. Да, я все еще сосу на Узле.