Skip to content

Commit

Permalink
DX-377: Account connect
Browse files Browse the repository at this point in the history
  • Loading branch information
rosatolen authored and akshay-8d66 committed Jul 15, 2022
1 parent 5345b5c commit dd624ce
Show file tree
Hide file tree
Showing 61 changed files with 7,910 additions and 2,956 deletions.
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
OAUTH_ACCESS_TOKEN='your-access-token'
CHECKR_API_URL='https://api.checkr-staging.com/v1'
CHECKR_OAUTH_URL='https://api.checkr-staging.com/oauth'
CHECKR_OAUTH_CLIENT_ID=your_partner_application_client_id
CHECKR_OAUTH_CLIENT_SECRET=your_partner_application_client_secret
26 changes: 26 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

# Local DB
localdb.json
test/testdb.json

# Logs
logs
Expand All @@ -28,3 +29,28 @@ dist-ssr
*.njsproj
*.sln
*.sw?

## Client directory

# dependencies
client/node_modules
client/.pnp
client/.pnp.js

# testing
client/coverage

# production
client/build

# misc
client/.DS_Store
client/.env.local
client/.env.development.local
client/.env.test.local
client/.env.production.local

client/npm-debug.log*
client/yarn-debug.log*
clientyarn-error.log*

58 changes: 54 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,68 @@ yarn setup
> Create an `.env` file and copy the variables from `.env.example`
```
OAUTH_ACCESS_TOKEN='0ca8195ca88db88a6df89a9cdd56a7f148f8714b'
CHECKR_API_URL='https://api.checkr-staging.com/v1'
CHECKR_OAUTH_URL='https://api.checkr-staging.com/oauth'
CHECKR_OAUTH_CLIENT_ID=your_partner_application_client_id
CHECKR_OAUTH_CLIENT_SECRET=your_partner_application_client_secret
```

3. Run it locally:

# Run Development Environment

