Skip to content

Commit

Permalink
Thodoris fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
JSReds committed Jan 9, 2025
1 parent 16ff839 commit 293b297
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 66 deletions.
159 changes: 116 additions & 43 deletions src/odata-to-abstract-sql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,28 @@ class Query {
);
this.from.push(tableRef);
}
joinResource(
odataToAbstractSql: OData2AbstractSQL,
resource: AliasedResource,
type: JoinType,
condition: BooleanTypeNodes,
args: {
extraBindVars: ODataBinds;
bindVarsLength: number;
} = odataToAbstractSql,
bypassDefinition?: boolean,
): void {
const tableRef = odataToAbstractSql.getTableReference(
resource,
args.extraBindVars,
args.bindVarsLength,
bypassDefinition,
resource.tableAlias,
undefined,
);
const joinNode: JoinTypeNodes = [type, tableRef, ['On', condition]];
this.joins.push(joinNode);
}
addNestedFieldSelect(fieldName: string, fieldNameAlias: string): void {
if (this.from.length !== 1) {
throw new Error(
Expand Down Expand Up @@ -309,23 +331,23 @@ class Query {
| UpdateQueryNode
| DeleteQueryNode;
}
addJoin(
odataToAbstractSql: OData2AbstractSQL,
type: JoinType,
resource: AliasedResource,
condition: BooleanTypeNodes,
): void {
const tableRef = odataToAbstractSql.getTableReference(
resource,
odataToAbstractSql.extraBindVars,
odataToAbstractSql.bindVarsLength,
undefined,
resource.tableAlias,
undefined,
);
const joinNode: JoinTypeNodes = [type, tableRef, ['On', condition]];
this.joins.push(joinNode);
}
// addJoin(
// odataToAbstractSql: OData2AbstractSQL,
// type: JoinType,
// resource: AliasedResource,
// condition: BooleanTypeNodes,
// ): void {
// const tableRef = odataToAbstractSql.getTableReference(
// resource,
// odataToAbstractSql.extraBindVars,
// odataToAbstractSql.bindVarsLength,
// undefined,
// resource.tableAlias,
// undefined,
// );
// const joinNode: JoinTypeNodes = [type, tableRef, ['On', condition]];
// this.joins.push(joinNode);
// }
}

export const sqlNameToODataName = memoize(
Expand Down Expand Up @@ -914,7 +936,7 @@ export class OData2AbstractSQL {
query.where.push(where);
}
OrderBy(orderby: OrderByOption, query: Query, resource: Resource) {
this.AddExtraFroms(query, resource, orderby.properties, 'LeftJoin');
this.AddJoins(query, resource, orderby.properties, 'LeftJoin');
query.extras.push([
'OrderBy',
...this.OrderByProperties(orderby.properties),
Expand All @@ -923,14 +945,7 @@ export class OData2AbstractSQL {
OrderByProperties(orderings: OrderByPropertyPath[]): Array<OrderByNode[1]> {
return orderings.map((ordering) => {
const field = this.ReferencedProperty(ordering);

// Use COALESCE to handle NULL values
const orderingExpression: AnyTypeNodes = ['Coalesce', field, ['Null']];

return [
ordering.order.toUpperCase(),
orderingExpression,
] as OrderByNode[1];
return [ordering.order.toUpperCase(), field] as OrderByNode[1];
});
}
BindVars(
Expand Down Expand Up @@ -1332,7 +1347,7 @@ export class OData2AbstractSQL {
const query = new Query();
const resource = this.AddNavigation(
query,
this.defaultResource,
this.defaultResource!,
resourceName,
);
this.resourceAliases = { ...this.resourceAliases };
Expand Down Expand Up @@ -1453,7 +1468,7 @@ export class OData2AbstractSQL {
query.select.push(['Count', '*']);
const aliasedResource = this.AddNavigation(
query,
this.defaultResource,
this.defaultResource!,
prop.name,
);
if (prop.options?.$filter) {
Expand All @@ -1464,7 +1479,7 @@ export class OData2AbstractSQL {
}
return query.compile('SelectQuery');
} else {
return { resource: this.defaultResource, name: prop.name };
return { resource: this.defaultResource!, name: prop.name };
}
}
NumberMatch(match: any, optional: true): NumberTypeNodes | undefined;
Expand Down Expand Up @@ -1696,12 +1711,7 @@ export class OData2AbstractSQL {
],
};
}
AddExtraFroms(
query: Query,
parentResource: Resource,
match: any,
joinType?: JoinType,
) {
AddExtraFroms(query: Query, parentResource: Resource, match: any) {
// TODO: try removing
try {
if (Array.isArray(match)) {
Expand All @@ -1726,12 +1736,11 @@ export class OData2AbstractSQL {
query,
parentResource,
prop.name,
joinType,
);
}
}
if (nextProp?.args) {
this.AddExtraFroms(query, parentResource, prop.args, joinType);
this.AddExtraFroms(query, parentResource, prop.args);
}
}
} catch {
Expand All @@ -1742,7 +1751,7 @@ export class OData2AbstractSQL {
query: Query,
resource: Resource,
extraResource: string,
joinType?: JoinType,
// joinType?: JoinType,
): AliasedResource {
const navigation = this.NavigateResources(resource, extraResource);
if (
Expand All @@ -1752,12 +1761,76 @@ export class OData2AbstractSQL {
(isAliasNode(from) && from[2] === navigation.resource.tableAlias),
)
) {
if (joinType) {
query.addJoin(this, joinType, navigation.resource, navigation.where);
} else {
query.fromResource(this, navigation.resource);
query.where.push(navigation.where);
// if (joinType) {
// query.addJoin(this, joinType, navigation.resource, navigation.where);
// } else {
query.fromResource(this, navigation.resource);
query.where.push(navigation.where);
// }
return navigation.resource;
} else {
throw new SyntaxError(
`Could not navigate resources '${resource.name}' and '${extraResource}'`,
);
}
}
AddJoins(
query: Query,
parentResource: Resource,
match: OrderByPropertyPath[] | OrderByPropertyPath, // TODO: Change to any
joinType: JoinType,
) {
// // TODO: try removing
// try {
if (Array.isArray(match)) {
match.forEach((v) => {
this.AddJoins(query, parentResource, v, joinType);
});
} else {
let nextProp = match;
let prop;
while (
// tslint:disable-next-line:no-conditional-assignment
(prop = nextProp) &&
prop.name &&
prop.property?.name
) {
nextProp = prop.property;
const resourceAlias = this.resourceAliases[prop.name];
if (resourceAlias) {
parentResource = resourceAlias;
} else {
parentResource = this.AddJoinNavigation(
query,
parentResource,
prop.name,
joinType,
);
}
}
// if (nextProp?.args) {
// this.AddJoins(query, parentResource, prop.args);
// }
}
// } catch {
// // ignore
// }
}
AddJoinNavigation(
query: Query,
resource: Resource,
extraResource: string,
joinType: JoinType,
): AliasedResource {
const navigation = this.NavigateResources(resource, extraResource);
if (
!query.from.some(
(from) =>
(isTableNode(from) && from[1] === navigation.resource.tableAlias) ||
(isAliasNode(from) && from[2] === navigation.resource.tableAlias),
)
) {
query.joinResource(this, navigation.resource, joinType, navigation.where);
return navigation.resource;
} else {
throw new SyntaxError(
Expand Down
4 changes: 4 additions & 0 deletions test/chai-sql-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ declare namespace Chai {
table: string | string[],
...tables: string[] | string[][]
) => Assertion;
leftJoin: (
join: [string | string[], any[]],
...joins: Array<[string | string[], any[]]>
) => Assertion;
where: (clause: any[]) => Assertion;
orderby: (...clause: any[]) => Assertion;
limit: (clause: any[]) => Assertion;
Expand Down
20 changes: 20 additions & 0 deletions test/chai-sql.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ chai.use(function ($chai, utils) {
);
return this;
};
const binaryClause = (bodyType) =>
function (...bodyClauses) {
const obj = utils.flag(this, 'object');
for (let i = 0; i < bodyClauses.length; i++) {
expect(obj).to.contain.something.that.deep.equals(
[bodyType, bodyClauses[i][0], bodyClauses[i][1]],
bodyType + ' - ' + i,
);
}
return this;
};

const select = (function () {
const bodySelect = bodyClause('Select');
Expand Down Expand Up @@ -69,6 +80,15 @@ chai.use(function ($chai, utils) {
});
return fromClause.apply(this, bodyClauses);
});
const leftJoinClause = binaryClause('LeftJoin');
utils.addMethod(assertionPrototype, 'leftJoin', function (...bodyClauses) {
bodyClauses = bodyClauses.map(function ([v, condition]) {
const resource =
typeof v === 'string' ? ['Table', v] : ['Alias', ['Table', v[0]], v[1]];
return [resource, ['On', condition]];
});
return leftJoinClause.apply(this, bodyClauses);
});
utils.addMethod(assertionPrototype, 'where', bodyClause('Where'));
utils.addMethod(assertionPrototype, 'orderby', function (...bodyClauses) {
const bodyType = 'OrderBy';
Expand Down
52 changes: 29 additions & 23 deletions test/orderby.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,40 +50,46 @@ test('/pilot?$orderby=name asc,age desc', (result) =>
)));

test('/pilot?$orderby=licence/id asc', (result) =>
it('should order by licence/id asc', () =>
it('should order by licence/id asc', () => {
expect(result)
.to.be.a.query.that.selects(pilotFields)
.from('pilot', ['licence', 'pilot.licence'])
.where([
'Equals',
['ReferencedField', 'pilot', 'licence'],
['ReferencedField', 'pilot.licence', 'id'],
.from('pilot')
.leftJoin([
['licence', 'pilot.licence'],
[
'Equals',
['ReferencedField', 'pilot', 'licence'],
['ReferencedField', 'pilot.licence', 'id'],
],
])
.orderby(['ASC', operandToAbstractSQL('licence/id')])));
.orderby(['ASC', operandToAbstractSQL('licence/id')]);
}));

test('/pilot?$orderby=can_fly__plane/plane/id asc', (result) =>
it('should order by can_fly__plane/plane/id asc', () =>
it('should order by can_fly__plane/plane/id asc', () => {
expect(result)
.to.be.a.query.that.selects(pilotFields)
.from(
'pilot',
['pilot-can fly-plane', 'pilot.pilot-can fly-plane'],
['plane', 'pilot.pilot-can fly-plane.plane'],
)
.where([
'And',
.from('pilot')
.leftJoin(
[
'Equals',
['ReferencedField', 'pilot', 'id'],
['ReferencedField', 'pilot.pilot-can fly-plane', 'pilot'],
['pilot-can fly-plane', 'pilot.pilot-can fly-plane'],
[
'Equals',
['ReferencedField', 'pilot', 'id'],
['ReferencedField', 'pilot.pilot-can fly-plane', 'pilot'],
],
],
[
'Equals',
['ReferencedField', 'pilot.pilot-can fly-plane', 'can fly-plane'],
['ReferencedField', 'pilot.pilot-can fly-plane.plane', 'id'],
['plane', 'pilot.pilot-can fly-plane.plane'],
[
'Equals',
['ReferencedField', 'pilot.pilot-can fly-plane', 'can fly-plane'],
['ReferencedField', 'pilot.pilot-can fly-plane.plane', 'id'],
],
],
])
.orderby(['ASC', operandToAbstractSQL('can_fly__plane/plane/id')])));
)
.orderby(['ASC', operandToAbstractSQL('can_fly__plane/plane/id')]);
}));

test.skip('/pilot?$orderby=favourite_colour/red', () =>
it("should order by how red the pilot's favourite colour is"));
Expand Down

0 comments on commit 293b297

Please sign in to comment.