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

Swap 3129 fix instrument filter #388

Merged
merged 13 commits into from
Mar 14, 2023
Merged
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
36 changes: 36 additions & 0 deletions migrate-mongo-config.js.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// In this file you can configure migrate-mongo

const config = {
mongodb: {
// TODO Change (or review) the url to your MongoDB:
url: "mongodb://<URI.to.your.mongo.db>",

// TODO Change this to your database name:
databaseName: "<your-database-name>",

options: {
useNewUrlParser: true, // removes a deprecation warning when connecting
useUnifiedTopology: true, // removes a deprecating warning when connecting
// connectTimeoutMS: 3600000, // increase connection timeout to 1 hour
// socketTimeoutMS: 3600000, // increase socket timeout to 1 hour
}
},

// The migrations dir, can be an relative or absolute path. Only edit this when really necessary.
migrationsDir: "migrations",

// The mongodb collection where the applied changes are stored. Only edit this when really necessary.
changelogCollectionName: "changelog",

// The file extension to create migrations and search for in migration dir
migrationFileExtension: ".js",

// Enable the algorithm to create a checksum of the file contents and use that in the comparison to determine
// if the file should be run. Requires that scripts are coded to be run multiple times.
useFileHash: false,

// Don't change this, unless you know what you're doing
moduleSystem: 'commonjs',
};

module.exports = config;
4 changes: 4 additions & 0 deletions src/auth/strategies/ldap.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export class LdapStrategy extends PassportStrategy(Strategy, "ldap") {
const userFilter: FilterQuery<UserDocument> = {
$or: [
{ username: `ldap.${payload.displayName}` },
{ username: payload.displayName },
{ email: payload.mail as string },
],
};
Expand All @@ -42,6 +43,7 @@ export class LdapStrategy extends PassportStrategy(Strategy, "ldap") {
authStrategy: "ldap",
};
const user = await this.usersService.create(createUser);
console.log("Created ldap user ", user?.username);

if (!user) {
throw new InternalServerErrorException(
Expand Down Expand Up @@ -81,6 +83,7 @@ export class LdapStrategy extends PassportStrategy(Strategy, "ldap") {
};

await this.usersService.createUserIdentity(createUserIdentity);
console.log("Created user identity for ldap user with id ", user._id);
}

const foundUser = await this.usersService.findOne(userFilter);
Expand Down Expand Up @@ -111,6 +114,7 @@ export class LdapStrategy extends PassportStrategy(Strategy, "ldap") {
},
user._id,
);
console.log("Updated user identity for ldap user with id ", user._id);
}

return user;
Expand Down
2 changes: 2 additions & 0 deletions src/auth/strategies/oidc.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export class OidcStrategy extends PassportStrategy(Strategy, "oidc") {
"Could not create User from OIDC response.",
);
}
console.log("Created oidc user ", newUser.username);

