mirror of
https://github.com/ansible/awx.git
synced 2026-05-14 12:57:40 -02:30
Wrap credential form submission code in useRequest to avoid the "unmounted component" errors we've seen if we try to push a new route onto the history but the coomponent is already unmounted.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useCallback, useState, useEffect } from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { PageSection, Card } from '@patternfly/react-core';
|
import { PageSection, Card } from '@patternfly/react-core';
|
||||||
import { CardBody } from '../../../components/Card';
|
import { CardBody } from '../../../components/Card';
|
||||||
@@ -11,14 +11,64 @@ import {
|
|||||||
CredentialsAPI,
|
CredentialsAPI,
|
||||||
} from '../../../api';
|
} from '../../../api';
|
||||||
import CredentialForm from '../shared/CredentialForm';
|
import CredentialForm from '../shared/CredentialForm';
|
||||||
|
import useRequest from '../../../util/useRequest';
|
||||||
|
|
||||||
function CredentialAdd({ me }) {
|
function CredentialAdd({ me }) {
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [credentialTypes, setCredentialTypes] = useState(null);
|
const [credentialTypes, setCredentialTypes] = useState(null);
|
||||||
const [formSubmitError, setFormSubmitError] = useState(null);
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
|
const {
|
||||||
|
error: submitError,
|
||||||
|
request: submitRequest,
|
||||||
|
result: credentialId,
|
||||||
|
} = useRequest(
|
||||||
|
useCallback(
|
||||||
|
async values => {
|
||||||
|
const { inputs, organization, ...remainingValues } = values;
|
||||||
|
const nonPluginInputs = {};
|
||||||
|
const pluginInputs = {};
|
||||||
|
Object.entries(inputs).forEach(([key, value]) => {
|
||||||
|
if (value.credential && value.inputs) {
|
||||||
|
pluginInputs[key] = value;
|
||||||
|
} else {
|
||||||
|
nonPluginInputs[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const {
|
||||||
|
data: { id: newCredentialId },
|
||||||
|
} = await CredentialsAPI.create({
|
||||||
|
user: (me && me.id) || null,
|
||||||
|
organization: (organization && organization.id) || null,
|
||||||
|
inputs: nonPluginInputs,
|
||||||
|
...remainingValues,
|
||||||
|
});
|
||||||
|
const inputSourceRequests = [];
|
||||||
|
Object.entries(pluginInputs).forEach(([key, value]) => {
|
||||||
|
inputSourceRequests.push(
|
||||||
|
CredentialInputSourcesAPI.create({
|
||||||
|
input_field_name: key,
|
||||||
|
metadata: value.inputs,
|
||||||
|
source_credential: value.credential.id,
|
||||||
|
target_credential: newCredentialId,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await Promise.all(inputSourceRequests);
|
||||||
|
|
||||||
|
return newCredentialId;
|
||||||
|
},
|
||||||
|
[me]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (credentialId) {
|
||||||
|
history.push(`/credentials/${credentialId}/details`);
|
||||||
|
}
|
||||||
|
}, [credentialId, history]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -42,45 +92,7 @@ function CredentialAdd({ me }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async values => {
|
const handleSubmit = async values => {
|
||||||
const { inputs, organization, ...remainingValues } = values;
|
await submitRequest(values);
|
||||||
const pluginInputs = [];
|
|
||||||
Object.entries(inputs).forEach(([key, value]) => {
|
|
||||||
if (value.credential && value.inputs) {
|
|
||||||
pluginInputs.push([key, value]);
|
|
||||||
delete inputs[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setFormSubmitError(null);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const {
|
|
||||||
data: { id: credentialId },
|
|
||||||
} = await CredentialsAPI.create({
|
|
||||||
user: (me && me.id) || null,
|
|
||||||
organization: (organization && organization.id) || null,
|
|
||||||
inputs: inputs || {},
|
|
||||||
...remainingValues,
|
|
||||||
});
|
|
||||||
const inputSourceRequests = [];
|
|
||||||
pluginInputs.forEach(([key, value]) => {
|
|
||||||
if (value.credential && value.inputs) {
|
|
||||||
inputSourceRequests.push(
|
|
||||||
CredentialInputSourcesAPI.create({
|
|
||||||
input_field_name: key,
|
|
||||||
metadata: value.inputs,
|
|
||||||
source_credential: value.credential.id,
|
|
||||||
target_credential: credentialId,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await Promise.all(inputSourceRequests);
|
|
||||||
const url = `/credentials/${credentialId}/details`;
|
|
||||||
history.push(`${url}`);
|
|
||||||
} catch (err) {
|
|
||||||
setFormSubmitError(err);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -113,7 +125,7 @@ function CredentialAdd({ me }) {
|
|||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
credentialTypes={credentialTypes}
|
credentialTypes={credentialTypes}
|
||||||
submitError={formSubmitError}
|
submitError={submitError}
|
||||||
/>
|
/>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useCallback, useState, useEffect } from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { object } from 'prop-types';
|
import { object } from 'prop-types';
|
||||||
|
|
||||||
@@ -11,15 +11,84 @@ import {
|
|||||||
import ContentError from '../../../components/ContentError';
|
import ContentError from '../../../components/ContentError';
|
||||||
import ContentLoading from '../../../components/ContentLoading';
|
import ContentLoading from '../../../components/ContentLoading';
|
||||||
import CredentialForm from '../shared/CredentialForm';
|
import CredentialForm from '../shared/CredentialForm';
|
||||||
|
import useRequest from '../../../util/useRequest';
|
||||||
|
|
||||||
function CredentialEdit({ credential, me }) {
|
function CredentialEdit({ credential, me }) {
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [credentialTypes, setCredentialTypes] = useState(null);
|
const [credentialTypes, setCredentialTypes] = useState(null);
|
||||||
const [inputSources, setInputSources] = useState(null);
|
const [inputSources, setInputSources] = useState({});
|
||||||
const [formSubmitError, setFormSubmitError] = useState(null);
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
|
const { error: submitError, request: submitRequest, result } = useRequest(
|
||||||
|
useCallback(
|
||||||
|
async (values, inputSourceMap) => {
|
||||||
|
const createAndUpdateInputSources = pluginInputs =>
|
||||||
|
Object.entries(pluginInputs).map(([fieldName, fieldValue]) => {
|
||||||
|
if (!inputSourceMap[fieldName]) {
|
||||||
|
return CredentialInputSourcesAPI.create({
|
||||||
|
input_field_name: fieldName,
|
||||||
|
metadata: fieldValue.inputs,
|
||||||
|
source_credential: fieldValue.credential.id,
|
||||||
|
target_credential: credential.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (fieldValue.touched) {
|
||||||
|
return CredentialInputSourcesAPI.update(
|
||||||
|
inputSourceMap[fieldName].id,
|
||||||
|
{
|
||||||
|
metadata: fieldValue.inputs,
|
||||||
|
source_credential: fieldValue.credential.id,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const destroyInputSources = inputs => {
|
||||||
|
const destroyRequests = [];
|
||||||
|
Object.values(inputSourceMap).forEach(inputSource => {
|
||||||
|
const { id, input_field_name } = inputSource;
|
||||||
|
if (!inputs[input_field_name]?.credential) {
|
||||||
|
destroyRequests.push(CredentialInputSourcesAPI.destroy(id));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return destroyRequests;
|
||||||
|
};
|
||||||
|
|
||||||
|
const { inputs, organization, ...remainingValues } = values;
|
||||||
|
const nonPluginInputs = {};
|
||||||
|
const pluginInputs = {};
|
||||||
|
Object.entries(inputs).forEach(([key, value]) => {
|
||||||
|
if (value.credential && value.inputs) {
|
||||||
|
pluginInputs[key] = value;
|
||||||
|
} else {
|
||||||
|
nonPluginInputs[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const [{ data }] = await Promise.all([
|
||||||
|
CredentialsAPI.update(credential.id, {
|
||||||
|
user: (me && me.id) || null,
|
||||||
|
organization: (organization && organization.id) || null,
|
||||||
|
inputs: nonPluginInputs,
|
||||||
|
...remainingValues,
|
||||||
|
}),
|
||||||
|
...destroyInputSources(inputs),
|
||||||
|
]);
|
||||||
|
await Promise.all(createAndUpdateInputSources(pluginInputs));
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
[credential.id, me]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (result) {
|
||||||
|
history.push(`/credentials/${result.id}/details`);
|
||||||
|
}
|
||||||
|
}, [result, history]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -54,67 +123,11 @@ function CredentialEdit({ credential, me }) {
|
|||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
const url = `/credentials/${credential.id}/details`;
|
const url = `/credentials/${credential.id}/details`;
|
||||||
|
|
||||||
history.push(`${url}`);
|
history.push(`${url}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createAndUpdateInputSources = pluginInputs =>
|
|
||||||
Object.entries(pluginInputs).map(([fieldName, fieldValue]) => {
|
|
||||||
if (!inputSources[fieldName]) {
|
|
||||||
return CredentialInputSourcesAPI.create({
|
|
||||||
input_field_name: fieldName,
|
|
||||||
metadata: fieldValue.inputs,
|
|
||||||
source_credential: fieldValue.credential.id,
|
|
||||||
target_credential: credential.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (fieldValue.touched) {
|
|
||||||
return CredentialInputSourcesAPI.update(inputSources[fieldName].id, {
|
|
||||||
metadata: fieldValue.inputs,
|
|
||||||
source_credential: fieldValue.credential.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
const destroyInputSources = inputs => {
|
|
||||||
const destroyRequests = [];
|
|
||||||
Object.values(inputSources).forEach(inputSource => {
|
|
||||||
const { id, input_field_name } = inputSource;
|
|
||||||
if (!inputs[input_field_name]?.credential) {
|
|
||||||
destroyRequests.push(CredentialInputSourcesAPI.destroy(id));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return destroyRequests;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async values => {
|
const handleSubmit = async values => {
|
||||||
const { inputs, organization, ...remainingValues } = values;
|
await submitRequest(values, inputSources);
|
||||||
const pluginInputs = {};
|
|
||||||
Object.entries(inputs).forEach(([key, value]) => {
|
|
||||||
if (value.credential && value.inputs) {
|
|
||||||
pluginInputs[key] = value;
|
|
||||||
delete inputs[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setFormSubmitError(null);
|
|
||||||
try {
|
|
||||||
await Promise.all([
|
|
||||||
CredentialsAPI.update(credential.id, {
|
|
||||||
user: (me && me.id) || null,
|
|
||||||
organization: (organization && organization.id) || null,
|
|
||||||
inputs: inputs || {},
|
|
||||||
...remainingValues,
|
|
||||||
}),
|
|
||||||
...destroyInputSources(pluginInputs),
|
|
||||||
]);
|
|
||||||
await Promise.all(createAndUpdateInputSources(pluginInputs));
|
|
||||||
const url = `/credentials/${credential.id}/details`;
|
|
||||||
history.push(`${url}`);
|
|
||||||
} catch (err) {
|
|
||||||
setFormSubmitError(err);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -133,7 +146,7 @@ function CredentialEdit({ credential, me }) {
|
|||||||
credential={credential}
|
credential={credential}
|
||||||
credentialTypes={credentialTypes}
|
credentialTypes={credentialTypes}
|
||||||
inputSources={inputSources}
|
inputSources={inputSources}
|
||||||
submitError={formSubmitError}
|
submitError={submitError}
|
||||||
/>
|
/>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ function CredentialPluginField(props) {
|
|||||||
isValid={isValid}
|
isValid={isValid}
|
||||||
label={label}
|
label={label}
|
||||||
>
|
>
|
||||||
{field.value.credential ? (
|
{field?.value?.credential ? (
|
||||||
<CredentialPluginSelected
|
<CredentialPluginSelected
|
||||||
credential={field.value.credential}
|
credential={field?.value?.credential}
|
||||||
onClearPlugin={() => helpers.setValue('')}
|
onClearPlugin={() => helpers.setValue('')}
|
||||||
onEditPlugin={() => setShowPluginWizard(true)}
|
onEditPlugin={() => setShowPluginWizard(true)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user