Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2755892
#373: first draft with in-memory implementation and tests
petermasking Jan 30, 2025
d12a189
#373: added event broker as resource
petermasking Jan 30, 2025
7cbcd10
#373: implemented post count related events
petermasking Jan 30, 2025
351eef3
#373: second draft (pub/sub)
petermasking Jan 30, 2025
7ba49dd
#373: implemented first notifications events (and refactored the rest)
petermasking Jan 31, 2025
05336c5
#373: added remaining events
petermasking Jan 31, 2025
e32edd5
#373 removed obsolete test cases
petermasking Jan 31, 2025
c2e0686
#373: removed kafka references
petermasking Jan 31, 2025
691cc25
Merge branch 'main' into 373-event-support
petermasking Feb 3, 2025
2d12e0d
#373: merged reactions into posts
petermasking Feb 3, 2025
213b8dc
#373: refactored rating system
petermasking Feb 4, 2025
f161ba3
#373: made publications a part of the SAGAs
petermasking Feb 4, 2025
47b809c
#373: split off metrics
petermasking Feb 4, 2025
906eaae
#373: refactored tests
petermasking Feb 5, 2025
791eaa1
#373: renamed post getAll feature (it doesn't get all)
petermasking Feb 5, 2025
81b3728
#373: refactored post metrics
petermasking Feb 5, 2025
e7fcd60
#373: refactored creator metrics
petermasking Feb 5, 2025
e36b32e
#373: fixed Firefox compatibility issue
petermasking Feb 5, 2025
f2f6dbe
#373: fixed subscription issues
petermasking Feb 5, 2025
a4d4e9b
#373: added back row to post details
petermasking Feb 5, 2025
b02e345
#373: cleanups
petermasking Feb 6, 2025
231716a
#373: increased body limit for services behind the proxy
petermasking Feb 7, 2025
cdc5a33
#373: solved sonar issues
petermasking Feb 7, 2025
0a1a010
#373: processed feedback
petermasking Feb 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ DATABASE_IMPLEMENTATION="mongodb"
MONGODB_CONNECTION_STRING="mongodb://development:development@localhost:27017"
MONGODB_DATABASE_NAME="comify"

# EVENT BROKER (memory)
EVENT_BROKER_IMPLEMENTATION="memory"

