Compare commits

..

1 Commits

Author SHA1 Message Date
Jessica Steurer
dc8006757a Revert "Adds support for a pseudolocalization and lang query params (#13661)"
This reverts commit 7f1750324f.
2023-03-15 08:31:26 -03:00
10 changed files with 44 additions and 64 deletions

View File

@@ -70,7 +70,7 @@ def reap_waiting(instance=None, status='failed', job_explanation=None, grace_per
reap_job(j, status, job_explanation=job_explanation) reap_job(j, status, job_explanation=job_explanation)
def reap(instance=None, status='failed', job_explanation=None, excluded_uuids=None, ref_time=None): def reap(instance=None, status='failed', job_explanation=None, excluded_uuids=None):
""" """
Reap all jobs in running for this instance. Reap all jobs in running for this instance.
""" """
@@ -80,7 +80,7 @@ def reap(instance=None, status='failed', job_explanation=None, excluded_uuids=No
hostname = instance.hostname hostname = instance.hostname
workflow_ctype_id = ContentType.objects.get_for_model(WorkflowJob).id workflow_ctype_id = ContentType.objects.get_for_model(WorkflowJob).id
jobs = UnifiedJob.objects.filter( jobs = UnifiedJob.objects.filter(
Q(status='running', modified__lte=ref_time) & (Q(execution_node=hostname) | Q(controller_node=hostname)) & ~Q(polymorphic_ctype_id=workflow_ctype_id) Q(status='running') & (Q(execution_node=hostname) | Q(controller_node=hostname)) & ~Q(polymorphic_ctype_id=workflow_ctype_id)
) )
if excluded_uuids: if excluded_uuids:
jobs = jobs.exclude(celery_task_id__in=excluded_uuids) jobs = jobs.exclude(celery_task_id__in=excluded_uuids)

View File

@@ -581,7 +581,7 @@ def cluster_node_heartbeat(dispatch_time=None, worker_tasks=None):
active_task_ids = [] active_task_ids = []
for task_list in worker_tasks.values(): for task_list in worker_tasks.values():
active_task_ids.extend(task_list) active_task_ids.extend(task_list)
reaper.reap(instance=this_inst, excluded_uuids=active_task_ids, ref_time=datetime.fromisoformat(dispatch_time)) reaper.reap(instance=this_inst, excluded_uuids=active_task_ids)
if max(len(task_list) for task_list in worker_tasks.values()) <= 1: if max(len(task_list) for task_list in worker_tasks.values()) <= 1:
reaper.reap_waiting(instance=this_inst, excluded_uuids=active_task_ids, ref_time=datetime.fromisoformat(dispatch_time)) reaper.reap_waiting(instance=this_inst, excluded_uuids=active_task_ids, ref_time=datetime.fromisoformat(dispatch_time))

View File

@@ -337,8 +337,6 @@ class TestTaskPublisher:
yesterday = tz_now() - datetime.timedelta(days=1) yesterday = tz_now() - datetime.timedelta(days=1)
minute = tz_now() - datetime.timedelta(seconds=120)
now = tz_now()
@pytest.mark.django_db @pytest.mark.django_db
@@ -347,8 +345,8 @@ class TestJobReaper(object):
'status, execution_node, controller_node, modified, fail', 'status, execution_node, controller_node, modified, fail',
[ [
('running', '', '', None, False), # running, not assigned to the instance ('running', '', '', None, False), # running, not assigned to the instance
('running', 'awx', '', minute, True), # running, has the instance as its execution_node ('running', 'awx', '', None, True), # running, has the instance as its execution_node
('running', '', 'awx', minute, True), # running, has the instance as its controller_node ('running', '', 'awx', None, True), # running, has the instance as its controller_node
('waiting', '', '', None, False), # waiting, not assigned to the instance ('waiting', '', '', None, False), # waiting, not assigned to the instance
('waiting', 'awx', '', None, False), # waiting, was edited less than a minute ago ('waiting', 'awx', '', None, False), # waiting, was edited less than a minute ago
('waiting', '', 'awx', None, False), # waiting, was edited less than a minute ago ('waiting', '', 'awx', None, False), # waiting, was edited less than a minute ago
@@ -370,7 +368,7 @@ class TestJobReaper(object):
# we have to edit the modification time _without_ calling save() # we have to edit the modification time _without_ calling save()
# (because .save() overwrites it to _now_) # (because .save() overwrites it to _now_)
Job.objects.filter(id=j.id).update(modified=modified) Job.objects.filter(id=j.id).update(modified=modified)
reaper.reap(i, ref_time=now) reaper.reap(i)
reaper.reap_waiting(i) reaper.reap_waiting(i)
job = Job.objects.first() job = Job.objects.first()
if fail: if fail:
@@ -381,15 +379,13 @@ class TestJobReaper(object):
assert job.status == status assert job.status == status
@pytest.mark.parametrize( @pytest.mark.parametrize(
'excluded_uuids, fail, modified', 'excluded_uuids, fail',
[ [
(['abc123'], False, None), (['abc123'], False),
([], False, None), ([], True),
([], True, minute),
], ],
) )
def test_do_not_reap_excluded_uuids(self, excluded_uuids, fail, modified): def test_do_not_reap_excluded_uuids(self, excluded_uuids, fail):
"""Modified Test to account for ref_time in reap()"""
i = Instance(hostname='awx') i = Instance(hostname='awx')
i.save() i.save()
j = Job( j = Job(
@@ -400,13 +396,10 @@ class TestJobReaper(object):
celery_task_id='abc123', celery_task_id='abc123',
) )
j.save() j.save()
if modified:
Job.objects.filter(id=j.id).update(modified=modified)
# if the UUID is excluded, don't reap it # if the UUID is excluded, don't reap it
reaper.reap(i, excluded_uuids=excluded_uuids, ref_time=now) reaper.reap(i, excluded_uuids=excluded_uuids)
job = Job.objects.first() job = Job.objects.first()
if fail: if fail:
assert job.status == 'failed' assert job.status == 'failed'
assert 'marked as failed' in job.job_explanation assert 'marked as failed' in job.job_explanation
@@ -419,6 +412,6 @@ class TestJobReaper(object):
i.save() i.save()
j = WorkflowJob(status='running', execution_node='awx') j = WorkflowJob(status='running', execution_node='awx')
j.save() j.save()
reaper.reap(i, ref_time=now) reaper.reap(i)
assert WorkflowJob.objects.first().status == 'running' assert WorkflowJob.objects.first().status == 'running'

View File

@@ -18,7 +18,7 @@ class DistinctParametrize(object):
@pytest.mark.survey @pytest.mark.survey
class TestSurveyVariableValidation: class SurveyVariableValidation:
def test_survey_answers_as_string(self, job_template_factory): def test_survey_answers_as_string(self, job_template_factory):
objects = job_template_factory('job-template-with-survey', survey=[{'variable': 'var1', 'type': 'text'}], persisted=False) objects = job_template_factory('job-template-with-survey', survey=[{'variable': 'var1', 'type': 'text'}], persisted=False)
jt = objects.job_template jt = objects.job_template
@@ -57,7 +57,7 @@ class TestSurveyVariableValidation:
accepted, rejected, errors = obj.accept_or_ignore_variables({"a": 5}) accepted, rejected, errors = obj.accept_or_ignore_variables({"a": 5})
assert rejected == {"a": 5} assert rejected == {"a": 5}
assert accepted == {} assert accepted == {}
assert str(errors['variables_needed_to_start'][0]) == "Value 5 for 'a' expected to be a string." assert str(errors[0]) == "Value 5 for 'a' expected to be a string."
def test_job_template_survey_default_variable_validation(self, job_template_factory): def test_job_template_survey_default_variable_validation(self, job_template_factory):
objects = job_template_factory( objects = job_template_factory(
@@ -88,7 +88,7 @@ class TestSurveyVariableValidation:
obj.survey_enabled = True obj.survey_enabled = True
accepted, _, errors = obj.accept_or_ignore_variables({"a": 2}) accepted, _, errors = obj.accept_or_ignore_variables({"a": 2})
assert accepted == {"a": 2.0} assert accepted == {{"a": 2.0}}
assert not errors assert not errors

View File

@@ -28,7 +28,7 @@ import { getLanguageWithoutRegionCode } from 'util/language';
import Metrics from 'screens/Metrics'; import Metrics from 'screens/Metrics';
import SubscriptionEdit from 'screens/Setting/Subscription/SubscriptionEdit'; import SubscriptionEdit from 'screens/Setting/Subscription/SubscriptionEdit';
import useTitle from 'hooks/useTitle'; import useTitle from 'hooks/useTitle';
import { dynamicActivate } from './i18nLoader'; import { dynamicActivate, locales } from './i18nLoader';
import getRouteConfig from './routeConfig'; import getRouteConfig from './routeConfig';
import { SESSION_REDIRECT_URL } from './constants'; import { SESSION_REDIRECT_URL } from './constants';
@@ -139,15 +139,16 @@ export function ProtectedRoute({ children, ...rest }) {
function App() { function App() {
const history = useHistory(); const history = useHistory();
const { hash, search, pathname } = useLocation(); const { hash, search, pathname } = useLocation();
const searchParams = Object.fromEntries(new URLSearchParams(search)); let language = getLanguageWithoutRegionCode(navigator);
const pseudolocalization = if (!Object.keys(locales).includes(language)) {
searchParams.pseudolocalization === 'true' || false; // If there isn't a string catalog available for the browser's
const language = // preferred language, default to one that has strings.
searchParams.lang || getLanguageWithoutRegionCode(navigator) || 'en'; language = 'en';
}
useEffect(() => { useEffect(() => {
dynamicActivate(language, pseudolocalization); dynamicActivate(language);
}, [language, pseudolocalization]); }, [language]);
useTitle(); useTitle();

View File

@@ -6,12 +6,7 @@ import { useField } from 'formik';
import styled from 'styled-components'; import styled from 'styled-components';
import { Split, SplitItem, Button, Modal } from '@patternfly/react-core'; import { Split, SplitItem, Button, Modal } from '@patternfly/react-core';
import { ExpandArrowsAltIcon } from '@patternfly/react-icons'; import { ExpandArrowsAltIcon } from '@patternfly/react-icons';
import { import { yamlToJson, jsonToYaml, isJsonString } from 'util/yaml';
yamlToJson,
jsonToYaml,
isJsonString,
parseVariableField,
} from 'util/yaml';
import { CheckboxField } from '../FormField'; import { CheckboxField } from '../FormField';
import MultiButtonToggle from '../MultiButtonToggle'; import MultiButtonToggle from '../MultiButtonToggle';
import CodeEditor from './CodeEditor'; import CodeEditor from './CodeEditor';
@@ -42,24 +37,36 @@ function VariablesField({
// track focus manually, because the Code Editor library doesn't wire // track focus manually, because the Code Editor library doesn't wire
// into Formik completely // into Formik completely
const [shouldValidate, setShouldValidate] = useState(false); const [shouldValidate, setShouldValidate] = useState(false);
const [mode, setMode] = useState(initialMode || YAML_MODE);
const validate = useCallback( const validate = useCallback(
(value) => { (value) => {
if (!shouldValidate) { if (!shouldValidate) {
return undefined; return undefined;
} }
try { try {
parseVariableField(value); if (mode === YAML_MODE) {
yamlToJson(value);
} else {
JSON.parse(value);
}
} catch (error) { } catch (error) {
return error.message; return error.message;
} }
return undefined; return undefined;
}, },
[shouldValidate] [shouldValidate, mode]
); );
const [field, meta, helpers] = useField({ name, validate }); const [field, meta, helpers] = useField({ name, validate });
const [mode, setMode] = useState(() =>
isJsonString(field.value) ? JSON_MODE : initialMode || YAML_MODE useEffect(() => {
); if (isJsonString(field.value)) {
// mode's useState above couldn't be initialized to JSON_MODE because
// the field value had to be defined below it
setMode(JSON_MODE);
onModeChange(JSON_MODE);
helpers.setValue(JSON.stringify(JSON.parse(field.value), null, 2));
}
}, []); // eslint-disable-line react-hooks/exhaustive-deps
useEffect( useEffect(
() => { () => {

View File

@@ -1,3 +1,4 @@
/* eslint-disable-next-line import/prefer-default-export */
export const JOB_TYPE_URL_SEGMENTS = { export const JOB_TYPE_URL_SEGMENTS = {
job: 'playbook', job: 'playbook',
project_update: 'project', project_update: 'project',

View File

@@ -27,21 +27,8 @@ i18n.loadLocaleData({
* We do a dynamic import of just the catalog that we need * We do a dynamic import of just the catalog that we need
* @param locale any locale string * @param locale any locale string
*/ */
export async function dynamicActivate(locale, pseudolocalization = false) { export async function dynamicActivate(locale) {
const { messages } = await import(`./locales/${locale}/messages`); const { messages } = await import(`./locales/${locale}/messages`);
if (pseudolocalization) {
Object.keys(messages).forEach((key) => {
if (Array.isArray(messages[key])) {
// t`Foo ${param}` -> ["Foo ", ['param']] => [">>", "Foo ", ['param'], "<<"]
messages[key] = ['»', ...messages[key], '«'];
} else {
// simple string
messages[key] = `»${messages[key]}«`;
}
});
}
i18n.load(locale, messages); i18n.load(locale, messages);
i18n.activate(locale); i18n.activate(locale);
} }

View File

@@ -208,7 +208,6 @@ function AWXLogin({ alt, isAuthenticated }) {
> >
{(formik) => ( {(formik) => (
<LoginForm <LoginForm
autoComplete="off"
data-cy="login-form" data-cy="login-form"
className={authError ? 'pf-m-error' : ''} className={authError ? 'pf-m-error' : ''}
helperText={helperText} helperText={helperText}

View File

@@ -115,14 +115,6 @@ describe('<Login />', () => {
); );
}); });
test.only('form has autocomplete off', async () => {
let wrapper;
await act(async () => {
wrapper = mountWithContexts(<AWXLogin isAuthenticated={() => false} />);
});
expect(wrapper.find('form[autoComplete="off"]').length).toBe(1);
});
test('custom logo renders Brand component with correct src and alt', async () => { test('custom logo renders Brand component with correct src and alt', async () => {
let wrapper; let wrapper;
await act(async () => { await act(async () => {