Skip to content

Instantly share code, notes, and snippets.

@panki
Created January 2, 2020 12:33
Show Gist options
  • Save panki/cd36eff3cfa55c641d0d0a51e0408082 to your computer and use it in GitHub Desktop.
Save panki/cd36eff3cfa55c641d0d0a51e0408082 to your computer and use it in GitHub Desktop.
Graph service example
import { Avatar } from '../schema/avatars.schema';
import { Place } from '../schema/places.schema';
import { Protest } from '../schema/protests.schema';
import { User, UserAvatar } from '../schema/users.schema';
export class GraphMap {
public avatarsMap: Map<number, Avatar>;
public avatars: Avatar[];
public placesMap: Map<number, Place>;
public places: Place[];
public protestsMap: Map<string, Protest>;
public protests: Protest[];
public usersMap: Map<string, User>;
public users: User[];
public userAvatarsMap: Map<string, UserAvatar>;
public userAvatars: UserAvatar[];
constructor() {
this.avatarsMap = new Map<number, Avatar>();
this.avatars = new Array<Avatar>();
this.placesMap = new Map<number, Place>();
this.places = new Array<Place>();
this.protestsMap = new Map<string, Protest>();
this.protests = new Array<Protest>();
this.usersMap = new Map<string, User>();
this.users = new Array<User>();
this.userAvatarsMap = new Map<string, UserAvatar>();
this.userAvatars = new Array<UserAvatar>();
}
addAvatar(a: Avatar) {
this.avatarsMap.set(a.id, a);
this.avatars.push(a);
}
addProtest(p: Protest) {
this.protestsMap.set(p.id, p);
this.protests.push(p);
}
addUser(u: User) {
this.usersMap.set(u.id, u);
this.users.push(u);
}
addUserAvatar(a: UserAvatar) {
this.userAvatarsMap.set(a.id, a);
this.userAvatars.push(a);
}
addPlace(p: Place) {
this.placesMap.set(p.id, p);
this.places.push(p);
}
}
import { Module } from '@nestjs/common';
import { AvatarsModule } from '../avatars/avatars.module';
import { PlacesModule } from '../places/places.module';
import { ProtestsModule } from '../protests/protests.module';
import { UsersModule } from '../users/users.module';
import { GraphService } from './graph.service';
@Module({
imports: [AvatarsModule, PlacesModule, ProtestsModule, UsersModule],
providers: [GraphService],
exports: [GraphService],
})
export class GraphModule {}
import { Injectable } from '@nestjs/common';
import { AvatarsService } from '../avatars/avatars.service';
import { PlacesService } from '../places/places.service';
import { ProtestsService } from '../protests/protests.service';
import { Avatar } from '../schema/avatars.schema';
import { Banner } from '../schema/banners.schema';
import { Graph } from '../schema/graph.schema';
import { Place } from '../schema/places.schema';
import { Participant, Protest } from '../schema/protests.schema';
import { User, UserAvatar } from '../schema/users.schema';
import { UsersService } from '../users/users.service';
import { GraphMap } from './graph.map';
@Injectable()
export class GraphService {
constructor(
private readonly placesService: PlacesService,
private readonly protestsService: ProtestsService,
private readonly usersService: UsersService,
private readonly avatarsService: AvatarsService,
) {}
async avatar(a: Avatar): Promise<Graph> {
return this.avatars([a]);
}
async avatars(a: Avatar[]): Promise<Graph> {
const graph = new GraphMap();
return this.fillAvatars(graph, a).then(() => graph);
}
async banner(b: Banner): Promise<Graph> {
return this.banners([b]);
}
async banners(b: Banner[]): Promise<Graph> {
const graph = new GraphMap();
return this.fillBanners(graph, b).then(() => graph);
}
async place(p: Place): Promise<Graph> {
return this.places([p]);
}
async places(p: Place[]): Promise<Graph> {
const graph = new GraphMap();
return this.fillPlaces(graph, p).then(() => graph);
}
async protest(p: Protest): Promise<Graph> {
return this.protests([p]);
}
async protests(p: Protest[]): Promise<Graph> {
const graph = new GraphMap();
return this.fillProtests(graph, p).then(() => graph);
}
async participant(p: Participant): Promise<Graph> {
return this.participants([p]);
}
async participants(p: Participant[]): Promise<Graph> {
const graph = new GraphMap();
return this.fillParticipants(graph, p).then(() => graph);
}
async user(u: User): Promise<Graph> {
return this.users([u]);
}
async users(u: User[]): Promise<Graph> {
const graph = new GraphMap();
return this.fillUsers(graph, u).then(() => graph);
}
// Fill
private async fillAvatars(m: GraphMap, avatars: Avatar[]): Promise<void> {}
private async fillBanners(m: GraphMap, banners: Banner[]): Promise<void> {}
private async fillUsers(m: GraphMap, users: User[]): Promise<void> {
for (const u of users) {
u.userAvatars = await this.usersService.queryAvatars(u.id);
u.participations = await this.protestsService.queryActiveParticipantsByUserId(
u.id,
);
await this.fillUserAvatars(m, u.userAvatars);
await this.fillParticipants(m, u.participations);
}
}
private async fillUserAvatars(
m: GraphMap,
userAvatars: UserAvatar[],
): Promise<void> {
const ids = userAvatars.map(p => p.avatarId);
return this.fillAvatarsIds(m, ids);
}
private async fillParticipants(
m: GraphMap,
participants: Participant[],
): Promise<void> {
const userAvatarsIds = participants.map(p => p.userAvatarId);
const protestIds = participants.map(p => p.protestId);
await this.fillUserAvatarsIds(m, userAvatarsIds);
await this.fillProtestsIds(m, protestIds);
}
private async fillPlaces(m: GraphMap, places: Place[]): Promise<void> {
const ids = places.map(place => place.protestId).filter(id => id);
return this.fillProtestsIds(m, ids);
}
private async fillProtests(m: GraphMap, protests: Protest[]): Promise<void> {
const usersIds = protests.map(p => p.organizerId);
const placesIds = protests.map(p => p.placeId);
await this.fillUsersIds(m, usersIds);
await this.fillPlacesIds(m, placesIds);
}
// Fill ids
private async fillProtestsIds(m: GraphMap, ids: string[]): Promise<void> {
const filteredIds = filterIds(ids, id => !m.protestsMap.has(id));
if (filteredIds.length > 0) {
const protests = await this.protestsService.queryByIds(filteredIds);
protests.forEach(p => m.addProtest(p));
}
}
private async fillUsersIds(m: GraphMap, ids: string[]): Promise<void> {
const filteredIds = filterIds(ids, id => !m.usersMap.has(id));
if (filteredIds.length > 0) {
const users = await this.usersService.queryByIds(ids);
users.forEach(u => m.addUser(u));
}
}
private async fillUserAvatarsIds(m: GraphMap, ids: string[]): Promise<void> {
const filteredIds = filterIds(ids, id => !m.userAvatarsMap.has(id));
if (filteredIds.length > 0) {
const usersAvatars = await this.usersService.queryAvatarsByIds(ids);
const avatarsIds = usersAvatars.map(a => a.avatarId);
usersAvatars.forEach(a => m.addUserAvatar(a));
await this.fillAvatarsIds(m, avatarsIds);
}
}
private async fillAvatarsIds(m: GraphMap, ids: number[]): Promise<void> {
const filteredIds = filterIds(ids, id => !m.avatarsMap.has(id));
if (filteredIds.length > 0) {
const avatars = await this.avatarsService.queryByIds(ids);
avatars.forEach(a => m.addAvatar(a));
}
}
private async fillPlacesIds(m: GraphMap, ids: number[]): Promise<void> {
const filteredIds = filterIds(ids, id => !m.placesMap.has(id));
if (filteredIds.length > 0) {
const places = await this.placesService.queryByIds(ids);
places.forEach(p => m.addPlace(p));
}
}
}
// Remove duplicates, undefined + custom filter
function filterIds<T>(ids: T[], filter: (id: T) => boolean): T[] {
return ids.filter(
(id, i, self) => id && self.indexOf(id) === i && filter(id),
);
}
import {
BadRequestException,
Body,
Controller,
Get,
Post,
Put,
Request,
UseGuards
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import {
ApiBadRequestResponse,
ApiBearerAuth,
ApiCreatedResponse,
ApiOkResponse,
ApiOperation,
ApiUnauthorizedResponse,
ApiUseTags
} from '@nestjs/swagger';
import { AuthService } from '../auth/auth.service';
import { GraphService } from '../graph/graph.service';
import {
CreateUserRequest,
CreateUserResponse,
UserResponse
} from '../schema/users.schema';
import { UsersService } from '../users/users.service';
@ApiUseTags('users')
@ApiBearerAuth()
@Controller('users')
export class UsersController {
constructor(
private readonly users: UsersService,
private readonly auth: AuthService,
private readonly graph: GraphService,
) {}
@ApiOperation({
title: 'Create new user',
description: 'Creates new user with unityId and returns auth token',
})
@ApiCreatedResponse({ type: CreateUserResponse, description: 'User created' })
@ApiBadRequestResponse({ description: 'Error' })
@Post('')
async create(@Body() params: CreateUserRequest): Promise<CreateUserResponse> {
try {
const user = await this.users.create(params);
const token = await this.auth.login(user);
return new CreateUserResponse({ token });
} catch (error) {
throw new BadRequestException(error.message);
}
}
@UseGuards(AuthGuard('jwt'))
@ApiOperation({
title: 'Get user profile',
description: 'Uses auth token to find user, so he must be authenticated',
})
@ApiOkResponse({ type: UserResponse, description: 'User found' })
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
@ApiBadRequestResponse({ description: 'Error' })
@Get('me')
async me(@Request() req): Promise<UserResponse> {
const user = await this.users.getById(req.user.id);
const graph = await this.graph.user(user);
return new UserResponse({ user, graph });
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment