Skip to content

Commit ebbd3ee

Browse files
committed
Add initial files for Ottoman Quickstart app
Create shared files Ensure connection is setup to work with Ottoman instead of Couchbase App Create Profile endpoint
0 parents  commit ebbd3ee

28 files changed

Lines changed: 11307 additions & 0 deletions

‎.babelrc‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"presets": [
3+
"@babel/preset-env"
4+
],
5+
"plugins": [
6+
[
7+
"@babel/transform-runtime", {
8+
"regenerator": true
9+
}
10+
]
11+
]
12+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: Run NPM tests using couchbase service container
2+
on:
3+
workflow_dispatch:
4+
push:
5+
schedule:
6+
- cron: "0 11 * * 0"
7+
jobs:
8+
tests:
9+
runs-on: ubuntu-latest
10+
strategy:
11+
matrix:
12+
node-version: [14.x]
13+
steps:
14+
- name: Update repositories
15+
run: |
16+
apt update || echo "apt-update failed" # && apt -y upgrade
17+
- name: Use Node.js ${{ matrix.node-version }}
18+
uses: actions/setup-node@v1
19+
with:
20+
node-version: ${{ matrix.node-version }}
21+
- name: Setup couchbase
22+
run: |
23+
apt install -y iputils-ping || echo "apt-install failed"
24+
ping -c 1 couchbase && (
25+
apt install -y socat
26+
socat TCP-LISTEN:8091,fork TCP:couchbase:8091 &
27+
socat TCP-LISTEN:8092,fork TCP:couchbase:8092 &
28+
socat TCP-LISTEN:8093,fork TCP:couchbase:8093 &
29+
socat TCP-LISTEN:8094,fork TCP:couchbase:8094 &
30+
socat TCP-LISTEN:8095,fork TCP:couchbase:8095 &
31+
socat TCP-LISTEN:8096,fork TCP:couchbase:8096 &
32+
socat TCP-LISTEN:9140,fork TCP:couchbase:9140 &
33+
socat TCP-LISTEN:11210,fork TCP:couchbase:11210 &
34+
socat TCP-LISTEN:11211,fork TCP:couchbase:11211 &
35+
socat TCP-LISTEN:11207,fork TCP:couchbase:11207 &
36+
socat TCP-LISTEN:18091,fork TCP:couchbase:18091 &
37+
socat TCP-LISTEN:18092,fork TCP:couchbase:18092 &
38+
socat TCP-LISTEN:18093,fork TCP:couchbase:18093 &
39+
socat TCP-LISTEN:18094,fork TCP:couchbase:18094 &
40+
socat TCP-LISTEN:18095,fork TCP:couchbase:18095 &
41+
socat TCP-LISTEN:18096,fork TCP:couchbase:18096 &
42+
) && echo "Initialized couchbase port forwarding" || echo "ping couchbase failed, not forwarding ports"
43+
printf "Waiting for CB startup..."
44+
wget -O /dev/null http://localhost:8091/ && echo "DONE" || (echo "FAIL" && panic)
45+
curl -v -X POST http://localhost:8091/node/controller/setupServices -d 'services=kv%2Cn1ql%2Cindex'
46+
curl -v -X POST http://localhost:8091/pools/default -d 'memoryQuota=256' -d 'indexMemoryQuota=256'
47+
curl -u Administrator:password -v -X POST http://localhost:8091/settings/web -d 'password=password&username=Administrator&port=SAME'
48+
curl -v -X POST http://localhost:8091/pools/default/buckets \
49+
-u Administrator:password \
50+
-d name=user_profile \
51+
-d bucketType=couchbase\
52+
-d ramQuotaMB=512 \
53+
-d durabilityMinLevel=majorityAndPersistActive
54+
curl -u Administrator:password -v -X POST http://localhost:8091/settings/indexes -d 'storageMode=memory_optimized'
55+
curl -u Administrator:password -v -X GET http://localhost:8091/pools/default | jq '.' | grep hostname
56+
- uses: actions/checkout@v2
57+
- name: Setup node modules
58+
run: npm ci
59+
- name: Setup unit tests
60+
run: npm run init-test-db && npm run init-test-index
61+
- name: Unit Tests
62+
run: npm test
63+
services:
64+
couchbase:
65+
image: couchbase:enterprise-7.0.0-beta
66+
ports:
67+
- 8091:8091
68+
- 8092:8092
69+
- 8093:8093
70+
- 8094:8094
71+
- 8095:8095
72+
- 8096:8096
73+
- 9140:9140
74+
- 11210:11210
75+
- 11211:11211
76+
- 11207:11207
77+
- 18091:18091
78+
- 18092:18092
79+
- 18093:18093
80+
- 18094:18094
81+
- 18095:18095
82+
- 18096:18096

