Skip to content

Commit

Permalink
Trying out axios
Browse files Browse the repository at this point in the history
  • Loading branch information
JohanObrink committed Dec 20, 2020
1 parent acf4828 commit 084e961
Show file tree
Hide file tree
Showing 14 changed files with 591 additions and 1,999 deletions.
23 changes: 5 additions & 18 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,9 @@
module.exports = {
collectCoverageFrom: ['**/*.{ts}', '!**/node_modules/**', '!**/tests/**', '!**/coverage/**', '!jest.config.js'],
coverageThreshold: {
global: {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
moduleNameMapper: {
'\\.(css|less)$': '<rootDir>/__mocks__/styleMock.js',
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/__mocks__/fileMock.js',
},
setupFiles: ['<rootDir>/test.setup.js'],
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
testPathIgnorePatterns: ['/.next/', '/node_modules/', '/tests/', '/coverage/'],
preset: 'ts-jest',
testEnvironment: 'node',
transform: {
'^.+\\.ts$': 'babel-jest',
'.(ts|tsx)': '<rootDir>/node_modules/ts-jest/preprocessor.js'
},
testRegex: '(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$',
moduleFileExtensions: ["ts", "tsx", "js"]
}
6 changes: 6 additions & 0 deletions lib/__mocks__/axios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const mockAxios: any = jest.genMockFromModule('axios')

// this is the key to fix the axios.create() undefined error!
mockAxios.create = jest.fn(() => mockAxios)

export default mockAxios
11 changes: 11 additions & 0 deletions lib/children.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { AxiosAdapter, AxiosInstance } from 'axios'
import routes from './routes'
import { Child } from './types'

export const list = (client: AxiosInstance) => async (): Promise<Child[]> => {
const url = routes.children
const response = await client.get(url)
return response.data
}

export const details = (client: AxiosAdapter) => async (id: string): Promise<Child> => ({})
12 changes: 0 additions & 12 deletions lib/client.ts

This file was deleted.

60 changes: 58 additions & 2 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,59 @@
export default function foo(): void {
alert('hello')
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
import { list } from './children'
import { checkStatus, getCookies, login } from './login'
import { EventEmitter } from 'events'

const pause = async (ms: number) => new Promise<void>((r) => setTimeout(r, ms))

const emitter = new EventEmitter()
const init = () => {
const config: AxiosRequestConfig = {
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.87 Safari/537.36'
},
maxRedirects: 0,
withCredentials: true
}
const client = axios.create(config)
let cookies: any = {}

client.interceptors.request.use((config) => {
console.log('request', config.method, config.url)
config.headers.Cookie = Object.entries(cookies)
.map(([key, value]) => `${key}=${value}`)
.join('; ')
return config
})
client.interceptors.response.use((response) => {
console.log('response', response.status, response.statusText, response.headers['set-cookie'])
if (response.headers['set-cookie']) {
const setCookies: string[] = response.headers['set-cookie']
setCookies.map((c) => c.split('=')).forEach(([key, value]) => cookies[key] = value)
}
return response
})

return {
...emitter,
login: async (personalNumber: string) => {
const ticket = await login(client)(personalNumber)
await pause(1000)
const check = checkStatus(client)(ticket)
check.on('OK', async () => {
console.log('get cookie')
const newCookies = await getCookies(client)()
cookies = {...cookies, ...newCookies}
console.log(cookies)

emitter.emit('login')
})
return check
},
getChildren: async () => {
const result = await list(client)()
return result
},
}
}

export default init
68 changes: 53 additions & 15 deletions lib/login.test.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,64 @@
import { Client } from './client'
import { login } from './login'
import routes from './routes'
import axios, { AxiosInstance } from 'axios'
import { login, checkStatus, getCookie } from './login'

jest.mock('axios')

let client: jest.Mocked<AxiosInstance>

describe('login', () => {
let client: jest.Mocked<Client>
beforeEach(() => {
client = {
post: jest.fn(),
}
client = axios.create() as jest.Mocked<AxiosInstance>
client.get.mockReset()
client.post.mockReset()
})
it('returns the correct result', async () => {
const personalNumber = 'my personal number'
const response = {
json: async () => ({
describe('#login', () => {
it('returns the correct result', async () => {
const personalNumber = 'my personal number'
const data = {
token: '9462cf77-bde9-4029-bb41-e599f3094613',
order: '5fe57e4c-9ad2-4b52-b794-48adef2f6663',
}

client.post.mockResolvedValue({ data })
const result = await login(client)(personalNumber)

expect(result).toEqual({ order: '5fe57e4c-9ad2-4b52-b794-48adef2f6663' })
})
})
describe('#checkStatus', () => {
const ticket = { order: '5fe57e4c-9ad2-4b52-b794-48adef2f6663' }

it('emits PENDING', (done) => {
client.get.mockResolvedValue({ data: 'PENDING' })

const check = checkStatus(client)(ticket)
check.on('PENDING', async () => {
await check.cancel()
done()
})
})
it('retries on PENDING', (done) => {
client.get.mockResolvedValueOnce({ data: 'PENDING' })
client.get.mockResolvedValueOnce({ data: 'OK' })

const check = checkStatus(client)(ticket)
check.on('OK', () => {
expect(client.get).toHaveBeenCalledTimes(2)
done()
})
})
})
describe('#getCookie', () => {
it('sets cookie as client interceptor', async () => {
client.get.mockResolvedValue({
headers: {
'set-cookie': 'cookie',
},
})
}

client.post.mockResolvedValue(response)
const result = await login(client)(personalNumber)
const cookie = await getCookie(client)()

expect(result).toEqual({ token: '9462cf77-bde9-4029-bb41-e599f3094613' })
expect(cookie).toEqual('cookie')
})
})
})
91 changes: 83 additions & 8 deletions lib/login.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,87 @@
import { Client } from './client'
import { EventEmitter } from 'events'
import { AxiosError, AxiosInstance, AxiosResponse } from 'axios'
import routes from './routes'
import { Auth } from './types'
import { AuthTicket } from './types'

export const login = (client: Client) => async (
personalNumber: string,
): Promise<Auth> => {
export const login = (client: AxiosInstance) => async (personalNumber: string): Promise<AuthTicket> => {
const url = routes.login(personalNumber)
const result = await client.post(url)
const { token } = await result.json()
return { token }
const result = await client.get<AuthTicket>(url)
return { order: result.data.order }
}

/*
export enum LoginEvent {
OK = 'OK',
PENDING = 'PENDING',
ERROR = 'ERROR',
USER_SIGN = 'USER_SIGN',
}
*/

export class LoginStatus extends EventEmitter {
private url: string

private client: AxiosInstance

private cancelled: boolean = false

constructor(client: AxiosInstance, url: string) {
super()
this.client = client
this.url = url
this.check()
}

async check() {
const status = await this.client.get<string>(this.url)
this.emit(status.data)
if (!this.cancelled && status.data !== 'OK' && status.data !== 'ERROR!') {
setTimeout(() => this.check(), 1000)
}
}

async cancel() {
this.cancelled = true
}
}

export const checkStatus = (client: AxiosInstance) => (ticket: AuthTicket): LoginStatus => {
const url = routes.loginStatus(ticket.order)
return new LoginStatus(client, url)
}

const parseCookies = (newCookies: string[]): any => {
return newCookies
.map((c) => c.split('=')).map(([key, val]) => ({[key]: val}))
.reduce((obj1, obj2) => ({...obj1, ...obj2}))
}

export const getCookies = (client: AxiosInstance) => async (url = routes.loginCookie, cookies = {}): Promise<any> => {
try {
const response = await client.get(url)
if (response.headers['set-cookie']) {
cookies = {
...cookies,
...parseCookies(response.headers['set-cookie'])
}
}
return cookies
} catch (err) {
const { response } = err as AxiosError
if (response?.status === 302) {
if (response.headers['set-cookie']) {
cookies = {
...cookies,
...parseCookies(response.headers['set-cookie'])
}
}
if (response.headers.location) {
return getCookies(client)(response.headers.location, cookies)
} else {
return cookies
}
} else {
throw err
}
}
}
10 changes: 9 additions & 1 deletion lib/routes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
const hosts = {
login: 'https://login003.stockholm.se',
etjanst: 'https://etjanst.stockholm.se',
}

const routes = {
login: (personalNumber: string) => `https://login003.stockholm.se/NECSadcmbid/authenticate/NECSadcmbid?TARGET=-SM-HTTPS%3a%2f%2flogin001%2estockholm%2ese%2fNECSadc%2fmbid%2fb64startpage%2ejsp%3fstartpage%3daHR0cHM6Ly9ldGphbnN0LnN0b2NraG9sbS5zZS92YXJkbmFkc2hhdmFyZS9pbmxvZ2dhZDIvaGVt&initialize=bankid&personalNumber=${personalNumber}&_=${Date.now()}`,
login: (personalNumber: string) => `${hosts.login}/NECSadcmbid/authenticate/NECSadcmbid?TARGET=-SM-HTTPS%3a%2f%2flogin001%2estockholm%2ese%2fNECSadc%2fmbid%2fb64startpage%2ejsp%3fstartpage%3daHR0cHM6Ly9ldGphbnN0LnN0b2NraG9sbS5zZS92YXJkbmFkc2hhdmFyZS9pbmxvZ2dhZDIvaGVt&initialize=bankid&personalNumber=${personalNumber}&_=${Date.now()}`,
loginStatus: (order: string) => `${hosts.login}/NECSadcmbid/authenticate/NECSadcmbid?TARGET=-SM-HTTPS%3a%2f%2flogin001%2estockholm%2ese%2fNECSadc%2fmbid%2fb64startpage%2ejsp%3fstartpage%3daHR0cHM6Ly9ldGphbnN0LnN0b2NraG9sbS5zZS92YXJkbmFkc2hhdmFyZS9pbmxvZ2dhZDIvaGVt&verifyorder=${order}&_=${Date.now()}`,
loginCookie: `${hosts.login}/NECSadcmbid/authenticate/SiteMinderAuthADC?TYPE=33554433&REALMOID=06-42f40edd-0c5b-4dbc-b714-1be1e907f2de&GUID=&SMAUTHREASON=0&METHOD=GET&SMAGENTNAME=IfNE0iMOtzq2TcxFADHylR6rkmFtwzoxRKh5nRMO9NBqIxHrc38jFyt56FASdxk1&TARGET=-SM-HTTPS%3a%2f%2flogin001%2estockholm%2ese%2fNECSadc%2fmbid%2fb64startpage%2ejsp%3fstartpage%3daHR0cHM6Ly9ldGphbnN0LnN0b2NraG9sbS5zZS92YXJkbmFkc2hhdmFyZS9pbmxvZ2dhZDIvR2V0Q2hpbGRyZW4%3d`,
children: `${hosts.etjanst}/vardnadshavare/inloggad2/GetChildren`,
}

export default routes
Loading

0 comments on commit 084e961

Please sign in to comment.