Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docker/keycloak/realm-export.json
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,8 @@
"alwaysDisplayInConsole": false,
"clientAuthenticatorType": "client-secret",
"redirectUris": [
"http://localhost:3000/rpc/domain/authentication/login/feature"
"http://localhost:3000/rpc/domain/authentication/login",
"http://localhost:5173/rpc/domain/authentication/login"
],
"webOrigins": [
"https://www.keycloak.org"
Expand Down
1 change: 1 addition & 0 deletions segments/bff.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"./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" } },

Expand Down
12 changes: 7 additions & 5 deletions src/domain/notification/aggregate/aggregate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,20 @@ import type { AggregatedData } from './types';

export default async function aggregate(requester: Requester, data: DataModel): Promise<AggregatedData>
{
const [relationData, postData, reactionData] = await Promise.all([
const [relationData, postData, targetReactionData, sourceReactionData] = await Promise.all([
getRelationData(data.receiverId, data.senderId),
data.postId ? getPostData(requester, data.postId) : undefined,
data.reactionId ? getReactionData(requester, data.reactionId) : undefined
data.targetPostId ? getPostData(requester, data.targetPostId) : undefined,
data.targetReactionId ? getReactionData(requester, data.targetReactionId) : undefined,
data.sourceReactionId ? getReactionData(requester, data.sourceReactionId) : undefined
]);

return {
id: data.id,
createdAt: data.createdAt,
type: data.type,
relation: relationData,
post: postData,
reaction: reactionData
targetPost: postData,
targetReaction: targetReactionData,
sourceReaction: sourceReactionData
};
}
5 changes: 3 additions & 2 deletions src/domain/notification/aggregate/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import { DataModel } from '../types';
type AggregatedData = Pick<DataModel, 'id' | 'createdAt' | 'type'> &
{
readonly relation: AggregatedRelationData;
readonly post?: AggregatedPostData;
readonly reaction?: AggregatedReactionData;
readonly targetPost?: AggregatedPostData;
readonly targetReaction?: AggregatedReactionData;
readonly sourceReaction?: AggregatedReactionData;
};

export type { AggregatedData };
4 changes: 2 additions & 2 deletions src/domain/notification/create/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { Type } from '../definitions';
import createData from './createData';
import insertData from './insertData';

export default async function feature(type: Type, senderId: string, receiverId: string, postId: string | undefined = undefined, reactionId: string | undefined = undefined): Promise<void>
export default async function feature(type: Type, senderId: string, receiverId: string, targetPostId: string | undefined = undefined, targetReactionId: string | undefined = undefined, sourceReactionId: string | undefined = undefined): Promise<void>
{
if (senderId === receiverId)
{
return;
}

const data = createData(type, senderId, receiverId, postId, reactionId);
const data = createData(type, senderId, receiverId, targetPostId, targetReactionId, sourceReactionId);

try
{
Expand Down
7 changes: 4 additions & 3 deletions src/domain/notification/create/createData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ import { generateId } from '^/integrations/utilities/crypto';

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

export default function createData(type: string, senderId: string, receiverId: string, postId: string | undefined = undefined, reactionId: string | undefined = undefined): DataModel
export default function createData(type: string, senderId: string, receiverId: string, targetPostId: string | undefined = undefined, targetReactionId: string | undefined = undefined, sourceReactionId: string | undefined = undefined): DataModel
{
return {
id: generateId(),
createdAt: new Date().toISOString(),
type,
senderId,
receiverId,
postId,
reactionId
targetPostId,
targetReactionId,
sourceReactionId
};
}
3 changes: 2 additions & 1 deletion src/domain/notification/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ export const Types = {
STARTED_FOLLOWING: 'started-following',
RATED_POST: 'rated-post',
RATED_REACTION: 'rated-reaction',
ADDED_REACTION: 'added-reaction'
ADDED_REACTION_POST: 'added-reaction-post',
ADDED_REACTION_REACTION: 'added-reaction-reaction'
} as const;

type TypeKeys = keyof typeof Types;
Expand Down
5 changes: 3 additions & 2 deletions src/domain/notification/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ type DataModel = BaseDataModel &
readonly type: string;
readonly senderId: string;
readonly receiverId: string;
readonly postId?: string;
readonly reactionId?: string;
readonly targetPostId?: string;
readonly targetReactionId?: string;
readonly sourceReactionId?: string;
};

export type { DataModel };
2 changes: 1 addition & 1 deletion src/domain/post/toggleRating/toggleRating.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default async function toggleRating(requester: Requester, postId: string)

const post = await getPost(postId);

await createNotification(Types.RATED_POST, requester.id, post.creatorId, postId);
await createNotification(Types.RATED_POST, requester.id, post.creatorId, postId, undefined);

return true;
}
Expand Down
2 changes: 2 additions & 0 deletions src/domain/reaction/aggregate/aggregate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ export default async function aggregate(requester: Requester, data: DataModel):
id: data.id,
createdAt: data.createdAt,
ratingCount: data.ratingCount,
reactionCount: data.reactionCount,
creator: relationData,
postId: data.postId,
reactionId: data.reactionId,
hasRated,
comic: comicData,
comment: commentData,
Expand Down
4 changes: 2 additions & 2 deletions src/domain/reaction/aggregate/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@

import type { AggregatedData as AggregatedComicData } from '^/domain/comic/aggregate';
import type { DataModel as CommentData } from '^/domain/comment/types';
import type { DataModel as CommentData } from '^/domain/comment';
import type { AggregatedData as AggregatedRelationData } from '^/domain/relation/aggregate';

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

type AggregatedData = Pick<DataModel, 'id' | 'createdAt' | 'ratingCount' | 'postId'> &
type AggregatedData = Pick<DataModel, 'id' | 'createdAt' | 'ratingCount' | 'reactionCount' | 'postId' | 'reactionId'> &
{
readonly creator: AggregatedRelationData;
readonly hasRated: boolean;
Expand Down
29 changes: 22 additions & 7 deletions src/domain/reaction/create/create.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,48 @@

import logger from '^/integrations/logging';

import { Types } from '^/domain/notification';
import createNotification from '^/domain/notification/create';
import { Types } from '^/domain/notification/definitions';
import retrievePost from '^/domain/post/getById';
import updateReactionCount from '^/domain/post/updateReactionCount';
import updatePostReactionCount from '^/domain/post/updateReactionCount';

import retrieveReaction from '../getById';
import updateReactionReactionCount from '../updateReactionCount';

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

export default async function create(creatorId: string, postId: string, comicId: string | undefined = undefined, commentId: string | undefined = undefined): Promise<string>
export default async function feature(creatorId: string, postId: string | undefined = undefined, reactionId: string | undefined = undefined, comicId: string | undefined = undefined, commentId: string | undefined = undefined): Promise<string>
{
let id;

try
{
const data = createData(creatorId, postId, comicId, commentId);
const data = createData(creatorId, postId, reactionId, comicId, commentId);

validateData(data);

id = await insertData(data);

await updateReactionCount(postId, 'increase');
if (postId !== undefined)
{
await updatePostReactionCount(postId, 'increase');

const post = await retrievePost(postId);

await createNotification(Types.ADDED_REACTION_POST, creatorId, post.creatorId, postId, undefined, id);
}

if (reactionId !== undefined)
{
await updateReactionReactionCount(reactionId, 'increase');

const post = await retrievePost(postId);
const reaction = await retrieveReaction(reactionId);

await createNotification(Types.ADDED_REACTION, creatorId, post.creatorId, postId, id);
await createNotification(Types.ADDED_REACTION_REACTION, creatorId, reaction.creatorId, undefined, reactionId, id);
}

return id;
}
Expand Down
6 changes: 4 additions & 2 deletions src/domain/reaction/create/createData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ import { generateId } from '^/integrations/utilities/crypto';

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

export default function createData(creatorId: string, postId: string, comicId: string | undefined = undefined, commentId: string | undefined = undefined): DataModel
export default function createData(creatorId: string, postId: string | undefined = undefined, reactionId: string | undefined = undefined, comicId: string | undefined = undefined, commentId: string | undefined = undefined): DataModel
{
return {
id: generateId(),
createdAt: new Date().toISOString(),
creatorId,
postId,
reactionId,
comicId,
commentId,
ratingCount: 0
ratingCount: 0,
reactionCount: 0
};
}
2 changes: 1 addition & 1 deletion src/domain/reaction/create/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

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

type ValidationModel = Pick<DataModel, 'creatorId' | 'postId' | 'comicId' | 'commentId'>;
type ValidationModel = Pick<DataModel, 'creatorId' | 'postId' | 'reactionId' | 'comicId' | 'commentId'>;

export type { ValidationModel };
14 changes: 12 additions & 2 deletions src/domain/reaction/create/validateData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ import { ValidationModel } from './types';
const schema: ValidationSchema =
{
creatorId: requiredIdValidation,
postId: requiredIdValidation,
postId: optionalIdValidation,
reactionId: optionalIdValidation,
comicId: optionalIdValidation,
commentId: optionalIdValidation
};

export default function validateData({ creatorId, postId, comicId, commentId }: ValidationModel): void
export default function validateData({ creatorId, postId, reactionId, comicId, commentId }: ValidationModel): void
{
if (postId === undefined && reactionId === undefined)
{
const messages = new Map()
.set('postId', 'Either postId or reactionId must be provided')
.set('reactionId', 'Either postId or reactionId must be provided');

throw new InvalidReaction(messages);
}

if (comicId === undefined && commentId === undefined)
{
const messages = new Map()
Expand Down
4 changes: 2 additions & 2 deletions src/domain/reaction/createWithComic/createWithComic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import createComic from '^/domain/comic/create';

import createReaction from '../create';

export default async function createWithComic(requester: Requester, postId: string, imageData: string): Promise<string>
export default async function createWithComic(requester: Requester, imageData: string, postId: string | undefined = undefined, reactionId: string | undefined = undefined): Promise<string>
{
const comicId = await createComic(imageData);

return createReaction(requester.id, postId, comicId);
return createReaction(requester.id, postId, reactionId, comicId);
}
4 changes: 2 additions & 2 deletions src/domain/reaction/createWithComment/createWithComment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import createComment from '^/domain/comment/create';

import createReaction from '../create';

export default async function createWithComment(requester: Requester, postId: string, message: string): Promise<string>
export default async function createWithComment(requester: Requester, message: string, postId: string | undefined = undefined, reactionId: string | undefined = undefined): Promise<string>
{
const commentId = await createComment(message);

return createReaction(requester.id, postId, undefined, commentId);
return createReaction(requester.id, postId, reactionId, undefined, commentId);
}
18 changes: 16 additions & 2 deletions src/domain/reaction/getById/getById.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@

import database from '^/integrations/database';
import database, { RecordQuery } from '^/integrations/database';

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

export default async function getById(id: string): Promise<DataModel>
{
return database.readRecord(RECORD_TYPE, id) as Promise<DataModel>;
const query: RecordQuery =
{
id: { 'EQUALS': id },
deleted: { 'EQUALS': false }
};

const record = await database.findRecord(RECORD_TYPE, query);

if (record === undefined)
{
throw new ReactionNotFound();
}

return record as DataModel;
}
18 changes: 18 additions & 0 deletions src/domain/reaction/getByReaction/getByReaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

import database, { RecordQuery, RecordSort, SortDirections } from '^/integrations/database';

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

export default async function getByReaction(reactionId: string, limit: number, offset: number): Promise<DataModel[]>
{
const query: RecordQuery =
{
reactionId: { 'EQUALS': reactionId },
deleted: { 'EQUALS': false }
};

const sort: RecordSort = { createdAt: SortDirections.DESCENDING };

return database.searchRecords(RECORD_TYPE, query, undefined, sort, limit, offset) as Promise<DataModel[]>;
}
2 changes: 2 additions & 0 deletions src/domain/reaction/getByReaction/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

export { default } from './getByReaction';
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

import type { Requester } from '^/domain/authentication/types';
import { Range } from '^/domain/common/validateRange';

import aggregate from '../aggregate';
import type { AggregatedData } from '../aggregate/types';
import getByReaction from '../getByReaction/getByReaction';

export default async function getByReactionAggregated(requester: Requester, reactionId: string, range: Range): Promise<AggregatedData[]>
{
const data = await getByReaction(reactionId, range.limit, range.offset);

return Promise.all(data.map(item => aggregate(requester, item)));
}
2 changes: 2 additions & 0 deletions src/domain/reaction/getByReactionAggregated/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

export { default } from './getByReactionAggregated';
2 changes: 2 additions & 0 deletions src/domain/reaction/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
export { RECORD_TYPE } from './definitions';

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

export { default as ReactionNotFound } from './ReactionNotFound';
2 changes: 0 additions & 2 deletions src/domain/reaction/remove/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@

export { default } from './remove';

export { default as ReactionNotFound } from './ReactionNotFound';
Loading
Loading