const createUserIdentity: CreateUserIdentityDto = {
authStrategy: "oidc",
Expand All @@ -105,6 +106,7 @@ export class OidcStrategy extends PassportStrategy(Strategy, "oidc") {
};

await this.usersService.createUserIdentity(createUserIdentity);
console.log("Created user identity for oidc user with id ", newUser._id);

user = newUser;
} else {
Expand Down
7 changes: 7 additions & 0 deletions src/casl/casl-ability.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ export class CaslAbilityFactory {

// Instrument permissions
can(Action.Read, Instrument);
if (user.currentGroups.some((g) => adminGroups.includes(g))) {
can(Action.Manage, Instrument);
}

can(Action.Manage, Job);

Expand Down Expand Up @@ -140,6 +143,10 @@ export class CaslAbilityFactory {
cannot(Action.Update, Datablock);
can(Action.Delete, Datablock);
can(Action.Delete, PublishedData);
// instruments
cannot(Action.Create, Instrument);
cannot(Action.Update, Instrument);
can(Action.Delete, Instrument);
}
if (user.currentGroups.includes(Role.GlobalAccess)) {
can(Action.Read, "all");
Expand Down
36 changes: 36 additions & 0 deletions src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Expression, FilterQuery, Model, PipelineStage } from "mongoose";
import { DatasetType } from "src/datasets/dataset-type.enum";
import {
IAxiosError,
IFilters,
ILimitsFilter,
IScientificFilter,
} from "./interfaces/common.interface";
Expand Down Expand Up @@ -680,3 +681,38 @@ export const parseBoolean = (v: unknown): boolean => {
return false;
}
};

export const replaceLikeOperator = <T>(filter: IFilters<T>): IFilters<T> => {
if (filter.where) {
filter.where = replaceLikeOperatorRecursive(
filter.where as Record<string, unknown>,
);
}
return filter;
};

const replaceLikeOperatorRecursive = (
input: Record<string, unknown>,
): Record<string, unknown> => {
const output = {} as Record<string, unknown>;
for (const k in input) {
if (k == "like" && typeof input[k] !== "object") {
// we have encountered a loopback operator like
output["$regex"] = input[k];
} else if (k == "$or" || k == "$and" || k == "$in") {
output[k] = (input[k] as Array<unknown>).map((v) =>
typeof v === "string"
? v
: replaceLikeOperatorRecursive(v as Record<string, unknown>),
);
} else if (typeof input[k] === "object") {
output[k] = replaceLikeOperatorRecursive(
input[k] as Record<string, unknown>,
);
} else {
output[k] = input[k];
}
}

return output;
};
36 changes: 23 additions & 13 deletions src/datasets/datasets.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@ import {
UpdateDerivedDatasetDto,
} from "./dto/update-derived-dataset.dto";
import { CreateDatasetDatablockDto } from "src/datablocks/dto/create-dataset-datablock";
import { filterDescription, filterExample } from "src/common/utils";
import {
filterDescription,
filterExample,
replaceLikeOperator,
} from "src/common/utils";
import { TechniqueClass } from "./schemas/technique.schema";
import { RelationshipClass } from "./schemas/relationship.schema";
import { JWTUser } from "src/auth/interfaces/jwt-user.interface";
Expand Down Expand Up @@ -375,10 +379,12 @@ export class DatasetsController {
@Headers() headers: Record<string, string>,
@Query(new FilterPipe()) queryFilter: { filter?: string },
): Promise<DatasetClass[] | null> {
const mergedFilters = this.updateMergedFiltersForList(
request,
this.getFilters(headers, queryFilter),
);
const mergedFilters = replaceLikeOperator(
this.updateMergedFiltersForList(
request,
this.getFilters(headers, queryFilter),
) as Record<string, unknown>,
) as IFilters<DatasetDocument, IDatasetFields>;

const datasets = await this.datasetsService.findAll(mergedFilters);
if (datasets && datasets.length > 0) {
Expand Down Expand Up @@ -610,10 +616,12 @@ export class DatasetsController {
@Headers() headers: Record<string, string>,
@Query(new FilterPipe()) queryFilter: { filter?: string },
): Promise<DatasetClass | null> {
const mergedFilters = this.updateMergedFiltersForList(
request,
this.getFilters(headers, queryFilter),
);
const mergedFilters = replaceLikeOperator(
this.updateMergedFiltersForList(
request,
this.getFilters(headers, queryFilter),
) as Record<string, unknown>,
) as IFilters<DatasetDocument, IDatasetFields>;

const dataset = await this.datasetsService.findOne(mergedFilters);
if (dataset) {
Expand Down Expand Up @@ -675,10 +683,12 @@ export class DatasetsController {
@Headers() headers: Record<string, string>,
@Query(new FilterPipe()) queryFilter: { filter?: string },
): Promise<{ count: number }> {
const mergedFilters = this.updateMergedFiltersForList(
request,
this.getFilters(headers, queryFilter),
);
const mergedFilters = replaceLikeOperator(
this.updateMergedFiltersForList(
request,
this.getFilters(headers, queryFilter),
) as Record<string, unknown>,
) as IFilters<DatasetDocument, IDatasetFields>;

return this.datasetsService.count(mergedFilters);
}
Expand Down
18 changes: 15 additions & 3 deletions src/instruments/dto/create-instrument.dto.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
import { ApiProperty } from "@nestjs/swagger";
import { ApiProperty, ApiTags } from "@nestjs/swagger";
import { IsObject, IsOptional, IsString } from "class-validator";

@ApiTags("instruments")
export class CreateInstrumentDto {
@ApiProperty({ type: String, required: true })
@ApiProperty({
type: String,
required: true,
})
@IsString()
readonly name: string;

@ApiProperty({ type: Object, required: false, default: {} })
@ApiProperty({
type: Object,
required: false,
default: {},
})
@IsOptional()
@IsObject()
readonly customMetadata?: Record<string, unknown>;
}
48 changes: 44 additions & 4 deletions src/instruments/instruments.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,25 @@ import {
import { InstrumentsService } from "./instruments.service";
import { CreateInstrumentDto } from "./dto/create-instrument.dto";
import { UpdateInstrumentDto } from "./dto/update-instrument.dto";
import { ApiBearerAuth, ApiQuery, ApiTags } from "@nestjs/swagger";
import {
ApiBearerAuth,
ApiOperation,
ApiQuery,
ApiResponse,
ApiTags,
} from "@nestjs/swagger";
import { PoliciesGuard } from "src/casl/guards/policies.guard";
import { CheckPolicies } from "src/casl/decorators/check-policies.decorator";
import { AppAbility } from "src/casl/casl-ability.factory";
import { Action } from "src/casl/action.enum";
import { Instrument, InstrumentDocument } from "./schemas/instrument.schema";
import { FormatPhysicalQuantitiesInterceptor } from "src/common/interceptors/format-physical-quantities.interceptor";
import { IFilters } from "src/common/interfaces/common.interface";
import {
filterDescription,
filterExample,
replaceLikeOperator,
} from "src/common/utils";

@ApiBearerAuth()
@ApiTags("instruments")
Expand Down Expand Up @@ -51,16 +62,45 @@ export class InstrumentsController {
required: false,
})
async findAll(@Query("filter") filter?: string): Promise<Instrument[]> {
const instrumentFilter: IFilters<InstrumentDocument> = JSON.parse(
filter ?? "{}",
const instrumentFilter: IFilters<InstrumentDocument> = replaceLikeOperator(
JSON.parse(filter ?? "{}"),
);
return this.instrumentsService.findAll(instrumentFilter);
}

// GET /instrument/findOne
@UseGuards(PoliciesGuard)
@CheckPolicies((ability: AppAbility) => ability.can(Action.Read, Instrument))
@Get("/findOne")
@ApiOperation({
summary: "It returns the first instrument found.",
description:
"It returns the first instrument of the ones that matches the filter provided. The list returned can be modified by providing a filter.",
})
@ApiQuery({
name: "filter",
description:
"Database filters to apply when retrieving instruments\n" +
filterDescription,
required: false,
type: String,
example: filterExample,
})
@ApiResponse({
status: 200,
type: Instrument,
description: "Return the instrument requested",
})
async findOne(@Query("filter") filter?: string): Promise<Instrument | null> {
const instrumentFilters = replaceLikeOperator(JSON.parse(filter ?? "{}"));

return this.instrumentsService.findOne(instrumentFilters);
}

@UseGuards(PoliciesGuard)
@CheckPolicies((ability: AppAbility) => ability.can(Action.Read, Instrument))
@Get(":id")
async findOne(@Param("id") pid: string): Promise<Instrument | null> {
async findById(@Param("id") pid: string): Promise<Instrument | null> {
return this.instrumentsService.findOne({ pid });
}

Expand Down
1 change: 0 additions & 1 deletion src/instruments/instruments.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const mockInstrument: Instrument = {
pid: "testPid",
name: "Test",
customMetadata: {},
datasets: [],
};

describe("InstrumentsService", () => {
Expand Down
15 changes: 11 additions & 4 deletions src/instruments/instruments.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,25 @@ export class InstrumentsService {
const whereFilter: FilterQuery<InstrumentDocument> = filter.where ?? {};
const { limit, skip, sort } = parseLimitFilters(filter.limits);

return this.instrumentModel
const instrumentPromise = this.instrumentModel
.find(whereFilter)
.limit(limit)
.skip(skip)
.sort(sort)
.exec();
.sort(sort);

const instruments = await instrumentPromise.exec();

return instruments;
}

async findOne(
filter: FilterQuery<InstrumentDocument>,
): Promise<Instrument | null> {
return this.instrumentModel.findOne(filter).exec();
const whereFilter: FilterQuery<InstrumentDocument> = filter.where ?? {};
const fieldsProjection: FilterQuery<InstrumentDocument> =
filter.fields ?? {};

return this.instrumentModel.findOne(whereFilter, fieldsProjection).exec();
}

async update(
Expand Down
Loading