Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[backend] Add drip management #55

Merged
merged 27 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6304ef1
Setting up the router for drips
joseph-flinn Sep 8, 2023
68b1ead
Merge branch 'main' into backend/add-drip-management
joseph-flinn Sep 12, 2023
055260d
Add in base functional test for GET and POST on /drip
joseph-flinn Sep 12, 2023
b9c91f8
Set up headers for the drip POST endpoint
joseph-flinn Sep 13, 2023
595a872
Clean up after feature flag
joseph-flinn Sep 13, 2023
3df4c4e
Removing feature flag for the CORS headers
joseph-flinn Sep 13, 2023
5d9f30e
Adding in the scaffolding for CRUD with drips
joseph-flinn Sep 14, 2023
2f7b99f
Migrated the RSS path to hono backend
joseph-flinn Sep 15, 2023
416c469
Moved the postList endpoint to Hono
joseph-flinn Sep 15, 2023
2f7bf88
post endpoint moved to hono
joseph-flinn Sep 15, 2023
88af66f
move create/update drip endpoint to hono
joseph-flinn Sep 15, 2023
c79ff17
Add header validation to post drip endpoint
joseph-flinn Sep 15, 2023
89d1e97
Moving get and delete drips to hono (not yet passing tests)
joseph-flinn Sep 15, 2023
03674eb
Tests updated
joseph-flinn Sep 16, 2023
85118db
Initial commit for the connecting to the databases
joseph-flinn Sep 16, 2023
21d6151
Adding edda to the main shell.nix file
joseph-flinn Sep 16, 2023
63be1c8
Drip backend done
joseph-flinn Sep 16, 2023
1d31a75
Merge branch 'main' into backend/add-drip-management
joseph-flinn Sep 18, 2023
3a7a599
Merge branch 'main' into backend/add-drip-management
joseph-flinn Sep 18, 2023
e06a35a
Merge branch 'main' into backend/add-drip-management
joseph-flinn Sep 19, 2023
09619a5
initial commit of pulling out the hardcoded development bearer token
joseph-flinn Sep 21, 2023
477e733
fixing tests with the dynamic secret
joseph-flinn Sep 21, 2023
dd43f6e
Update testing directions
joseph-flinn Sep 21, 2023
d074c8b
Install dependencies to build/deploy backend
joseph-flinn Sep 21, 2023
ad63738
Fix path
joseph-flinn Sep 21, 2023
f38f374
Updating testing framework to support testing both locally and in sta…
joseph-flinn Sep 21, 2023
61bde00
fix the staging backend url
joseph-flinn Sep 22, 2023
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
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ jobs:
- name: Checkout
uses: actions/[email protected]

- name: Set up Node
uses: actions/[email protected]
with:
cache: 'npm'
cache-dependency-path: 'backend/package-lock.json'
node-version: '18'

- name: Install dependencies
run: |
cd backend
npm ci

- name: Deploy backend
uses: cloudflare/[email protected]
with:
Expand All @@ -94,5 +106,8 @@ jobs:

- name: Run Integration Tests
uses: grafana/[email protected]
env:
ENV: staging
PSK: ${{ secrets.BLOG_ADMIN_TOKEN }}
with:
filename: backend/test/script.js
1 change: 1 addition & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
.dev.vars
14 changes: 14 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Backend

### Development

```
# .dev.vars
TOKEN=${TOKEN}
```

### Testing

```
k6 -e PSK=${TOKEN} test/script.js
```
308 changes: 156 additions & 152 deletions backend/package-lock.json

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
"version": "0.0.0",
"private": true,
"scripts": {
"deploy": "wrangler deploy",
"start": "wrangler dev --remote"
"dev": "wrangler dev --remote",
"test": "k6 -e ENV=dev -e PSK=${TOKEN} run/script.js",
"deploy": "wrangler deploy"
},
"devDependencies": {
"wrangler": "^3.6.0"
"wrangler": "^3.7.0"
},
"dependencies": {
"hono": "^3.6.1"
}
}
230 changes: 123 additions & 107 deletions backend/src/worker.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,29 @@
/**
* Welcome to Cloudflare Workers! This is your first worker.
*
* - Run `npm run dev` in your terminal to start a development server
* - Open a browser tab at http://localhost:8787/ to see your worker in action
* - Run `npm run deploy` to publish your worker
*
* Learn more at https://developers.cloudflare.com/workers/
*/

const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET',
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
}

const getRSS = async (key, env) => {
console.log(`RSS match found`)
const rssBlob = await env.BLOG_BUCKET.get(key);

if (rssBlob === null) {
return new Response('Object Not Found', { status: 404 });
}
import { Hono } from 'hono'
import { validator } from 'hono/validator'
import { bearerAuth } from 'hono/bearer-auth'
import { cors } from 'hono/cors'
import { etag } from 'hono/etag'


const app = new Hono()

app.use('/*', cors());
app.use('/*', etag());


app.get('/rss.xml', async c => {
const rssBlob = await c.env.BLOG_BUCKET.get('rss.xml');

if (rssBlob === null) return c.text('Object not found', 404)

c.header('content-type', 'application/xml')
c.header('etag', rssBlob.httpEtag)
return c.text(rssBlob.body, 200)
})

if (env.FLAG_USE_HEADERS) {
return new Response(rssBlob.body, { status: 200, headers: {
...corsHeaders,
'etag': rssBlob.httpEtag,
'Content-type': 'application/xml'
}});
} else {
return new Response(rssBlob.body, { status: 200 });
}
};

