Skip to content

Commit

Permalink
Merge pull request #168 from plezanje-net/crags-updates
Browse files Browse the repository at this point in the history
Crags updates
  • Loading branch information
demshy authored Apr 1, 2024
2 parents 588bdc8 + 606c42b commit 45ca8b6
Show file tree
Hide file tree
Showing 14 changed files with 354 additions and 5 deletions.
19 changes: 17 additions & 2 deletions 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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"dotenv": "^16.0.3",
"fs": "0.0.1-security",
"graphql": "^16.6.0",
"graphql-type-json": "^0.3.2",
"handlebars": "^4.7.7",
"hbs": "^4.2.0",
"ioredis": "^5.2.3",
Expand Down
2 changes: 2 additions & 0 deletions src/crags/crags.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ import { RouteLoader } from './loaders/route.loader';
import { RouteEvent } from './entities/route-event.entity';
import { Parking } from './entities/parking.entity';
import { ParkingsService } from './services/parkings.service';
import { AreaLoader } from './loaders/area.loader';

@Module({
imports: [
Expand Down Expand Up @@ -143,6 +144,7 @@ import { ParkingsService } from './services/parkings.service';
MailService,
SummaryQueueConsumer,
ConfigService,
AreaLoader,
CragLoader,
RouteLoader,
ParkingsService,
Expand Down
2 changes: 1 addition & 1 deletion src/crags/entities/area.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class Area extends BaseEntity {
@Field()
name: string;

@Column({ nullable: true })
@Column()
@Field()
slug: string;

Expand Down
19 changes: 18 additions & 1 deletion src/crags/entities/crag.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { GradingSystem } from './grading-system.entity';
import { User } from '../../users/entities/user.entity';
import { EntityStatus } from './enums/entity-status.enum';
import { PublishStatus } from './enums/publish-status.enum';
import GraphQLJSON from 'graphql-type-json';

export enum CragType {
SPORT = 'sport',
Expand Down Expand Up @@ -257,10 +258,26 @@ export class Crag extends BaseEntity {

@Column({ nullable: true })
@Field({ nullable: true })
rainProof: boolean;
rainproof: boolean;

@OneToOne(() => Image, { nullable: true })
@JoinColumn()
@Field(() => Image, { nullable: true })
coverImage: Promise<Image>;

@Column({ type: 'jsonb', nullable: true })
@Field((type) => GraphQLJSON, { nullable: true })
nrRoutesByGrade: JSON;

@Column({ default: false })
@Field()
hasSport: boolean;

@Column({ default: false })
@Field()
hasBoulder: boolean;

@Column({ default: false })
@Field()
hasMultipitch: boolean;
}
24 changes: 24 additions & 0 deletions src/crags/loaders/area.loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import DataLoader from 'dataloader';
import { Injectable } from '@nestjs/common';
import { NestDataLoader } from '../../core/interceptors/data-loader.interceptor';
import { Area } from '../entities/area.entity';
import { AreasService } from '../services/areas.service';

@Injectable()
export class AreaLoader implements NestDataLoader<string, Area> {
constructor(private readonly areasService: AreasService) {}

generateDataLoader(): DataLoader<string, Area> {
return new DataLoader<string, Area>(async (keys) => {
const areas = await this.areasService.findByIds(keys.map((k) => k));

const areasMap: { [key: string]: Area } = {};

areas.forEach((area) => {
areasMap[area.id] = area;
});

return keys.map((areaId) => areasMap[areaId] ?? null);
});
}
}
23 changes: 22 additions & 1 deletion src/crags/resolvers/areas.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Resolver, Mutation, Args, Query } from '@nestjs/graphql';
import {
Resolver,
Mutation,
Args,
Query,
ResolveField,
Parent,
} from '@nestjs/graphql';
import { Area } from '../entities/area.entity';
import { Roles } from '../../auth/decorators/roles.decorator';
import { UseFilters, UseInterceptors } from '@nestjs/common';
Expand All @@ -8,6 +15,11 @@ import { CreateAreaInput } from '../dtos/create-area.input';
import { AreasService } from '../services/areas.service';
import { AllowAny } from '../../auth/decorators/allow-any.decorator';
import { NotFoundFilter } from '../filters/not-found.filter';
import { Country } from '../entities/country.entity';
import { Loader } from '../../core/interceptors/data-loader.interceptor';
import { CountryLoader } from '../loaders/country.loader';
import { Crag } from '../entities/crag.entity';
import DataLoader from 'dataloader';

@Resolver(() => Area)
export class AreasResolver {
Expand Down Expand Up @@ -44,4 +56,13 @@ export class AreasResolver {
async deleteArea(@Args('id') id: string): Promise<boolean> {
return this.areasService.delete(id);
}

@ResolveField('country', () => Country)
async getCountry(
@Parent() crag: Crag,
@Loader(CountryLoader)
loader: DataLoader<Country['id'], Country>,
): Promise<Country> {
return loader.load(crag.countryId);
}
}
25 changes: 25 additions & 0 deletions src/crags/resolvers/crags.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ import { NotificationService } from '../../notification/services/notification.se
import { ForeignKeyConstraintFilter } from '../filters/foreign-key-constraint.filter';
import { ImagesService } from '../services/images.service';
import { Roles } from '../../auth/decorators/roles.decorator';
import { Area } from '../entities/area.entity';
import { AreaLoader } from '../loaders/area.loader';
import { FindCragsServiceInput } from '../dtos/find-crags-service.input';

@Resolver(() => Crag)
@UseInterceptors(DataLoaderInterceptor)
Expand Down Expand Up @@ -84,6 +87,19 @@ export class CragsResolver {
});
}

@AllowAny()
@UseGuards(UserAuthGuard)
@Query(() => [Crag])
async crags(
@CurrentUser() user: User,
@Args('input', { nullable: true }) input: FindCragsServiceInput = {},
): Promise<Crag[]> {
return this.cragsService.find({
...input,
user,
});
}

/* MUTATIONS */

@UseGuards(UserAuthGuard)
Expand Down Expand Up @@ -211,6 +227,15 @@ export class CragsResolver {
return loader.load(crag.countryId);
}

@ResolveField('area', () => Area, { nullable: true })
async getArea(
@Parent() crag: Area,
@Loader(AreaLoader)
loader: DataLoader<Area['id'], Area>,
): Promise<Area> {
return crag.areaId ? loader.load(crag.areaId) : null;
}

@Mutation(() => Boolean)
@Roles('admin')
@UseGuards(UserAuthGuard)
Expand Down
26 changes: 26 additions & 0 deletions src/crags/services/areas.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { FindManyOptions, IsNull, MoreThan, Repository } from 'typeorm';
import { Country } from '../entities/country.entity';
import { FindAreasInput } from '../dtos/find-areas.input';
import slugify from 'slugify';

@Injectable()
export class AreasService {
Expand All @@ -24,6 +25,11 @@ export class AreasService {
return this.areasRepository.findOneByOrFail({ slug });
}

findByIds(ids: string[]): Promise<Area[]> {
const qb = this.areasRepository.createQueryBuilder('area');
return qb.whereInIds(ids).getMany();
}

find(params: FindAreasInput = {}): Promise<Area[]> {
const options: FindManyOptions = {
order: {},
Expand Down Expand Up @@ -61,6 +67,8 @@ export class AreasService {
await this.countryRepository.findOneByOrFail({ id: data.countryId }),
);

area.slug = await this.generateAreaSlug(area.name);

return this.areasRepository.save(area);
}

Expand All @@ -81,4 +89,22 @@ export class AreasService {

return this.areasRepository.remove(area).then(() => true);
}

private async generateAreaSlug(areaName: string) {
let slug = slugify(areaName, { lower: true });
let suffixCounter = 0;
let suffix = '';

while (
await this.areasRepository.findOne({
where: { slug: slug + suffix },
})
) {
suffixCounter++;
suffix = '-' + suffixCounter;
}
slug += suffix;

return slug;
}
}
6 changes: 6 additions & 0 deletions src/crags/services/crags.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ export class CragsService {
return crags;
}

async findAll(): Promise<Crag[]> {
const qb = this.cragsRepository.createQueryBuilder('c');
const result = qb.getMany();
return result;
}

async processAllCrags() {
(
await this.cragsRepository.find({
Expand Down
51 changes: 51 additions & 0 deletions src/migration/1703958939270-areaSlugNonNullable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import slugify from 'slugify';
import { MigrationInterface, QueryRunner } from 'typeorm';

export class areaSlugNonNullable1703958939270 implements MigrationInterface {
name = 'areaSlugNonNullable1703958939270';

public async up(queryRunner: QueryRunner): Promise<void> {
// Some areas that were added by users have slugs missing. Add slugs before making slug non-nullable.
const missingSlugAreas = await queryRunner.query(
`SELECT a.name, a.id FROM "area" a WHERE a.slug IS NULL`,
);

for (let i = 0; i < missingSlugAreas.length; i++) {
const area = missingSlugAreas[i];
const slug = await this.generateAreaSlug(area.name, queryRunner);
await queryRunner.query(
`UPDATE "area" a SET "slug" = '${slug}' WHERE a.id = '${area.id}'`,
);
}

await queryRunner.query(
`ALTER TABLE "area" ALTER COLUMN "slug" SET NOT NULL`,
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "area" ALTER COLUMN "slug" DROP NOT NULL`,
);
}

private async generateAreaSlug(areaName: string, queryRunner: QueryRunner) {
let slug = slugify(areaName, { lower: true });
let suffixCounter = 0;
let suffix = '';

while (
(
await queryRunner.query(
`SELECT * FROM "area" a WHERE a.slug = '${slug}${suffix}'`,
)
).length > 0
) {
suffixCounter++;
suffix = '-' + suffixCounter;
}
slug += suffix;

return slug;
}
}
17 changes: 17 additions & 0 deletions src/migration/1705000587590-fixRainproofFieldName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class fixRainproofFieldName1705000587590 implements MigrationInterface {
name = 'fixRainproofFieldName1705000587590';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "crag" RENAME COLUMN "rain_proof" TO "rainproof"`,
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "crag" RENAME COLUMN "rainproof" TO "rain_proof"`,
);
}
}
Loading

0 comments on commit 45ca8b6

Please sign in to comment.