Skip to content

Commit

Permalink
Merge pull request #4 from funidata/trunk
Browse files Browse the repository at this point in the history
Copy over database entities from Hybridilusmu
  • Loading branch information
joonashak authored Sep 11, 2024
2 parents f8cae0a + e68200e commit 4bb3697
Show file tree
Hide file tree
Showing 21 changed files with 437 additions and 14 deletions.
70 changes: 67 additions & 3 deletions app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/swagger": "^7.4.0",
"@nestjs/typeorm": "^10.0.2",
"@slack/bolt": "^3.21.2",
"class-transformer": "^0.5.1",
Expand Down
14 changes: 13 additions & 1 deletion app/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,20 @@ import { Module } from "@nestjs/common";
import { BoltModule } from "./bolt/bolt.module";
import { ConfigModule } from "./common/config/config.module";
import { DatabaseModule } from "./database/database.module";
import { OfficeModule } from "./entities/office/office.module";
import { PresenceModule } from "./entities/presence/presence.module";
import { UserSettingsModule } from "./entities/user-settings/user-settings.module";
import { UserModule } from "./entities/user/user.module";

@Module({
imports: [ConfigModule, DatabaseModule, BoltModule],
imports: [
ConfigModule,
DatabaseModule,
BoltModule,
OfficeModule,
UserSettingsModule,
UserModule,
PresenceModule,
],
})
export class AppModule {}
16 changes: 6 additions & 10 deletions app/src/bolt/bolt-register.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { DiscoveredMethod, DiscoveryService } from "@golevelup/nestjs-discovery";
import { Injectable } from "@nestjs/common";
import { ModuleRef } from "@nestjs/core";
import { App } from "@slack/bolt";
import { StringIndexed } from "@slack/bolt/dist/types/helpers";
import { BoltService } from "./bolt.service";
import { BOLT_ACTION_KEY } from "./decorators/bolt-action.decorator";
import { BOLT_EVENT_KEY } from "./decorators/bolt-event.decorator";
Expand All @@ -11,15 +9,11 @@ type EventType = typeof BOLT_ACTION_KEY | typeof BOLT_EVENT_KEY;

@Injectable()
export class BoltRegisterService {
private bolt: App<StringIndexed>;

constructor(
boltService: BoltService,
private boltService: BoltService,
private discoveryService: DiscoveryService,
private moduleRef: ModuleRef,
) {
this.bolt = boltService.getBolt();
}
) {}

