From d577989343324903bf9030c7d459c6c9d56477a5 Mon Sep 17 00:00:00 2001 From: Amritpal Singh Date: Thu, 24 Oct 2024 18:10:44 +0530 Subject: [PATCH] LENS-305: implemented backend service to store sharelink analytic --- backend/src/client.js | 4 + backend/src/hooks/handle-public-info-query.js | 26 +++--- backend/src/services/index.js | 5 ++ .../share-link-analytics.class.js | 11 +++ .../share-link-analytics.js | 83 +++++++++++++++++++ .../share-link-analytics.schema.js | 67 +++++++++++++++ .../share-link-analytics.shared.js | 11 +++ 7 files changed, 195 insertions(+), 12 deletions(-) create mode 100644 backend/src/services/share-link-analytics/share-link-analytics.class.js create mode 100644 backend/src/services/share-link-analytics/share-link-analytics.js create mode 100644 backend/src/services/share-link-analytics/share-link-analytics.schema.js create mode 100644 backend/src/services/share-link-analytics/share-link-analytics.shared.js diff --git a/backend/src/client.js b/backend/src/client.js index a3f1b1b1..fea96f87 100644 --- a/backend/src/client.js +++ b/backend/src/client.js @@ -1,6 +1,8 @@ // For more information about this file see https://dove.feathersjs.com/guides/cli/client.html import { feathers } from '@feathersjs/feathers' import authenticationClient from '@feathersjs/authentication-client' +import { shareLinkAnalyticsClient } from './services/share-link-analytics/share-link-analytics.shared.js' + import { userEngagementsClient } from './services/user-engagements/user-engagements.shared.js' import { notificationsClient } from './services/notifications/notifications.shared.js' @@ -108,5 +110,7 @@ export const createClient = (connection, authenticationOptions = {}) => { client.configure(publisherClient) + client.configure(shareLinkAnalyticsClient) + return client } diff --git a/backend/src/hooks/handle-public-info-query.js b/backend/src/hooks/handle-public-info-query.js index ecf41904..62ce6293 100644 --- a/backend/src/hooks/handle-public-info-query.js +++ b/backend/src/hooks/handle-public-info-query.js @@ -54,15 +54,17 @@ export const ThrowBadRequestIfNotForPublicInfo = async (context) => { return context; } -// -// export const tryToAuthenticate = () => { -// // this is an "around" hook -// // Try To authenticate context but also allow anonymous calls -// return async (context, next) => { -// if (context.params.user) { -// await authenticate("jwt")(context, next); -// } -// await next(); -// return context; -// } -// } \ No newline at end of file + +export const tryToAuthenticate = () => { + // this is an "around" hook + // Try To authenticate context but also allow anonymous calls + return async (context, next) => { + try { + await authenticate("jwt")(context, next); + } catch (e) { + console.log(e); + await next(); + } + return context; + } +} \ No newline at end of file diff --git a/backend/src/services/index.js b/backend/src/services/index.js index 9fc2d46b..cc693f21 100644 --- a/backend/src/services/index.js +++ b/backend/src/services/index.js @@ -44,6 +44,9 @@ import { download } from './download/download.js' import { publisher } from './publisher/publisher.js' +import { shareLinkAnalytics } from './share-link-analytics/share-link-analytics.js' + + export const services = (app) => { app.configure(orgSecondaryReferences) @@ -91,5 +94,7 @@ export const services = (app) => { app.configure(publisher) + app.configure(shareLinkAnalytics) + // All services will be registered here } diff --git a/backend/src/services/share-link-analytics/share-link-analytics.class.js b/backend/src/services/share-link-analytics/share-link-analytics.class.js new file mode 100644 index 00000000..bdb4e9cc --- /dev/null +++ b/backend/src/services/share-link-analytics/share-link-analytics.class.js @@ -0,0 +1,11 @@ +import { MongoDBService } from '@feathersjs/mongodb' + +// By default calls the standard MongoDB adapter service methods but can be customized with your own functionality. +export class ShareLinkAnalyticsService extends MongoDBService {} + +export const getOptions = (app) => { + return { + paginate: app.get('paginate'), + Model: app.get('mongodbClient').then((db) => db.collection('share-link-analytics')) + } +} diff --git a/backend/src/services/share-link-analytics/share-link-analytics.js b/backend/src/services/share-link-analytics/share-link-analytics.js new file mode 100644 index 00000000..a4e3e93a --- /dev/null +++ b/backend/src/services/share-link-analytics/share-link-analytics.js @@ -0,0 +1,83 @@ +// For more information about this file see https://dove.feathersjs.com/guides/cli/service.html +import { authenticate } from '@feathersjs/authentication' + +import { hooks as schemaHooks } from '@feathersjs/schema' +import { + shareLinkAnalyticsDataValidator, + shareLinkAnalyticsPatchValidator, + shareLinkAnalyticsQueryValidator, + shareLinkAnalyticsResolver, + shareLinkAnalyticsExternalResolver, + shareLinkAnalyticsDataResolver, + shareLinkAnalyticsPatchResolver, + shareLinkAnalyticsQueryResolver, + shareLinkAnalyticsPatchSchema, + shareLinkAnalyticsDataSchema, + shareLinkAnalyticsQuerySchema, + shareLinkAnalyticsSchema, +} from './share-link-analytics.schema.js' +import { ShareLinkAnalyticsService, getOptions } from './share-link-analytics.class.js' +import { shareLinkAnalyticsPath, shareLinkAnalyticsMethods } from './share-link-analytics.shared.js' +import {disallow} from "feathers-hooks-common"; +import {authenticateJwtWhenPrivate, tryToAuthenticate} from "../../hooks/handle-public-info-query.js"; +import swagger from "feathers-swagger"; + +export * from './share-link-analytics.class.js' +export * from './share-link-analytics.schema.js' + +// A configure function that registers the service and its hooks via `app.configure` +export const shareLinkAnalytics = (app) => { + // Register our service on the Feathers application + app.use(shareLinkAnalyticsPath, new ShareLinkAnalyticsService(getOptions(app)), { + // A list of all methods this service exposes externally + methods: shareLinkAnalyticsMethods, + // You can add additional custom events to be sent to clients here + events: [], + docs: swagger.createSwaggerServiceOptions({ + schemas: { shareLinkAnalyticsSchema, shareLinkAnalyticsDataSchema, shareLinkAnalyticsQuerySchema, shareLinkAnalyticsPatchSchema }, + docs: { + description: 'A Share link analytics service', + idType: 'string', + securities: ['all'], + } + }) + }) + // Initialize hooks + app.service(shareLinkAnalyticsPath).hooks({ + around: { + all: [ + schemaHooks.resolveExternal(shareLinkAnalyticsExternalResolver), + schemaHooks.resolveResult(shareLinkAnalyticsResolver) + ], + find: [authenticate('jwt')], + get: [authenticate('jwt')], + create: [tryToAuthenticate()], + update: [disallow('external')], + patch: [disallow('external')], + remove: [disallow('external')], + }, + before: { + all: [ + schemaHooks.validateQuery(shareLinkAnalyticsQueryValidator), + schemaHooks.resolveQuery(shareLinkAnalyticsQueryResolver) + ], + find: [], + get: [], + create: [ + schemaHooks.validateData(shareLinkAnalyticsDataValidator), + schemaHooks.resolveData(shareLinkAnalyticsDataResolver) + ], + patch: [ + schemaHooks.validateData(shareLinkAnalyticsPatchValidator), + schemaHooks.resolveData(shareLinkAnalyticsPatchResolver) + ], + remove: [] + }, + after: { + all: [] + }, + error: { + all: [] + } + }) +} diff --git a/backend/src/services/share-link-analytics/share-link-analytics.schema.js b/backend/src/services/share-link-analytics/share-link-analytics.schema.js new file mode 100644 index 00000000..e64add73 --- /dev/null +++ b/backend/src/services/share-link-analytics/share-link-analytics.schema.js @@ -0,0 +1,67 @@ +// // For more information about this file see https://dove.feathersjs.com/guides/cli/service.schemas.html +import { resolve } from '@feathersjs/schema' +import { Type, getValidator, querySyntax } from '@feathersjs/typebox' +import { ObjectIdSchema } from '@feathersjs/typebox' +import { dataValidator, queryValidator } from '../../validators.js' +import {userSummarySchema} from "../users/users.subdocs.schema.js"; +import {SourceType} from "../user-engagements/user-engagement.subdocs.schema.js"; +import {buildUserSummary} from "../users/users.distrib.js"; + +// Main data model schema +export const shareLinkAnalyticsSchema = Type.Object( + { + _id: ObjectIdSchema(), + sharedModelId: ObjectIdSchema(), + accessedBy: Type.Optional(userSummarySchema), + accessedFrom: SourceType, + accessedAt: Type.Number(), + additionalData: Type.Optional(Type.Object({})), + }, + { $id: 'ShareLinkAnalytics', additionalProperties: false } +) +export const shareLinkAnalyticsValidator = getValidator(shareLinkAnalyticsSchema, dataValidator) +export const shareLinkAnalyticsResolver = resolve({}) + +export const shareLinkAnalyticsExternalResolver = resolve({}) + +// Schema for creating new entries +export const shareLinkAnalyticsDataSchema = Type.Pick(shareLinkAnalyticsSchema, [ + 'sharedModelId', + 'accessedFrom', + 'additionalData', +], { + $id: 'ShareLinkAnalyticsData' +}) +export const shareLinkAnalyticsDataValidator = getValidator(shareLinkAnalyticsDataSchema, dataValidator) +export const shareLinkAnalyticsDataResolver = resolve({ + accessedBy: async (_value, _message, context) => { + // Associate the record with the id of the authenticated user + if (context.params.user) { + return buildUserSummary(context.params.user); + } + return undefined; + }, + accessedAt: async () => Date.now(), +}) + +// Schema for updating existing entries +export const shareLinkAnalyticsPatchSchema = Type.Partial(shareLinkAnalyticsSchema, { + $id: 'ShareLinkAnalyticsPatch' +}) +export const shareLinkAnalyticsPatchValidator = getValidator(shareLinkAnalyticsPatchSchema, dataValidator) +export const shareLinkAnalyticsPatchResolver = resolve({}) + +// Schema for allowed query properties +export const shareLinkAnalyticsQueryProperties = Type.Pick(shareLinkAnalyticsSchema, [ + '_id', 'sharedModelId', 'accessedBy', 'accessedAt', 'accessedFrom' +]) +export const shareLinkAnalyticsQuerySchema = Type.Intersect( + [ + querySyntax(shareLinkAnalyticsQueryProperties), + // Add additional query properties here + Type.Object({}, { additionalProperties: false }) + ], + { additionalProperties: false } +) +export const shareLinkAnalyticsQueryValidator = getValidator(shareLinkAnalyticsQuerySchema, queryValidator) +export const shareLinkAnalyticsQueryResolver = resolve({}) diff --git a/backend/src/services/share-link-analytics/share-link-analytics.shared.js b/backend/src/services/share-link-analytics/share-link-analytics.shared.js new file mode 100644 index 00000000..0637c72d --- /dev/null +++ b/backend/src/services/share-link-analytics/share-link-analytics.shared.js @@ -0,0 +1,11 @@ +export const shareLinkAnalyticsPath = 'share-link-analytics' + +export const shareLinkAnalyticsMethods = ['find', 'get', 'create', 'patch', 'remove'] + +export const shareLinkAnalyticsClient = (client) => { + const connection = client.get('connection') + + client.use(shareLinkAnalyticsPath, connection.service(shareLinkAnalyticsPath), { + methods: shareLinkAnalyticsMethods + }) +}