Countly 插件 API 端


Countly 插件 API 端

https://support.count.ly/hc/en-us/articles/360037502212-Plugin-API-Side

示例api.js文件可能如下所示:

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
var plugin = {},
common = require('../../../api/utils/common.js'),
plugins = require('../../pluginManager.js');

(function(plugin) {
//write api call
plugins.register("/i", function(ob) {
//get request parameters
var params = ob.params;

//check if it has data we need
if (params.qstring.user_details) {
//if it is string, but we expect json, lets parse it
if (typeof params.qstring.ourplugin == "string") {
try {
params.qstring.ourplugin = JSON.parse(params.qstring.ourplugin);
} catch (SyntaxError) {
console.log('Parse JSON failed');
//we are not doing anything with request
return false;
}
//start doing something with request

//and tell core we are working on it, by returning true
return true;
}

//we did not have data we were interested in
return false;
}
});
}(plugin));

module.exports = plugin;
  • Params 对象

在api端有一个通过许多方法传递的公共对象,它通常存储在一个名为“params”的变量中。此对象的 Contens 可能取决于 api 请求的类型以及处理此请求的阶段

属性名称 它包含什么 何时添加
href 完整的请求网址 From the start
qstring 随请求传递的查询字符串或正文的对象 From the start
res Response 对象 From the start
req 请求对象 From the start
apiPath 两级路径字符串 From the start
fullPath 完整 api 端点路径的字符串 From the start
files 通过请求上传的文件 从 POST 请求开始
cancelRequest 如果包含 true,则应忽略且不处理请求 任何插件都可以随时设置,但 API 仅在 / 和 /sdk 事件之后开始检查它,因此插件应该在需要时设置它
bulk 如果从批量方法处理此 SDK 请求,则为 True 使用 /i/bulk 终结点时
promises 不同事件的承诺数组 当所有承诺都得到满足时,请求就结束了
ip_address 设备提交请求的 IP 地址 在所有 SDK 请求上
user 包含一些用户信息的数据,例如来自请求的国家/地区地理信息等 在所有 SDK 请求上
app_user_id 用户app_users文档的 ID 在所有 SDK 请求上
member 有关仪表板用户的所有数据 对于所有包含api_key的请求,在通过验证方法进行验证后
app 应用程序的文档 在所有 SDK 请求上和 validateUserForDataReadAPI 验证之后
app_user app_user文件 在所有 SDK 请求上
time 包含以下时间对象: 在所有 SDK 请求上
1
2
3
4
5
6
7
8
9
10
11
12
now - 请求时间的 moment 对象
nowUTC - 以 UTC 为单位的请求时间的 moment 对象
nowWithoutTimestamp - 时刻对象或当前时间
timestamp - 请求时间戳
mstimestamp - 请求毫秒时间戳
yearly - moment.format("YYYY"),
monthly - moment.format("YYYY.M"),
daily - moment.format("YYYY.M.D"),
hourly - moment.format("YYYY.M.D.H"),
weekly - Math.ceil(moment.format("DDD") / 7),
month - moment.format("M"), day - moment.format("D"),
hour - moment.format("H")
  • 可用的事件路径和参数
