Skip to content

Commit

Permalink
Add support for meta and search url params
Browse files Browse the repository at this point in the history
  • Loading branch information
maxwofford committed Dec 29, 2019
1 parent de25cb2 commit 63c68e2
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 11 deletions.
32 changes: 31 additions & 1 deletion README.md
Expand Up @@ -42,4 +42,34 @@ curl https://api2.hackclub.com/v0/Operations/Badges?select={"sort":[{field:"Peop
# and select the single record at the top of the list to get the badge with the
# most users
curl https://api2.hackclub.com/v0/Operations/Badges?select={"sort":[{field:"People Count",direction:"desc"}],"maxResults":1}
```
```

## Developing & Contributing

```sh
git clone https://github.com/hackclub/api2
cd api2/
pnpm install

# Run locally with nodemon
pnpm run dev
# Run tests
pnpm test
```

### Development tools

```sh
# The `meta` url param will return a JSON object with metadata about the request you've just made
curl http://localhost:5000/v0/Operations/Clubs/?meta=true
# returns the following:
{
result: [...], # Array of badges from Airtable
meta: {
duration: 241, # Time in ms to complete request
query: {...}, # List of URL query params parsed by the server
params: {...}, # List of URL params parsed by the server
}
}
```

2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "hackclub-info",
"version": "0.1.0",
"version": "0.2.0",
"description": "Public-facing API for getting info from Hack Club",
"main": "index.js",
"scripts": {
Expand Down
19 changes: 16 additions & 3 deletions src/index.js
Expand Up @@ -59,12 +59,25 @@ app.get('/v0/:base/:tableName?/:recordID?', async(req, res, next) => {
if (env === 'development' || env === 'test') {
providedAuth = req.query.authKey
}
const result = await airtableLookup(req.params, req.query, providedAuth)
const options = {
base: req.params.base,
tableName: req.params.tableName,
authKey: providedAuth,
}
if (req.query.select) {
options.select = JSON.parse(req.query.select)
}
const result = await airtableLookup(options, providedAuth)

meta.duration = Date.now() - startTime
res.json({result, meta})

if (req.query.meta) {
res.json({result, meta})
} else {
res.json(result)
}
} catch (err) {
console.error(err.message)
console.log(err.message)

const statusCode = err.statusCode || 500
meta.duration = Date.now() - startTime
Expand Down
5 changes: 2 additions & 3 deletions src/utils.js
Expand Up @@ -8,9 +8,8 @@ export function lookupBaseID(baseID) {
return lookedUpID || baseID
}

export async function airtableLookup(params, query = {}, auth) {
const { base, tableName } = params
const select = query.select
export async function airtableLookup(options, auth) {
const { base, tableName, select } = options
const baseID = lookupBaseID(base)

if (auth) {
Expand Down
4 changes: 2 additions & 2 deletions src/whitelist.js
Expand Up @@ -27,7 +27,7 @@ const whitelistInfo = {
export function whitelistBaseTable(baseID, tableName) {
const whitelistedBase = Object.keys(whitelistInfo).find(key => lookupBaseID(key) === lookupBaseID(baseID))
if (!whitelistedBase) {
const err = new Error("Base either doesn't exist or isn't publicly accessible")
const err = new Error("Not found: base either doesn't exist or isn't publicly accessible")
err.statusCode = 404
throw err
} else {
Expand All @@ -36,7 +36,7 @@ export function whitelistBaseTable(baseID, tableName) {

const whitelistedTable = whitelistInfo[whitelistedBase][tableName]
if (!whitelistedTable) {
const err = new Error("Table either doesn't exist or isn't publicly accessible")
const err = new Error("Not found: table either doesn't exist or isn't publicly accessible")
err.statusCode = 404
throw err
} else {
Expand Down
60 changes: 59 additions & 1 deletion tests/routes.test.js
Expand Up @@ -18,4 +18,62 @@ describe('GET /ping', () => {
expect(res.body).toBeDefined()
expect(res.body.message).toEqual('pong!')
})
})
})

describe('GET /Operations/Badges (missing version number)', () => {
it('responds with Not Found', async () => {
const res = await request(app).get('/Operations/Badges')
expect(res.statusCode).toEqual(404)
})
// it('responds with json error', async () => {
// const res = await request(app).get('/Operations/Badges')
// expect(res.body).toBeDefined()
// expect(res.body.error).stringContaining('Not found')
// })
})

describe('GET /v0/Cake/Badges (invalid base)', () => {
it('responds with Not Found', async () => {
const res = await request(app).get('/v0/Cake/Badges')
expect(res.statusCode).toEqual(404)
})
it('responds with json error', async () => {
const res = await request(app).get('/v0/Cake/Badges')
expect(res.body).toBeDefined()
expect(res.body.error).toBeDefined()
expect(res.body.error.message).toMatch('Not found')
})
})

describe('GET /v0/Operations/Cake (invalid table)', () => {
it('responds with Not Found', async () => {
const res = await request(app).get('/v0/Operations/Cake')
expect(res.statusCode).toEqual(404)
})
it('responds with json error', async () => {
const res = await request(app).get('/v0/Operations/Cake')
expect(res.body).toBeDefined()
expect(res.body.error).toBeDefined()
expect(res.body.error.message).toMatch('Not found')
})
})

describe('GET /v0/Operations/Badges', () => {
it('responds with successful status code', async () => {
const res = await request(app).get('/v0/Operations/Badges')
expect(res.statusCode).toEqual(200)
})
it('responds with an array of airtable records', async () => {
const res = await request(app).get('/v0/Operations/Badges')
expect(res.body).toBeDefined()
expect(Array.isArray(res.body)).toEqual(true)
})
})

// describe('GET /v0/Operations/Badges', async () => {
// it('responds with badge data', async () => {
// const res = request(app).get('/v0/Operations/Badges')
// console.log(res.body)
// expect(res.body).toBeDefined()
// })
// })

0 comments on commit 63c68e2

Please sign in to comment.