const getPostList = async (key, env) => {
const postBlob = await env.BLOG_BUCKET.get('posts.json');
app.get('/posts', async c => {
const postBlob = await c.env.BLOG_BUCKET.get('posts.json');

if (postBlob === null) {
return new Response('Object Not Found', { status: 404 });
Expand All @@ -52,85 +41,112 @@ const getPostList = async (key, env) => {
})
))
.then(postList => {
if (env.FLAG_USE_HEADERS) {
return new Response(JSON.stringify({ postList: postList }, null, 4), {
status: 200,
headers: {
...corsHeaders,
'etag': postBlob.httpEtag,
'Content-type': 'application/json'
}
});
} else {
return new Response(JSON.stringify({ postList: postList }, null, 4), { status: 200, });
}
c.header('content-type', 'application/json')
c.header('etag', postBlob.httpEtag)
return c.text(JSON.stringify({ postList: postList }), 200)
})
}
})


app.get('/posts/:slug', async c => {
const { slug } = c.req.param()

const getPost = async (key, env) => {
const postBlob = await env.BLOG_BUCKET.get('posts.json');
const postBlob = await c.env.BLOG_BUCKET.get('posts.json');

if (postBlob === null) {
return new Response('Object Not Found', { status: 404 });
}

const postSlug = key.split("/")[1];

return postBlob.json()
.then(posts => posts[postSlug])
.then(posts => posts[slug])
.then(post => {
if (env.FLAG_USE_HEADERS) {
return new Response(JSON.stringify({ post: post }, null, 4), {
status: 200,
headers: {
...corsHeaders,
'etag': postBlob.httpEtag,
'Content-type': 'application/json'
}
});
}
return new Response(JSON.stringify({ post: post }, null, 4), {
status: 200,
});
c.header('content-type', 'application/json')
c.header('etag', postBlob.httpEtag)
return c.text(JSON.stringify({ post: post}), 200)
})
}

export default {
async fetch(request, env) {
const url = new URL(request.url);
const key = url.pathname.slice(1);
console.log(`key: ${key}`)

const routeRSS = /rss.xml/,
routePostList = /posts$/,
routePost = /posts\/*/;

switch (true) {
case routeRSS.test(key):
return getRSS(key, env);
break;
case routePostList.test(key):
return getPostList(key, env);
break;
case routePost.test(key):
return getPost(key, env);
break;
default:
if (env.FLAG_USE_HEADERS) {
return new Response(JSON.stringify({ message: `/${key} not found`}, null, 2), {
status: 404,
headers: {
...corsHeaders,
'Content-type': 'application/json',
'My-Header-test': 'did it come through?'
},
});
} else {
return new Response(JSON.stringify({ message: `/${key} not found`}, null, 2), {
status: 404,
});
}
break;
})


app.post('/drip', async(c, next) => {
const token = c.env.TOKEN
const auth = bearerAuth({
token
})
return auth(c, next)
})


app.post(
'/drip',
validator('header', (value, c) => {
if (!value["content-type"] || value["content-type"] != "application/json") {
return c.text("Invalid headers", 400)
}
},
};
return value
}),
validator('json', (value, c) => {
if (!("message" in value)) return c.text('Invalid body', 400)

return value
}),
async c => {
const headers = c.req.valid('header')
const body = c.req.valid('json')

const action = 'id' in body ? 'update' : 'create'
const response = `POST called on /drip. Action: ${action}`

if (action == "update") {
if (!("message" in body)) return c.text('Invalid body for update', 400)

const { success } = await c.env.DB_DRIP.prepare(`
update drip set message=? where id=?
`).bind(body['message'], body['id']).run()
if (success) return c.text(JSON.stringify({ message: 'drip updated' }, null, 2), 201)
} else {
const { success } = await c.env.DB_DRIP.prepare(`
insert into drip (message) values (?)
`).bind(body['message']).run()
if (success) return c.text(JSON.stringify({ message: 'drip created' }, null, 2), 201)
}

return c.text(JSON.stringify({ message: 'something went wrong'}, null, 2), 500)
}
)


app.get('/drip', async c => {
const { success, results } = await c.env.DB_DRIP.prepare(`
select * from drip ORDER BY created_at DESC LIMIT 1
`).bind().all()

if (!success) return c.text(JSON.stringify({ message: "something went wrong"}), 400)

return c.text( JSON.stringify({ data: results }, null, 2), 200);

})


app.delete('/drip/*', async(c, next) => {
const token = c.env.TOKEN
const auth = bearerAuth({
token
})
return auth(c, next)
})


app.delete(
'/drip/:id',
async c => {
const { id } = c.req.param()
const { success } = await c.env.DB_DRIP.prepare(`
delete from drip where id=?
`).bind(id).run()

if (success) return c.text(JSON.stringify({ message: `drip deleted` }, null, 2), 201)

return c.text('something went wrong', 400)
}
)

export default app
Loading