# FILE STORE (memory | minio)
FILE_STORE_IMPLEMENTATION="minio"
MINIO_END_POINT="localhost"
Expand Down
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@
"standalone": "jitar start --env-file=development.env --service=services/standalone.json --http-body-limit=512000",
"repository": "jitar start --env-file=development.env --service=services/repository.json",
"proxy": "jitar start --env-file=development.env --service=services/proxy.json --http-body-limit=512000",
"gateway": "jitar start --env-file=development.env --service=services/gateway.json --http-body-limit=512000",
"bff": "jitar start --env-file=development.env --service=services/bff.json --http-body-limit=512000",
"notification": "jitar start --env-file=development.env --service=services/notification.json --http-body-limit=512000",
"notification2": "jitar start --env-file=development.env --service=services/notification2.json --http-body-limit=512000",
"reads": "jitar start --env-file=development.env --service=services/reads.json --http-body-limit=512000",
"writes": "jitar start --env-file=development.env --service=services/writes.json --http-body-limit=512000"
"gateway": "jitar start --env-file=development.env --service=services/gateway.json --http-body-limit=640000",
"bff": "jitar start --env-file=development.env --service=services/bff.json --http-body-limit=640000",
"notification": "jitar start --env-file=development.env --service=services/notification.json --http-body-limit=640000",
"notification2": "jitar start --env-file=development.env --service=services/notification2.json --http-body-limit=640000",
"reads": "jitar start --env-file=development.env --service=services/reads.json --http-body-limit=640000",
"writes": "jitar start --env-file=development.env --service=services/writes.json --http-body-limit=640000"
},
"files": [
"CHANGELOG.md",
Expand Down
1 change: 1 addition & 0 deletions resources/global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[
"./integrations/authentication",
"./integrations/database",
"./integrations/eventbroker",
"./integrations/filestore",
"./integrations/logging",
"./integrations/notification"
Expand Down
36 changes: 21 additions & 15 deletions segments/bff.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,37 @@

"./domain/creator/getByNicknameAggregated": { "default": { "access": "public" } },
"./domain/creator/getMeAggregated": { "default": { "access": "public" } },
"./domain/creator/updateFullName": { "default": { "access": "public" }},
"./domain/creator/updateNickname": { "default": { "access": "public" }},
"./domain/creator/updateFullName": { "default": { "access": "public" } },
"./domain/creator/updateNickname": { "default": { "access": "public" } },

"./domain/creator.metrics/create": { "subscriptions": { "access": "private" } },
"./domain/creator.metrics/updateFollowers": { "subscriptions": { "access": "private" } },
"./domain/creator.metrics/updateFollowing": { "subscriptions": { "access": "private" } },
"./domain/creator.metrics/updatePosts": { "subscriptions": { "access": "private" } },

"./domain/notification/notify": { "subscriptions": { "access": "private" } },
"./domain/notification/getRecentAggregated": { "default": { "access": "public" } },
"./domain/notification/getByIdAggregated": { "default": { "access": "public" } },

"./domain/post/add": { "default": { "access": "public" } },
"./domain/post/create": { "default": { "access": "private" }, "subscribe": { "access": "private" } },
"./domain/post/createWithComic": { "default": { "access": "public" } },
"./domain/post/createWithComment": { "default": { "access": "public" } },
"./domain/post/exploreAggregated": { "default": { "access": "public" } },
"./domain/post/getByIdAggregated": { "default": { "access": "public" } },
"./domain/post/getByParentAggregated": { "default": { "access": "public" } },
"./domain/post/getByCreatorAggregated": { "default": { "access": "public" } },
"./domain/post/getAllAggregated": { "default": { "access": "public"}},
"./domain/post/getByFollowingAggregated": { "default": { "access": "public" } },
"./domain/post/remove": { "default": { "access": "public" } },
"./domain/post/toggleRating": { "default": { "access": "public" } },
"./domain/post/getRecommendedAggregated": { "default": { "access": "public"}},
"./domain/post/remove": { "default": { "access": "public" }, "subscribe": { "access": "private" } },

"./domain/post.metrics/create": { "subscriptions": { "access": "private" } },
"./domain/post.metrics/updateRatings": { "subscriptions": { "access": "private" } },
"./domain/post.metrics/updateReactions": { "subscriptions": { "access": "private" } },

"./domain/rating/toggle": { "default": { "access": "public" }, "subscribe": { "access": "private" } },

"./domain/reaction/createWithComic": { "default": { "access": "public" } },
"./domain/reaction/createWithComment": { "default": { "access": "public" } },
"./domain/reaction/getByIdAggregated": { "default": { "access": "public" } },
"./domain/reaction/getByPostAggregated": { "default": { "access": "public" } },
"./domain/reaction/getByReactionAggregated": { "default": { "access": "public"} },
"./domain/reaction/remove": { "default": { "access": "public" } },
"./domain/reaction/toggleRating": { "default": { "access": "public" } },

"./domain/relation/exploreAggregated": { "default": { "access": "public" } },
"./domain/relation/establish": { "default": { "access": "public" }},
"./domain/relation/establish": { "default": { "access": "public" }, "subscribe": { "access": "private" } },
"./domain/relation/getAggregated": { "default": { "access": "public" } },
"./domain/relation/getFollowersAggregated": { "default": { "access": "public" } },
"./domain/relation/getFollowingAggregated": { "default": { "access": "public" } }
Expand Down
12 changes: 7 additions & 5 deletions segments/reads.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,22 @@
"./domain/creator/getByNickname": { "default": { "access": "protected" } },
"./domain/creator/getMe": { "default": { "access": "protected" } },

"./domain/creator.metrics/getByCreator": { "default": { "access": "protected" } },

"./domain/image/getById": { "default": { "access": "protected" } },

"./domain/post/explore": { "default": { "access": "protected" } },
"./domain/post/getAll": { "default": { "access": "protected" } },
"./domain/post/getByCreator": { "default": { "access": "protected" } },
"./domain/post/getByFollowing": { "default": { "access": "protected" } },
"./domain/post/getById": { "default": { "access": "protected" } },
"./domain/post/getByParent": { "default": { "access": "protected" } },
"./domain/post/getRecommended": { "default": { "access": "protected" } },

"./domain/rating/exists": { "default": { "access": "protected" } },
"./domain/post.metrics/getByPost": { "default": { "access": "protected" } },

"./domain/reaction/getById": { "default": { "access": "protected" } },
"./domain/reaction/getByPost": { "default": { "access": "protected" } },
"./domain/rating/exists": { "default": { "access": "protected" } },

"./domain/relation/establish": { "default": { "access": "protected" } },
"./domain/relation/exists": { "default": { "access": "protected" } },
"./domain/relation/explore": { "default": { "access": "protected" } },
"./domain/relation/get": { "default": { "access": "protected" } },
"./domain/relation/getFollowers": { "default": { "access": "protected" } },
Expand Down
15 changes: 8 additions & 7 deletions segments/writes.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@
"./domain/creator/create": { "default": { "access": "protected" } },
"./domain/creator/update": { "default": { "access": "protected" } },

"./domain/creator.metrics/create/insertData": { "default": { "access": "protected" } },

"./domain/image/save": { "default": { "access": "protected" } },
"./domain/image/erase": { "default": { "access": "protected" } },

"./domain/post/create": { "default": { "access": "protected" } },
"./domain/post/create/insertData": { "default": { "access": "protected" } },
"./domain/post/erase": { "default": { "access": "protected" } },
"./domain/post/remove/deleteData": { "default": { "access": "protected" }},
"./domain/post/update": { "default": { "access": "protected" } },

"./domain/rating/update": { "default": { "access": "protected" } },
"./domain/post.metrics/create/insertData": { "default": { "access": "protected" } },

"./domain/reaction/create": { "default": { "access": "protected" } },
"./domain/reaction/remove/deleteData": { "default": { "access": "protected" } },
"./domain/reaction/update": { "default": { "access": "protected" } },
"./domain/rating/create": { "default": { "access": "protected" } },
"./domain/rating/erase": { "default": { "access": "protected" } },

"./domain/relation/establish/insertData": { "default": { "access": "protected" } },
"./domain/relation/establish/eraseData": { "default": { "access": "protected" } }
"./domain/relation/create": { "default": { "access": "protected" } },
"./domain/relation/erase": { "default": { "access": "protected" } }
}
10 changes: 10 additions & 0 deletions src/domain/creator.metrics/create/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

import createData from './createData';
import insertData from './insertData';

export default async function create(creatorId: string): Promise<string>
{
const data = createData(creatorId);

return insertData(data);
}
16 changes: 16 additions & 0 deletions src/domain/creator.metrics/create/createData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

import { generateId } from '^/integrations/utilities/crypto';

import type { DataModel } from '../types';

export default function createData(creatorId: string): DataModel
{
return {
id: generateId(),
creatorId,
posts: 0,
followers: 0,
following: 0,
popularity: 0
};
}
4 changes: 4 additions & 0 deletions src/domain/creator.metrics/create/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export { default } from './create';

export { default as subscriptions } from './subscriptions';
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
import database from '^/integrations/database';

import { RECORD_TYPE } from '../definitions';
import { DataModel } from '../types';
import type { DataModel } from '../types';

export default async function insertData(data: DataModel): Promise<string>
{
return database.createRecord(RECORD_TYPE, { ...data, deleted: false });
return database.createRecord(RECORD_TYPE, { ...data });
}
13 changes: 13 additions & 0 deletions src/domain/creator.metrics/create/subscriptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

import { subscribe as subscribeToCreatorRegistered } from '^/domain/creator/register';

import create from './create';

async function subscribe(): Promise<void>
{
await Promise.all([
subscribeToCreatorRegistered(({ creatorId }) => create(creatorId)),
]);
}

export default subscribe();
3 changes: 3 additions & 0 deletions src/domain/creator.metrics/definitions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

export const RECORD_TYPE = 'creator.metrics';
export const EVENT_CHANNEL = 'creator.metrics';
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

import { NotFound } from '^/integrations/runtime';

export default class CreatorMetricsNotFound extends NotFound
{
constructor()
{
super('Creator metrics not found');
}
}
21 changes: 21 additions & 0 deletions src/domain/creator.metrics/getByCreator/getByCreator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

import database from '^/integrations/database';

import { RECORD_TYPE } from '../definitions';
import type { DataModel } from '../types';

import CreatorMetricsNotFound from './CreatorMetricsNotFound';

export default async function getByCreator(creatorId: string): Promise<DataModel>
{
const query = { creatorId: { EQUALS: creatorId } };

const data = await database.findRecord(RECORD_TYPE, query) as DataModel;

if (data === undefined)
{
throw new CreatorMetricsNotFound();
}

return data;
}
4 changes: 4 additions & 0 deletions src/domain/creator.metrics/getByCreator/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export { default } from './getByCreator';

export { default as CreatorMetricsNotFound } from './CreatorMetricsNotFound';
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,3 @@
export { RECORD_TYPE } from './definitions';

export type { DataModel } from './types';

export { default as ReactionNotFound } from './ReactionNotFound';
13 changes: 13 additions & 0 deletions src/domain/creator.metrics/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

import { BaseDataModel, CountOperation } from '../types';

type DataModel = BaseDataModel &
{
readonly creatorId: string;
readonly posts: number;
readonly followers: number;
readonly following: number;
readonly popularity: number;
};

export type { CountOperation, DataModel };
4 changes: 4 additions & 0 deletions src/domain/creator.metrics/updateFollowers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export { default } from './updateFollowers';

export { default as subscriptions } from './subscriptions';
13 changes: 13 additions & 0 deletions src/domain/creator.metrics/updateFollowers/subscriptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

import { subscribe as subscribeToRelationEstablished } from '^/domain/relation/establish';

import updateFollowerCount from './updateFollowers';

async function subscribe(): Promise<void>
{
await Promise.all([
subscribeToRelationEstablished(({ followingId }) => updateFollowerCount(followingId, 'increase'))
]);
}

export default subscribe();
18 changes: 18 additions & 0 deletions src/domain/creator.metrics/updateFollowers/updateFollowers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

import getByCreator from '../getByCreator';
import update from '../update';

import type { CountOperation } from '../types';

export default async function updateFollowers(creatorId: string, operation: CountOperation): Promise<number>
{
const data = await getByCreator(creatorId);

const followers = operation === 'increase'
? data.followers + 1
: data.followers - 1;

await update(data.id, { followers });

return followers;
}
4 changes: 4 additions & 0 deletions src/domain/creator.metrics/updateFollowing/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export { default } from './updateFollowing';

export { default as subscriptions } from './subscriptions';
13 changes: 13 additions & 0 deletions src/domain/creator.metrics/updateFollowing/subscriptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

import { subscribe as subscribeToRelationEstablished } from '^/domain/relation/establish';

import updateFollowing from './updateFollowing';

async function subscribe(): Promise<void>
{
await Promise.all([
subscribeToRelationEstablished(({ followerId }) => updateFollowing(followerId, 'increase'))
]);
}

export default subscribe();
18 changes: 18 additions & 0 deletions src/domain/creator.metrics/updateFollowing/updateFollowing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

import getByCreator from '../getByCreator';
import update from '../update';

import type { CountOperation } from '../types';

export default async function updateFollowing(creatorId: string, operation: CountOperation): Promise<number>
{
const data = await getByCreator(creatorId);

const following = operation === 'increase'
? data.following + 1
: data.following - 1;

await update(data.id, { following });

return following;
}
4 changes: 4 additions & 0 deletions src/domain/creator.metrics/updatePosts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export { default } from './updatePosts';

export { default as subscriptions } from './subscriptions';
28 changes: 28 additions & 0 deletions src/domain/creator.metrics/updatePosts/subscriptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

import { subscribe as subscribeToPostCreated } from '^/domain/post/create';
import { subscribe as subscribeToPostRemoved } from '^/domain/post/remove';

import updatePosts from './updatePosts';

async function subscribe(): Promise<void>
{
await Promise.all([

subscribeToPostCreated(({ creatorId, parentId }) =>
{
if (parentId !== undefined) return;

return updatePosts(creatorId, 'increase');
}),

subscribeToPostRemoved(({ creatorId, parentId }) =>
{
if (parentId !== undefined) return;

return updatePosts(creatorId, 'decrease');
})

]);
}

export default subscribe();
18 changes: 18 additions & 0 deletions src/domain/creator.metrics/updatePosts/updatePosts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

import getByCreator from '../getByCreator';
import update from '../update';

import type { CountOperation } from '../types';

export default async function updatePosts(creatorId: string, operation: CountOperation): Promise<number>
{
const data = await getByCreator(creatorId);

const posts = operation === 'increase'
? data.posts + 1
: data.posts - 1;

await update(data.id, { posts });

return posts;
}
Loading