Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6475b3a
ensures fields required for the lookups are included
abdimo101 Sep 16, 2025
6aa1deb
changed comments and eslint fix
abdimo101 Sep 16, 2025
497573e
API test added
abdimo101 Sep 16, 2025
9c5f112
ensures fields required for the lookups are included
abdimo101 Sep 16, 2025
3d7a81b
changed comments and eslint fix
abdimo101 Sep 16, 2025
48af15a
API test added
abdimo101 Sep 16, 2025
646ac51
modified parsePipelineProjection to add include field to the projection
abdimo101 Sep 18, 2025
6e101b1
Merge branch 'fix-preserve-lookup-fields-for-relationships' of https:…
abdimo101 Sep 18, 2025
c779675
fixed failing tests
abdimo101 Sep 18, 2025
4a1e565
changes to fix failing tests + added a new api test
abdimo101 Sep 22, 2025
39790ff
Merge branch 'master' into fix-preserve-lookup-fields-for-relationships
abdimo101 Sep 22, 2025
404c4a9
eslint fix
abdimo101 Sep 22, 2025
da48ded
removed .only from api test
abdimo101 Sep 22, 2025
cf6c02b
fixed api test
abdimo101 Sep 22, 2025
1216b51
Merge branch 'master' into fix-preserve-lookup-fields-for-relationships
abdimo101 Sep 22, 2025
427685f
Merge branch 'master' into fix-preserve-lookup-fields-for-relationships
abdimo101 Sep 29, 2025
0388494
removed fields inside scope and added some requested changes
abdimo101 Sep 29, 2025
0d262ab
reverted back some changes to only fix the bug, also added error hand…
abdimo101 Oct 3, 2025
34ee21b
eslint fix
abdimo101 Oct 6, 2025
9344d17
fixed failing api tests
abdimo101 Oct 6, 2025
ff897ba
Merge branch 'master' into fix-preserve-lookup-fields-for-relationships
abdimo101 Oct 6, 2025
0b1bde8
Merge branch 'master' into fix-preserve-lookup-fields-for-relationships
minottic Oct 15, 2025
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
2 changes: 1 addition & 1 deletion src/attachments/attachments.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class AttachmentsService {

const pipeline: PipelineStage[] = [{ $match: whereFilter }];
if (!isEmpty(fieldsProjection)) {
const projection = parsePipelineProjection(fieldsProjection);
const projection = parsePipelineProjection(fieldsProjection, filter);
pipeline.push({ $project: projection });
}

Expand Down
2 changes: 1 addition & 1 deletion src/attachments/attachments.v4.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class AttachmentsV4Service {

const pipeline: PipelineStage[] = [{ $match: whereFilter }];
if (!isEmpty(fieldsProjection)) {
const projection = parsePipelineProjection(fieldsProjection);
const projection = parsePipelineProjection(fieldsProjection, filter);
pipeline.push({ $project: projection });
}

Expand Down
57 changes: 56 additions & 1 deletion src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ import {
} from "./interfaces/common.interface";
import { ScientificRelation } from "./scientific-relation.enum";
import { DatasetType } from "src/datasets/types/dataset-type.enum";
import { DatasetDocument } from "src/datasets/schemas/dataset.schema";
import { AttachmentDocument } from "src/attachments/schemas/attachment.schema";
import { JobDocument } from "src/jobs/schemas/job.schema";
import { OrigDatablockDocument } from "src/origdatablocks/schemas/origdatablock.schema";
import { DatasetLookupKeysEnum } from "src/datasets/types/dataset-lookup";
import { AttachmentLookupKeysEnum } from "src/attachments/types/attachment-lookup";
import { OrigDatablockLookupKeysEnum } from "src/origdatablocks/types/origdatablock-lookup";
import { JobLookupKeysEnum } from "src/jobs/types/job-lookup";
import { InstrumentDocument } from "src/instruments/schemas/instrument.schema";

// add Å to mathjs accepted units as equivalent to angstrom
const isAlphaOriginal = Unit.isValidAlpha;
Expand Down Expand Up @@ -414,8 +423,33 @@ export const parsePipelineSort = (sort: Record<string, "asc" | "desc">) => {
return pipelineSort;
};

export const parsePipelineProjection = (fieldsProjection: string[]) => {
export const parsePipelineProjection = (
fieldsProjection: string[],
filter: FilterQuery<
| DatasetDocument
| AttachmentDocument
| OrigDatablockDocument
| JobDocument
| InstrumentDocument
>,
) => {
const pipelineProjection: Record<string, boolean> = {};
const embeddedKeys = [
"datasets",
"origdatablocks",
"datablocks",
"jobs",
"attachments",
"instruments",
"proposals",
"samples",
];

const embeddedDocumentNames = new Set(
fieldsProjection
.filter((field) => field.includes("."))
.map((field) => field.split(".")[0]),
);

if (!Array.isArray(fieldsProjection)) {
throw new HttpException("fields must be an array", HttpStatus.BAD_REQUEST);
Expand All @@ -424,6 +458,27 @@ export const parsePipelineProjection = (fieldsProjection: string[]) => {
pipelineProjection[field] = true;
});

if (filter.include) {
filter.include.forEach(
(
field:
| DatasetLookupKeysEnum
| AttachmentLookupKeysEnum
| OrigDatablockLookupKeysEnum
| JobLookupKeysEnum,
) => {
if (field === "all") {
embeddedKeys.forEach((key) => {
if (!embeddedDocumentNames.has(key)) {
pipelineProjection[key] = true;
}
});
} else if (!embeddedDocumentNames.has(String(field))) {
pipelineProjection[String(field)] = true;
}
},
);
}
return pipelineProjection;
};

Expand Down
8 changes: 4 additions & 4 deletions src/datasets/datasets.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export class DatasetsService {
this.addLookupFields(pipeline, filter.include);

if (!isEmpty(fieldsProjection)) {
const projection = parsePipelineProjection(fieldsProjection);
const projection = parsePipelineProjection(fieldsProjection, filter);
pipeline.push({ $project: projection });
}

Expand Down Expand Up @@ -258,8 +258,10 @@ export class DatasetsService {
};

const pipeline: PipelineStage[] = [{ $match: whereFilter }];
this.addLookupFields(pipeline, filter.include);

if (!isEmpty(fieldsProjection)) {
const projection = parsePipelineProjection(fieldsProjection);
const projection = parsePipelineProjection(fieldsProjection, filter);
pipeline.push({ $project: projection });
}

Expand All @@ -270,8 +272,6 @@ export class DatasetsService {

pipeline.push({ $skip: limits.skip || 0 });

this.addLookupFields(pipeline, filter.include);

const [data] = await this.datasetModel
.aggregate<OutputDatasetDto | undefined>(pipeline)
.exec();
Expand Down
2 changes: 1 addition & 1 deletion src/jobs/jobs.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export class JobsService {

if (fieldsProjection && fieldsProjection.length > 0) {
const projectionFields = [...fieldsProjection];
const projection = parsePipelineProjection(projectionFields);
const projection = parsePipelineProjection(projectionFields, filter);
pipeline.push({ $project: projection });
} else {
// remove field datasetIds
Expand Down
6 changes: 3 additions & 3 deletions src/origdatablocks/origdatablocks.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export class OrigDatablocksService {
this.addLookupFields(pipeline, filter.include);

if (!isEmpty(fieldsProjection)) {
const projection = parsePipelineProjection(fieldsProjection);
const projection = parsePipelineProjection(fieldsProjection, filter);
pipeline.push({ $project: projection });
}

Expand Down Expand Up @@ -160,7 +160,7 @@ export class OrigDatablocksService {
this.addLookupFields(pipeline, filter.include);

if (!isEmpty(fieldsProjection)) {
const projection = parsePipelineProjection(fieldsProjection);
const projection = parsePipelineProjection(fieldsProjection, filter);
pipeline.push({ $project: projection });
}

Expand Down Expand Up @@ -230,7 +230,7 @@ export class OrigDatablocksService {

const pipeline: PipelineStage[] = [{ $match: whereFilter }];
if (!isEmpty(fieldsProjection)) {
const projection = parsePipelineProjection(fieldsProjection);
const projection = parsePipelineProjection(fieldsProjection, filter);
pipeline.push({ $project: projection });
}

Expand Down
108 changes: 93 additions & 15 deletions test/DatasetV4.js
Original file line number Diff line number Diff line change
Expand Up @@ -594,12 +594,12 @@ describe("2500: Datasets v4 tests", () => {
dataset.should.have.property("datasetName");
dataset.should.have.property("pid");
dataset.should.not.have.property("description");
dataset.should.not.have.property("instruments");
dataset.should.not.have.property("proposals");
dataset.should.not.have.property("datablocks");
dataset.should.not.have.property("attachments");
dataset.should.not.have.property("origdatablocks");
dataset.should.not.have.property("samples");
dataset.should.have.property("instruments");
dataset.should.have.property("proposals");
dataset.should.have.property("datablocks");
dataset.should.have.property("attachments");
dataset.should.have.property("origdatablocks");
dataset.should.have.property("samples");

dataset.datasetName.should.match(/Dataset/i);
});
Expand All @@ -626,7 +626,7 @@ describe("2500: Datasets v4 tests", () => {
},
};
const malformedJson = JSON.stringify(filter).replace("}", "},");

return request(appUrl)
.get(`/api/v4/datasets`)
.query({ filter: malformedJson })
Expand Down Expand Up @@ -721,7 +721,33 @@ describe("2500: Datasets v4 tests", () => {
});
});

it("0303: should fetch dataset relation fields if provided in the filter", async () => {
it("0303: should fetch specific dataset fields only if fields is provided in the filter with relationships", async () => {
const filter = {
include: ["instruments", "proposals"],
fields: ["datasetName"],
};

return request(appUrl)
.get(`/api/v4/datasets/findOne`)
.query({ filter: JSON.stringify(filter) })
.auth(accessTokenAdminIngestor, { type: "bearer" })
.expect(TestData.SuccessfulGetStatusCode)
.expect("Content-Type", /json/)
.then((res) => {
res.body.should.be.a("object");

res.body.should.have.property("datasetName");

res.body.should.not.have.property("description");
res.body.should.not.have.property("pid");

res.body.should.have.property("instruments");
res.body.should.have.property("proposals");
res.body.should.not.have.property("datablocks");
});
});

it("0304: should fetch dataset relation fields if provided in the filter", async () => {
const filter = {
include: ["instruments", "proposals"],
};
Expand All @@ -742,7 +768,7 @@ describe("2500: Datasets v4 tests", () => {
});
});

it("0304: should fetch all dataset relation fields if provided in the filter", async () => {
it("0305: should fetch all dataset relation fields if provided in the filter", async () => {
const filter = {
include: ["all"],
};
Expand All @@ -766,7 +792,7 @@ describe("2500: Datasets v4 tests", () => {
});
});

it("0305: should be able to fetch the dataset providing where filter", async () => {
it("0306: should be able to fetch the dataset providing where filter", async () => {
const filter = {
where: {
datasetName: {
Expand All @@ -788,7 +814,7 @@ describe("2500: Datasets v4 tests", () => {
});
});

it("0306: should be able to fetch a dataset providing all allowed filters together", async () => {
it("0307: should be able to fetch a dataset providing all allowed filters together", async () => {
const filter = {
where: {
datasetName: {
Expand Down Expand Up @@ -831,7 +857,7 @@ describe("2500: Datasets v4 tests", () => {
});
});

it("0307: should not be able to provide filters that are not allowed", async () => {
it("0308: should not be able to provide filters that are not allowed", async () => {
const filter = {
customField: { datasetName: "test" },
};
Expand All @@ -843,6 +869,54 @@ describe("2500: Datasets v4 tests", () => {
.expect(TestData.BadRequestStatusCode)
.expect("Content-Type", /json/);
});

it("0309: should only return the field and its subfields within the embedded documents even when 'all' is used under include", async () => {
const filter = {
include: ["all"],
fields: [
"datasetName",
"attachments.thumbnail",
"attachments.relationships.targetType",
"origdatablocks.dataFileList.path",
],
};

return request(appUrl)
.get(`/api/v4/datasets/findOne`)
.query({ filter: JSON.stringify(filter) })
.auth(accessTokenAdminIngestor, { type: "bearer" })
.expect(TestData.SuccessfulGetStatusCode)
.expect("Content-Type", /json/)
.then((res) => {
console.log("RES BODY ", JSON.stringify(res.body, null, 2));
res.body.should.be.a("object");

res.body.should.have.property("datasetName");

res.body.should.not.have.property("description");
res.body.should.not.have.property("pid");

res.body.attachments.forEach((attachment) => {
attachment.should.have.property("thumbnail");
attachment.should.not.have.property("caption");

attachment.relationships.forEach((relationship) => {
relationship.should.have.property("targetType");
relationship.should.not.have.property("relationType");
});
});

res.body.origdatablocks.forEach((origDatablock) => {
origDatablock.should.have.property("dataFileList");
origDatablock.should.not.have.property("size");

origDatablock.dataFileList.forEach((dataFile) => {
dataFile.should.have.property("path");
dataFile.should.not.have.property("size");
});
});
});
});
});

describe("Datasets v4 count tests", () => {
Expand Down Expand Up @@ -1038,7 +1112,7 @@ describe("2500: Datasets v4 tests", () => {

it("0604: should be able to partially update dataset's scientific metadata field", () => {
const updatedDataset = {
datasetlifecycle:{storageLocation:"new location"},
datasetlifecycle: { storageLocation: "new location" },
scientificMetadata: {
with_unit_and_value_si: {
value: 600,
Expand Down Expand Up @@ -1070,7 +1144,9 @@ describe("2500: Datasets v4 tests", () => {
value: 111,
unit: "",
});
res.body.datasetlifecycle.should.have.property("storageLocation").and.equal("new location");
res.body.datasetlifecycle.should.have
.property("storageLocation")
.and.equal("new location");
});
});

Expand Down Expand Up @@ -1457,7 +1533,9 @@ describe("2500: Datasets v4 tests", () => {
.then((res) => {
res.body.should.be.a("object");
res.body.should.be.deep.include(updatedDataset);
res.body.should.have.property("storageLocation").and.equal("new location");
res.body.should.have
.property("storageLocation")
.and.equal("new location");
});
});

Expand Down
12 changes: 6 additions & 6 deletions test/DatasetV4Public.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,12 +269,12 @@ describe("2600: Datasets v4 public endpoints tests", () => {
dataset.should.have.property("datasetName");
dataset.should.have.property("pid");
dataset.should.not.have.property("description");
dataset.should.not.have.property("instruments");
dataset.should.not.have.property("proposals");
dataset.should.not.have.property("datablocks");
dataset.should.not.have.property("attachments");
dataset.should.not.have.property("origdatablocks");
dataset.should.not.have.property("samples");
dataset.should.have.property("instruments");
dataset.should.have.property("proposals");
dataset.should.have.property("datablocks");
dataset.should.have.property("attachments");
dataset.should.have.property("origdatablocks");
dataset.should.have.property("samples");

dataset.datasetName.should.match(/Dataset/i);
});
Expand Down