Skip to content

Commit

Permalink
Merge pull request #90 from codex-team/feat/teams-base
Browse files Browse the repository at this point in the history
  • Loading branch information
slaveeks authored Oct 22, 2023
2 parents c4993b8 + 4dfb4c2 commit 4598c30
Show file tree
Hide file tree
Showing 14 changed files with 427 additions and 20 deletions.
11 changes: 9 additions & 2 deletions migrations/tenant/[email protected]
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
ALTER TABLE IF EXISTS public.note_settings
RENAME COLUMN enabled TO is_public;
DO $$
BEGIN
IF EXISTS(SELECT *
FROM information_schema.columns
WHERE table_name='note_settings' and column_name='enabled')
THEN
ALTER TABLE "public"."note_settings" RENAME COLUMN "enables" TO "is_public";
END IF;
END $$;
44 changes: 44 additions & 0 deletions src/domain/entities/team.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { NoteInternalId } from './note.js';
import type User from './user.js';

export enum MemberRole {
/**
* Team member can read and write notes
*/
read = 'read',

/**
* Team member can only read notes
*/
write = 'write',
}

/**
* Class representing a team entity
* Team is a relation between note and user, which shows what user can do with note
*/
export interface TeamMember {
/**
* Team relation id
*/
id: number;

/**
* Note ID
*/
noteId: NoteInternalId;

/**
* Team member user id
*/
userId: User['id'];

/**
* Team member role, show what user can do with note
*/
role: MemberRole;
}

export type Team = TeamMember[];

