Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/embedding events to attraction #109

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
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: 3 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { TagsRoutes } from "./resources/tags/tags.routes";
import { UsersRoutes } from "./resources/users/UsersRoutes";
import { MongoDBUsersRepository } from "./resources/users/repositories/MongoDBUsersRepository";
import { UsersService } from "./resources/users/services/UsersService";
import { MongoDBFilterFactory } from "./common/filter/FilterFactory";

export class KulturdatenBerlinApp {
constructor(public app: express.Application) {}
Expand Down Expand Up @@ -115,6 +116,8 @@ export class KulturdatenBerlinApp {

Container.set("TagsRepository", new MongoDBTagsRepository(Container.get("Database")));
Container.import([]);

Container.set("FilterFactory", new MongoDBFilterFactory());
}

private initLogger() {
Expand Down
107 changes: 107 additions & 0 deletions src/common/filter/FilterFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { Service } from "typedi";
import { Filter } from "../../generated/models/Filter.generated";

export interface FilterFactory {
createExactMatchFilter(propertyName: string, value: string | undefined): Filter | undefined;

createPartialMatchFilter(propertyName: string, value: string | undefined): Filter | undefined;

createNotEqualFilter(propertyName: string, value: string | undefined): Filter | undefined;

createAnyMatchFilter(propertyName: string, values: string[] | undefined): Filter | undefined;

createAllMatchFilter(propertyName: string, values: string[] | undefined): Filter | undefined;

createFutureDateFilter(propertyName: string): Filter | undefined;

createDateRangeFilter(
startDateProperty: string,
endDateProperty: string,
startDate?: string,
endDate?: string,
): Filter | undefined;

combineWithAnd(filters: (Filter | undefined)[]): Filter;

combineWithOr(filters: (Filter | undefined)[]): Filter;
}

@Service()
export class MongoDBFilterFactory implements FilterFactory {
createExactMatchFilter(propertyName: string, value: string | undefined): Filter | undefined {
if (!value) {
return undefined;
}
return { [propertyName as string]: value };
}

createPartialMatchFilter(propertyName: string, value: string | undefined): Filter | undefined {
if (!value) {
return undefined;
}
return { [propertyName as string]: { $regex: value, $options: "i" } };
}

createNotEqualFilter(propertyName: string, value: string | undefined): Filter | undefined {
if (value === undefined) {
return undefined;
}
return { [propertyName]: { $ne: value } };
}

createAnyMatchFilter(propertyName: string, values: string[] | undefined): Filter | undefined {
if (!values) {
return undefined;
}
return { [propertyName as string]: { $in: values } };
}

createAllMatchFilter(propertyName: string, values: string[] | undefined): Filter | undefined {
if (!values) {
return undefined;
}
return { [propertyName as string]: { $all: values } };
}

createFutureDateFilter(propertyName: string): Filter | undefined {
const currentDate = new Date().toISOString().split("T")[0]; // YYYY-MM-DD Format
return { [propertyName]: { $gte: currentDate } };
}

createDateRangeFilter(
startDateProperty: string,
endDateProperty: string,
startDate?: string,
endDate?: string,
): Filter | undefined {
if (startDate && endDate) {
return {
$and: [{ [startDateProperty]: { $gte: startDate } }, { [endDateProperty]: { $lte: endDate } }],
};
} else if (startDate) {
return { [startDateProperty]: { $gte: startDate } };
} else if (endDate) {
return { [endDateProperty]: { $lte: endDate } };
} else {
return undefined;
}
}

combineWithAnd(filters: (Filter | undefined)[]): Filter {
const validFilters = filters.filter((f) => f && Object.keys(f).length > 0) as Filter[];
if (validFilters.length > 0) {
return { $and: validFilters };
} else {
return {};
}
}

combineWithOr(filters: (Filter | undefined)[]): Filter {
const validFilters = filters.filter((f) => f && Object.keys(f).length > 0) as Filter[];
if (validFilters.length > 0) {
return { $or: validFilters };
} else {
return {};
}
}
}
43 changes: 43 additions & 0 deletions src/common/filter/FilterFactory.unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { FilterFactory, MongoDBFilterFactory } from "./FilterFactory";

describe("MongoDBFilterFactory", () => {
let factory: FilterFactory;

beforeEach(() => {
factory = new MongoDBFilterFactory();
});

it("should create an exact match filter", () => {
const filter = factory.createExactMatchFilter("identifier", "123");
expect(filter).toEqual({ identifier: "123" });
});

it("should create a partial match filter", () => {
const filter = factory.createPartialMatchFilter("identifier", "123");
expect(filter).toEqual({ identifier: { $regex: "123", $options: "i" } });
});

it("should create an any match filter", () => {
const filter = factory.createAnyMatchFilter("tags", ["tag1", "tag2"]);
expect(filter).toEqual({ tags: { $in: ["tag1", "tag2"] } });
});

it("should create an all match filter", () => {
const filter = factory.createAllMatchFilter("tags", ["tag1", "tag2"]);
expect(filter).toEqual({ tags: { $all: ["tag1", "tag2"] } });
});

it("should combine filters with AND", () => {
const filter1 = { identifier: "123" };
const filter2 = { type: "type.Attraction" };
const combinedFilter = factory.combineWithAnd([filter1, filter2]);
expect(combinedFilter).toEqual({ $and: [filter1, filter2] });
});

it("should combine filters with OR", () => {
const filter1 = { identifier: "123" };
const filter2 = { type: "type.Attraction" };
const combinedFilter = factory.combineWithOr([filter1, filter2]);
expect(combinedFilter).toEqual({ $or: [filter1, filter2] });
});
});
35 changes: 34 additions & 1 deletion src/common/parameters/Params.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@
export type Params = {
[key: string]: string;
[key: string]: string | string[] | boolean | undefined;
};

export type AttractionParams = Params & {
curatedBy?: string;
editableBy?: string;
anyTags?: string[];
allTags?: string[];
withEvents?: boolean;
};

export type EventParams = Params & {
asReference?: string;
organizedBy?: string;
editableBy?: string;
byLocation?: string;
byAttraction?: string;
isFreeOfCharge?: boolean;
inFuture?: boolean;
startDate?: string;
endDate?: string;
};

export type LocationParams = Params & {
asReference?: string;
managedBy?: string;
editableBy?: string;
anyAccessibilities?: string[];
allAccessibilities?: string[];
};

export type OrganizationParams = Params & {
asReference?: string;
editableBy?: string;
};
13 changes: 11 additions & 2 deletions src/common/responses/SuccessResponseBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export class SuccessResponseBuilder<T extends Response> {
private success: boolean = true;
private message?: string;
private data?: T["data"];
private related?: T["related"];

setMessage(message: string): SuccessResponseBuilder<T> {
this.message = message;
Expand All @@ -15,15 +16,22 @@ export class SuccessResponseBuilder<T extends Response> {
return this;
}

okResponse(data?: T["data"]): SuccessResponseBuilder<T> {
setRelated(related: T["related"]): SuccessResponseBuilder<T> {
this.related = related;
return this;
}

okResponse(data?: T["data"], related?: T["related"]): SuccessResponseBuilder<T> {
this.message = "Operation Successful";
this.data = data;
this.related = related;
return this;
}

createdResponse(data?: T["data"]): SuccessResponseBuilder<T> {
createdResponse(data?: T["data"], related?: T["related"]): SuccessResponseBuilder<T> {
this.message = "Resource Created";
this.data = data;
this.related = related;
return this;
}

Expand All @@ -32,6 +40,7 @@ export class SuccessResponseBuilder<T extends Response> {
success: this.success,
message: this.message,
data: this.data,
related: this.related,
} as T;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/common/services/MongoDBConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export class MongoDBConnector {

const attractions = await this.attractions();
await attractions.createIndex({ identifier: 1 }, { name: "id_index" });
await attractions.createIndex({ "attractions.referenceId": 1 }, { name: "attractions_referenceId_index" });

const tags = await this.tags();
await tags.createIndex({ identifier: 1 }, { name: "id_index" });
Expand Down
1 change: 1 addition & 0 deletions src/config/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export const pagination = {
defaultPage: 1,
defaultPageSize: 30,
maxPageSize: 500,
maxComplexRequestPageSize: 30,
};

export const MONGO_DB_DEFAULT_PROJECTION = {
Expand Down
1 change: 1 addition & 0 deletions src/integrationtests/EventsResources.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ describe("Search events", () => {
new FindEventsByLocationAccessibilityTagsFilterStrategy(env.eventsRepository, env.locationsRepository),
findInTheFutureStrategy,
];
console.log("Search events setup done " + env.eventsService.filterStrategies);
});

afterEach(async () => {
Expand Down
60 changes: 36 additions & 24 deletions src/integrationtests/integrationtestutils/TestEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { UsersRepository } from "../../resources/users/repositories/UsersReposit
import { UsersService } from "../../resources/users/services/UsersService";
import { MongoDBUsersRepository } from "../../resources/users/repositories/MongoDBUsersRepository";
import { AuthUser } from "../../generated/models/AuthUser.generated";
import { MongoDBFilterFactory } from "../../common/filter/FilterFactory";

export class TestEnvironment {
createUser(
Expand Down Expand Up @@ -118,48 +119,59 @@ export class TestEnvironment {
}

withEventsRoutes(): TestEnvironment {
this.eventsRepository = new MongoDBEventsRepository(this.connector);
this.eventsService = new EventsService(this.eventsRepository);
this.eventsController = new EventsController(this.eventsService);
this.eventsRoutes = new EventsRoutes(this.eventsController);
this.events = this.db.collection("events");
this.eventsRepository = this.eventsRepository || new MongoDBEventsRepository(this.connector);
this.eventsService = this.eventsService || new EventsService(this.eventsRepository);
this.eventsController =
this.eventsController || new EventsController(this.eventsService, new MongoDBFilterFactory());
this.eventsRoutes = this.eventsRoutes || new EventsRoutes(this.eventsController);
this.events = this.events || this.db.collection("events");
this.app.use("/", this.eventsRoutes.getRouter());

return this;
}

withLocationsRoutes(): TestEnvironment {
this.locationsRepository = new MongoDBLocationsRepository(this.connector);
this.locationsService = new LocationsService(this.locationsRepository);
this.locationsController = new LocationsController(this.locationsService);
this.locationsRoutes = new LocationsRoutes(this.locationsController);
this.locations = this.db.collection("locations");
this.locationsRepository = this.locationsRepository || new MongoDBLocationsRepository(this.connector);
this.locationsService = this.locationsService || new LocationsService(this.locationsRepository);
this.locationsController =
this.locationsController || new LocationsController(this.locationsService, new MongoDBFilterFactory());
this.locationsRoutes = this.locationsRoutes || new LocationsRoutes(this.locationsController);
this.locations = this.locations || this.db.collection("locations");
this.app.use("/", this.locationsRoutes.getRouter());

return this;
}

withOrganizationsRoutes(): TestEnvironment {
this.usersRepository = new MongoDBUsersRepository(this.connector);
this.usersService = new UsersService(this.usersRepository);
this.users = this.db.collection("users");

this.organizationsRepository = new MongoDBOrganizationsRepository(this.connector);
this.organizationsService = new OrganizationsService(this.organizationsRepository);
this.organizationsController = new OrganizationsController(this.organizationsService, this.usersService);
this.organizationsRoutes = new OrganizationsRoutes(this.organizationsController);
this.organizations = this.db.collection("organizations");
this.usersRepository = this.usersRepository || new MongoDBUsersRepository(this.connector);
this.usersService = this.usersService || new UsersService(this.usersRepository);
this.users = this.users || this.db.collection("users");

this.organizationsRepository = this.organizationsRepository || new MongoDBOrganizationsRepository(this.connector);
this.organizationsService = this.organizationsService || new OrganizationsService(this.organizationsRepository);
this.organizationsController =
this.organizationsController ||
new OrganizationsController(this.organizationsService, this.usersService, new MongoDBFilterFactory());
this.organizationsRoutes = this.organizationsRoutes || new OrganizationsRoutes(this.organizationsController);
this.organizations = this.organizations || this.db.collection("organizations");
this.app.use("/", this.organizationsRoutes.getRouter());

return this;
}

withAttractionsRoutes(): TestEnvironment {
this.attractionsRepository = new MongoDBAttractionsRepository(this.connector);
this.attractionsService = new AttractionsService(this.attractionsRepository, this.eventsRepository);
this.attractionsController = new AttractionsController(this.attractionsService);
this.attractionsRoutes = new AttractionsRoutes(this.attractionsController);
this.attractions = this.db.collection("attractions");
this.eventsRepository = this.eventsRepository || new MongoDBEventsRepository(this.connector);
this.eventsService = this.eventsService || new EventsService(this.eventsRepository);

this.attractionsRepository = this.attractionsRepository || new MongoDBAttractionsRepository(this.connector);
this.attractionsService =
this.attractionsService || new AttractionsService(this.attractionsRepository, this.eventsRepository);

this.attractionsController =
this.attractionsController ||
new AttractionsController(this.attractionsService, this.eventsService, new MongoDBFilterFactory());
this.attractionsRoutes = this.attractionsRoutes || new AttractionsRoutes(this.attractionsController);
this.attractions = this.attractions || this.db.collection("attractions");
this.app.use("/", this.attractionsRoutes.getRouter());

return this;
Expand Down
Loading