openapi-to-graphql-lxwang2
Version:
Generates a GraphQL schema for a given OpenAPI Specification (OAS)
809 lines (715 loc) • 18.6 kB
JavaScript
// Copyright IBM Corp. 2017,2018. All Rights Reserved.
// Node module: openapi-to-graphql
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
let server // holds server object for shutdown
/**
* Starts the server at the given port
*/
function startServer(PORT) {
const express = require('express')
const app = express()
const bodyParser = require('body-parser')
app.use(bodyParser.text())
app.use(bodyParser.json())
const Users = {
arlene: {
name: 'Arlene L McMahon',
address: {
street: '4656 Cherry Camp Road',
city: 'Elk Grove Village'
},
address2: {
street: '3180 Little Acres Lane',
city: 'Macomb'
},
employerId: 'binsol',
hobbies: ['tap dancing', 'bowling'],
status: 'staff',
nomenclature: {
suborder: 'Haplorhini',
family: 'Hominidae',
genus: 'Homo',
species: 'sapiens'
},
friends: ['will', 'johnny', 'heather']
},
will: {
name: 'William B Ropp',
address: {
street: '3180 Little Acres Lane',
city: 'Macomb'
},
employerId: 'binsol',
hobbies: ['tap dancing', 'baseball'],
status: 'staff',
nomenclature: {
suborder: 'Haplorhini',
family: 'Hominidae',
genus: 'Homo',
species: 'sapiens'
},
friends: ['arlene', 'johnny']
},
johnny: {
name: 'John C Barnes',
address: {
street: '372 Elk Rd Little',
city: 'Tucson'
},
employerId: 'binsol',
hobbies: ['chess', 'tennis'],
status: 'staff',
nomenclature: {
suborder: 'Haplorhini',
family: 'Hominidae',
genus: 'Homo',
species: 'sapiens'
},
friends: ['arlene']
},
heather: {
name: 'Heather J Tate',
address: {
street: '3636 Poplar Chase Lane',
city: 'Post Falls'
},
employerId: 'ccc',
hobbies: ['making money', 'counting money'],
status: 'alumni',
nomenclature: {
suborder: 'Haplorhini',
family: 'Hominidae',
genus: 'Homo',
species: 'ihavelotsofmoneyus'
},
friends: []
}
}
const Cars = {
arlene: {
model: 'Retro Rides',
color: 'yellow',
kind: 'SEDAN',
rating: 100,
features: {
color: 'banana yellow to be specific'
}
},
will: {
model: 'Speedzone Speedster',
color: 'red',
tags: {
speed: 'extreme'
},
kind: 'RACE_CAR',
rating: 100
},
johnny: {
model: 'Glossy German',
color: 'silver',
tags: {
impression: 'decadent',
condition: 'slightly beat-up'
},
kind: 'LIMOSINE',
rating: 101
},
heather: {
model: 'Glossy German',
color: 'black',
tags: {
impression: 'decadent'
},
kind: 'LIMOSINE',
rating: 200
}
}
const Companies = {
binsol: {
id: 'binsol',
name: 'Binary Solutions',
legalForm: 'public',
ceoUsername: 'johnny',
offices: [
{
street: '122 Elk Rd Little',
city: 'Tucson'
},
{
street: '124 Elk Rd Little',
city: 'Tucson'
}
]
},
ccc: {
id: 'ccc',
name: 'Cool Computers Company',
legalForm: 'public',
ceoUsername: 'heather',
offices: [
{
street: '300 Elk Rd Little',
city: 'Tucson'
},
{
street: '301 Elk Rd Little',
city: 'Tucson'
}
]
}
}
const Offices = [
{
employeeId: 'arlene',
'room number': 100,
employerId: 'binsol'
},
{
employeeId: 'will',
'room number': 101,
employerId: 'binsol'
},
{
employeeId: 'johnny',
'room number': 102,
employerId: 'binsol'
},
{
employeeId: 'heather',
'room number': 100,
employerId: 'ccc'
}
]
const Patents = {
'CCC OSv1': {
'patent-id': '100',
'inventor-id': 'heather'
}
}
const Projects = {
'Peace Among Companies': {
projectId: 1,
active: true,
leadId: 'arlene',
patentId: ''
},
'Operation: Control CCC': {
projectId: 2,
active: false,
leadId: 'will',
patentId: ''
},
'CCC operation system': {
projectId: 3,
active: true,
leadId: 'will',
patentId: '100'
}
}
const Papers = {
apples: {
name: 'Deliciousness of apples',
published: true
},
coffee: {
name: 'How much coffee is too much coffee?',
published: false
},
tennis: {
name: 'How many tennis balls can fit into the average building?',
published: true
}
}
const TrashCans = {
arlene: {
brand: 'Garbage Emporium',
contents: [
{
type: 'apple',
message: 'Half-eaten'
},
{
type: 'sock',
message: 'Lost one'
}
]
},
will: {
brand: 'Garbage Emporium',
contents: [
{
type: 'sock',
message: 'Lost one'
}
]
},
johnny: {
brand: 'Garbage Emporium',
contents: []
},
heather: {
brand: 'Solid Gold Products',
contents: [
{
type: 'tissue',
message: 'Used'
}
]
}
}
const Auth = {
arlene: {
username: 'arlene123',
password: 'password123',
accessToken: 'abcdef'
},
will: {
username: 'catloverxoxo',
password: 'IActuallyPreferDogs',
accessToken: '123456'
},
johnny: {
username: 'johnny',
password: 'password',
accessToken: 'xyz'
},
heather: {
username: 'cccrulez',
password: 'johnnyisabully',
accessToken: 'ijk'
}
}
const authMiddleware = (req, res, next) => {
if (req.headers.authorization) {
const encoded = req.headers.authorization.split(' ')[1]
const decoded = new Buffer(encoded, 'base64').toString('utf8').split(':')
if (decoded.length === 2) {
const credentials = {
username: decoded[0],
password: decoded[1]
}
for (let user in Auth) {
if (
Auth[user].username === credentials.username &&
Auth[user].password === credentials.password
) {
return next()
}
}
res.status(401).send({
message: 'Incorrect credentials'
})
} else {
res.status(401).send({
message: 'Basic Auth expects a single username and a single password'
})
}
} else if ('access_token' in req.headers) {
for (let user in Auth) {
if (Auth[user].accessToken === req.headers.access_token) {
return next()
}
}
res.status(401).send({
message: 'Incorrect credentials'
})
return false
} else if ('cookie' in req.headers) {
for (let user in Auth) {
if (Auth[user].accessToken === req.headers.cookie.split('=')[1]) {
return next()
}
}
res.status(401).send({
message: 'Incorrect credentials'
})
return false
} else if ('access_token' in req.query) {
for (let user in Auth) {
if (Auth[user].accessToken === req.query.access_token) {
return next()
}
}
res.status(401).send({
message: 'Incorrect credentials'
})
} else {
res.status(401).send({
message: 'Unknown/missing credentials'
})
}
}
app.get('/api/users', (req, res) => {
console.log(req.method, req.path)
const limit = req.query.limit
if (typeof limit === 'string') {
res.send(Object.values(Users).slice(0, Number(limit)))
} else {
res.send(Object.values(Users))
}
})
app.get('/api/users/:username', (req, res) => {
console.log(req.method, req.path)
if (req.params.username in Users) {
res.send(Users[req.params.username])
} else {
res.status(404).send({
message: 'Wrong username'
})
}
})
app.get('/api/users/:username/car', (req, res) => {
console.log(req.method, req.path)
if (req.params.username in Users) {
res.send(Cars[req.params.username])
} else {
res.status(404).send({
message: 'Wrong username'
})
}
})
app.get('/api/users/:username/friends', (req, res) => {
console.log(req.method, req.path)
if (req.params.username in Users) {
const friends = Users[req.params.username].friends.map(friendName => {
return Users[friendName]
})
res.status(200).send(friends)
} else {
res.status(404).send({
message: 'Wrong username'
})
}
})
app.post('/api/users', (req, res) => {
console.log(req.method, req.path)
const user = req.body
if (
'name' in user &&
'address' in user &&
'employerId' in user &&
'hobbies' in user
) {
res.status(201).send(user)
} else {
res.status(400).send({
message: 'Wrong data'
})
}
})
app.get('/api/assets/:companyId', (req, res) => {
console.log(req.method, req.path)
const assets = []
Object.entries(Users).forEach(([username, user]) => {
if (req.params.companyId === user.employerId) {
assets.push(user)
if (username in Cars) {
assets.push(Cars[username])
}
if (username in TrashCans) {
assets.push(TrashCans[username])
}
}
})
res.send(assets)
})
app.get('/api/cars', (req, res) => {
console.log(req.method, req.path)
res.send(Object.values(Cars))
})
app.get('/api/companies/:id', (req, res) => {
console.log(req.method, req.path)
if (req.params.id in Companies) {
res.status(200).send(Companies[req.params.id])
} else {
res.status(404).send({
message: 'Wrong company ID.'
})
}
})
app.get('/api/coffeeLocation', (req, res) => {
console.log(req.method, req.path)
res.send({
lat: parseFloat(req.query.lat) + 5,
long: parseFloat(req.query.long) + 5
})
})
app.get('/api/cookie', (req, res) => {
console.log(req.method, req.path, req.query, req.headers)
if ('cookie' in req.headers) {
res
.set('Content-Type', 'text/plain')
.status(200)
.send(`Thanks for your cookie preferences: "${req.headers.cookie}"`)
} else {
res.status(400).send('Need Cookie header parameter')
}
})
app.get('/api/copier', (req, res) => {
console.log(req.method, req.path, req.query, req.headers)
res.status(200).send({
body: req.query.query
})
})
app.get('/api/cleanDesks', (req, res) => {
console.log(req.method, req.path)
res
.set('Content-Type', 'text/plain')
.status(200)
.send('5 clean desks')
})
app.get('/api/dirtyDesks', (req, res) => {
console.log(req.method, req.path)
res
.set('Content-Type', 'text/plain')
.status(200)
.send('5 dirty desks')
})
app.get('/api/bonuses', (req, res) => {
console.log(req.method, req.path)
res.status(204).send()
})
app.get('/api/offices/:id', (req, res) => {
console.log(req.method, req.path)
const accept = req.headers['accept']
if (accept.includes('text/plain')) {
res
.set('Content-Type', 'text/plain')
.status(200)
.send('You asked for text!')
} else if (accept.includes('application/json')) {
if (req.params.id >= 0 && req.params.id < Offices.length) {
res.status(200).send(Offices[req.params.id])
} else {
res.status(404).send({
message: 'Cannot find office'
})
}
} else {
res
.set('Content-Type', 'text/plain')
.status(412)
.send('Please try with an accept parameter!')
}
})
app.post('/api/products', (req, res) => {
console.log(req.method, req.path)
const product = req.body
if (
'product-name' in product &&
'product-id' in product &&
'product-tag' in product
) {
res.status(201).send(product)
} else {
res.status(400).send({
message: 'wrong data'
})
}
})
app.get('/api/products/:id', (req, res) => {
console.log(req.method, req.path, req.params, req.query)
if (
typeof req.params['id'] !== 'undefined' &&
typeof req.query['product-tag'] !== 'undefined'
) {
const product = {
product_id: req.params['id'],
'product-tag': req.query['product-tag'],
'product-name': 'Super Product'
}
res.status(200).send(product)
} else {
res.status(400).send({
message: 'Wrong data'
})
}
})
app.get('/api/products/:id/reviews', (req, res) => {
console.log(req.method, req.path, req.params, req.query)
if (
typeof req.params.id !== 'undefined' &&
typeof req.query['product-tag'] !== 'undefined'
) {
res
.status(200)
.send([
{ text: 'Great product', timestamp: 1502787600000000 },
{ text: 'I love it', timestamp: 1502787400000000 }
])
} else {
res.status(400).send({
message: 'wrong data'
})
}
})
app.get('/api/papers', (req, res) => {
console.log(req.method, req.path)
res.status(200).send(Object.values(Papers))
})
app.post('/api/papers', (req, res) => {
console.log(req.method, req.path)
const contentType = req.headers['content-type']
if (contentType.includes('text/plain')) {
res
.set('Content-Type', 'text/plain')
.status(201)
.send('You sent the paper idea: ' + req.body)
} else {
res.status(400).send({
message:
"Wrong content-type, expected 'text/plain' but received " +
contentType
})
}
})
app.get('/api/patents/:id', authMiddleware, (req, res) => {
console.log(req.method, req.path)
// Find patent based off of patent ID
const patent = Object.values(Patents).find(currentPatent => {
return currentPatent['patent-id'] === req.params.id
})
if (typeof patent === 'object') {
res.status(200).send(patent)
} else {
res.status(404).send({ message: 'Patent does not exist.' })
}
})
app.post('/api/projects', authMiddleware, (req, res) => {
console.log(req.method, req.path)
const project = req.body
if ('project-id' in project && 'lead-id' in project) {
res.status(201).send(project)
} else {
res.status(400).send({
message: 'Wrong data'
})
}
})
app.get('/api/projects/:id', authMiddleware, (req, res) => {
console.log(req.method, req.path)
// Find project based off of projectId
const project = Object.values(Projects).find(currentProject => {
return currentProject.projectId === Number(req.params.id)
})
if (typeof project === 'object') {
res.status(200).send(project)
} else {
res.status(404).send({ message: 'Project does not exist.' })
}
})
app.get('/api/scanner', (req, res) => {
console.log(req.method, req.path, req.query, req.headers)
res.status(200).send({ body: req.query.query })
})
app.post('/api/scanner/:path', (req, res) => {
console.log(req.method, req.path)
res.status(200).send({
body: `req.body: ${req.body}, req.query.query: ${req.query.query}, req.path.path: ${req.params.path}`
})
})
app.get('/api/snack', (req, res) => {
console.log(req.method, req.path, req.query, req.headers)
if ('snack_type' in req.headers && 'snack_size' in req.headers) {
res
.set('Content-Type', 'text/plain')
.status(200)
.send(`Here is a ${req.headers.snack_size} ${req.headers.snack_type}`)
} else {
res.status(400).send('Need snack_type and snack_size header parameters')
}
})
app.get('/api/status', (req, res) => {
console.log(req.method, req.path, req.query, req.headers)
if (
typeof req.query.limit !== 'undefined' &&
typeof req.get('exampleHeader') !== 'undefined'
) {
res
.set('Content-Type', 'text/plain')
.status(200)
.send('Ok')
} else {
res.status(400).send({
message: 'wrong request'
})
}
})
app.post('/api/status', (req, res) => {
console.log(req.method, req.path, req.query, req.headers)
if ('hello' in req.body && req.body['hello'] === 'world') {
res.status(200).send('success')
} else {
res.status(400).send({
message: "Wrong data, try 'hello': 'world'"
})
}
})
app.get('/api/secure', (req, res) => {
console.log(req.method, req.path, req.query, req.headers)
if (req.get('authorization') === 'Bearer abcdef') {
res
.status(200)
.set('Content-Type', 'text/plain')
.send('A secure message.')
} else {
res.status(401).send({
message: 'Missing authorization header'
})
}
})
app.get('/api/trashcans', (req, res) => {
console.log(req.method, req.path)
res.status(200).send(Array.from(Object.values(TrashCans)))
})
app.get('/api/trashcans/:username', (req, res) => {
console.log(req.method, req.path)
if (req.params.username in Users) {
res.status(200).send(TrashCans[req.params.username])
} else {
res.status(404).send({
message: 'Wrong username'
})
}
})
app.post('/api/trashcans/:username', (req, res) => {
console.log(req.method, req.path)
const trashItem = req.body
if (req.params.username in Users) {
const trashCan = TrashCans[req.params.username]
trashCan.contents.push(trashItem)
res.status(201).send(TrashCans[req.params.username])
} else {
res.status(404).send({
message: 'Wrong username'
})
}
})
return new Promise(resolve => {
server = app.listen(PORT, () => {
console.log(`Example API accessible on port ${PORT}`)
resolve()
})
})
}
/**
* Stops server.
*/
function stopServer() {
return new Promise(resolve => {
server.close(() => {
console.log(`Stopped API server`)
resolve()
})
})
}
// if run from command line, start server:
if (require.main === module) {
startServer(3001)
}
module.exports = {
startServer,
stopServer
}