mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-13 03:40:48 +00:00
Fix login form issue when using password auto-fill (#5047)
Signed-off-by: Vlad Timofeev <11474041+vlad-timofeev@users.noreply.github.com>
This commit is contained in:
parent
04bb873191
commit
07ff2a9d3c
@ -6,10 +6,12 @@
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "compile ui",
|
||||
"test": "jest --passWithNoTests --silent",
|
||||
"build:docs": "api-extractor run --local",
|
||||
"format": "format src",
|
||||
"build:watch": "compile ui",
|
||||
"_phase:build": "compile ui",
|
||||
"_phase:test": "jest --passWithNoTests --silent",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
},
|
||||
|
28
plugins/login-resources/src/__tests__/mutex.test.ts
Normal file
28
plugins/login-resources/src/__tests__/mutex.test.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { makeSequential } from '../mutex'
|
||||
|
||||
describe('mutex', () => {
|
||||
let results: number[]
|
||||
|
||||
beforeEach(() => {
|
||||
results = []
|
||||
})
|
||||
|
||||
async function waitAndPushResult (millis: number): Promise<void> {
|
||||
await new Promise<void>((resolve) => {
|
||||
setTimeout(resolve, millis)
|
||||
})
|
||||
results.push(millis)
|
||||
}
|
||||
|
||||
it('expect sequential execution', async () => {
|
||||
const myUpdate = makeSequential(waitAndPushResult)
|
||||
await Promise.all([myUpdate(50), myUpdate(30), myUpdate(40), myUpdate(10), myUpdate(20)])
|
||||
expect(results).toEqual([50, 30, 40, 10, 20])
|
||||
})
|
||||
|
||||
it('expect parallel execution', async () => {
|
||||
const myUpdate = waitAndPushResult
|
||||
await Promise.all([myUpdate(50), myUpdate(30), myUpdate(40), myUpdate(10), myUpdate(20)])
|
||||
expect(results).toEqual([10, 20, 30, 40, 50])
|
||||
})
|
||||
})
|
@ -31,6 +31,7 @@
|
||||
import { onMount } from 'svelte'
|
||||
import { BottomAction, getHref } from '..'
|
||||
import login from '../plugin'
|
||||
import { makeSequential } from '../mutex'
|
||||
import Providers from './Providers.svelte'
|
||||
|
||||
interface Field {
|
||||
@ -66,7 +67,7 @@
|
||||
|
||||
$: $themeStore.language && validate($themeStore.language)
|
||||
|
||||
async function validate (language: string): Promise<boolean> {
|
||||
const validate = makeSequential(async function validateAsync (language: string): Promise<boolean> {
|
||||
if (ignoreInitialValidation) return true
|
||||
for (const field of fields) {
|
||||
const v = object[field.name]
|
||||
@ -101,7 +102,7 @@
|
||||
}
|
||||
status = OK
|
||||
return true
|
||||
}
|
||||
})
|
||||
validate($themeStore.language)
|
||||
|
||||
let inAction = false
|
||||
|
84
plugins/login-resources/src/mutex.ts
Normal file
84
plugins/login-resources/src/mutex.ts
Normal file
@ -0,0 +1,84 @@
|
||||
//
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
/**
|
||||
Class that allows synchronization of async functions to prevent race conditions.
|
||||
Inspired by https://stackoverflow.com/a/51086893
|
||||
**/
|
||||
class Mutex {
|
||||
currentLock: Promise<void>
|
||||
|
||||
constructor () {
|
||||
// initially the lock is not held
|
||||
this.currentLock = Promise.resolve()
|
||||
}
|
||||
|
||||
/**
|
||||
Acquires the lock. Usage example:
|
||||
|
||||
```
|
||||
let mutex = new Mutex()
|
||||
|
||||
async function synchronizedFunction() {
|
||||
const unlockMutex = await mutex.lock()
|
||||
try {
|
||||
// critical section
|
||||
} finally {
|
||||
unlockMutex()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@return {Promise<VoidFunction>} promise that must be awaited in order to acquire the lock.
|
||||
When the promise is fulfulled it returns a function that must be invoked to release the lock.
|
||||
**/
|
||||
async lock (): Promise<VoidFunction> {
|
||||
// function which invocation releases the lock
|
||||
let releaseLock: VoidFunction
|
||||
// this Promise is fulfilled as soon as the function above is invoked
|
||||
const afterReleasePromise = new Promise<void>((resolve) => {
|
||||
releaseLock = () => {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
// Caller gets a promise that resolves
|
||||
// only after the current outstanding lock resolves
|
||||
const blockingPromise = this.currentLock.then(() => releaseLock)
|
||||
// Don't allow the next request until the new promise is done
|
||||
this.currentLock = afterReleasePromise
|
||||
// Return the new promise
|
||||
return await blockingPromise
|
||||
}
|
||||
}
|
||||
|
||||
type AnyFunction<R> = (...args: any[]) => Promise<R>
|
||||
/**
|
||||
This function wraps around the Mutex implementation above and provides a simple interface.
|
||||
|
||||
@return {Promise<VoidFunction>} a sequential version of the passed function. This version guarantees
|
||||
that its invocations are executed one by one.
|
||||
**/
|
||||
export function makeSequential<R, T extends AnyFunction<R>> (fn: T): (...args: Parameters<T>) => Promise<R> {
|
||||
const mutex = new Mutex()
|
||||
|
||||
return async function (...args: Parameters<T>): Promise<R> {
|
||||
const unlockMutex = await mutex.lock()
|
||||
try {
|
||||
return await fn(...args)
|
||||
} finally {
|
||||
unlockMutex()
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user