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

add odata nextlink #6

Merged
merged 1 commit into from
Dec 1, 2020
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
30 changes: 28 additions & 2 deletions src/lib/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ export class ODataProcessor extends Transform {
private streamObject = false;
private streamEnd = false;
private streamInlineCount: number;
private streamNextLink: string;
private elementType: any;
private resultCount = 0;

Expand Down Expand Up @@ -530,6 +531,9 @@ export class ODataProcessor extends Transform {
this.push("{");
if (this.options.metadata != ODataMetadataType.none) {
this.push(`"@odata.context":"${this.odataContext}",`);
if (this.streamNextLink) {
this.push(`"@odata.nextLink":"${this.streamNextLink}",`);
}
}
this.push('"value":[');
}
Expand All @@ -546,6 +550,15 @@ export class ODataProcessor extends Transform {
delete chunk.inlinecount;
}
}
if (chunk["@odata.nextLink"] || chunk.nextLink) {
this.streamNextLink = chunk["@odata.nextLink"] || chunk.nextLink;
if (Object.keys(chunk).length == 1) {
return typeof done == "function" ? done() : null;
} else {
delete chunk["@odata.nextLink"];
delete chunk.nextLink;
}
}
let entity = {};
let defer;
if (this.ctrl) defer = this.__appendLinks(this.ctrl, this.elementType || this.ctrl.prototype.elementType, entity, chunk);
Expand Down Expand Up @@ -592,6 +605,9 @@ export class ODataProcessor extends Transform {
if (this.streamStart && typeof this.streamInlineCount == "number") {
flushObject["@odata.count"] = this.streamInlineCount;
}
if (this.options.metadata != ODataMetadataType.none && this.streamNextLink) {
flushObject["@odata.nextLink"] = this.streamNextLink;
}
this.push(flushObject);
} else {
if (this.streamStart) {
Expand All @@ -602,15 +618,19 @@ export class ODataProcessor extends Transform {
if (this.options.metadata == ODataMetadataType.none) {
this.push('{"value":[]}');
} else {
this.push(`{"@odata.context":"${this.odataContext}","value":[]}`);
let md = `{"@odata.context":"${this.odataContext}"`;
if (this.streamNextLink) { md = `${md},{"@odata.nextLink":"${this.streamNextLink}"`; }
this.push(`${md}","value":[]}`);
}
}
}
} else if (this.streamEnabled && !this.streamStart) {
if (this.options.metadata == ODataMetadataType.none) {
this.push('{"value":[]}');
} else {
this.push(`{"@odata.context":"${this.odataContext}","value":[]}`);
let md = `{"@odata.context":"${this.odataContext}"`;
if (this.streamNextLink) { md = `${md},{"@odata.nextLink":"${this.streamNextLink}"`; }
this.push(`${md}","value":[]}`);
}
}
this.streamEnd = true;
Expand Down Expand Up @@ -796,10 +816,14 @@ export class ODataProcessor extends Transform {
if (this.method == "get") {
currentResult.then((value) => {
try {
const nl = result.body["@odata.context"];
result.body = {
"@odata.context": this.options.metadata != ODataMetadataType.none ? result.body["@odata.context"] : undefined,
value: value
};
if (this.options.metadata != ODataMetadataType.none && nl) {
result.body["@odata.context"] = nl;
}
let elementType = result.elementType;
//if (value instanceof Object)
result.elementType = Edm.isEnumType(result.elementType, part.name)
Expand Down Expand Up @@ -1281,6 +1305,7 @@ export class ODataProcessor extends Transform {
let elementType = result.elementType = jsPrimitiveTypes.indexOf(result.elementType) >= 0 || result.elementType == String || typeof result.elementType != "function" ? ctrlType : result.elementType;
if (typeof result.body == "object" && result.body) {
if (typeof result.body["@odata.count"] == "number") context["@odata.count"] = result.body["@odata.count"];
if (result.body && result.body.value && result.body.value["nextLink"] && typeof result.body.value["nextLink"] == "string") context["@odata.nextLink"] = result.body.value["nextLink"];
if (!result.body["@odata.context"]) {
let ctrl = this.ctrl && this.ctrl.prototype.elementType == ctrlType ? this.ctrl : this.serverType.getController(ctrlType);
if (result.body.value && Array.isArray(result.body.value)) {
Expand Down Expand Up @@ -1540,6 +1565,7 @@ export class ODataProcessor extends Transform {
}
if (isCollection && navigationResult.body.value && Array.isArray(navigationResult.body.value)) {
if (typeof navigationResult.body["@odata.count"] == "number") context[prop + "@odata.count"] = navigationResult.body["@odata.count"];
if (typeof navigationResult.body["@odata.nextLink"] == "string") context[prop + "@odata.nextLink"] = navigationResult.body["@odata.nextLink"];
context[prop] = navigationResult.body.value;
} else if (navigationResult.body && Object.keys(navigationResult.body).length !== 0) {
context[prop] = navigationResult.body;
Expand Down
20 changes: 16 additions & 4 deletions src/test/execute.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,18 @@ describe("OData execute", () => {
});
});

it("should return entity set result with nextLink", async () => {
const result = await TestServer.execute("/NextLinkEntitySet", "GET");

return expect(result.body["@odata.nextLink"]).to.equal("http://localhost/NextLinkEntitySet?$skip=1&$top=1");
});

it("should return navigation property expanded with nextLink", async () => {
const result = await TestServer.execute("/GeneratorCategories?$expand=GeneratorProducts($top=2)&$top=1&$orderby=Name desc", "GET");

return expect(result.body.value[0]["[email protected]"]).to.equal("http://localhost/GeneratorCategories('578f2baa12eaebabec4af28d')?$expand=GeneratorProducts($top=2&$skip=2)");
});

it("should create category reference on product", () => {
return TestServer.execute("/Products('578f2b8c12eaebabec4af286')/Category/$ref", "POST", {
"@odata.id": "http://localhost/Categories(categoryId='578f2baa12eaebabec4af28c')"
Expand Down Expand Up @@ -430,10 +442,10 @@ describe("OData execute", () => {
describe("Non existent entity", () => {
it('should return cannot read property node error', () => {
return TestServer.execute("/NonExistent", "GET")
.then((result) => {})
.catch(err => {
expect(err.message).to.equal("Cannot read property 'node' of undefined");
});
.then((result) => { })
.catch(err => {
expect(err.message).to.equal("Cannot read property 'node' of undefined");
});
});
});
});
38 changes: 38 additions & 0 deletions src/test/server.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,44 @@ export function testFactory(createTest: any) {
elementType: GeneratorCategory,
contentType: "application/json"
});
createTest("should return GeneratorCategories expanded with [email protected] when using $top in $expand subquery",
TestServer, "GET /GeneratorCategories?$expand=GeneratorProducts($top=2)&$top=1&$orderby=Name desc", {
statusCode: 200,
body: {
"@odata.context": "http://localhost/$metadata#GeneratorCategories",
"value": [
{
"@odata.id": "http://localhost/GeneratorCategories('578f2baa12eaebabec4af28d')",
"Description": "Seaweed and fish",
"Name": "Seafood",
"_id": new ObjectID("578f2baa12eaebabec4af28d"),
"GeneratorProducts": [
{
"@odata.id": "http://localhost/GeneratorProducts('578f2b8c12eaebabec4af242')",
"Discontinued": false,
"Name": "Ikura",
"QuantityPerUnit": "12 - 200 ml jars",
"UnitPrice": 31,
"_id": new ObjectID("578f2b8c12eaebabec4af242"),
"CategoryId": new ObjectID("578f2baa12eaebabec4af28d")
},
{
"@odata.id": "http://localhost/GeneratorProducts('578f2b8c12eaebabec4af245')",
"Discontinued": false,
"Name": "Konbu",
"QuantityPerUnit": "2 kg box",
"UnitPrice": 6,
"_id": new ObjectID("578f2b8c12eaebabec4af245"),
"CategoryId": new ObjectID("578f2baa12eaebabec4af28d"),
}
],
"[email protected]": "http://localhost/GeneratorCategories('578f2baa12eaebabec4af28d')?$expand=GeneratorProducts($top=2&$skip=2)"
}
]
},
elementType: GeneratorCategory,
contentType: "application/json"
});

createTest("should return GeneratorCategories expanded with GeneratorProduct using $top,$filter,$expand subqueries",
TestServer, "GET /GeneratorCategories?$expand=GeneratorProducts($orderby=Name desc)&$top=1&$orderby=Name desc", {
Expand Down
15 changes: 15 additions & 0 deletions src/test/test.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,16 @@ export class InlineCountController extends ODataController {
}
}

@odata.type(Foobar)
export class NextLinkController extends ODataController {
@odata.GET
entitySet() {
let result = [{ id: 1, a: 1 }];
(<any>result).nextLink = "http://localhost/NextLinkEntitySet?$skip=1&$top=1";
return result;
}
}

@odata.type(Foobar)
export class BoundOperationController extends ODataController {
@Edm.Action
Expand Down Expand Up @@ -821,6 +831,10 @@ export class CategoriesAdvancedGeneratorController extends ODataController {
response = yield doSkip(response, options);
response = yield doTop(response, options);

if (query && query.raw && query.raw === "$top=2") {
(<any>response).nextLink = "http://localhost/GeneratorCategories('578f2baa12eaebabec4af28d')?$expand=GeneratorProducts($top=2&$skip=2)";
}

return response
}
}
Expand Down Expand Up @@ -1017,6 +1031,7 @@ export class HiddenController extends ODataController { }
@odata.controller(GeneratorTestController, "GeneratorEntitySet")
@odata.controller(AsyncTestController, "AsyncEntitySet")
@odata.controller(InlineCountController, "InlineCountEntitySet")
@odata.controller(NextLinkController, "NextLinkEntitySet")
@odata.controller(BoundOperationController, "BoundOperationEntitySet")
@odata.controller(ImagesController, "ImagesControllerEntitySet")
@odata.controller(MusicController, "MusicControllerEntitySet")
Expand Down