UNPKG

lincd

Version:

LINCD is a JavaScript library for building user interfaces with linked data (also known as 'structured data', or RDF)

1,041 lines 125 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var Pet_1, Person_1; import { linkedComponent, linkedSetComponent, linkedShape, } from '../../package.js'; import { Shape } from '../../shapes/Shape.js'; import { literalProperty, objectProperty } from '../../shapes/SHACL.js'; import { Literal, NamedNode } from '../../models.js'; import { xsd } from '../../ontologies/xsd.js'; import { TestNode } from '../../utils/TraceShape.js'; import { describe, expect, test } from '@jest/globals'; import { render, waitFor } from '@testing-library/react'; import { ShapeSet } from '../../collections/ShapeSet.js'; import { setDefaultPageLimit } from '../../utils/Package.js'; import React from 'react'; import { getQueryContext, setQueryContext } from '../../queries/QueryContext.js'; let dogClass = NamedNode.getOrCreate(NamedNode.TEMP_URI_BASE + 'Dog'); let petClass = NamedNode.getOrCreate(NamedNode.TEMP_URI_BASE + 'Pet'); let personClass = NamedNode.getOrCreate(NamedNode.TEMP_URI_BASE + 'Person'); let name = NamedNode.getOrCreate(NamedNode.TEMP_URI_BASE + 'name'); let nickName = NamedNode.getOrCreate(NamedNode.TEMP_URI_BASE + 'nickName'); let bestFriend = NamedNode.getOrCreate(NamedNode.TEMP_URI_BASE + 'bestFriend'); let hobby = NamedNode.getOrCreate(NamedNode.TEMP_URI_BASE + 'hobby'); let hasFriend = NamedNode.getOrCreate(NamedNode.TEMP_URI_BASE + 'hasFriend'); let birthDate = NamedNode.getOrCreate(NamedNode.TEMP_URI_BASE + 'birthDate'); let owner = NamedNode.getOrCreate(NamedNode.TEMP_URI_BASE + 'owner'); let guardDogLevel = NamedNode.getOrCreate(NamedNode.TEMP_URI_BASE + 'guardDogLevel'); let hasPet = NamedNode.getOrCreate(NamedNode.TEMP_URI_BASE + 'hasPet'); let isRealPerson = NamedNode.getOrCreate(NamedNode.TEMP_URI_BASE + 'isRealPerson'); let pluralTestProp = NamedNode.getOrCreate(NamedNode.TEMP_URI_BASE + 'pluralTestProp'); let Pet = Pet_1 = class Pet extends Shape { get bestFriend() { return this.getOneAs(bestFriend, Pet_1); } set bestFriend(val) { this.overwrite(bestFriend, val.namedNode); } }; Pet.targetClass = petClass; __decorate([ objectProperty({ path: bestFriend, maxCount: 1, shape: Pet, }), __metadata("design:type", Pet), __metadata("design:paramtypes", [Pet]) ], Pet.prototype, "bestFriend", null); Pet = Pet_1 = __decorate([ linkedShape ], Pet); export { Pet }; let Person = Person_1 = class Person extends Shape { get name() { return this.getValue(name); } set name(val) { this.overwrite(name, new Literal(val)); } get bestFriend() { return this.getOneAs(bestFriend, Person_1); } set bestFriend(val) { this.overwrite(bestFriend, val.namedNode); } get hobby() { return this.getValue(hobby); } set hobby(val) { this.overwrite(hobby, new Literal(val)); } get friends() { return this.getAllAs(hasFriend, Person_1); } get pets() { return this.getAllAs(hasPet, Pet); } get firstPet() { return this.getOneAs(hasPet, Pet); } set firstPet(val) { this.overwrite(hasPet, val.namedNode); } get pluralTestProp() { return this.getAllAs(pluralTestProp, Person_1); } get birthDate() { return this.hasProperty(birthDate) ? toNativeDate(this.getOne(birthDate)) : null; } set birthDate(nativeDate) { this.overwrite(birthDate, fromNativeDate(nativeDate)); } get isRealPerson() { return this.hasProperty(isRealPerson) ? this.getValue(isRealPerson) === 'true' : undefined; } set isRealPerson(val) { this.overwrite(isRealPerson, new Literal(val ? 'true' : 'false', xsd.boolean)); } }; Person.targetClass = personClass; __decorate([ literalProperty({ path: name, maxCount: 1, }), __metadata("design:type", String), __metadata("design:paramtypes", [String]) ], Person.prototype, "name", null); __decorate([ objectProperty({ path: bestFriend, maxCount: 1, shape: Person, }), __metadata("design:type", Person), __metadata("design:paramtypes", [Person]) ], Person.prototype, "bestFriend", null); __decorate([ literalProperty({ path: hobby, maxCount: 1, }), __metadata("design:type", String), __metadata("design:paramtypes", [String]) ], Person.prototype, "hobby", null); __decorate([ objectProperty({ path: hasFriend, shape: Person, }), __metadata("design:type", Object), __metadata("design:paramtypes", []) ], Person.prototype, "friends", null); __decorate([ objectProperty({ path: hasPet, shape: Pet, }), __metadata("design:type", Object), __metadata("design:paramtypes", []) ], Person.prototype, "pets", null); __decorate([ objectProperty({ path: hasPet, shape: Pet, maxCount: 1, }), __metadata("design:type", Pet), __metadata("design:paramtypes", [Pet]) ], Person.prototype, "firstPet", null); __decorate([ objectProperty({ path: pluralTestProp, shape: Person, }), __metadata("design:type", Object), __metadata("design:paramtypes", []) ], Person.prototype, "pluralTestProp", null); __decorate([ literalProperty({ path: birthDate, datatype: xsd.dateTime, maxCount: 1, }), __metadata("design:type", Date), __metadata("design:paramtypes", [Date]) ], Person.prototype, "birthDate", null); __decorate([ literalProperty({ path: isRealPerson, datatype: xsd.boolean, maxCount: 1, }), __metadata("design:type", Boolean), __metadata("design:paramtypes", [Boolean]) ], Person.prototype, "isRealPerson", null); Person = Person_1 = __decorate([ linkedShape ], Person); export { Person }; let Dog = class Dog extends Pet { get guardDogLevel() { return this.hasProperty(guardDogLevel) ? parseInt(this.getValue(guardDogLevel)) : undefined; } set guardDogLevel(val) { this.overwrite(guardDogLevel, new Literal(val.toString(), xsd.integer)); } }; Dog.targetClass = dogClass; __decorate([ literalProperty({ path: guardDogLevel, maxCount: 1, datatype: xsd.integer, }), __metadata("design:type", Number), __metadata("design:paramtypes", [Number]) ], Dog.prototype, "guardDogLevel", null); Dog = __decorate([ linkedShape ], Dog); export { Dog }; function fromNativeDate(nativeDate) { if (!nativeDate) return null; var value = nativeDate.toISOString(); return new Literal(value, xsd.dateTime); } function toNativeDate(literal) { return literal ? new Date(literal instanceof TestNode ? null : literal.value) : null; } let p1 = Person.getFromURI(NamedNode.TEMP_URI_BASE + 'p1-semmy'); p1.name = 'Semmy'; p1.birthDate = new Date('1990-01-01'); p1.set(nickName, new Literal('Sem1')); p1.set(nickName, new Literal('Sem')); let p2 = Person.getFromURI(NamedNode.TEMP_URI_BASE + 'p2-moa'); p2.name = 'Moa'; p2.hobby = 'Jogging'; let p3 = Person.getFromURI(NamedNode.TEMP_URI_BASE + 'p3-jinx'); p3.name = 'Jinx'; let p4 = Person.getFromURI(NamedNode.TEMP_URI_BASE + 'p4-quinn'); p4.name = 'Quinn'; p1.friends.add(p2); p1.friends.add(p3); p2.bestFriend = p3; p2.friends.add(p3); p2.friends.add(p4); p1.pluralTestProp.add(p1); p1.pluralTestProp.add(p2); p1.pluralTestProp.add(p3); p1.pluralTestProp.add(p4); p1.isRealPerson = true; p2.isRealPerson = false; p3.isRealPerson = true; let dog1 = Dog.getFromURI(NamedNode.TEMP_URI_BASE + 'dog1'); dog1.guardDogLevel = 2; let dog2 = Dog.getFromURI(NamedNode.TEMP_URI_BASE + 'dog2'); dog1.bestFriend = dog2; p1.pets.add(dog1); p2.pets.add(dog2); //kind of a duplicate (it replaces previously set pets), but helpful for testing .as() on singular values p1.firstPet = dog1; export const testEntities = [p1, p2, p3, p4, dog1, dog2]; export const testProps = { name, nickName, bestFriend, hobby, hasFriend, birthDate }; export const testTypes = { person: personClass }; setQueryContext('user', p3, Person); /** * * @param startPromise if provided it will be awaited before running the tests */ export const runQueryTests = (startPromise = Promise.resolve()) => { describe('query tests', () => { describe('1. Basic Property Selection', () => { test('can select a literal property of all instances', () => __awaiter(void 0, void 0, void 0, function* () { yield startPromise; // x:LinkedQuery<Person, QueryString<Person, "name">> let names = yield Person.select((p) => { let res = p.name; return res; }); // let names = resolveLocal(x); /** * Expected result: * [{ * "id:"..." * "shape": a Person * "name:"Semmy" * },{ * "name":"Moa", * },... ] */ names.forEach((name) => { }); expect(Array.isArray(names)).toBe(true); expect(names.length).toBe(4); expect(typeof names[0] === 'object').toBe(true); expect(names[0].hasOwnProperty('name')).toBe(true); expect(names[0].name).toBe('Semmy'); expect(names[0].id).toBe(p1.uri); })); test('can select an object property of all instances', () => __awaiter(void 0, void 0, void 0, function* () { // x:LinkedQuery<Person, QueryString<Person, "name">> // QueryShapeSet<Person, Person, "friends"> & ToQueryShapeSetValue<QueryShapeSet<Person, Person, "friends">, Person, "friends"> //Needs to become: // -> QResult<Person, {friends: QResult<Person, {}>[]}>[] //From person the property friends is requested. //The results is a shapeset of persons, with source Person //S / ShapeType: Person //Source:Person //Property: "friends" //Shapeset turns into QResult<Person,{friends:QResult<Person,{}>[]}> ... thats just the shapeset //then let personFriends = yield Person.select((p) => { let res = p.friends; return res; }); /** * Expected result: * [{ * "id:"..." * "shape": a Person * "friends:[{ * "id"..., * "shape": a Person * },...] * },... ] */ let firstResult = personFriends[0]; expect(Array.isArray(personFriends)).toBe(true); expect(personFriends.length).toBe(4); expect(typeof personFriends[0] === 'object').toBe(true); expect(firstResult.hasOwnProperty('id')).toBe(true); expect(firstResult.id).toBe(p1.uri); expect(firstResult.friends.length).toBe(2); expect(firstResult.friends[0].id).toBe(p2.uri); expect(firstResult.friends[1].id).toBe(p3.uri); })); test('can select a date', () => __awaiter(void 0, void 0, void 0, function* () { let birthDates = yield Person.select((p) => { return [p.birthDate, p.name]; }); let firstResult = birthDates[0]; expect(Array.isArray(birthDates)).toBe(true); expect(birthDates.length).toBe(4); expect(typeof firstResult.birthDate === 'object').toBe(true); expect(firstResult.birthDate.toString()).toBe(p1.birthDate.toString()); })); test('can select a boolean', () => __awaiter(void 0, void 0, void 0, function* () { let isRealPersons = yield Person.select((p) => { return p.isRealPerson; }); expect(Array.isArray(isRealPersons)).toBe(true); expect(isRealPersons.length).toBe(4); expect(isRealPersons.filter(p => p.isRealPerson !== null).length).toBe(3); let p1Result = isRealPersons.find(p => p.id === p1.uri); expect(p1Result.isRealPerson).toBe(true); let p2Result = isRealPersons.find(p => p.id === p2.uri); expect(p2Result.isRealPerson).toBe(false); let p4Result = isRealPersons.find(p => p.id === p4.uri); expect(p4Result.isRealPerson).toBeNull(); })); test('can select properties of a specific subject', () => __awaiter(void 0, void 0, void 0, function* () { let qRes = yield Person.select(p1, p => p.name); expect(qRes.name).toBe(p1.name); expect(qRes.id).toBe(p1.uri); })); test('can select properties of a specific subject by ID reference', () => __awaiter(void 0, void 0, void 0, function* () { let qRes = yield Person.select({ id: p1.uri }, p => p.name); expect(qRes.name).toBe(p1.name); expect(qRes.id).toBe(p1.uri); })); test('select with a non existing returns null', () => __awaiter(void 0, void 0, void 0, function* () { let qRes = yield Person.select({ id: 'https://does.not/exist' }, p => p.name); expect(qRes).toBeNull(); })); }); describe('2. Nested & Path Selection', () => { test('can select sub properties of a first property that returns a set', () => __awaiter(void 0, void 0, void 0, function* () { let namesOfFriends = yield Person.select((p) => { // QueryString<QueryShapeSet<Person, Person, "friends">, "name"> //step 1) --> QResult<QueryShapeSet<Person, Person, "friends">, {name: string}>[][] //step 2) --> QResult<Person, {friends: QResult<Person, {name: string}>}>[][] //--> QResult<Person, {friends: QResult<Person, {name:string}>}>[] //QueryString<QueryShapeSet<Person, Person, "friends">, "name"> //Source : QueryShapeSet<Person, Person, "friends"> //Property: "name" // QueryShapeSet<Person, Person, "friends"> // ShapeType : Person // Source: Person // Property: "friends // in other words. Person.friends is a set of persons //which needs to be converted to QResult<Person (Source), friends: is a QResult<Person (ShapeType),{name:string}> array let res = p.friends.name; return res; }); let first = namesOfFriends[0]; expect(Array.isArray(namesOfFriends)).toBe(true); expect(namesOfFriends.length).toBe(4); expect(first.id).toBe(p1.uri); expect(first.friends.length).toBe(2); expect(first.friends[0].id).toBe(p2.uri); expect(first.friends[0].name).toBe('Moa'); expect(first.friends[0]['hobby']).toBeUndefined(); })); test('can select a nested set of shapes', () => __awaiter(void 0, void 0, void 0, function* () { let friendsOfFriends = yield Person.select((p) => { return p.friends.friends; }); expect(Array.isArray(friendsOfFriends)).toBe(true); let first = friendsOfFriends[0]; expect(friendsOfFriends.length).toBe(4); expect(first.friends.length).toBe(2); expect(first.friends[0].friends.some((f) => f.id == p3.uri)).toBe(true); expect(first.friends[0].friends.some((f) => f.id == p4.uri)).toBe(true); expect(first.friends[1].friends.length).toBe(0); expect(friendsOfFriends[3].friends.length).toBe(0); })); test('can select multiple property paths', () => __awaiter(void 0, void 0, void 0, function* () { let result = yield Person.select((p) => { let res = [p.name, p.friends, p.bestFriend.name]; return res; }); expect(Array.isArray(result)).toBe(true); expect(result.length).toBe(4); let first = result[0]; expect(first.name).toBe('Semmy'); expect(Array.isArray(first.friends)).toBe(true); expect(first.friends.length).toBe(2); expect(first.friends.some((f) => f.id === p2.uri)).toBe(true); expect(first.friends.some((f) => f.id === p4.uri)).toBe(false); })); test('can select property of single shape value', () => __awaiter(void 0, void 0, void 0, function* () { //( // QResult<Person, {bestFriend: QResult<Person, {name: string}>}> | // QResult<Shape, {}> | // QResult<...>[] // )[] //QResult<Person, {bestFriend: QResult<Person>}>[] //QResult<Person, {bestFriend: QResult<Person, {name: string}>}> // |QResult<Shape, {}> |QResult<...>[])[]QResult<Person, {bestFriend: QResult<Person>}>[] let result = yield Person.select((p) => { // QShape<Person, QShape<Person, null, "">, "bestFriend"> let r = p.bestFriend.name; // let r3 = [p.bestFriend]; // let r2 = [p.friends.friends.name]; return r; }); //expected result: /** * [ * { * "id": "p1", * "bestFriend": { * "id": "p3", * "name": "Jinx" * } * ... * ] */ expect(Array.isArray(result)).toBe(true); expect(result.length).toBe(4); let second = result[1]; expect(second.bestFriend.id).toBe(p3.uri); })); test('can select 3 level deep nested paths', () => __awaiter(void 0, void 0, void 0, function* () { let level3Friends = yield Person.select((p) => { return p.friends.friends.friends; }); expect(level3Friends.length).toBe(4); expect(level3Friends.every((p) => p.friends.every((f) => f.friends.every((f2) => f2.friends.length === 0)))).toBe(true); })); }); describe('3. Filtering (Where Clauses)', () => { test('can use where() to filter a string in a set of Literals with equals', () => __awaiter(void 0, void 0, void 0, function* () { //we select the friends of all persons, but only those friends whose name is moa //this will return an array, where each entry represents the results for a single person. // the entry contains those friends of the person whose name is Moa - (as a set of persons) //QResult<Person, {friends: QResult<Person, {}>[]}>[] let friendsCalledMoa = yield Person.select((p) => { return p.friends.where((f) => f.name.equals('Moa')); }); let first = friendsCalledMoa[0]; let second = friendsCalledMoa[1]; expect(Array.isArray(friendsCalledMoa)).toBe(true); expect(first.friends.length).toBe(1); expect(first.friends[0].id).toBe(p2.uri); expect(second.friends.length).toBe(0); })); test('where object value', () => __awaiter(void 0, void 0, void 0, function* () { //we select the friends of all persons, but only those friends whose name is moa //this will return an array, where each entry represents the results for a single person. // the entry contains those friends of the person whose name is Moa - (as a set of persons) //QResult<Person, {friends: QResult<Person, {}>[]}>[] let hasBestFriend = yield Person.select().where(p => { return p.bestFriend.equals({ id: p3.uri }); }); expect(Array.isArray(hasBestFriend)).toBe(true); expect(hasBestFriend.length).toBe(1); expect(hasBestFriend[0].id).toBe(p2.uri); })); test('where on literal', () => __awaiter(void 0, void 0, void 0, function* () { const hobbies = yield Person.select((p) => { return p.hobby.where(h => h.equals(p2.hobby)); }); expect(Array.isArray(hobbies)).toBe(true); expect(hobbies.length).toBe(4); let p1Result = hobbies.find(h => h.id === p1.uri); let p2Result = hobbies.find(h => h.id === p2.uri); expect(p1Result).toBeDefined(); expect(p2Result).toBeDefined(); expect(p1Result.hobby).toBeUndefined(); expect(p2Result.hobby).toBe(p2.hobby); })); test('where and', () => __awaiter(void 0, void 0, void 0, function* () { //we select the friends of all persons, but only those friends whose name is moa //this will return an array, where each entry represents the results for a single person. // the entry contains those friends of the person whose name is Moa - (as a set of persons) let friendsCalledMoaThatJog = yield Person.select((p) => { return p.friends.where((f) => f.name.equals('Moa').and(f.hobby.equals('Jogging'))); }); let first = friendsCalledMoaThatJog[0]; let second = friendsCalledMoaThatJog[1]; expect(Array.isArray(friendsCalledMoaThatJog)).toBe(true); expect(first.friends.length).toBe(1); expect(first.friends[0].id).toBe(p2.uri); expect(second.friends.length).toBe(0); })); test('where or', () => __awaiter(void 0, void 0, void 0, function* () { //we select the friends of all persons, but only those friends whose name is moa //this will return an array, where each entry represents the results for a single person. // the entry contains those friends of the person whose name is Moa - (as a set of persons) let orFriends = yield Person.select((p) => { return p.friends.where((f) => f.name.equals('Jinx').or(f.hobby.equals('Jogging'))); }); let first = orFriends[0]; let second = orFriends[1]; expect(Array.isArray(orFriends)).toBe(true); expect(first.friends.length).toBe(2); expect(first.friends[0].id).toBe(p2.uri); expect(first.friends[1].id).toBe(p3.uri); expect(second.friends.length).toBe(1); expect(second.friends[0].id).toBe(p3.uri); })); test('select all', () => __awaiter(void 0, void 0, void 0, function* () { let all = yield Person.select(); expect(Array.isArray(all)).toBe(true); expect(all.length).toBe(4); })); test('empty select with where ', () => __awaiter(void 0, void 0, void 0, function* () { let filteredNoProps = yield Person.select().where((p) => { return p.name.equals(p1.name); }); expect(Array.isArray(filteredNoProps)).toBe(true); expect(filteredNoProps.length).toBe(1); expect(filteredNoProps[0].id).toBe(p1.uri); })); test('where and or and', () => __awaiter(void 0, void 0, void 0, function* () { //we combine AND & OR. AND should be done first, then OR //Boolean logic, AND always comes before OR //friend.name === A || friend.hobby === B && friend.name === C //Therefor we expect p2 and p3 to match as friends //(p3 would not match if the OR was done first) let persons = yield Person.select((p) => { return p.friends.where((f) => f.name .equals('Jinx') .or(f.hobby.equals('Jogging')) .and(f.name.equals('Moa'))); }); //test the same thing again, but now the and clause is done within the or clause //the result should be the same let persons2 = yield Person.select((p) => { return p.friends.where((f) => f.name .equals('Jinx') .or(f.hobby.equals('Jogging').and(f.name.equals('Moa')))); }); //(friend.name === A || friend.hobby === B) && friend.name === C // let persons3 = await Person.select((p) => { // return p.friends.where((f) => // f.name.equals('Moa').and( // f.name.equals('Jinx') // .or(f.hobby.equals('Jogging')), // ) // ); // }); //TODO: implement f.or(A,B) [persons, persons2].forEach((result) => { expect(Array.isArray(result)).toBe(true); expect(result[0].friends.length).toBe(2); expect(result[1].friends.length).toBe(1); expect(result[2].friends.length).toBe(0); expect(result[3].friends.length).toBe(0); expect(result[0].friends[0].id).toBe(p2.uri); expect(result[0].friends[1].id).toBe(p3.uri); expect(result[1].friends[0].id).toBe(p3.uri); }); })); test('where some implicit', () => __awaiter(void 0, void 0, void 0, function* () { //select all persons that have a friend called Moa //the test relies on the fact that by default, some() is applied. //in other words, the person matches if at least 1 friend is called Moa let peopleWithFriendsCalledMoa = yield Person.select().where((p) => { return p.friends.name.equals('Moa'); }); expect(Array.isArray(peopleWithFriendsCalledMoa)).toBe(true); expect(peopleWithFriendsCalledMoa.length).toBe(1); expect(peopleWithFriendsCalledMoa[0].id).toBe(p1.uri); })); test('where some explicit', () => __awaiter(void 0, void 0, void 0, function* () { // same as last test but with explicit some() let peopleWithFriendsCalledMoa = yield Person.select().where((p) => { return p.friends.some((f) => { return f.name.equals('Moa'); }); }); expect(Array.isArray(peopleWithFriendsCalledMoa)).toBe(true); expect(peopleWithFriendsCalledMoa.length).toBe(1); expect(peopleWithFriendsCalledMoa[0].id).toBe(p1.uri); })); test('where every', () => __awaiter(void 0, void 0, void 0, function* () { // select people that only have friends that are called Moa or Jinx let allFriendsCalledMoaOrJinx = yield Person.select().where((p) => { return p.friends.every((f) => { return f.name.equals('Moa').or(f.name.equals('Jinx')); }); }); expect(Array.isArray(allFriendsCalledMoaOrJinx)).toBe(true); expect(allFriendsCalledMoaOrJinx.length).toBe(1); expect(allFriendsCalledMoaOrJinx[0].id).toBe(p1.uri); })); test('where sequences', () => __awaiter(void 0, void 0, void 0, function* () { // select people that have a friend called Jinx and a name "Semmy" (so that's only p1) //Should be QResult<Person, {name:string}>[] let friendCalledJinxAndNameIsSemmy = yield Person.select().where((p) => { let res = p.friends .some((f) => { return f.name.equals('Jinx'); }) .and(p.name.equals('Semmy')); return res; }); expect(Array.isArray(friendCalledJinxAndNameIsSemmy)).toBe(true); expect(friendCalledJinxAndNameIsSemmy.length).toBe(1); expect(friendCalledJinxAndNameIsSemmy[0].id).toBe(p1.uri); // select people that have a friend called Jinx, BUT ONLY SELECT THEIR NAME if their name is "Semmy" //so we should get p1 and p2 (the outher .where filters 4 down to 2), but only the name of p1 should be defined let friendCalledJinxAndNameIsSemmy2 = yield Person.select((p) => { let res = p.name.where((n) => { return n.equals('Semmy'); }); return res; }).where((p) => p.friends.some((f) => { return f.name.equals('Jinx'); })); //make sure type is undefined. Then make everything with single shapes work only with QResult expect(Array.isArray(friendCalledJinxAndNameIsSemmy2)).toBe(true); expect(friendCalledJinxAndNameIsSemmy2.length).toBe(2); expect(friendCalledJinxAndNameIsSemmy2[0].id).toBe(p1.uri); expect(friendCalledJinxAndNameIsSemmy2[1].id).toBe(p2.uri); expect(friendCalledJinxAndNameIsSemmy2[0].name).toBe('Semmy'); expect(typeof friendCalledJinxAndNameIsSemmy2[1].name).toBe('undefined'); })); test('outer where()', () => __awaiter(void 0, void 0, void 0, function* () { // QResult<Person, {friends: QResult<Person, {}>[]}>[] let friendsOfP1 = yield Person.select((p) => { return p.friends; }).where((p) => { return p.name.equals(p1.name); }); let first = friendsOfP1[0]; expect(Array.isArray(friendsOfP1)).toBe(true); expect(friendsOfP1).toHaveLength(1); expect(first.id).toBe(p1.uri); expect(first.friends.length).toBe(2); expect(first.friends[0].id).toBe(p2.uri); })); test('where with query context', () => __awaiter(void 0, void 0, void 0, function* () { //should return name of p2 let namesHasBestFriendUser = yield Person.select((p) => { return p.name; }).where((p) => { return p.bestFriend.equals(getQueryContext('user')); }); expect(Array.isArray(namesHasBestFriendUser)).toBe(true); let first = namesHasBestFriendUser[0]; expect(namesHasBestFriendUser).toHaveLength(1); expect(first.id).toBe(p2.uri); expect(first.name).toBe(p2.name); })); test('where with query context as base of property path', () => __awaiter(void 0, void 0, void 0, function* () { //should return name of p2 let hasUserAsFriend = yield Person.select((p) => { return p.name; }).where((p) => { const userName = getQueryContext('user').name; return p.friends.some(f => f.name.equals(userName)); }); expect(Array.isArray(hasUserAsFriend)).toBe(true); expect(hasUserAsFriend).toHaveLength(2); expect(hasUserAsFriend.some(p => p.id === p1.uri && p.name === p1.name)).toBeTruthy(); expect(hasUserAsFriend.some(p => p.id === p2.uri && p.name === p2.name)).toBeTruthy(); })); }); describe('4. Aggregation & Sub-Select', () => { test('count a shapeset', () => __awaiter(void 0, void 0, void 0, function* () { //count the number of friends that each person has //QResult<Person, {friends: number}>[] let numberOfFriends = yield Person.select((p) => { let res = p.friends.size(); return res; }); //Note that when no argument is given to count, we expect the key to be the label of the // last property before count. So that's "friends" //expected result /** * [{ * id: "p1", * friends: 2 * },{ * id: "p2", * friends: 2 * },...] */ expect(Array.isArray(numberOfFriends)).toBe(true); expect(numberOfFriends[0].friends).toBe(2); expect(numberOfFriends[1].friends).toBe(2); expect(numberOfFriends[2].friends).toBe(0); expect(numberOfFriends[3].friends).toBe(0); })); test('count a nested property', () => __awaiter(void 0, void 0, void 0, function* () { //count the number of friends that each person has //QResult<Person, {friends: number}>[] let numberOfFriends = yield Person.select((p) => { let res = p.friends.friends.size(); //TODO: count() -> let res = p.count(friends.friends); --> would return the total sum of friends of friends return res; }); //expected result /** * [{ * id: "p1", * friends: [{ * id: "p2", * friends: 2 * },{ * id: "p3", * friends: 0 * }] * },...] */ expect(Array.isArray(numberOfFriends)).toBe(true); expect(Array.isArray(numberOfFriends[0].friends)).toBe(true); expect(numberOfFriends[0].friends[0].friends).toBe(2); expect(numberOfFriends[0].friends[1].friends).toBe(0); })); // test('shape.count() with a countable argument', async () => { // //count the number of friends that each person has // //QResult<Person, {friends: number}>[] // let numberOfFriends = await Person.select((p) => { // let res = p.count(p.friends); // // return res; // }); // //expected result // /** // * [{ // * id: "p1", // * count: 2 // * },{ // * id: "p2", // * count: 2 // * },...] // */ // // expect(Array.isArray(numberOfFriends)).toBe(true); // expect(numberOfFriends[0].count).toBe(2); // expect(numberOfFriends[1].count).toBe(2); // expect(numberOfFriends[2].count).toBe(0); // expect(numberOfFriends[3].count).toBe(0); // }); test('labeling the key of count()', () => __awaiter(void 0, void 0, void 0, function* () { //count the number of friends that each person has let numberOfFriends3 = yield Person.select((p) => { let res = p.friends.select((f) => ({ numFriends: f.friends.size() })); return res; }); //expected result /** * [{ * id: "p1", * friends: [ * {numFriends: 2,id: "p2"}, * {numFriends: 0,id: "p3"} * ] * },{ * id: "p2", * friends: [ * {numFriends: 0,id: "p3"}, * {numFriends: 0,id: "p4"} * ] * },...] */ //We want outcome to be {numFriends: number} //So ObjectToPlainResult should convert the SetSize to a number //if Source (SetSize<Source>) extends QueryShapeSet, then its an object, else a number //SelectQueryFactory< // Person, // { // numFriends: SetSize<QShapeSet<Person,QShape<Person,null,''>,'friends'>> // }, // QueryShapeSet<Person,QShape<Person,null,''>,'friends'> //> // //Step 1: QueryResponseToResultType // SelectQueryFactory extends GetNestedQueryResultType< // any // Response = {numFriends: SetSize<QShapeSet<Person,QShape<Person,null,''>,'friends'>> // Source = QueryShapeSet<Person,QShape<Person,null,''>,'friends'> //Step 2: GetNestedQueryResultType //Source extends QueryBuilderObject<Source, Response> //Step 3: GetQueryObjectResultType< //QV = Source = QueryShapeSet<Person,QShape<Person,null,''>,'friends'> //SubProperties = ResponseToObject<Response> //QV extends QueryShapeSet< //ShapeType = Person //Source = QShape<Person,null,''> //Property = 'friends' //Step 4: CreateShapeSetQResult< //ShapeType=ShapeType=Person, //Source=QShape<Person,null,''>, //Property='friends, //SubProperties=ResponseToObject<Response> //HasName=false //> //Source extends QueryShape< //SourceShapeType = Person //ParentSource = null //> -> QResult< // SourceShapeType=Person, // {[P in Property='friends']: CreateQResult<Source, null, null, SubProperties>[]} // > //Step 5: CreateQResult< // Source=QShape<Person,null,''> // Value=null, // Property=null, // SubProperties=ResponseToObject<Response> //> //Source extends QShape< // SourceShapeType = Person // ParentSource = null // SourceProperty = '' let first = numberOfFriends3[0]; let firstNumFriends = first.friends[0].numFriends; // let firstNumFriends: number = first.friends; expect(first.hasOwnProperty('friends')).toBe(true); expect(first.hasOwnProperty('count')).toBe(false); expect(firstNumFriends).toBe(2); })); // test('count a nested path as argument', async () => { // //count the number of second level friends that each person has // //count is expected to count the total number of final nodes (friends) in the p.friends.friends set // //by counting each sub result and combinging the results // let numberOfFriends = await Person.select((p) => { // let res = p.count(p.friends.friends, 'numFriends'); // return res; // }); // //expected result // /** // * [{ // * id: "p1", // * count: 2 // * },{ // * id: "p2", // * count: 0 // * },...] // */ // // let first = numberOfFriends[0]; // expect(Array.isArray(numberOfFriends)).toBe(true); // expect(numberOfFriends[0].count).toBe(2); // expect(numberOfFriends[1].count).toBe(0); // expect(numberOfFriends[2].count).toBe(0); // expect(numberOfFriends[3].count).toBe(0); // }); test('nested object property', () => __awaiter(void 0, void 0, void 0, function* () { //NOTE: this test is currently just here for typescript types. let res = yield Person.select((p) => { let res = { friends: p.friends, bestFriends: p.friends.bestFriend, }; return res; }); //({firstName,lastName}) => { // return <div>...</div> //} //has to be an array of objects let friends = res[0].friends; //bestFriends should be a PATH, but it's not. Its a end result only let bestFriends = res[0].bestFriends; let res2 = yield Person.select((p) => { let res = p.friends.bestFriend; return res; }); //This works, friends is an array and bestFriend is a single shape let firstBestFriend = res2[0].friends[0].bestFriend; })); test('sub select single prop', () => __awaiter(void 0, void 0, void 0, function* () { let bestFriendProps = yield Person.select((p) => { let res = p.bestFriend.select((f) => { let props = [f.name, f.hobby]; return props; }); return res; }).where(p => { return p.equals({ id: p2.uri }); }); // SelectQueryFactory< // Person, // ( // QueryString<QueryShape<Person, null, ""> & QueryShapeProps<Person, null, "">, "name">| // QueryString<QueryShape<Person, null, ""> & QueryShapeProps<Person, null, "">, "hobby"> // )[], // QueryShape< // Person, // QueryShape<Person, null, ""> & QueryShapeProps<Person, null, "">, // "bestFriend" // > // > //step 1: GetNestedQueryResultType<Response, Source> //Response = ( // QueryString<QueryShape<Person, null, ""> & QueryShapeProps<Person, null, "">, "name">| // QueryString<QueryShape<Person, null, ""> & QueryShapeProps<Person, null, "">, "hobby"> //)[] //Source = QueryShape< // Person, // QueryShape<Person, null, ""> & QueryShapeProps<Person, null, "">, // "bestFriend" // > //Source extends QueryBuilderObject //--> step 2: GetQueryObjectResultType<QV=Source, SubProperties=ResponseToObject<Response> //QV extends QueryShape<ShapeType,Source,Property> //ShapeType = Person (value type of property?) //Source = QueryShape<Person, null, ""> & QueryShapeProps<Person, null, ""> //Property = "bestFriend" //--> step3: CreateQResult<Source,ShapeType,Property,SubProperties,HasName=false> //Source=Source=QueryShape<Person, null, ""> & QueryShapeProps<Person, null, ""> (original request Person.select()) //Value=ShapeType=Person (value type of property?) //Property="bestFriend" //SubProperties=ResponseToObject<Response> //HasName=false //Source extends QueryShape<SourceShapeType,ParentSource,SourceProperty> //SourceShapeType = Person //ParentSource = null //SourceProperty = '' //--> //QResult< // SourceShapeType=Person // { // [P in Property("bestFriend")]: CreateQResult<Value=ShapeType=Person,Value=ShapeType=Person> // } & SubProperties //CONFLICTING //Step 5: CreateQResult< // Source=QShape<Person,null,''> // Value=null, // Property=null, // SubProperties=ResponseToObject<Response> //> //Source extends QShape< // SourceShapeType = Person // ParentSource = null // SourceProperty = '' //We want: //QResult< // Person, // { // bestFriend: QResult<Person, { // name:string, // hobby:string // }> // } //>[] /** * Expected result: * [{ * "id:"..." * "bestFriend": { * id:"...", * name:"Jinx", * hobby:undefined * } * }] */ let first = bestFriendProps[0]; // let name = first.bestFriend.name; expect(Array.isArray(bestFriendProps)).toBe(true); expect(bestFriendProps.length).toBe(1); expect(first.id).toBe(p2.uri); expect(first.bestFriend.id).toBe(p3.uri); expect(first.bestFriend.name).toBe(p3.name); expect(first.bestFriend.hobby).toBeNull(); })); test('sub select plural prop - custom object', () => __awaiter(void 0, void 0, void 0, function* () { let namesAndHobbiesOfFriends = yield Person.select((p) => { let res = p.friends.select((f) => { let res2 = { _name: f.name, _hobby: f.hobby, }; return res2; }); return res; }); // //LinkedQuery<Person,{ // _name: QueryString<QShape<Person,null,''>,'name'> // _hobby: QueryString<QShape<Person,null,''>,'hobby'> //},QueryShapeSet<...>> //QShapeSet<Person,QShape<Person,null,''>,'friends'> //GetNestedQueryResultType< // Response = {name,hobby}, // Source = QShapeSet<Person,QShape<Person,null,''>,'friends'> //> // --> GetQueryObjectResultType< // Source = QShapeSet<Person,QShape<Person,null,''>,'friends'>, // SubProperties = ResponseToObject<{ // _name: QueryString<QShape<Person,null,''>,'name'>, // _hobby: QueryString<QShape<Person,null,''>,'hobby'> // }> // > //--> QV extends QueryShapeSet.. -> // CreateShapeSetQResult< // ShapeType = Person, // Source = QShape<Person,null,''>, // Property = 'friends', // SubProperties = {} // HasName = false? (we're currently not passing this in GetNestedQueryResultType) //> //-> QResult< // Source = SourceShapeType = Person, // {'friends': CreateQResult< Person, null, null, {_name: string, _hobby: string}>[]} //