async-mutex#MutexInterface TypeScript Examples

The following examples show how to use async-mutex#MutexInterface. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: authenticator.ts    From smartthings-core-sdk with Apache License 2.0 5 votes vote down vote up
acquireRefreshMutex(): Promise<MutexInterface.Releaser> {
		return this.refreshMutex.acquire()
	}
Example #2
Source File: authenticator.ts    From smartthings-core-sdk with Apache License 2.0 5 votes vote down vote up
constructor(token: string, tokenStore: RefreshTokenStore, private refreshMutex: MutexInterface) {
		super(token, tokenStore)
	}
Example #3
Source File: index.ts    From integration-services with Apache License 2.0 5 votes vote down vote up
private locks: Map<string, MutexInterface>;
Example #4
Source File: authenticator.test.ts    From smartthings-core-sdk with Apache License 2.0 4 votes vote down vote up
describe('authenticators', () => {
	afterEach(() => {
		jest.clearAllMocks()
	})

	const config = { url: 'https://api.smartthings.com', headers: { Test: 'test' } }

	describe('NoOpAuthenticator', () => {
		test('authenticate returns config unchanged', async () => {
			const authenticator = new NoOpAuthenticator()
			const data = await authenticator.authenticate(config)

			expect(data).toBe(config)
		})

		test('authenticateGeneric returns empty string for token', async () => {
			const authenticator = new NoOpAuthenticator()
			const token = await authenticator.authenticateGeneric()

			expect(token).toBe('')
		})
	})

	describe('BearerTokenAuthenticator', () => {
		test('authenticate adds header with specified token', async () => {
			const authenticator = new BearerTokenAuthenticator('a-bearer-token')
			const data = await authenticator.authenticate(config)

			expect(data.url).toBe(config.url)
			expect(data.headers?.Authorization).toBe('Bearer a-bearer-token')
			expect(data.headers?.Test).toBe('test')
		})

		test('authenticateGeneric returns specified token', async () => {
			const authenticator = new BearerTokenAuthenticator('a-bearer-token')
			const token = await authenticator.authenticateGeneric()

			expect(token).toBe('a-bearer-token')
		})
	})

	describe('RefreshTokenAuthenticator', () => {
		test('authenticate adds header with specified token', async () => {
			const tokenStore = new TokenStore()
			const authenticator = new RefreshTokenAuthenticator('a-refreshable-bearer-token', tokenStore)
			const data = await authenticator.authenticate(config)

			expect(data.url).toBe(config.url)
			expect(data.headers?.Authorization).toBe('Bearer a-refreshable-bearer-token')
		})

		test('refresh updates token', async () => {
			axios.request.mockResolvedValueOnce({
				status: 200,
				data: {
					'access_token': 'the-access-token',
					'refresh_token': 'the-refresh-token',
				},
			})

			const tokenStore = new TokenStore()
			const authenticator = new RefreshTokenAuthenticator('a-refreshable-bearer-token', tokenStore)
			const endpointConfig = { urlProvider: defaultSmartThingsURLProvider, authenticator }
			await authenticator.refresh(config, endpointConfig)

			expect(axios.request).toHaveBeenCalledTimes(1)
			expect(axios.request).toHaveBeenCalledWith({
				'url': 'https://auth-global.api.smartthings.com/oauth/token',
				'method': 'POST',
				'headers': {
					'Content-Type': 'application/x-www-form-urlencoded',
					'Authorization': 'Basic YWFhOmJiYg==',
					'Accept': 'application/json',
				},
				'data': 'grant_type=refresh_token&client_id=aaa&refresh_token=xxx',
			})
			expect(tokenStore.authData?.authToken).toBe('the-access-token')
			expect(tokenStore.authData?.refreshToken).toBe('the-refresh-token')
		})

		test('refresh rejects promise on failure', async () => {
			axios.request.mockResolvedValueOnce({
				status: 401,
				data: 'Authorization failed',
			})

			const tokenStore = new TokenStore()
			const authenticator = new RefreshTokenAuthenticator('a-refreshable-bearer-token', tokenStore)
			const endpointConfig = { urlProvider: defaultSmartThingsURLProvider, authenticator }
			let message
			try {
				await authenticator.refresh(config, endpointConfig)
			// eslint-disable-next-line @typescript-eslint/no-explicit-any
			} catch (error: any) {
				message = error.message
			}
			expect(axios.request).toHaveBeenCalledTimes(1)
			expect(message).toBe('error 401 refreshing token, with message Authorization failed')
		})
	})

	describe('SequentialRefreshTokenAuthenticator', () => {
		const tokenStore = new TokenStore()
		const releaseMock = jest.fn()
		const acquireMock = (jest.fn() as jest.Mock<Promise<MutexInterface.Releaser>, []>)
			.mockResolvedValue(releaseMock)
		const mutex = { acquire: acquireMock } as unknown as MutexInterface
		const authenticator = new SequentialRefreshTokenAuthenticator('a-bearer-token', tokenStore, mutex)

		test('authenticate adds header with specified token', async () => {
			const data = await authenticator.authenticate(config)

			expect(data.url).toBe(config.url)
			expect(data.headers?.Authorization).toBe('Bearer a-bearer-token')
		})

		describe('refresh', () => {
			axios.request.mockResolvedValue({
				status: 200,
				data: {
					'access_token': 'the-access-token',
					'refresh_token': 'the-refresh-token',
				},
			})

			const endpointConfig = { urlProvider: defaultSmartThingsURLProvider, authenticator }

			it('updates token', async () => {
				await authenticator.refresh(config, endpointConfig)

				expect(axios.request).toHaveBeenCalledTimes(1)
				expect(axios.request).toHaveBeenCalledWith({
					'url': 'https://auth-global.api.smartthings.com/oauth/token',
					'method': 'POST',
					'headers': {
						'Content-Type': 'application/x-www-form-urlencoded',
						'Authorization': 'Basic YWFhOmJiYg==',
						'Accept': 'application/json',
					},
					'data': 'grant_type=refresh_token&client_id=aaa&refresh_token=xxx',
				})
				expect(tokenStore.authData?.authToken).toBe('the-access-token')
				expect(tokenStore.authData?.refreshToken).toBe('the-refresh-token')
			})

			it('works on request with no existing headers', async () => {
				const configWithoutHeaders = { url: 'https://api.smartthings.com' }
				await authenticator.refresh(configWithoutHeaders, endpointConfig)

				expect(axios.request).toHaveBeenCalledTimes(1)
				expect(axios.request).toHaveBeenCalledWith({
					'url': 'https://auth-global.api.smartthings.com/oauth/token',
					'method': 'POST',
					'headers': {
						'Content-Type': 'application/x-www-form-urlencoded',
						'Authorization': 'Basic YWFhOmJiYg==',
						'Accept': 'application/json',
					},
					'data': 'grant_type=refresh_token&client_id=aaa&refresh_token=xxx',
				})
				expect(tokenStore.authData?.authToken).toBe('the-access-token')
				expect(tokenStore.authData?.refreshToken).toBe('the-refresh-token')
			})
		})

		test('acquireRefreshMutex', async () => {
			const release = await authenticator.acquireRefreshMutex()

			expect(acquireMock).toHaveBeenCalledTimes(1)
			expect(acquireMock).toHaveBeenCalledWith()

			release()
			expect(releaseMock).toHaveBeenCalledTimes(1)
			expect(releaseMock).toHaveBeenCalledWith()
		})
	})
})