UNPKG

api-core

Version:

Model-based dynamic multi-level APIs for any provider, plus multiple consumption channels

467 lines (417 loc) 16.6 kB
import {ApiEdgeError} from "../src/query/ApiEdgeError"; const tap = require('tap'); import {Api} from "../src/Api"; import * as builder from "../src/query/ApiQueryBuilder"; import {OneToOneRelation} from "../src/relations/OneToOneRelation"; import {OneToManyRelation} from "../src/relations/OneToManyRelation"; import {StudentEdge} from "./env/edges/StudentEdge"; import {ClassEdge} from "./env/edges/ClassEdge"; import {CourseEdge} from "./env/edges/CourseEdge"; import {CourseTypeEdge} from "./env/edges/CourseTypeEdge"; import {SchoolEdge} from "./env/edges/SchoolEdge"; import {ApiRequestType, ApiRequest} from "../src/request/ApiRequest"; const studentEdge = new StudentEdge, classEdge = new ClassEdge, courseEdge = new CourseEdge, courseTypeEdge = new CourseTypeEdge, schoolEdge = new SchoolEdge; var api: Api; tap.test('creating the API should work', (t: any) => { api = new Api({name: 'test-service', version: '1.0'}) .edge(studentEdge) .edge(classEdge) .edge(courseEdge) .edge(courseTypeEdge) .edge(schoolEdge) .relation(new OneToOneRelation(courseEdge, courseTypeEdge)) .relation(new OneToManyRelation(courseTypeEdge, courseEdge)) .relation(new OneToManyRelation(studentEdge, courseEdge)) .relation(new OneToOneRelation(studentEdge, classEdge)) .relation(new OneToOneRelation(classEdge, schoolEdge)) .relation(new OneToOneRelation(courseEdge, classEdge)) .relation(new OneToManyRelation(classEdge, studentEdge)) .relation(new OneToManyRelation(classEdge, courseEdge)) .relation(new OneToManyRelation(schoolEdge, studentEdge)) .relation(new OneToManyRelation(schoolEdge, classEdge)); t.end() }); tap.test('Any type queries should fail', (t: any) => { try { let request = new ApiRequest(api); request.type = ApiRequestType.Any; api.buildQuery(request); t.ok(false, 'an invalid query should not succeed'); } catch(e) { t.ok(e instanceof ApiEdgeError); t.equal(e.message, 'Unsupported Query Type'); t.equal(e.status, 400); } t.end() }); tap.test('Change type queries should fail', (t: any) => { try { let request = new ApiRequest(api); request.type = ApiRequestType.Change; api.buildQuery(request); t.ok(false, 'an invalid query should not succeed'); } catch(e) { t.ok(e instanceof ApiEdgeError); t.equal(e.message, 'Unsupported Query Type'); t.equal(e.status, 400); } t.end() }); tap.test('/schools', async (t: any) => { const request = await api.parseRequest([ 'schools' ]), query = api.buildQuery(request); t.equal(query.steps.length, 2, 'should build a 2 step query'); t.ok(query.steps[0] instanceof builder.ExtendContextQueryStep); t.ok(query.steps[1] instanceof builder.QueryEdgeQueryStep); return query.execute() .then(resp => { t.same(resp.data, [ { address: "16, Test street, North Pole, HA23535", id: "s1", name: "First School", phone: "435234523" }, { address: "12, Test street, North Pole, HA23535", id: "s2", name: "Second School", phone: "456345283" } ]); t.equal(resp.metadata, null); t.end() }) .catch((e) => { t.ok(false, "a valid query should not fail"); t.end() }); }); tap.test('/schools/s2', async (t: any) => { const request = await api.parseRequest([ 'schools', 's2' ]), query = api.buildQuery(request); t.equal(query.steps.length, 3, 'should build a 3 step query'); t.ok(query.steps[0] instanceof builder.ExtendContextLiveQueryStep); t.ok(query.steps[1] instanceof builder.ExtendContextQueryStep); t.ok(query.steps[2] instanceof builder.QueryEdgeQueryStep); return query.execute() .then(resp => { t.same(resp.data, { address: "12, Test street, North Pole, HA23535", id: "s2", name: "Second School", phone: "456345283" }); t.equal(resp.metadata, null); t.end() }) .catch(() => { t.ok(false, "a valid query should not fail"); t.end() }); }); tap.test('/schools/s5', async (t: any) => { const request = await api.parseRequest([ 'schools', 's5' ]), query = api.buildQuery(request); return query.execute() .then(() => { t.ok(false, "an invalid query should not succeed"); t.end() }) .catch(e => { t.ok(e instanceof ApiEdgeError); t.equal(e.status, 404); t.equal(e.message, 'Not Found'); t.end() }); }); tap.test('/schools/s1/classes', async (t: any) => { const request = await api.parseRequest([ 'schools', 's1', 'classes' ]), query = api.buildQuery(request); t.equal(query.steps.length, 5, 'should build a 5 step query'); t.ok(query.steps[0] instanceof builder.ExtendContextLiveQueryStep, 'EXTEND'); t.ok(query.steps[1] instanceof builder.QueryEdgeQueryStep, 'QUERY /schools'); t.ok(query.steps[2] instanceof builder.RelateQueryStep, 'RELATE schoolId'); t.ok(query.steps[3] instanceof builder.ExtendContextQueryStep, 'APPLY PARAMS'); t.ok(query.steps[4] instanceof builder.QueryEdgeQueryStep, 'QUERY /classes'); return query.execute() .then(resp => { t.same(resp.data, [ { id: "c1", name: "A", year: 1, room: "Room 1", schoolId: "s1" } ]); t.equal(resp.metadata, null); t.end() }) .catch(() => { t.ok(false, "a valid query should not fail"); t.end() }); }); tap.test('/schools/s1/classes/c1', async (t: any) => { const request = await api.parseRequest([ 'schools', 's1', 'classes', 'c1' ]), query = api.buildQuery(request); t.equal(query.steps.length, 6, 'should build a 6 step query'); t.ok(query.steps[0] instanceof builder.ExtendContextLiveQueryStep, 'EXTEND'); t.ok(query.steps[1] instanceof builder.QueryEdgeQueryStep, 'QUERY /schools'); t.ok(query.steps[2] instanceof builder.RelateQueryStep, 'RELATE schoolId'); t.ok(query.steps[3] instanceof builder.ExtendContextLiveQueryStep, 'EXTEND'); t.ok(query.steps[4] instanceof builder.ExtendContextQueryStep, 'APPLY PARAMS'); t.ok(query.steps[5] instanceof builder.QueryEdgeQueryStep, 'QUERY /classes'); return query.execute() .then(resp => { t.same(resp.data, { id: "c1", name: "A", year: 1, room: "Room 1", schoolId: "s1" }); t.equal(resp.metadata, null); t.end() }) .catch(() => { t.ok(false, "a valid query should not fail"); t.end() }); }); tap.test('/students/s2/class', async (t: any) => { const request = await api.parseRequest([ 'students', 's2', 'class' ]), query = api.buildQuery(request); t.equal(query.steps.length, 5, 'should build a 5 step query'); t.ok(query.steps[0] instanceof builder.ExtendContextLiveQueryStep, 'EXTEND'); t.ok(query.steps[1] instanceof builder.QueryEdgeQueryStep, 'QUERY /students'); t.ok(query.steps[2] instanceof builder.ProvideIdQueryStep, 'PROVIDE ID: classId'); t.ok(query.steps[3] instanceof builder.ExtendContextQueryStep, 'APPLY PARAMS'); t.ok(query.steps[4] instanceof builder.QueryEdgeQueryStep, 'QUERY /classes'); return query.execute() .then(resp => { t.same(resp.data, { id: "c1", name: "A", year: 1, room: "Room 1", schoolId: "s1" }); t.equal(resp.metadata, null); t.end() }) .catch(() => { t.ok(false, "a valid query should not fail"); t.end() }); }); tap.test('/schools/s1/classes/c2', async (t: any) => { const request = await api.parseRequest([ 'schools', 's1', 'classes', 'c2' ]), query = api.buildQuery(request); return query.execute() .then(() => { t.ok(false, "an invalid query should not succeed"); t.end() }) .catch(e => { t.ok(e instanceof ApiEdgeError); t.equal(e.status, 404); t.equal(e.message, 'Not Found'); t.end() }); }); tap.test('POST /schools', async (t: any) => { const request = await api.parseRequest([ 'schools' ]); request.type = ApiRequestType.Create; const body = request.body = { address: "12, Test street, North Pole, HA23535", id: "s3", name: "Third School", phone: "456345283" }; const query = api.buildQuery(request); t.equal(query.steps.length, 2, 'should build a 2 step query'); t.ok(query.steps[0] instanceof builder.SetBodyQueryStep); t.ok(query.steps[1] instanceof builder.QueryEdgeQueryStep); return query.execute() .then(resp => { t.same(resp.data, body); t.equal(resp.metadata, null); t.end() }) .catch(() => { t.ok(false, "a valid query should not fail"); t.end() }); }); tap.test('DELETE /schools/s3', async (t: any) => { const request = await api.parseRequest([ 'schools', 's3' ]); request.type = ApiRequestType.Delete; const query = api.buildQuery(request); t.equal(query.steps.length, 3, 'should build a 3 step query'); t.ok(query.steps[0] instanceof builder.ExtendContextLiveQueryStep); t.ok(query.steps[1] instanceof builder.ExtendContextQueryStep); t.ok(query.steps[2] instanceof builder.QueryEdgeQueryStep); return query.execute() .then(resp => { t.equal(resp.metadata, null); t.end() }) .catch(() => { t.ok(false, "a valid query should not fail"); t.end() }); }); tap.test('DELETE /students/s2/class', async (t: any) => { const request = await api.parseRequest([ 'students', 's2', 'class' ]); request.type = ApiRequestType.Delete; try { api.buildQuery(request); t.ok(false, "an invalid query should not succeed"); t.end() } catch(e) { t.ok(e instanceof ApiEdgeError); t.equal(e.status, 400); t.equal(e.message, 'Invalid Delete Query'); t.end() } }); tap.test('PATCH /schools/s2', async (t: any) => { const request = await api.parseRequest([ 'schools', 's2' ]); request.type = ApiRequestType.Patch; request.body = { name: "Cool School" }; const query = api.buildQuery(request); t.equal(query.steps.length, 4, 'should build a 4 step query'); t.ok(query.steps[0] instanceof builder.SetBodyQueryStep, 'SET BODY'); t.ok(query.steps[1] instanceof builder.ExtendContextLiveQueryStep, 'EXTEND'); t.ok(query.steps[2] instanceof builder.ExtendContextQueryStep, 'APPLY PARAMS'); t.ok(query.steps[3] instanceof builder.QueryEdgeQueryStep, 'QUERY'); return query.execute() .then(resp => { t.same(resp.data, { address: "12, Test street, North Pole, HA23535", id: "s2", name: "Cool School", phone: "456345283" }); t.equal(resp.metadata, null); t.end() }) .catch(() => { t.ok(false, "a valid query should not fail"); t.end() }); }); tap.test('PUT /schools/s2', async (t: any) => { const request = await api.parseRequest([ 'schools', 's2' ]); request.type = ApiRequestType.Update; request.body = { name: "Cool School" }; const query = api.buildQuery(request); t.equal(query.steps.length, 4, 'should build a 4 step query'); t.ok(query.steps[0] instanceof builder.SetBodyQueryStep, 'SET BODY'); t.ok(query.steps[1] instanceof builder.ExtendContextLiveQueryStep, 'EXTEND'); t.ok(query.steps[2] instanceof builder.ExtendContextQueryStep, 'APPLY PARAMS'); t.ok(query.steps[3] instanceof builder.QueryEdgeQueryStep, 'QUERY'); return query.execute() .then(resp => { t.same(resp.data, { id: "s2", name: "Cool School", address: null, phone: null }); t.equal(resp.metadata, null); t.end() }) .catch(() => { t.ok(false, "a valid query should not fail"); t.end() }); }); tap.test('GET /students/s2/rename', async (t: any) => { const request = await api.parseRequest([ 'students', 's2', 'rename' ]); request.type = ApiRequestType.Read; try { api.buildQuery(request); t.ok(false, 'an invalid query should not succeed') } catch(e) { t.ok(e instanceof ApiEdgeError); t.equal(e.message, 'Method Not Allowed'); t.equal(e.status, 405); } t.end() }); tap.test('/students/s2/withHungarianName', async (t: any) => { const request = await api.parseRequest([ 'students', 's2', 'withHungarianName' ]), query = api.buildQuery(request); t.equal(query.steps.length, 5, 'should build an 5 step query'); t.ok(query.steps[0] instanceof builder.ExtendContextLiveQueryStep, 'EXTEND'); t.ok(query.steps[1] instanceof builder.QueryEdgeQueryStep, 'QUERY /students'); t.ok(query.steps[2] instanceof builder.ExtendContextQueryStep, 'APPLY PARAMS'); t.ok(query.steps[3] instanceof builder.ProvideIdQueryStep, 'PROVIDE ID'); t.ok(query.steps[4] instanceof builder.CallMethodQueryStep, 'call{withHungarianName}'); return query.execute() .then(resp => { t.same(resp.data, { id: "s2", fullName: "Dave Test", hungarianName: "Test Dave", email: "dave.test@gmail.com", schoolId: "s1", classId: "c1" }); t.equal(resp.metadata, null); t.end() }) .catch(() => { t.ok(false, "a valid query should not fail"); t.end() }); }); tap.test('POST /students/s2/rename', async (t: any) => { const request = await api.parseRequest([ 'students', 's2', 'rename' ]); request.type = ApiRequestType.Update; request.body = { name: "Test David" }; const query = api.buildQuery(request); t.equal(query.steps.length, 5, 'should build a 4 step query'); t.ok(query.steps[0] instanceof builder.SetBodyQueryStep, 'SET BODY'); t.ok(query.steps[1] instanceof builder.SetResponseQueryStep, 'SET RESPONSE'); t.ok(query.steps[2] instanceof builder.ExtendContextQueryStep, 'APPLY PARAMS'); t.ok(query.steps[3] instanceof builder.ProvideIdQueryStep, 'PROVIDE ID'); t.ok(query.steps[4] instanceof builder.CallMethodQueryStep, 'call{rename}'); return query.execute() .then(resp => { t.same(resp.data, { id: "s2", fullName: "David Test", email: "dave.test@gmail.com", schoolId: "s1", classId: "c1" }); t.equal(resp.metadata, null); t.end() }) .catch(() => { t.ok(false, "a valid query should not fail"); t.end() }); });