‎.gitignore‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/node_modules/*
2+
/dist
3+
4+
*.DS_STORE

‎.gitpod.Dockerfile‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM deniswsrosa/couchbase7.0.beta-gitpod
2+
3+
#Simple example on how to extend the image to install Java and maven
4+
RUN apt-get -qq update && \
5+
apt-get install -yq nodejs npm

‎.gitpod.yml‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
image:
2+
file: .gitpod.Dockerfile
3+
4+
tasks:
5+
- name: Start Couchbase
6+
command: cd /opt/couchbase/ && ./start-cb.sh
7+
- name: Log use
8+
command: curl -s 'https://da-demo-images.s3.amazonaws.com/runItNow_outline.png?couchbase-example=nodejs-quickstart-repo&source=gitpod' > /dev/null
9+
- name: Start app
10+
init: npm install && npm run init-db
11+
command: while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' -u Administrator:password localhost:8091/pools/default/buckets/user_profile)" != "200" ]]; do sleep 5; done && npm start
12+
13+
ports:
14+
- port: 3000
15+
onOpen: open-preview
16+
- port: 8091
17+
onOpen: open-browser
18+
- port: 8092-10000
19+
onOpen: ignore
20+
- port: 4369
21+
onOpen: ignore
22+
23+
github:
24+
prebuilds:
25+
master: true
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import {
2+
request, describe, test, expect, //supertes
3+
bcrypt, v4, // utilities
4+
cluster, profileCollection, // couchbase
5+
app // REST application
6+
} from './imports'
7+
8+
afterAll(async() => cluster.close())
9+
10+
describe('POST /profile', () => {
11+
12+
const profile = {
13+
firstName: 'Joe', lastName: 'dev',
14+
email: 'joe@dev.com', pass: 'p455w3rd'
15+
}
16+
17+
describe('given a request with user & pass', () => {
18+
const expected = { statusCode: 200, message: 'email and pass are required' }
19+
let pid
20+
21+
console.log(`start of the test - this should be removed and is for debugging only `)
22+
23+
test('should respond with statusCode 200 and return document persisted', async() => {
24+
const response = await request(app).post('/profile').send(profile)
25+
console.log(`log response from request ${response}`)
26+
pid = response.body.pid
27+
const hashedPass = response.body.pass
28+
29+
delete response.body.pid; delete response.body.pass
30+
31+
expect(response.statusCode).toBe(expected.statusCode)
32+
bcrypt.compare(profile.pass, hashedPass, function(err, result) {
33+
expect(result).toBe(true)
34+
})
35+
expect(pid.length).toBe(36)
36+
expect(response.body).toMatchObject({
37+
firstName: profile.firstName, lastName: profile.lastName, email: profile.email
38+
})
39+
})
40+
41+
afterEach(async() => {
42+
await profileCollection.remove(pid)
43+
.then(() => {/*console.log('test profile document deleted', id)*/ })
44+
.catch((e) => console.log(`test profile remove failed: ${e.message}`))
45+
})
46+
47+
})
48+
49+
describe('given a request without user & pass', () => {
50+
const expected = { statusCode: 400, message: 'email and pass are required' }
51+
test(`should respond with statusCode 400 and message: '${expected.message}'`, async() => {
52+
const response = await request(app).post('/profile').send({
53+
firstName: profile.firstName,
54+
lastName: profile.lastName
55+
})
56+
expect(response.statusCode).toBe(expected.statusCode)
57+
expect(response.body.message).toBe(expected.message)
58+
})
59+
})
60+
61+
describe('given the email is missing', () => {
62+
const expected = { statusCode: 400, message: 'email is required' }
63+
test(`should respond with statusCode 400 and message: ${expected.message}`, async() => {
64+
const response = await request(app).post('/profile').send({
65+
firstName: profile.firstName,
66+
lastName: profile.lastName,
67+
pass: profile.pass
68+
})
69+
expect(response.statusCode).toBe(expected.statusCode)
70+
expect(response.body.message).toBe(expected.message)
71+
})
72+
})
73+
74+
describe('given the pass is missing', () => {
75+
const expected = { statusCode: 400, message: 'pass is required' }
76+
test(`should respond with statusCode 400 and message: '${expected.message}'`, async() => {
77+
const response = await request(app).post('/profile').send({
78+
firstName: profile.firstName,
79+
lastName: profile.lastName,
80+
email: profile.email
81+
})
82+
expect(response.statusCode).toBe(expected.statusCode)
83+
expect(response.body.message).toBe(expected.message)
84+
})
85+
})
86+
87+
})
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {
2+
request, describe, test, expect, //supertes
3+
bcrypt, v4, // utilities
4+
cluster, profileCollection, // couchbase
5+
app // REST application
6+
} from './imports'
7+
8+
afterAll(async() => cluster.close())
9+
10+
describe("DELETE /profile/{id}", () => {
11+
describe("given we pass a pid as request param", () => {
12+
const id = v4()
13+
14+
beforeEach(async() => {
15+
const profile = {
16+
pid: id, firstName: "Joseph", lastName: "Developer",
17+
email: "joseph.developer@couchbase.com", pass: bcrypt.hashSync('mypassword', 10)
18+
}
19+
await profileCollection.insert(id, profile)
20+
.then(() => {/*console.log('test item inserted', profile)*/ })
21+
.catch((e) => console.log(`Test Profile Insert Failed: ${e.message}`))
22+
})
23+
24+
test("should respond with status code 200 OK", async() => {
25+
const response = await request(app).delete(`/profile/${id}`).send()
26+
expect(response.statusCode).toBe(200)
27+
})
28+
29+
})
30+
})
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import {
2+
request, describe, test, expect, //supertes
3+
bcrypt, v4, // utilities
4+
cluster, profileCollection, // couchbase
5+
app // REST application
6+
} from './imports'
7+
8+
afterAll(async() => cluster.close())
9+
10+
describe("GET /profile/{id}", () => {
11+
describe("given we pass a pid as request param", () => {
12+
const id = v4()
13+
const profile = {
14+
pid: id, firstName: "Joseph", lastName: "Developer",
15+
email: "joseph.developer@couchbase.com", pass: bcrypt.hashSync('mypassword', 10)
16+
}
17+
18+
beforeEach(async() => {
19+
await profileCollection.insert(id, profile)
20+
.then(() => {/*console.log('test profile document inserted', profile)*/ })
21+
.catch((e) => console.log(`test profile insert failed: ${e.message}`))
22+
})
23+
24+
test("should respond with status code 200 OK and return profile as object", async() => {
25+
const response = await request(app).get(`/profile/${id}`).send()
26+
expect(response.statusCode).toBe(200)
27+
expect(response.body).toMatchObject(profile)
28+
})
29+
30+
afterEach(async() => {
31+
await profileCollection.remove(id)
32+
.then(() => {/*console.log('test profile document deleted', id)*/ })
33+
.catch((e) => console.log(`test profile remove failed: ${e.message}`))
34+
})
35+
36+
})
37+
})
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import {
2+
request, describe, test, expect, //supertes
3+
bcrypt, v4, // utilities
4+
cluster, profileCollection, // couchbase
5+
app // REST application
6+
} from './imports'
7+
8+
afterAll(async() => cluster.close())
9+
10+
describe("PUT /profile", () => {
11+
describe("given the profile object is updated", () => {
12+
const id = v4()
13+
const initialProfile = {
14+
pid: id, firstName: "Joseph", lastName: "Developer",
15+
email: "joseph.developer@couchbase.com", pass: bcrypt.hashSync('mypassword', 10)
16+
}
17+
const updatedProfile = {
18+
firstName: "Joe", lastName: "dev",
19+
email: "joe@dev.com", pass: "p455w3rd"
20+
}
21+
22+
beforeEach(async() => {
23+
await profileCollection.insert(id, initialProfile)
24+
.then(() => {/*console.log('test profile document inserted', profile)*/ })
25+
.catch((e) => console.log(`test profile insert failed: ${e.message}`))
26+
})
27+
28+
test("should respond with status code 200 OK and updated values of document returned", async() => {
29+
const response = await request(app).put(`/profile/${id}`).send(updatedProfile)
30+
expect(response.statusCode).toBe(200)
31+
expect(response.body.firstName).toBe(updatedProfile.firstName)
32+
expect(response.body.lastName).toBe(updatedProfile.lastName)
33+
expect(response.body.email).toBe(updatedProfile.email)
34+
expect(response.body.pass).not.toBe(updatedProfile.pass)
35+
})
36+
37+
afterEach(async() => {
38+
await profileCollection.remove(id)
39+
.then(() => {/*console.log('test profile document deleted', id)*/ })
40+
.catch((e) => console.log(`test profile remove failed: ${e.message}`))
41+
})
42+
43+
})
44+
})

