Skip to content
This repository has been archived by the owner on Sep 4, 2020. It is now read-only.

Implement server using Hapi.js (succeeded) with GraphQL support (failed) #3

Merged
merged 7 commits into from
Mar 31, 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
13 changes: 8 additions & 5 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,30 @@
"license": "MIT",
"private": true,
"scripts": {
"build": "tsc",
"build": "rimraf out && tsc && copyfiles src/schema.graphql out/",
"dev": "node --inspect=4141 -r ts-node/register ./src/server.ts",
"lint": "eslint **/*.ts --fix",
"start": "yarn build && node out/server.js",
"watch": "nodemon"
},
"dependencies": {
"@apollo/server": "^3.0.0-alpha.1",
"@hapi/hapi": "^19.1.1",
"@hapi/joi": "^17.1.1",
"axios": "^0.19.1",
"body-parser": "^1.19.0",
"cheerio": "^1.0.0-rc.3",
"cors": "^2.8.5",
"express": "^4.17.1",
"moment-timezone": "^0.5.27"
},
"devDependencies": {
"@types/body-parser": "^1.17.1",
"@types/cheerio": "^0.22.15",
"@types/cors": "^2.8.6",
"@types/express": "^4.17.2",
"@types/hapi__hapi": "^19.0.2",
"@types/hapi__joi": "^16.0.12",
"@types/moment-timezone": "^0.5.12",
"copyfiles": "^2.2.0",
"nodemon": "^2.0.2",
"rimraf": "^3.0.2",
"ts-node": "^8.6.2"
}
}
33 changes: 0 additions & 33 deletions server/src/middleware.ts

This file was deleted.