/**
* Register all controller methods decorated with one of our Bolt event decorators.
Expand Down Expand Up @@ -65,10 +59,12 @@ export class BoltRegisterService {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
handler: any,
) {
const bolt = this.boltService.getBolt();

if (eventType === BOLT_ACTION_KEY) {
this.bolt.action(eventName, handler);
bolt.action(eventName, handler);
} else if (eventType === BOLT_EVENT_KEY) {
this.bolt.event(eventName, handler);
bolt.event(eventName, handler);
}
}
}
12 changes: 12 additions & 0 deletions app/src/entities/office/office.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Column, Entity, PrimaryGeneratedColumn, Repository } from "typeorm";

@Entity()
export class Office {
@PrimaryGeneratedColumn()
id: number;

@Column()
name: string;
}

export type OfficeRepository = Repository<Office>;
11 changes: 11 additions & 0 deletions app/src/entities/office/office.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { Office } from "./office.model";
import { OfficeService } from "./office.service";

@Module({
imports: [TypeOrmModule.forFeature([Office])],
providers: [OfficeService],
exports: [TypeOrmModule, OfficeService],
})
export class OfficeModule {}
12 changes: 12 additions & 0 deletions app/src/entities/office/office.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Office, OfficeRepository } from "./office.model";

@Injectable()
export class OfficeService {
constructor(@InjectRepository(Office) private officeRepository: OfficeRepository) {}

async findAll() {
return this.officeRepository.find();
}
}
8 changes: 8 additions & 0 deletions app/src/entities/presence/dto/presence.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { OmitType, PickType } from "@nestjs/swagger";
import { Presence } from "../presence.model";

export class UpsertPresenceDto extends OmitType(Presence, ["office"]) {}

export class SetOfficeDto extends PickType(Presence, ["userId", "date"]) {
officeId: number;
}
61 changes: 61 additions & 0 deletions app/src/entities/presence/presence.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Controller, InternalServerErrorException } from "@nestjs/common";
import dayjs from "dayjs";
import BoltAction from "../../bolt/decorators/bolt-action.decorator";
import BoltActions from "../../bolt/enums/bolt-actions.enum";
import { BoltActionArgs } from "../../bolt/types/bolt-action-types";
import { PresenceType } from "./presence.model";
import { PresenceService } from "./presence.service";

@Controller()
export class PresenceController {
constructor(private presenceService: PresenceService) {}

@BoltAction(BoltActions.SET_OFFICE_PRESENCE)
async setOfficePresence({ ack, body, payload }: BoltActionArgs) {
await ack();
const date = dayjs(payload["value"]).toDate();
await this.presenceService.upsert({
userId: body.user.id,
type: PresenceType.AT_OFFICE,
date,
});
}

@BoltAction(BoltActions.SET_REMOTE_PRESENCE)
async setRemotePresence({ ack, body, payload }: BoltActionArgs) {
await ack();
const date = dayjs(payload["value"]).toDate();
await this.presenceService.upsert({
userId: body.user.id,
type: PresenceType.REMOTE,
date,
});
}

@BoltAction(BoltActions.SELECT_OFFICE_FOR_DATE)
async selectOfficeForDate({ ack, body, payload }: BoltActionArgs) {
await ack();
const { value, date } = JSON.parse(payload["selected_option"].value);
await this.presenceService.setOffice({
userId: body.user.id,
date: dayjs(date).toDate(),
officeId: value,
});
}

// TODO: Should this be moved?
@BoltAction(BoltActions.DAY_LIST_ITEM_OVERFLOW)
async dayListItemOverflow({ ack, body, payload }: BoltActionArgs) {
await ack();
const { type, date } = JSON.parse(payload["selected_option"].value);

if (type !== "remove_presence") {
throw new InternalServerErrorException("Not implemented.");
}

await this.presenceService.remove({
userId: body.user.id,
date: dayjs(date).toDate(),
});
}
}
24 changes: 24 additions & 0 deletions app/src/entities/presence/presence.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Column, Entity, ManyToOne, PrimaryColumn, Repository } from "typeorm";
import { Office } from "../office/office.model";

export enum PresenceType {
AT_OFFICE = "at_office",
REMOTE = "remote",
}

@Entity()
export class Presence {
@PrimaryColumn()
userId: string;

@PrimaryColumn({ type: "date" })
date: Date;

@Column({ type: "enum", enum: PresenceType, nullable: true })
type: PresenceType | null;

@ManyToOne(() => Office, { nullable: true })
office: Office;
}

export type PresenceRepository = Repository<Presence>;
14 changes: 14 additions & 0 deletions app/src/entities/presence/presence.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { OfficeModule } from "../office/office.module";
import { PresenceController } from "./presence.controller";
import { Presence } from "./presence.model";
import { PresenceService } from "./presence.service";

@Module({
imports: [TypeOrmModule.forFeature([Presence]), OfficeModule],
providers: [PresenceService],
controllers: [PresenceController],
exports: [TypeOrmModule],
})
export class PresenceModule {}
47 changes: 47 additions & 0 deletions app/src/entities/presence/presence.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { DataSource } from "typeorm";
import { SetOfficeDto, UpsertPresenceDto } from "./dto/presence.dto";
import { Presence, PresenceRepository } from "./presence.model";

@Injectable()
export class PresenceService {
constructor(
@InjectRepository(Presence) private presenceRepository: PresenceRepository,
private dataSource: DataSource,
) {}

async remove(presence: Pick<Presence, "userId" | "date">) {
return this.presenceRepository.delete(presence);
}

async upsert(presence: Partial<UpsertPresenceDto>) {
// Select only existing cols for the upsert operation to avoid overriding
// existing data with defaults/nulls.
const primaryKeys = ["userId", "date"];
const updatableCols = Object.keys(presence).filter((key) => !primaryKeys.includes(key));

if (updatableCols.length === 0) {
return;
}

return this.dataSource
.createQueryBuilder()
.insert()
.into(Presence)
.values(presence)
.orUpdate(updatableCols, primaryKeys)
.execute();
}

async setOffice({ userId, date, officeId }: SetOfficeDto) {
await this.upsert({ userId, date });

return this.presenceRepository.update(
{ userId, date },
{
office: { id: officeId },
},
);
}
}
8 changes: 8 additions & 0 deletions app/src/entities/user-settings/dto/user-settings.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { OmitType } from "@nestjs/swagger";
import { UserSettings } from "../user-settings.model";

export class UpsertUserSettingsDto extends OmitType(UserSettings, ["visibleOffice"]) {}

export class SetVisibleOfficeDto {
visibleOfficeId: number;
}
Loading

0 comments on commit 4bb3697

Please sign in to comment.