This implementation uses
[localtunnel](https://github.com/localtunnel/localtunnel) to enable testing of
your integration in your localhost environment.

1. Setup the development backend by running this command

```
yarn dev:backend
```

2. In a new terminal window, setup the localtunnel connection for the backend by
running this command

```
yarn dev
yarn dev:backend:localtunnel
```

3. Navigate to localhost:3000 in the browser to see it working!
3. Enable the localtunnel URL created in step 2 by navigating to this location
in the browser and clicking "Continue".

4. Append the URL with "/api/checkr/webhooks" to create your Webhooks URL. For
example, if your Public URL is
`https://all-kings-matter-104-164-31-146.loca.lt`, your Webhooks URL will
look like
`https://all-kings-matter-104-177-32-148.loca.lt/api/checkr/webhooks`.

5. Use this Webhooks URL in your
[Partner Application Settings](https://dashboard.checkrhq-staging.net/account/applications).

6. In a new terminal window, setup the frontend by running this command

```
cd client/ && yarn dev:frontend
```

7. In a new terminal window, setup the localtunnel connection for the frontend
by running this command

```
cd client/ && yarn dev:frontend:localtunnel
```

8. Enable the localtunnel URL created in step 6 by navigating to the URL in the
browser and clicking "Continue". Then copy the Public URL into the Redirect
URL field in your
[Partner Application Settings](https://dashboard.checkrhq-staging.net/account/applications).

9. Navigate to your frontend Public URL in your browser.

# VS Code Recommended Settings

Checkr recommends the VS Code extensions and settings in the .vscode/ folder
The `.vscode` folder contains opt-in
[Workspace Settings](https://code.visualstudio.com/docs/getstarted/settings) and
[Extension Recommendations](https://code.visualstudio.com/docs/editor/extension-gallery#_workspace-recommended-extensions)
that the Checkr team recommends using when working on this repository.
88 changes: 88 additions & 0 deletions __tests__/accounts.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import express from 'express'
import request from 'supertest'
import accountsRouter from '../routes/accounts.js'
import {faker} from '@faker-js/faker'
import {createAccountWithName} from './testSupport/helpers/accountHelper.js'
import {clearDB} from './testSupport/helpers/dbHelper.js'

describe('/api/accounts', () => {
const accountsApi = request(express().use(express.json()).use(accountsRouter))

beforeEach(async () => await clearDB())

it('should GET accounts', async () => {
const existingID = 'ce04e0a2-ecec-11ec-b7ed-f33adcba9538'
const existingAccountName = 'Default Account'
const readAccountResponse = await accountsApi.get(
`/api/accounts/${existingID}`,
)

expect(readAccountResponse.status).toEqual(200)
expect(readAccountResponse.body).toEqual(
expect.objectContaining({
name: existingAccountName,
id: existingID,
createdAt: expect.any(String),
updatedAt: expect.any(String),
}),
)

const readAllAccountsResponse = await accountsApi.get('/api/accounts')
expect(readAllAccountsResponse.status).toEqual(200)
const actualAccount = readAllAccountsResponse.body.find(
a => a.name === existingAccountName,
)
expect(actualAccount).toEqual(
expect.objectContaining({
name: existingAccountName,
id: existingID,
createdAt: expect.any(String),
updatedAt: expect.any(String),
}),
)
})

it('should POST and DELETE an account', async () => {
const expectedName = faker.lorem.slug()
const createAccountResponse = await accountsApi
.post('/api/accounts/')
.send({name: expectedName})
expect(createAccountResponse.body).toEqual(
expect.objectContaining({
name: expectedName,
id: expect.any(String),
createdAt: expect.any(String),
updatedAt: expect.any(String),
}),
)

let deleteAccountResponse = await accountsApi.delete(
`/api/accounts/${createAccountResponse.body.id}`,
)

expect(deleteAccountResponse.status).toEqual(204)

deleteAccountResponse = await accountsApi.delete(
`/api/accounts/${createAccountResponse.body.id}`,
)

expect(deleteAccountResponse.status).toEqual(404)
})

it('should PUT an account', async () => {
const account = await createAccountWithName(faker.lorem.slug())

const newName = faker.lorem.slug()
const updateAccountResponse = await accountsApi
.put(`/api/accounts/${account.id}`)
.send({name: newName})
expect(updateAccountResponse.body).toEqual(
expect.objectContaining({
name: newName,
id: expect.any(String),
createdAt: expect.any(String),
updatedAt: expect.any(String),
}),
)
})
})
95 changes: 95 additions & 0 deletions __tests__/candidates.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import express from 'express'
import request from 'supertest'
import candidatesRouter from '../routes/candidates.js'
import {faker} from '@faker-js/faker'
import {clearDB} from './testSupport/helpers/dbHelper.js'

describe('/api/candidates', () => {
const candidatesApi = request(
express().use(express.json()).use(candidatesRouter),
)

beforeEach(async () => await clearDB())

it('should GET candidates', async () => {
const response = await candidatesApi.get(`/api/candidates`)

expect(response.status).toEqual(200)
expect(response.body.length).toEqual(2)
})

it('should POST a candidate', async () => {
const expectedName = faker.lorem.slug()
const expectedEmail = faker.internet.email()
const expectedNotes = faker.lorem.slug()
const expectedPhone = faker.phone.number()
const expectedStep = faker.lorem.slug()
const response = await candidatesApi.post('/api/candidates').send({
email: expectedEmail,
name: expectedName,
notes: expectedNotes,
phone: expectedPhone,
step: expectedStep,
})

expect(response.body).toEqual(
expect.objectContaining({
id: expect.any(String),
email: expectedEmail,
name: expectedName,
notes: expectedNotes,
phone: expectedPhone,
step: expectedStep,
createdAt: expect.any(String),
updatedAt: expect.any(String),
}),
)

const getAccountResponse = await candidatesApi.get(
`/api/candidates/${response.body.id}`,
)

expect(getAccountResponse.body).toEqual(
expect.objectContaining({
id: expect.any(String),
email: expectedEmail,
name: expectedName,
notes: expectedNotes,
phone: expectedPhone,
step: expectedStep,
createdAt: expect.any(String),
updatedAt: expect.any(String),
}),
)
})

it('should PUT a candidate', async () => {
const expectedName = faker.lorem.slug()
const expectedEmail = faker.internet.email()
const expectedNotes = faker.lorem.slug()
const expectedPhone = faker.phone.number()
const expectedStep = faker.lorem.slug()
const response = await candidatesApi
.put(`/api/candidates/efb0d238-a455-41c6-b923-e0cc4f5761e7`)
.send({
email: expectedEmail,
name: expectedName,
notes: expectedNotes,
phone: expectedPhone,
step: expectedStep,
})

expect(response.body).toEqual(
expect.objectContaining({
id: expect.any(String),
email: expectedEmail,
name: expectedName,
notes: expectedNotes,
phone: expectedPhone,
step: expectedStep,
createdAt: expect.any(String),
updatedAt: expect.any(String),
}),
)
})
})
87 changes: 87 additions & 0 deletions __tests__/checkr.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import express from 'express'
import request from 'supertest'
import checkrRouter from '../routes/checkr.js'
import {faker} from '@faker-js/faker'
import {
createAccountWithCheckrAccountId,
createAccountWithCheckrAccessToken,
findAccountWithCheckrId,
findAccountWithId,
} from './testSupport/helpers/accountHelper.js'
import {getValidSignature} from './testSupport/helpers/webhooksHelper.js'
import {encrypt, decrypt, clearDB} from './testSupport/helpers/dbHelper.js'
import testSeedData from './testSupport/testSeedData.js'
import mockBackend from '../client/src/__tests__/testSupport/helpers/mockBackend.js'

describe('/api/checkr', () => {
const existingAccountId = testSeedData.accounts[0].id
const checkrApi = request(express().use(express.json()).use(checkrRouter))
const checkrApiMock = new mockBackend()

beforeEach(async () => await clearDB())
beforeAll(() => checkrApiMock.server.listen({onUnhandledRequest: 'bypass'}))
afterAll(() => checkrApiMock.server.close())

it('should link a new Checkr account', async () => {
const expectedCheckrAccountId = faker.lorem.slug()
const expectedAccessToken = faker.lorem.slug()

checkrApiMock.stubHttpPost(`${process.env.CHECKR_OAUTH_URL}/tokens`, {
access_token: expectedAccessToken,
checkr_account_id: expectedCheckrAccountId,
})

const response = await checkrApi
.post('/api/checkr/oauth')
.send({code: 'foobar', accountId: existingAccountId})

expect(response.status).toEqual(200)

const updatedAccount = await findAccountWithId(existingAccountId)
expect(updatedAccount.checkrAccount.id).toEqual(expectedCheckrAccountId)
expect(decrypt(updatedAccount.checkrAccount.accessToken)).toEqual(
expectedAccessToken,
)
})

it('should credentialize a Checkr account', async () => {
const existingCheckrId = faker.lorem.slug()
await createAccountWithCheckrAccountId(existingCheckrId)
const requestBody = {
type: 'account.credentialed',
account_id: existingCheckrId,
}

const response = await checkrApi
.post('/api/checkr/webhooks')
.set({'x-checkr-signature': getValidSignature(requestBody)})
.send(requestBody)

expect(response.status).toEqual(200)
const account = await findAccountWithCheckrId(existingCheckrId)
expect(account.checkrAccount.credentialed).toEqual(true)
})

it('should deauthorize a Checkr account', async () => {
const existingAccessToken = encrypt(faker.lorem.slug())
const account = await createAccountWithCheckrAccessToken(
existingAccessToken,
)

checkrApiMock.stubHttpPost(`${process.env.CHECKR_OAUTH_URL}/deauthorize`, {
token: existingAccessToken,
})

const disconnectBody = {
token: existingAccessToken,
}

const disconnect = await checkrApi
.post('/api/checkr/disconnect')
.send(disconnectBody)

expect(disconnect.status).toEqual(204)
const updatedAccount = await findAccountWithId(account.id)
expect(updatedAccount.checkrAccount).toBe(undefined)
})
})
Loading

0 comments on commit dd624ce

Please sign in to comment.