AppInformationService can now use clickhouse to pull metrics

This commit is contained in:
jelveh
2024-12-25 17:57:22 -08:00
parent 3d32f4123b
commit 89eb6a80e1
2 changed files with 62 additions and 9 deletions
+35 -1
View File
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const APIError = require("../../api/APIError");
const { app_name_exists, get_user, refresh_apps_cache } = require("../../helpers");
const { app_name_exists, refresh_apps_cache } = require("../../helpers");
const { AppUnderUserActorType } = require("../../services/auth/Actor");
const { DB_WRITE } = require("../../services/database/consts");
@@ -35,6 +35,13 @@ class AppES extends BaseES {
const services = this.context.get('services');
this.db = services.get('database').get(DB_WRITE, 'apps');
},
/**
* Creates query predicates for filtering apps
* @param {string} id - Predicate identifier
* @param {...any} args - Additional arguments for predicate creation
* @returns {Promise<Eq|Like>} Query predicate object
*/
async create_predicate (id, ...args) {
if ( id === 'user-can-edit' ) {
return new Eq({
@@ -53,6 +60,12 @@ class AppES extends BaseES {
const svc_appInformation = this.context.get('services').get('app-information');
await svc_appInformation.delete_app(uid);
},
/**
* Filters app selection based on user permissions and visibility settings
* @param {Object} options - Selection options including predicates
* @returns {Promise<Object>} Filtered selection results
*/
async select (options) {
const actor = Context.get('actor');
const user = actor.type.user;
@@ -85,6 +98,13 @@ class AppES extends BaseES {
return await this.upstream.select(options);
},
/**
* Creates or updates an application with proper name handling and associations
* @param {Object} entity - Application entity to upsert
* @param {Object} extra - Additional upsert parameters
* @returns {Promise<Object>} Upsert operation results
*/
async upsert (entity, extra) {
if ( await app_name_exists(await entity.get('name')) ) {
const { old_entity } = extra;
@@ -242,6 +262,11 @@ class AppES extends BaseES {
};
return await recurse(predicate);
},
/**
* Transforms app data before reading by adding associations and handling permissions
* @param {Object} entity - App entity to transform
*/
async read_transform (entity) {
// Add file associations
const rows = await this.db.read(
@@ -264,6 +289,7 @@ class AppES extends BaseES {
? origin : null ;
})());
// Check if the user is the owner
const is_owner = await (async () => {
let owner = await entity.get('owner');
@@ -277,6 +303,7 @@ class AppES extends BaseES {
return actor.type.user.id === owner.id;
})();
// Remove fields that are not allowed for non-owners
if ( ! is_owner ) {
entity.del('approved_for_listing');
entity.del('approved_for_opening_items');
@@ -304,6 +331,13 @@ class AppES extends BaseES {
}
}
},
/**
* Creates a subdomain entry for the app if required
* @param {Object} entity - App entity
* @returns {Promise<number|undefined>} Subdomain ID if created
* @private
*/
async maybe_insert_subdomain_ (entity) {
// Create and update is a situation where we might create a subdomain
@@ -136,20 +136,39 @@ class AppInformationService {
const key_open_count = `apps:open_count:uid:${app_uid}`;
let open_count = kv.get(key_open_count);
if ( ! open_count ) {
open_count = (await db.read(
`SELECT COUNT(_id) AS open_count FROM app_opens WHERE app_uid = ?`,
[app_uid]
))[0].open_count;
if(global.clickhouseClient) {
const result = await global.clickhouseClient.query({
query: `SELECT COUNT(_id) AS open_count FROM app_opens WHERE app_uid = '${app_uid}'`,
format: 'JSONEachRow'
});
const rows = await result.json();
open_count = rows[0].open_count;
}else{
open_count = (await db.read(
`SELECT COUNT(_id) AS open_count FROM app_opens WHERE app_uid = ?`,
[app_uid]
))[0].open_count;
}
}
// TODO: cache
const key_user_count = `apps:user_count:uid:${app_uid}`;
let user_count = kv.get(key_user_count);
if ( ! user_count ) {
user_count = (await db.read(
`SELECT COUNT(DISTINCT user_id) AS user_count FROM app_opens WHERE app_uid = ?`,
[app_uid]
))[0].user_count;
if(global.clickhouseClient) {
const result = await global.clickhouseClient.query({
query: `SELECT COUNT(DISTINCT user_id) AS uniqueUsers FROM app_opens WHERE app_uid = '${app_uid}'`,
format: 'JSONEachRow'
});
const rows = await result.json();
user_count = rows[0].uniqueUsers;
}else{
user_count = (await db.read(
`SELECT COUNT(DISTINCT user_id) AS user_count FROM app_opens WHERE app_uid = ?`,
[app_uid]
))[0].user_count;
}
}
const key_referral_count = `apps:referral_count:uid:${app_uid}`;