mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-21 07:46:24 +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",
|
"license": "EPL-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "compile ui",
|
"build": "compile ui",
|
||||||
|
"test": "jest --passWithNoTests --silent",
|
||||||
"build:docs": "api-extractor run --local",
|
"build:docs": "api-extractor run --local",
|
||||||
"format": "format src",
|
"format": "format src",
|
||||||
"build:watch": "compile ui",
|
"build:watch": "compile ui",
|
||||||
"_phase:build": "compile ui",
|
"_phase:build": "compile ui",
|
||||||
|
"_phase:test": "jest --passWithNoTests --silent",
|
||||||
"_phase:format": "format src",
|
"_phase:format": "format src",
|
||||||
"_phase:validate": "compile validate"
|
"_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 { onMount } from 'svelte'
|
||||||
import { BottomAction, getHref } from '..'
|
import { BottomAction, getHref } from '..'
|
||||||
import login from '../plugin'
|
import login from '../plugin'
|
||||||
|
import { makeSequential } from '../mutex'
|
||||||
import Providers from './Providers.svelte'
|
import Providers from './Providers.svelte'
|
||||||
|
|
||||||
interface Field {
|
interface Field {
|
||||||
@ -66,7 +67,7 @@
|
|||||||
|
|
||||||
$: $themeStore.language && validate($themeStore.language)
|
$: $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
|
if (ignoreInitialValidation) return true
|
||||||
for (const field of fields) {
|
for (const field of fields) {
|
||||||
const v = object[field.name]
|
const v = object[field.name]
|
||||||
@ -101,7 +102,7 @@
|
|||||||
}
|
}
|
||||||
status = OK
|
status = OK
|
||||||
return true
|
return true
|
||||||
}
|
})
|
||||||
validate($themeStore.language)
|
validate($themeStore.language)
|
||||||
|
|
||||||
let inAction = false
|
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