‎__test__/api-profiles-read.test.js‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {
2+
request, describe, test, expect, //supertes
3+
bcrypt, v4, // utilities
4+
cluster, profileCollection, // couchbase
5+
app // REST application
6+
} from './imports'
7+
8+
import { delay } from '../src/delay'
9+
10+
const profile1 = {
11+
pid: v4(), firstName: "Joe", lastName: "Schmoe",
12+
email: "joe.schmoe@couchbase.com", pass: bcrypt.hashSync('mypassword1', 10)
13+
}
14+
const profile2 = {
15+
pid: v4(), firstName: "John", lastName: "Dear",
16+
email: "john.dear@couchbase.com", pass: bcrypt.hashSync('mypassword2', 10)
17+
}
18+
19+
beforeAll(async () => {
20+
await profileCollection.insert(profile1.pid, profile1)
21+
.then(() => {/*console.log('test profile document inserted', profile)*/ })
22+
.catch((e) => console.log(`test profile insert failed: ${e.message}`))
23+
await profileCollection.insert(profile2.pid, profile2)
24+
.then(() => {/*console.log('test profile document inserted', profile)*/ })
25+
.catch((e) => console.log(`test profile insert failed: ${e.message}`))
26+
})
27+
28+
beforeEach(async() => await delay(2000))
29+
30+
describe("GET /profiles", () => {
31+
describe("given we get profiles with skip 1, limit 2, and search text of 'jo'", () => {
32+
test("should respond with status code 200 OK and return two documents", async () => {
33+
const response = await request(app)
34+
.get(`/profiles`)
35+
.query({
36+
skip: 0, limit: 2, search: 'jo'
37+
})
38+
expect(response.statusCode).toBe(200)
39+
expect(response.body).toHaveLength(2)
40+
})
41+
})
42+
})
43+
44+
afterAll(async () => {
45+
await profileCollection.remove(profile1.pid)
46+
.then(() => { /*console.log('test profile document deleted', id)*/ })
47+
.catch((e) => console.log(`test profile remove failed: ${e.message}`))
48+
await profileCollection.remove(profile2.pid)
49+
.then(() => { /*console.log('test profile document deleted', id)*/ })
50+
.catch((e) => console.log(`test profile remove failed: ${e.message}`))
51+
cluster.close()
52+
})

0 commit comments

Comments
 (0)