Path Description Usable Properties
/master 从管理工作线程的主集群派发出来的 当启动后台任务,您想要与计数服务器工作线程并行执行时 没有传递给它的参数
/worker worker的初始化,基本上在nodejs服务器启动时只执行一次 若要完成每次启动需要执行一次的任务,例如创建数据库的连接池(如果未使用 Countly 默认连接) common 常见 具有通用 js 作为获取 db 连接和其他通用实用程序的另一种方式
/ 对 Countly API 的任何 HTTP 请求 在 Countly core 处理数据之前或处理特定 URL 的文件上传时需要修改数据时 params - params 对象,带有路径请求的 apiPath 字符串已发出,urlParts 字符串与 url parts
/sdk 来自 SDK 的 HTTP 请求 当您想要取消来自 SDK 的请求时 params - params 对象
/sdk/end 处理完 SDK 请求后 当您想要对一些插入的信息进行后期处理时 params - params 对象
/i 默认写入路径,基本上从 SDK 中检索所有标准数据 从 SDK 中检索新数据。此事件已通过验证,这意味着您可以写入收到的数据 params - params 对象,包含有关数据用于哪些内容的信息的应用程序
/o 默认读取路径。您应将此路径视为自定义路径,这意味着如果您正在处理此请求并为此请求写入输出,则应返回 true。 若要通过提供特定的方法查询字符串来读取一些基本数据,请执行此事件,需要验证以查看用户是否具有读取数据的任何权限 params - params 对象
/validation/user 如果您的插件对用户有权访问某些 API 端点或特定数据进行一些验证 允许或禁止用户访问,如果允许,则返回 false,如果禁止,则返回 true params - params 对象
/o/validate 具有已验证数据的默认读取请求,这意味着存在用户API_KEY,并且他至少具有此应用的用户角色。 通常为一些现有的系统预定义的读取方法读取数据,而不是自定义方法 params - params 对象,应用包含有关数据所针对的应用的信息
/i/events 处理每个事件 用于获取或修改事件数据 params - params 对象,currEvent 事件数据
/session/begin 用户会话开始时 记录新会话已启动或已添加新用户 params - params 对象,isNewUser - 如果用户是新用户,则为 bool
/session/extend 当用户扩展会话时 记录该会话已延长 params - params 对象
/session/end 当会话结束请求收到且会话可能结束时 记录会话结束 params - 参数对象 dbApp用户关于用户的信息
/session/post 当会话实际结束时,冷却时间过去或在colldown后收到新会话,而没有关闭前一个会话 当无法保证 SDK 会发送任何end_session请求时,将其用作会话结束 params - params 对象,如果会话被end_session请求结束,则为 true,如果新会话启动而未关闭上一个会话,则为 false end_session
/session/duration 在会话结束后计算总会话持续时间时 记录已结束会话的会话持续时间 params - params 对象,session_duration会话持续时间(以秒为单位)
/session/user 有关会话已启动的用户的信息 记录或更新用户信息 params - 参数对象 dbApp用户关于用户的信息
/session/metrics 处理指标 添加要处理的新指标数据 params - params 对象,predefinedMetrics 对象,将应处理的指标添加到该对象中,用户信息 关于用户,isNewUser - 如果用户是新用户,则为 bool
/session/retention 更新用户的会话计数和 fs、ls 时间戳和指标数据时 如果需要对用户的指标数据进行后处理 params - params 对象,isNewUser 如果用户是新用户,则为 true,如果已经有任何会话,则为 false
/o/method/total_users 输出指标值的总用户数时 提供指标名称以获取其用户总数 shortcodesForMetrics - 集合到指标名称映射匹配 - 从集合中获取用户app_users查询部分
/i/apps/create 创建新应用时 修改数据或向应用特定集合添加索引 params - params 对象,appId - 创建的应用数据的 ID - 应用数据
/i/apps/update 更新应用信息时 记录更改或通知第三方服务 params - params 对象,appId - 创建的应用数据的 ID - 更新的数据
/i/apps/reset 删除应用和应用的分析数据时 删除与此应用相关的分析数据 params - params 对象,appId - 重置的应用数据的 ID - 应用数据
/i/apps/delete 删除应用时 删除所有应用和应用分析相关数据 params - params 对象,appId - 已删除应用的 ID,data - 应用数据
/i/apps/clear_all 删除所有应用的分析数据时 删除所有与应用分析相关的数据 params - params 对象,appId - 已删除应用数据的 ID - 应用数据
/i/users/create 创建新用户时 添加默认设置或索引 params - params 对象,data - 一些创建的用户数据
/i/users/update 更新用户信息时 记录更改或通知第三方服务 params - params 对象,数据 - 一些更新的用户数据,member - 成员文档
/i/users/delete 删除用户时 删除任何特定用户设置或数 params - params 对象,data - 已删除的用户数据
/i/device_id SDK 更改应用用户device_id时 更改您的插件数据(如果存储给特定应用程序用户) params - params 对象,oldUser 旧应用用户文档,newUser 新应用用户文档
/systemlogs 侦听以在系统日志中注册用户操作 当您要为仪表板用户录制操作时 params - params 对,action - 要记录的操作,data - 要以 JSON 格式记录的数据
/plugins/drill 当有人想要录制事件时,通过演练进行侦听 在演练中记录事件,通常是 SDK 未接收的内部事件,因为 SDK 事件由演练自动处理 params - params 对象,dbAppUser 应用程序用户文档,events - 事件对象的数组,与 SDK 请求接收的数组相同
/view/duration 侦听更新视图的持续时间 在查看结束时报告查看持续时间 params - params 对象,duration - 视图的持续时间
{custom paths} 任何未由核心处理或在此表中列出的 API 路径 创建新的插件特定路径 params - params 对象
  • 取消请求