17 changes: 10 additions & 7 deletions server/src/models/accommodation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class Accommodation {
description: string
photoLink: string
vacancy: number
distance: { [campusName: string]: number } = {}
distance: { [campusName in CampusNames]: number }
leaseDetails: ILeaseDetails
bedroomNumber: number
bedrooms: IBedroom[]
Expand Down Expand Up @@ -128,12 +128,15 @@ export class Accommodation {
this.commonAreasAccess =
dict['Common Areas Accessible']?.[0].split(', ') ?? []
this.rentMin = Number(stats[0][1].split('$')[1]) || 0
if (stats[1].includes('North') && stats[1].includes('Ryde')) {
this.distance[CampusNames.NorthRyde] = Number(stats[1][0])
this.distance[CampusNames.City] = Number(stats[2][0])
} else if (stats[1].includes('City')) {
this.distance[CampusNames.City] = Number(stats[1][0])
this.distance[CampusNames.NorthRyde] = Number(stats[2][0])

const NorthRydeFirst = stats[1].includes('North Ryde')
const CityFirst = stats[1].includes('City')
const first = Number(stats[1][0])
const second = Number(stats[2][0])

this.distance = {
[CampusNames.NorthRyde]: NorthRydeFirst ? first : second,
[CampusNames.City]: CityFirst ? first : second,
}
}
}
70 changes: 70 additions & 0 deletions server/src/schema.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
type Distance {
properties: Float
rooms: Float
}

type LeaseDetails {
maxLease: Float
minLease: Float
leaseType: String
}

type Accommodation {
title: String
suburb: String
description: String
photoLink: String
vacancy: Int
distance: Distance
leaseDetails: LeaseDetails
bedroomNumber: Int
bedrooms: [Bedroom]
rentMin: Int
heatingCooling: [String]
wheelchairAccess: Boolean
bathroomNumber: Int
bedroomFurnishing: String
houseFurnishing: String
hasInternet: Boolean
cantSmoke: Boolean
safety: [String]
bills: String
utilities: [String]
noPets: Boolean
prefGender: [String]
shortStay: Boolean
commonAreasAccess: [String]
link: String
}

type Query {
getAccommodations(
housingOption: HousingOptions!,
campusName: CampusNames!,
maxRent: Int!,
bathroom: Int,
ensuite: Boolean,
leaseMin: Int,
leaseMax: Int,
distanceMax: Float,
vacancy: Int,
bedroom: Int,
heating: Boolean,
cooling: Boolean,
internet: Boolean,
cantSmoke: Boolean,
prefGender: [String],
wheelchairAccess: Boolean
): [Accommodation]
}

enum HousingOptions {
properties
rooms
}

enum OrderOptions {
cheapest
closest
newest
}
61 changes: 38 additions & 23 deletions server/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,45 @@
import express, { Request, Response, NextFunction } from 'express'
import cors from 'cors'
import { Server, Request } from '@hapi/hapi'
// import { ApolloServer } from '@apollo/server'
// import { readFileSync } from 'fs'
import querySchema from './validation'
import GetDetails from './services'
import { queryBuilder } from './models/interfaces'
import { errorHandler, logger } from './middleware'

const LISTEN_PORT = process.env.LISTEN_PORT || 4100
const init = async (): Promise<void> => {
const server = new Server({
port: process.env.LISTEN_PORT || 4100,
host: process.env.HOST || 'localhost',
routes: { cors: { origin: ['*'] } },
})

const app = express()
.use(express.json())
.use(cors())
server.route({
method: 'GET',
path: '/',
options: { validate: { query: querySchema } },
handler: (request: Request) => {
console.log(`REST ${request.url} ${new Date().toLocaleString()}`)
return GetDetails(queryBuilder(request.query))
},
})

app.get('/', (req: Request, res: Response, next: NextFunction) => {
const { housingOption, campusName, maxRent } = req.query
if (!housingOption || !campusName || !maxRent) {
res.statusCode = 400
res.send(`housingOption, campusName, maxRent are required`)
}
const query = queryBuilder(req.query)
GetDetails(query)
.then(str => res.send(str))
.then(next)
})
// const gqlServer = new ApolloServer({
// typeDefs: readFileSync('./schema.graphql').toString('utf-8'),
// resolvers: {
// Query: {
// getAccommodation: (query: object) => GetDetails(queryBuilder(query)),
// },
// },
// })

await server.start()
console.log(
`Server started on ${server.info.uri} at ${new Date().toLocaleString()}`,
)
}

app.use(logger)
app.use(errorHandler)
process.on('unhandledRejection', (err: any) => {
console.log(err)
process.exit(1)
})

app.listen(LISTEN_PORT, () =>
console.log(`Server started on port ${LISTEN_PORT}...`),
)
init()
7 changes: 6 additions & 1 deletion server/src/services/listings.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import axios from 'axios'
import { load } from 'cheerio'
import { IResponse } from '../models/interfaces'
import { BASE_URL, CampusNames, HousingOptions, OrderOptions } from '../models/constants'
import {
BASE_URL,
CampusNames,
HousingOptions,
OrderOptions,
} from '../models/constants'

const queryUrl = BASE_URL + '/Listings/Search?'

Expand Down
31 changes: 31 additions & 0 deletions server/src/validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { object, string, number, bool } from '@hapi/joi'
import { CampusNames, HousingOptions } from './models/constants'

const querySchema = object({
housingOption: string()
.valid(HousingOptions.Properties, HousingOptions.Rooms)
.required(),
campusName: string()
.valid(CampusNames.City, CampusNames.NorthRyde)
.required(),
maxRent: number()
.integer()
.min(0)
.default(200)
.required(),
bathroom: number().min(1),
ensuite: bool(),
leaseMin: number().min(1),
leaseMax: number().min(1),
distanceMax: number().min(0.1),
vacancy: number().min(1),
bedroom: number().min(1),
heating: bool(),
cooling: bool(),
internet: bool(),
canSmoke: bool(),
prefGender: string().valid('F', 'F,M', 'M'),
wheelchairAccess: bool(),
})

export default querySchema
Loading