export type TeamMemberCreationAttributes = Omit<TeamMember, 'id'>;
2 changes: 1 addition & 1 deletion src/domain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export interface DomainServices {
*/
export function init(repositories: Repositories, appConfig: AppConfig): DomainServices {
const noteService = new NoteService(repositories.noteRepository);
const noteSettingsService = new NoteSettingsService(repositories.noteSettingsRepository);
const noteSettingsService = new NoteSettingsService(repositories.noteSettingsRepository, repositories.teamRepository);

const noteListService = new NoteListService(repositories.noteRepository);

Expand Down
63 changes: 55 additions & 8 deletions src/domain/service/noteSettings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { NoteInternalId } from '@domain/entities/note.js';
import type NoteSettings from '@domain/entities/noteSettings.js';
import type NoteSettingsRepository from '@repository/noteSettings.repository.js';
import type TeamRepository from '@repository/team.repository.js';
import type { MemberRole, Team, TeamMember, TeamMemberCreationAttributes } from '@domain/entities/team.js';
import type User from '@domain/entities/user.js';

/**
* Service responsible for Note Settings
Expand All @@ -9,15 +12,19 @@ export default class NoteSettingsService {
/**
* Note Settings repository
*/
public repository: NoteSettingsRepository;
public noteSettingsRepository: NoteSettingsRepository;

private readonly teamRepository: TeamRepository;

/**
* Note Settings service constructor
*
* @param repository - note repository
* @param noteSettingsrepository - note settings repository
* @param teamRepository - team repository
*/
constructor(repository: NoteSettingsRepository) {
this.repository = repository;
constructor(noteSettingsrepository: NoteSettingsRepository, teamRepository: TeamRepository) {
this.noteSettingsRepository = noteSettingsrepository;
this.teamRepository = teamRepository;
}

/**
Expand All @@ -26,7 +33,7 @@ export default class NoteSettingsService {
* @param id - note internal id
*/
public async getNoteSettingsByNoteId(id: NoteInternalId): Promise<NoteSettings> {
return await this.repository.getNoteSettingsByNoteId(id);
return await this.noteSettingsRepository.getNoteSettingsByNoteId(id);
}

/**
Expand All @@ -37,7 +44,7 @@ export default class NoteSettingsService {
* @returns added note settings
*/
public async addNoteSettings(noteId: NoteInternalId, isPublic: boolean = true): Promise<NoteSettings> {
return await this.repository.addNoteSettings({
return await this.noteSettingsRepository.addNoteSettings({
noteId: noteId,
isPublic: isPublic,
});
Expand All @@ -51,8 +58,48 @@ export default class NoteSettingsService {
* @returns updated note settings
*/
public async patchNoteSettingsByNoteId(noteId: NoteInternalId, data: Partial<NoteSettings>): Promise<NoteSettings | null> {
const noteSettings = await this.repository.getNoteSettingsByNoteId(noteId);
const noteSettings = await this.noteSettingsRepository.getNoteSettingsByNoteId(noteId);

return await this.noteSettingsRepository.patchNoteSettingsById(noteSettings.id, data);
}

return await this.repository.patchNoteSettingsById(noteSettings.id, data);
/**
* Get user role in team by user id and note id
* If user is not a member of note, return null
*
* @param userId - user id to check his role
* @param noteId - note id where user should have role
*/
public async getUserRoleByUserIdAndNoteId(userId: User['id'], noteId: NoteInternalId): Promise<MemberRole | null> {
return await this.teamRepository.getUserRoleByUserIdAndNoteId(userId, noteId);
}

/**
* Get all team members by note id
*
* @param noteId - note id to get all team members
* @returns team members
*/
public async getTeamByNoteId(noteId: NoteInternalId): Promise<Team> {
return await this.teamRepository.getByNoteId(noteId);
}

/**
* Remove team member by id
*
* @param id - team member id
*/
public async removeTeamMemberById(id: TeamMember['id']): Promise<boolean> {
return await this.teamRepository.removeMemberById(id);
}

/**
* Creates team member
*
* @param team - data for team member creation
* @returns created team member
*/
public async createTeamMember(team: TeamMemberCreationAttributes): Promise<TeamMember> {
return await this.teamRepository.create(team);
}
}
6 changes: 3 additions & 3 deletions src/presentation/http/router/note.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ describe('Note API', () => {
'createdAt': '2023-10-16T13:49:19.000Z',
'updatedAt': '2023-10-16T13:49:19.000Z',
'noteSettings': {
'custom_hostname': 'codex.so',
'enabled': true,
'customHostname': 'codex.so',
'isPublic': true,
'id': 1,
'note_id': 1,
'noteId': 1,
},
};
/* eslint-enable @typescript-eslint/naming-convention */
Expand Down
21 changes: 21 additions & 0 deletions src/presentation/http/router/noteSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { isEmpty } from '@infrastructure/utils/empty.js';
import useNoteResolver from '../middlewares/note/useNoteResolver.js';
import type NoteService from '@domain/service/note.js';
import type { NotePublicId } from '@domain/entities/note.js';
import type { Team } from '@domain/entities/team.js';

/**
* Interface for the note settings router.
Expand Down Expand Up @@ -113,6 +114,26 @@ const NoteSettingsRouter: FastifyPluginCallback<NoteSettingsRouterOptions> = (fa
return reply.send(updatedNoteSettings);
});

/**
* TODO add policy for this route (check if user is collaborator)
*/
fastify.get<{
Params: {
notePublicId: NotePublicId,
},
Reply: Team,
}>('/:notePublicId/team', {
preHandler: [
noteResolver,
],
}, async (request, reply) => {
const noteId = request.note?.id as number;

const team = await noteSettingsService.getTeamByNoteId(noteId);

return reply.send(team);
});

done();
};

Expand Down
21 changes: 19 additions & 2 deletions src/repository/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import UserRepository from '@repository/user.repository.js';
import AIRepository from './ai.repository.js';
import OpenAIApi from './transport/openai-api/index.js';
import EditorToolsRepository from '@repository/editorTools.repository.js';
import TeamRepository from '@repository/team.repository.js';
import TeamStorage from '@repository/storage/team.storage.js';

/**
* Interface for initiated repositories
Expand Down Expand Up @@ -43,6 +45,11 @@ export interface Repositories {
*/
aiRepository: AIRepository
editorToolsRepository: EditorToolsRepository,

/**
* Team repository instance
*/
teamRepository: TeamRepository,
}

/**
Expand Down Expand Up @@ -71,16 +78,23 @@ export async function init(orm: Orm): Promise<Repositories> {
* Create storage instances
*/
const userStorage = new UserStorage(orm);
const noteSettingsStorage = new NoteSettingsStorage(orm);
const noteStorage = new NoteStorage(orm);
const userSessionStorage = new UserSessionStorage(orm);
const noteSettingsStorage = new NoteSettingsStorage(orm);
const teamStorage = new TeamStorage(orm);

/**
* Create associations between note and note settings
*/
noteStorage.createAssociationWithNoteSettingsModel(noteSettingsStorage.model);
noteSettingsStorage.createAssociationWithNoteModel(noteStorage.model);

const userSessionStorage = new UserSessionStorage(orm);
/**
* Create associations between note and team, user and team
*/
teamStorage.createAssociationWithNoteModel(noteStorage.model);
teamStorage.createAssociationWithUserModel(userStorage.model);

const editorToolsStorage = new EditorToolsStorage(orm);

/**
Expand All @@ -89,6 +103,7 @@ export async function init(orm: Orm): Promise<Repositories> {
await userStorage.model.sync();
await noteStorage.model.sync();
await noteSettingsStorage.model.sync();
await teamStorage.model.sync();
await userSessionStorage.model.sync();
await editorToolsStorage.model.sync();

Expand All @@ -107,6 +122,7 @@ export async function init(orm: Orm): Promise<Repositories> {
const userRepository = new UserRepository(userStorage, googleApiTransport);
const aiRepository = new AIRepository(openaiApiTransport);
const editorToolsRepository = new EditorToolsRepository(editorToolsStorage);
const teamRepository = new TeamRepository(teamStorage);

return {
noteRepository,
Expand All @@ -115,5 +131,6 @@ export async function init(orm: Orm): Promise<Repositories> {
userRepository,
aiRepository,
editorToolsRepository,
teamRepository,
};
}
2 changes: 1 addition & 1 deletion src/repository/storage/postgres/orm/sequelize/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export default class NoteSequelizeStorage {
* Create association with note settings, one-to-one
*/
this.model.hasOne(this.settingsModel, {
foreignKey: 'note_id',
foreignKey: 'noteId',
as: 'noteSettings',
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export default class NoteSettingsSequelizeStorage {
* We can not create note settings without note
*/
this.model.belongsTo(model, {
foreignKey: 'note_id',
foreignKey: 'noteId',
as: this.noteModel.tableName,
});
}
Expand Down
Loading

0 comments on commit 4598c30

Please sign in to comment.