在两种情况下,您可以取消请求,这样核心就不会再处理它。

其中一个用于侦听 / 路径(任何请求),另一个用于侦听 /sdk 路径(任何 SDK 请求)。要取消此请求,您的插件所要做的就是将参数对象的 cancelRequest 设置为 true。

1
2
3
plugins.register("/", function(ob){
ob.params.cancelRequest = true;
});
  • 用户验证

并非所有数据都应该公开,可供任何向 Countly API 请求的人添加、编辑、删除甚至查看。因此,您需要验证用户提供的API_KEY是否有权查看修改数据。

由于插件的某些事件是由系统直接创建的,或者在传递给插件之前经过验证,因此它们不需要验证,因此只有少数情况需要验证用户的请求。

“/” - 在处理数据之前发出的任何 HTTP 请求

“/o” - 读取基本指标时的路径

{自定义路径} - 您为插件定义

对于此路径上的插件事件,Countly 提供了您可以选择用于验证的验证函数。

1
2
3
4
validateUser - 检查用户是否存在
validateUserForRead - 检查用户是否具有所提供应用的读取权限
validateUserForWrite - 检查用户是否具有所提供应用的写入权限
validateGlobalAdmin - 检查用户是否为全局管理员

验证方法的先决条件是必须以以下形式提供用户凭据:

1
2
3
params.qstring.api_key 或
params.qstring.auth_token 或
params.req.headers["countly-token"]

对于还验证对应用的访问的方法,需要提供app_id:

1
params.qstring.app_id

验证完成后,将向 params 对象添加其他参数,如下所示:

1
2
3
4
5
6
7
8
params.app - 应用文档
params.member - 用户文档
params.time - 请求时间
params.app_id - 应用 ID
params.app_cc - 应用国家/地区
params.app_name - 应用名称
params.appTimezone - 应用时区
params.time - 请求时间

验证示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var plugin = {},
common = require('../../../api/utils/common.js'),
{validateUserForWrite} = require('../../../api/utils/rights.js'),
plugins = require('../../pluginManager.js');

(function(plugin) {

//handling some custom path
plugins.register("/i/ourplugin", function(ob) {

//get parameters
var params = ob.params; //request params

validateUserForWrite(params, function(params) {
//user is validated
//you can process request
});

//need to return true, so core does not repond that path does not exist
return true;
});
}(plugin));

module.exports = plugin;
  • 处理自定义路径

除了预定义的事件路径外,任何未被核心api.js处理的路径(基本上没有在参考 api 和上表中列出)都将传递给插件。

例如,如果您调用 http://count.ly/o/foobar 之类的路径,它将首先将其分派给插件,如果没有插件会说它们使用此路径,则 api 将返回错误消息,即它是错误的路径。

要告诉核心插件使用此路径或正在异步执行事件中的某些操作,您必须简单地从事件处理程序返回 true。

此外,当您处理自己的自定义路径时,您的插件负责向客户端提供任何输出,否则此类 HTTP 请求只会超时。

另请注意,如果您只需要注册单个子路径,则为 /o/foo,并且对 /o/foo/bar1 和 /o/foo/bar2 等的所有请求都将定向到 /o/foo 事件,您可以在其中检索路径数组并处理请求。

处理自定义路径的示例可能如下所示:

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
var plugin = {},
common = require('../../../api/utils/common.js'),
{validateUserForWrite} = require('../../../api/utils/rights.js');
plugins = require('../../pluginManager.js');

