Compare commits

..

1 Commits

Author SHA1 Message Date
Roman Acevedo
9575cc1c87 fake commit to run flaky tests 2025-12-05 18:07:27 +01:00
35 changed files with 63 additions and 146 deletions

View File

@@ -93,7 +93,7 @@ public class Property<T> {
* @return a new {@link Property} without a pre-rendered value * @return a new {@link Property} without a pre-rendered value
*/ */
public Property<T> skipCache() { public Property<T> skipCache() {
return new Property<>(expression, true); return Property.ofExpression(expression);
} }
/** /**

View File

@@ -32,12 +32,10 @@ public class NamespaceFilesUtils {
private ExecutorsUtils executorsUtils; private ExecutorsUtils executorsUtils;
private ExecutorService executorService; private ExecutorService executorService;
private int maxThreads;
@PostConstruct @PostConstruct
public void postConstruct() { public void postConstruct() {
this.maxThreads = Math.max(Runtime.getRuntime().availableProcessors() * 4, 32); this.executorService = executorsUtils.maxCachedThreadPool(Math.max(Runtime.getRuntime().availableProcessors() * 4, 32), "namespace-file");
this.executorService = executorsUtils.maxCachedThreadPool(maxThreads, "namespace-file");
} }
public void loadNamespaceFiles( public void loadNamespaceFiles(
@@ -65,11 +63,7 @@ public class NamespaceFilesUtils {
matchedNamespaceFiles.addAll(files); matchedNamespaceFiles.addAll(files);
} }
// Use half of the available threads to avoid impacting concurrent tasks
int parallelism = maxThreads / 2;
Flux.fromIterable(matchedNamespaceFiles) Flux.fromIterable(matchedNamespaceFiles)
.parallel(parallelism)
.runOn(Schedulers.fromExecutorService(executorService))
.doOnNext(throwConsumer(nsFile -> { .doOnNext(throwConsumer(nsFile -> {
InputStream content = runContext.storage().getFile(nsFile.uri()); InputStream content = runContext.storage().getFile(nsFile.uri());
Path path = folderPerNamespace ? Path path = folderPerNamespace ?
@@ -77,7 +71,7 @@ public class NamespaceFilesUtils {
Path.of(nsFile.path()); Path.of(nsFile.path());
runContext.workingDir().putFile(path, content, fileExistComportment); runContext.workingDir().putFile(path, content, fileExistComportment);
})) }))
.sequential() .publishOn(Schedulers.fromExecutorService(executorService))
.blockLast(); .blockLast();
Duration duration = stopWatch.getDuration(); Duration duration = stopWatch.getDuration();

View File

@@ -157,7 +157,7 @@ public class LoopUntil extends Task implements FlowableTask<LoopUntil.Output> {
public Instant nextExecutionDate(RunContext runContext, Execution execution, TaskRun parentTaskRun) throws IllegalVariableEvaluationException { public Instant nextExecutionDate(RunContext runContext, Execution execution, TaskRun parentTaskRun) throws IllegalVariableEvaluationException {
if (!this.reachedMaximums(runContext, execution, parentTaskRun, false)) { if (!this.reachedMaximums(runContext, execution, parentTaskRun, false)) {
String continueLoop = runContext.render(this.condition).skipCache().as(String.class).orElse(null); String continueLoop = runContext.render(this.condition).as(String.class).orElse(null);
if (!TruthUtils.isTruthy(continueLoop)) { if (!TruthUtils.isTruthy(continueLoop)) {
return Instant.now().plus(runContext.render(this.getCheckFrequency().getInterval()).as(Duration.class).orElseThrow()); return Instant.now().plus(runContext.render(this.getCheckFrequency().getInterval()).as(Duration.class).orElseThrow());
} }

View File

@@ -123,7 +123,7 @@ public class Switch extends Task implements FlowableTask<Switch.Output> {
} }
private String rendererValue(RunContext runContext) throws IllegalVariableEvaluationException { private String rendererValue(RunContext runContext) throws IllegalVariableEvaluationException {
return runContext.render(this.value).skipCache().as(String.class).orElseThrow(); return runContext.render(this.value).as(String.class).orElseThrow();
} }
@Override @Override

View File

@@ -1,11 +1,9 @@
package io.kestra.plugin.core.flow; package io.kestra.plugin.core.flow;
import static io.kestra.core.tenant.TenantService.MAIN_TENANT; import static io.kestra.core.tenant.TenantService.MAIN_TENANT;
import static org.assertj.core.api.Assertions.as;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.kestra.core.junit.annotations.ExecuteFlow;
import io.kestra.core.junit.annotations.KestraTest; import io.kestra.core.junit.annotations.KestraTest;
import io.kestra.core.junit.annotations.LoadFlows; import io.kestra.core.junit.annotations.LoadFlows;
import io.kestra.core.models.executions.Execution; import io.kestra.core.models.executions.Execution;
@@ -102,14 +100,4 @@ class SwitchTest {
assertThat(execution.getState().getCurrent()).isEqualTo(State.Type.FAILED); assertThat(execution.getState().getCurrent()).isEqualTo(State.Type.FAILED);
} }
@Test
@ExecuteFlow("flows/valids/switch-in-concurrent-loop.yaml")
void switchInConcurrentLoop(Execution execution) {
assertThat(execution.getState().getCurrent()).isEqualTo(State.Type.SUCCESS);
assertThat(execution.getTaskRunList()).hasSize(5);
// we check that OOMCRM_EB_DD_000 and OOMCRM_EB_DD_001 have been processed once
assertThat(execution.getTaskRunList().stream().filter(t -> t.getTaskId().equals("OOMCRM_EB_DD_000")).count()).isEqualTo(1);
assertThat(execution.getTaskRunList().stream().filter(t -> t.getTaskId().equals("OOMCRM_EB_DD_001")).count()).isEqualTo(1);
}
} }

View File

@@ -1,23 +0,0 @@
id: switch-in-concurrent-loop
namespace: io.kestra.tests
tasks:
- id: iterate_and_check_name
type: io.kestra.plugin.core.flow.ForEach
tasks:
- id: switch
type: io.kestra.plugin.core.flow.Switch
value: "{{ taskrun.value }}"
cases:
"Alice":
- id: OOMCRM_EB_DD_000
type: io.kestra.plugin.core.log.Log
message: Alice
"Bob":
- id: OOMCRM_EB_DD_001
type: io.kestra.plugin.core.log.Log
message: Bob
values: ["Alice", "Bob"]
concurrencyLimit: 0

View File

@@ -13,19 +13,18 @@ tasks:
- io.test.second - io.test.second
- io.test.third - io.test.third
enabled: true enabled: true
folderPerNamespace: true
exclude: exclude:
- /ignore/** - /ignore/**
tasks: tasks:
- id: t1 - id: t1
type: io.kestra.core.tasks.test.Read type: io.kestra.core.tasks.test.Read
path: "/io.test.third/test/a/b/c/1.txt" path: "/test/a/b/c/1.txt"
- id: t2 - id: t2
type: io.kestra.core.tasks.test.Read type: io.kestra.core.tasks.test.Read
path: "/io.test.second/a/b/c/2.txt" path: "/a/b/c/2.txt"
- id: t3 - id: t3
type: io.kestra.core.tasks.test.Read type: io.kestra.core.tasks.test.Read
path: "/io.test.first/a/b/3.txt" path: "/a/b/3.txt"
- id: t4 - id: t4
type: io.kestra.core.tasks.test.Read type: io.kestra.core.tasks.test.Read
path: "/ignore/4.txt" path: "/ignore/4.txt"

View File

@@ -36,7 +36,7 @@ public class MysqlQueue<T> extends JdbcQueue<T> {
AbstractJdbcRepository.field("offset") AbstractJdbcRepository.field("offset")
) )
// force using the dedicated index, or it made a scan of the PK index // force using the dedicated index, or it made a scan of the PK index
.from(this.table.useIndex("ix_type__offset")) .from(this.table.useIndex("ix_type__consumers"))
.where(AbstractJdbcRepository.field("type").eq(queueType())) .where(AbstractJdbcRepository.field("type").eq(queueType()))
.and(DSL.or(List.of( .and(DSL.or(List.of(
AbstractJdbcRepository.field("consumers").isNull(), AbstractJdbcRepository.field("consumers").isNull(),

View File

@@ -658,16 +658,21 @@ public class JdbcExecutor implements ExecutorInterface {
workerTaskResults.add(new WorkerTaskResult(taskRun)); workerTaskResults.add(new WorkerTaskResult(taskRun));
} }
} }
// flowable attempt state transition to running /// flowable attempt state transition to running
if (workerTask.getTask().isFlowable()) { if (workerTask.getTask().isFlowable()) {
TaskRun updatedTaskRun = workerTask.getTaskRun() List<TaskRunAttempt> attempts = Optional.ofNullable(workerTask.getTaskRun().getAttempts())
.withAttempts( .map(ArrayList::new)
List.of( .orElseGet(ArrayList::new);
attempts.add(
TaskRunAttempt.builder() TaskRunAttempt.builder()
.state(new State().withState(State.Type.RUNNING)) .state(new State().withState(State.Type.RUNNING))
.build() .build()
) );
)
TaskRun updatedTaskRun = workerTask.getTaskRun()
.withAttempts(attempts)
.withState(State.Type.RUNNING); .withState(State.Type.RUNNING);
workerTaskResults.add(new WorkerTaskResult(updatedTaskRun)); workerTaskResults.add(new WorkerTaskResult(updatedTaskRun));

View File

@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
public class DockerService { public class DockerService {
// DDDDDDD
public static DockerClient client(DockerClientConfig dockerClientConfig) { public static DockerClient client(DockerClientConfig dockerClientConfig) {
DockerHttpClient dockerHttpClient = new ApacheDockerHttpClient.Builder() DockerHttpClient dockerHttpClient = new ApacheDockerHttpClient.Builder()
.dockerHost(dockerClientConfig.getDockerHost()) .dockerHost(dockerClientConfig.getDockerHost())

8
ui/package-lock.json generated
View File

@@ -10,7 +10,7 @@
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@js-joda/core": "^5.6.5", "@js-joda/core": "^5.6.5",
"@kestra-io/ui-libs": "^0.0.264", "@kestra-io/ui-libs": "^0.0.263",
"@vue-flow/background": "^1.3.2", "@vue-flow/background": "^1.3.2",
"@vue-flow/controls": "^1.1.2", "@vue-flow/controls": "^1.1.2",
"@vue-flow/core": "^1.47.0", "@vue-flow/core": "^1.47.0",
@@ -2829,9 +2829,9 @@
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/@kestra-io/ui-libs": { "node_modules/@kestra-io/ui-libs": {
"version": "0.0.264", "version": "0.0.263",
"resolved": "https://registry.npmjs.org/@kestra-io/ui-libs/-/ui-libs-0.0.264.tgz", "resolved": "https://registry.npmjs.org/@kestra-io/ui-libs/-/ui-libs-0.0.263.tgz",
"integrity": "sha512-yUZDNaE0wUPOuEq/FL/TQBRd1fTV2dyM8s+VcGRjNSM1uv1uZcsSHro56/heHQx17lo00FDcPT7BMKEifrVhBg==", "integrity": "sha512-j1rWqcQAK2CudNBkcDPjUXyaGFeBzJ7QEhPKFAbleHSw0N3QFu/iy0rFZxJNIMWRi1mGZBh74D6vL0OqQJkT2Q==",
"dependencies": { "dependencies": {
"@nuxtjs/mdc": "^0.17.3", "@nuxtjs/mdc": "^0.17.3",
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",

View File

@@ -24,7 +24,7 @@
}, },
"dependencies": { "dependencies": {
"@js-joda/core": "^5.6.5", "@js-joda/core": "^5.6.5",
"@kestra-io/ui-libs": "^0.0.264", "@kestra-io/ui-libs": "^0.0.263",
"@vue-flow/background": "^1.3.2", "@vue-flow/background": "^1.3.2",
"@vue-flow/controls": "^1.1.2", "@vue-flow/controls": "^1.1.2",
"@vue-flow/core": "^1.47.0", "@vue-flow/core": "^1.47.0",

View File

@@ -87,7 +87,7 @@
</el-form-item> </el-form-item>
<div class="password-requirements mb-4"> <div class="password-requirements mb-4">
<el-text> <el-text>
{{ t('setup.form.password_requirements') }} 8+ chars, 1 upper, 1 number
</el-text> </el-text>
</div> </div>
</el-form> </el-form>
@@ -502,7 +502,7 @@
localStorage.removeItem("basicAuthUserCreated") localStorage.removeItem("basicAuthUserCreated")
localStorage.setItem("basicAuthSetupCompletedAt", new Date().toISOString()) localStorage.setItem("basicAuthSetupCompletedAt", new Date().toISOString())
router.push({name: "welcome"}) router.push({name: "login"})
} }
</script> </script>

View File

@@ -101,7 +101,7 @@ $checkbox-checked-color: #8405FF;
.el-text { .el-text {
color: var(--ks-content-tertiary); color: var(--ks-content-tertiary);
font-size: 12px; font-size: 14px;
} }
} }

View File

@@ -3,8 +3,6 @@ import Utils from "../../../utils/utils";
import {cssVariable, State} from "@kestra-io/ui-libs"; import {cssVariable, State} from "@kestra-io/ui-libs";
import {getSchemeValue} from "../../../utils/scheme"; import {getSchemeValue} from "../../../utils/scheme";
import {useMiscStore} from "override/stores/misc";
export function tooltip(tooltipModel: { export function tooltip(tooltipModel: {
title?: string[]; title?: string[];
body?: { lines: string[] }[]; body?: { lines: string[] }[];
@@ -117,7 +115,7 @@ export function extractState(value: any) {
return value; return value;
} }
export function chartClick(moment: any, router: any, route: any, event: any, parsedData: any, elements: any, type = "label", filters: Record<string, any> = {}) { export function chartClick(moment: any, router: any, route: any, event: any, parsedData: any, elements: any, type = "label") {
const query: Record<string, any> = {}; const query: Record<string, any> = {};
if (elements && parsedData) { if (elements && parsedData) {
@@ -194,11 +192,7 @@ export function chartClick(moment: any, router: any, route: any, event: any, par
params: { params: {
tenant: route.params.tenant, tenant: route.params.tenant,
}, },
query: { query: query,
...query,
...filters,
"filters[timeRange][EQUALS]":useMiscStore()?.configs?.chartDefaultDuration ?? "P30D"
},
}); });
} }
} }

View File

@@ -49,8 +49,6 @@
showDefault: {type: Boolean, default: false}, showDefault: {type: Boolean, default: false},
short: {type: Boolean, default: false}, short: {type: Boolean, default: false},
execution: {type: Boolean, default: false}, execution: {type: Boolean, default: false},
flow: {type: String, default: undefined},
namespace: {type: String, default: undefined},
}); });
@@ -155,10 +153,7 @@
if (data.type === "io.kestra.plugin.core.dashboard.data.Logs" || props.execution) { if (data.type === "io.kestra.plugin.core.dashboard.data.Logs" || props.execution) {
return; return;
} }
chartClick(moment, router, route, {}, parsedData.value, elements, "label", { chartClick(moment, router, route, {}, parsedData.value, elements, "label");
...(props.namespace ? {"filters[namespace][IN]": props.namespace} : {}),
...(props.flow ? {"filters[flowId][EQUALS]": props.flow} : {})
});
}, },
}, theme.value); }, theme.value);
}); });

View File

@@ -213,8 +213,6 @@
:filters="chartFilters()" :filters="chartFilters()"
showDefault showDefault
short short
:flow="scope.row.id"
:namespace="scope.row.namespace"
/> />
</template> </template>
</el-table-column> </el-table-column>

View File

@@ -7,14 +7,14 @@
:disabled="!playgroundStore.readyToStart" :disabled="!playgroundStore.readyToStart"
> >
<el-icon><Play /></el-icon> <el-icon><Play /></el-icon>
<span>{{ $t('playground.run_task') }}</span> <span>{{ t('playground.run_task') }}</span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item :icon="Play" @click="playgroundStore.runUntilTask(taskId)"> <el-dropdown-item :icon="Play" @click="playgroundStore.runUntilTask(taskId)">
{{ $t('playground.run_this_task') }} {{ t('playground.run_this_task') }}
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item :icon="PlayBoxMultiple" @click="playgroundStore.runUntilTask(taskId, true)"> <el-dropdown-item :icon="PlayBoxMultiple" @click="playgroundStore.runUntilTask(taskId, true)">
{{ $t('playground.run_task_and_downstream') }} {{ t('playground.run_task_and_downstream') }}
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
@@ -22,10 +22,12 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {useI18n} from "vue-i18n";
import {usePlaygroundStore} from "../../stores/playground"; import {usePlaygroundStore} from "../../stores/playground";
import Play from "vue-material-design-icons/Play.vue"; import Play from "vue-material-design-icons/Play.vue";
import PlayBoxMultiple from "vue-material-design-icons/PlayBoxMultiple.vue"; import PlayBoxMultiple from "vue-material-design-icons/PlayBoxMultiple.vue";
const {t} = useI18n();
const playgroundStore = usePlaygroundStore(); const playgroundStore = usePlaygroundStore();
defineProps<{ defineProps<{

View File

@@ -12,7 +12,6 @@
import {useCoreStore} from "../../stores/core"; import {useCoreStore} from "../../stores/core";
import {useMiscStore} from "override/stores/misc"; import {useMiscStore} from "override/stores/misc";
import {computed, onMounted} from "vue"; import {computed, onMounted} from "vue";
import {useLayoutStore} from "../../stores/layout";
const coreStore = useCoreStore(); const coreStore = useCoreStore();
const miscStore = useMiscStore(); const miscStore = useMiscStore();
@@ -23,9 +22,7 @@
document.getElementsByTagName("html")[0].classList.remove(collapse ? "menu-not-collapsed" : "menu-collapsed"); document.getElementsByTagName("html")[0].classList.remove(collapse ? "menu-not-collapsed" : "menu-collapsed");
} }
const layoutStore = useLayoutStore();
onMounted(() => { onMounted(() => {
onMenuCollapse(Boolean(layoutStore.sideMenuCollapsed)) onMenuCollapse(localStorage.getItem("menuCollapsed") === "true")
}); });
</script> </script>

View File

@@ -28,7 +28,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {onUpdated, computed, h, watch} from "vue"; import {onUpdated, ref, computed, h, watch} from "vue";
import {useI18n} from "vue-i18n"; import {useI18n} from "vue-i18n";
import {useRoute} from "vue-router"; import {useRoute} from "vue-router";
import {useMediaQuery} from "@vueuse/core"; import {useMediaQuery} from "@vueuse/core";
@@ -118,10 +118,7 @@
]; ];
}); });
const collapsed = computed({ const collapsed = ref(localStorage.getItem("menuCollapsed") === "true")
get: () => layoutStore.sideMenuCollapsed,
set: (v: boolean) => layoutStore.setSideMenuCollapsed(v),
})
const isSmallScreen = useMediaQuery("(max-width: 768px)") const isSmallScreen = useMediaQuery("(max-width: 768px)")

View File

@@ -25,6 +25,10 @@ const handleAuthError = (error, to) => {
initApp(app, routes, null, en).then(({router, piniaStore}) => { initApp(app, routes, null, en).then(({router, piniaStore}) => {
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
if (to.meta?.anonymous === true) {
return next();
}
if(to.path === from.path && to.query === from.query) { if(to.path === from.path && to.query === from.query) {
return next(); // Prevent navigation if the path and query are the same return next(); // Prevent navigation if the path and query are the same
} }
@@ -41,28 +45,13 @@ initApp(app, routes, null, en).then(({router, piniaStore}) => {
if (validationErrors?.length > 0) { if (validationErrors?.length > 0) {
// Creds exist in config but failed validation // Creds exist in config but failed validation
// Route to login to show errors // Route to login to show errors
if (to.name === "login") {
return next();
}
return next({name: "login"}) return next({name: "login"})
} else { } else {
// No creds in config - redirect to set it up // No creds in config - redirect to set it up
if (to.name === "setup") {
return next();
}
return next({name: "setup"}) return next({name: "setup"})
} }
} }
if (to.meta?.anonymous === true) {
if (to.name === "setup") {
return next({name: "login"});
}
return next();
}
const hasCredentials = BasicAuth.isLoggedIn() const hasCredentials = BasicAuth.isLoggedIn()
if (!hasCredentials) { if (!hasCredentials) {
@@ -103,6 +92,6 @@ initApp(app, routes, null, en).then(({router, piniaStore}) => {
}, null, router, true); }, null, router, true);
// mount // mount
router.isReady().then(() => app.mount("#app")) app.mount("#app")
}); });

View File

@@ -12,13 +12,7 @@ export const useLayoutStore = defineStore("layout", {
topNavbar: undefined, topNavbar: undefined,
envName: localStorage.getItem("envName") || undefined, envName: localStorage.getItem("envName") || undefined,
envColor: localStorage.getItem("envColor") || undefined, envColor: localStorage.getItem("envColor") || undefined,
sideMenuCollapsed: (() => { sideMenuCollapsed: localStorage.getItem("menuCollapsed") === "true",
if (typeof window === "undefined") {
return false;
}
return localStorage.getItem("menuCollapsed") === "true" || window.matchMedia("(max-width: 768px)").matches;
})(),
}), }),
getters: {}, getters: {},
actions: { actions: {

View File

@@ -1612,8 +1612,7 @@
"email": "E-Mail", "email": "E-Mail",
"firstName": "Vorname", "firstName": "Vorname",
"lastName": "Nachname", "lastName": "Nachname",
"password": "Passwort", "password": "Passwort"
"password_requirements": "Das Passwort muss mindestens 8 Zeichen lang sein und mindestens 1 Großbuchstaben und 1 Zahl enthalten."
}, },
"login": "Anmelden", "login": "Anmelden",
"logout": "Abmelden", "logout": "Abmelden",

View File

@@ -1477,8 +1477,7 @@
"email": "Email", "email": "Email",
"firstName": "First Name", "firstName": "First Name",
"lastName": "Last Name", "lastName": "Last Name",
"password": "Password", "password": "Password"
"password_requirements": "Password must be at least 8 characters long and include at least 1 uppercase letter and 1 number."
}, },
"validation": { "validation": {
"email_required": "Email is required", "email_required": "Email is required",

View File

@@ -1612,8 +1612,7 @@
"email": "Correo electrónico", "email": "Correo electrónico",
"firstName": "Nombre", "firstName": "Nombre",
"lastName": "Apellido", "lastName": "Apellido",
"password": "Contraseña", "password": "Contraseña"
"password_requirements": "La contraseña debe tener al menos 8 caracteres y contener al menos 1 letra mayúscula y 1 número."
}, },
"login": "Iniciar sesión", "login": "Iniciar sesión",
"logout": "Cerrar sesión", "logout": "Cerrar sesión",

View File

@@ -1612,8 +1612,7 @@
"email": "E-mail", "email": "E-mail",
"firstName": "Prénom", "firstName": "Prénom",
"lastName": "Nom de famille", "lastName": "Nom de famille",
"password": "Mot de passe", "password": "Mot de passe"
"password_requirements": "Le mot de passe doit comporter au moins 8 caractères, inclure au moins 1 lettre majuscule et 1 chiffre."
}, },
"login": "Connexion", "login": "Connexion",
"logout": "Déconnexion", "logout": "Déconnexion",

View File

@@ -1612,8 +1612,7 @@
"email": "ईमेल", "email": "ईमेल",
"firstName": "पहला नाम", "firstName": "पहला नाम",
"lastName": "अंतिम नाम", "lastName": "अंतिम नाम",
"password": "पासवर्ड", "password": "पासवर्ड"
"password_requirements": "पासवर्ड कम से कम 8 अक्षरों का होना चाहिए और इसमें कम से कम 1 बड़ा अक्षर और 1 संख्या शामिल होनी चाहिए।"
}, },
"login": "लॉगिन", "login": "लॉगिन",
"logout": "लॉगआउट", "logout": "लॉगआउट",

View File

@@ -1612,8 +1612,7 @@
"email": "Email", "email": "Email",
"firstName": "Nome", "firstName": "Nome",
"lastName": "Cognome", "lastName": "Cognome",
"password": "Password", "password": "Password"
"password_requirements": "La password deve essere lunga almeno 8 caratteri e includere almeno 1 lettera maiuscola e 1 numero."
}, },
"login": "Accedi", "login": "Accedi",
"logout": "Logout", "logout": "Logout",

View File

@@ -1612,8 +1612,7 @@
"email": "メール", "email": "メール",
"firstName": "名", "firstName": "名",
"lastName": "姓", "lastName": "姓",
"password": "パスワード", "password": "パスワード"
"password_requirements": "パスワードは8文字以上で、少なくとも1つの大文字と1つの数字を含める必要があります。"
}, },
"login": "ログイン", "login": "ログイン",
"logout": "ログアウト", "logout": "ログアウト",

View File

@@ -1612,8 +1612,7 @@
"email": "이메일", "email": "이메일",
"firstName": "이름", "firstName": "이름",
"lastName": "성씨", "lastName": "성씨",
"password": "비밀번호", "password": "비밀번호"
"password_requirements": "비밀번호는 최소 8자 이상이어야 하며, 최소 1개의 대문자와 1개의 숫자를 포함해야 합니다."
}, },
"login": "로그인", "login": "로그인",
"logout": "로그아웃", "logout": "로그아웃",

View File

@@ -1612,8 +1612,7 @@
"email": "Email", "email": "Email",
"firstName": "Imię", "firstName": "Imię",
"lastName": "Nazwisko", "lastName": "Nazwisko",
"password": "Hasło", "password": "Hasło"
"password_requirements": "Hasło musi mieć co najmniej 8 znaków i zawierać co najmniej 1 wielką literę oraz 1 cyfrę."
}, },
"login": "Zaloguj się", "login": "Zaloguj się",
"logout": "Wyloguj się", "logout": "Wyloguj się",

View File

@@ -1612,8 +1612,7 @@
"email": "Email", "email": "Email",
"firstName": "Nome", "firstName": "Nome",
"lastName": "Sobrenome", "lastName": "Sobrenome",
"password": "Senha", "password": "Senha"
"password_requirements": "A senha deve ter pelo menos 8 caracteres e incluir pelo menos 1 letra maiúscula e 1 número."
}, },
"login": "Login", "login": "Login",
"logout": "Sair", "logout": "Sair",

View File

@@ -1612,8 +1612,7 @@
"email": "Email", "email": "Email",
"firstName": "Nome", "firstName": "Nome",
"lastName": "Sobrenome", "lastName": "Sobrenome",
"password": "Senha", "password": "Senha"
"password_requirements": "A senha deve ter pelo menos 8 caracteres e incluir pelo menos 1 letra maiúscula e 1 número."
}, },
"login": "Login", "login": "Login",
"logout": "Sair", "logout": "Sair",

View File

@@ -1612,8 +1612,7 @@
"email": "Электронная почта", "email": "Электронная почта",
"firstName": "Имя", "firstName": "Имя",
"lastName": "Фамилия", "lastName": "Фамилия",
"password": "Пароль", "password": "Пароль"
"password_requirements": "Пароль должен содержать не менее 8 символов, включая как минимум 1 заглавную букву и 1 цифру."
}, },
"login": "Войти", "login": "Войти",
"logout": "Выход", "logout": "Выход",

View File

@@ -1612,8 +1612,7 @@
"email": "电子邮件", "email": "电子邮件",
"firstName": "名字", "firstName": "名字",
"lastName": "姓氏", "lastName": "姓氏",
"password": "密码", "password": "密码"
"password_requirements": "密码必须至少包含8个字符并且至少包含1个大写字母和1个数字。"
}, },
"login": "登录", "login": "登录",
"logout": "注销", "logout": "注销",