(function(plugin) {
//handling custom path
plugins.register("/i/ourplugin", function(ob) {
//get parameters
var params = ob.params; //request params
var paths = ob.paths;
validateUserForWrite(params, function(params) {
//user is validated process request
switch (paths[3]) {
case 'create':
//create new object
var data = params.qstring;
//validate data if needed and write object to db
common.db.collection('ourplugin').insert(data, function(err, app) {
if (err)
common.returnMessage(params, 200, err);
else
common.returnMessage(params, 200, "Success");
});
break;
case 'update':
//update existing object
var id = params.qstring.id;
var data = params.qstring;
//validate data if needed and write object to db
common.db.collection('ourplugin').update({
_id: id
}, data, function(err, app) {
if (err)
common.returnMessage(params, 200, err);
else
common.returnMessage(params, 200, "Success");
});
break;
case 'delete':
//delete existing object
var id = params.qstring.id;
common.db.collection('ourplugin').remove({
_id: id
}, function(err, app) {
if (err)
common.returnMessage(params, 200, err);
else
common.returnMessage(params, 200, "Success");
});
break;
default:
common.returnMessage(params, 400, 'Invalid path, must be one of /create, /update or /delete');
break;
}
});
//need to return true, so core does not repond that path does not exist
return true;
});
}(plugin));

module.exports = plugin;
  • 处理 metrics 指标

Countly 最常见的插件用法之一可能是添加新指标来跟踪数据并显示在仪表板上,就像现有指标一样:分辨率、运营商等。因此,我们试图使这个过程变得非常容易实现。

添加新指标时,API 端要做的第一件事是侦听 /session/metric 事件并修改要从中收集数据的指标对象。之后,Countly 核心将自动处理指标数据并将其存储在数据库中。

当然,在某些时候,您需要侦听读取路径 /o?method=mymetric 并返回此请求的指标数据。

验证用户后,可以通过要求 Countly fetch 模块并使用其辅助方法 fetch.fetchTimeObj 来输出指标数据

唯一需要考虑的其他事项是,当应用重置或被删除时,您需要删除指标数据。

这就是 API 端,下面是一个添加指标的示例,称为 my metric:

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
67
68
69
var plugin = {},
common = require('../../../api/utils/common.js'),
{validateUserForRead} = require('../../../api/utils/rights.js');
plugins = require('../../pluginManager.js'),
fetch = require('../../../api/parts/data/fetch.js');

(function(plugin) {

//waiting for metrics to be received
plugins.register("/session/metrics", function(ob) {
var predefinedMetrics = ob.predefinedMetrics;

//tell countly to process our metric
predefinedMetrics.push({
db: "mymetric", //collection name
metrics: [{
name: "_mymetric", //what to wait for in query string
set: "mymetric", // metric mymetric
short_code: "mymetric"
} //optionally can provide short name
]
});
});

//waiting for read request
plugins.register("/o", function(ob) {
var params = ob.params;

//if user requested to read our metric
if (params.qstring.method == "mymetric") {

//validate user and output data using fetchTimeObj method
validateUserForRead(params, fetch.fetchTimeObj, 'mymetric');

//return true, we responded to this request
return true;
}

//else we are not interested in this request
return false;
});

//waiting for app delete event
plugins.register("/i/apps/delete", function(ob) {
var appId = ob.appId;

//delete all app data from our metric collection
common.db.collection('mymetric').remove({
'_id': {
$regex: appId + ".*"
}
}, function() {});
});

//waiting for app reset event
plugins.register("/i/apps/reset", function(ob) {
var appId = ob.appId;

//delete all app data from our metric collection
common.db.collection('mymetric').remove({
'_id': {
$regex: appId + ".*"
}
}, function() {});

});
}(plugin));

module.exports = plugin;

就是这样。现在,您可以在 Countly 设置中使用您自己的指标向 Countly 设置发出标准指标请求,它将像其他所有指标一样被跟踪。

对于这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
/i?
begin_session=1
&app_key=YOUR-APP-KEY
&device_id=YOUR_DEVICE_ID
&metrics={
"_os": "Android",
"_os_version": "4.1",
"_device": "Samsung Galaxy",
"_resolution": "1200x800",
"_carrier": "Vodafone",
"_app_version": "1.2",
"_mymetric": "myvalue"
}

/i/bulk requests 批量请求

您不需要专门处理批量请求,Countly core 会为您完成,将批量请求拆分为单个 /i 请求,这些请求由您的插件像往常一样处理。