teamid
This commit is contained in:
1 parent
2b0c523047
commit
940247b7bf
18 files changed
+109
-151
No files matched your search
@@ -85,7 +85,7 @@ const Search = ({
|
||||
const enhancedOptions = React.useMemo(() => {
|
||||
return multiple && isAdorned
|
||||
? [
|
||||
{ [filteredBy]: t("selectAll"), isSelectAll: true, _id: "select_all" },
|
||||
{ [filteredBy]: t("selectAll"), isSelectAll: true, _id: "select_all", id: "select_all" },
|
||||
...options,
|
||||
]
|
||||
: options;
|
||||
@@ -93,7 +93,7 @@ const Search = ({
|
||||
const isOptionSelected = (option) => {
|
||||
if (!multiple && !isAdorned) return false;
|
||||
if (Array.isArray(value)) {
|
||||
return value.some((item) => item._id === option._id);
|
||||
return value.some((item) => (item._id ?? item.id) === (option._id ?? option.id));
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -145,7 +145,7 @@ const Search = ({
|
||||
disableClearable
|
||||
options={enhancedOptions}
|
||||
getOptionLabel={(option) => option[filteredBy]}
|
||||
isOptionEqualToValue={(option, value) => option._id === value._id} // Compare by unique identifier
|
||||
isOptionEqualToValue={(option, value) => (option._id ?? option.id) === (value?._id ?? value?.id)} // Compare by unique identifier
|
||||
renderInput={(params) => (
|
||||
<FieldWrapper
|
||||
label={label}
|
||||
@@ -206,7 +206,7 @@ const Search = ({
|
||||
return filtered;
|
||||
}}
|
||||
getOptionKey={(option) => {
|
||||
return option._id;
|
||||
return option._id ?? option.id;
|
||||
}}
|
||||
renderOption={(props, option) => {
|
||||
const { key, ...optionProps } = props;
|
||||
|
||||
@@ -62,6 +62,7 @@ const Select = ({
|
||||
fieldWrapperSx = {},
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const getItemValue = (item) => item?._id ?? item?.id;
|
||||
const itemStyles = {
|
||||
fontSize: "var(--env-var-font-size-medium)",
|
||||
color: theme.palette.primary.contrastTextTertiary,
|
||||
@@ -123,7 +124,7 @@ const Select = ({
|
||||
...sx,
|
||||
}}
|
||||
renderValue={(selected) => {
|
||||
const selectedItem = items.find((item) => item._id === selected);
|
||||
const selectedItem = items.find((item) => getItemValue(item) === selected);
|
||||
const displayName = selectedItem ? selectedItem.name : placeholder;
|
||||
return (
|
||||
<Typography
|
||||
@@ -152,17 +153,25 @@ const Select = ({
|
||||
{placeholder}
|
||||
</MenuItem>
|
||||
)}
|
||||
{items.map((item) => (
|
||||
<MenuItem
|
||||
value={item._id}
|
||||
key={`${id}-${item._id}`}
|
||||
sx={{
|
||||
...itemStyles,
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
{items
|
||||
.map((item) => {
|
||||
const itemValue = getItemValue(item);
|
||||
if (itemValue === undefined || itemValue === null) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<MenuItem
|
||||
value={itemValue}
|
||||
key={`${id}-${itemValue}`}
|
||||
sx={{
|
||||
...itemStyles,
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
);
|
||||
})
|
||||
.filter(Boolean)}
|
||||
</MuiSelect>
|
||||
</FieldWrapper>
|
||||
);
|
||||
@@ -179,8 +188,8 @@ Select.propTypes = {
|
||||
.isRequired,
|
||||
items: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool])
|
||||
.isRequired,
|
||||
_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]),
|
||||
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]),
|
||||
|
||||
name: PropTypes.string.isRequired,
|
||||
})
|
||||
|
||||
@@ -100,22 +100,9 @@ const useFetchMonitorsWithChecks = ({
|
||||
return [monitors, count, isLoading, networkError];
|
||||
};
|
||||
|
||||
const useFetchMonitorsByTeamId = ({
|
||||
types,
|
||||
limit,
|
||||
page,
|
||||
rowsPerPage,
|
||||
filter,
|
||||
field,
|
||||
order,
|
||||
checkOrder,
|
||||
normalize,
|
||||
status,
|
||||
updateTrigger,
|
||||
}) => {
|
||||
const useFetchMonitorsByTeamId = ({ types, filter, updateTrigger }) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [monitors, setMonitors] = useState(undefined);
|
||||
const [summary, setSummary] = useState(undefined);
|
||||
const [networkError, setNetworkError] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -123,20 +110,11 @@ const useFetchMonitorsByTeamId = ({
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const res = await networkService.getMonitorsByTeamId({
|
||||
limit,
|
||||
types,
|
||||
page,
|
||||
rowsPerPage,
|
||||
filter,
|
||||
field,
|
||||
order,
|
||||
checkOrder,
|
||||
status,
|
||||
normalize,
|
||||
});
|
||||
if (res?.data?.data?.filteredMonitors) {
|
||||
setMonitors(res.data.data.filteredMonitors);
|
||||
setSummary(res.data.data.summary);
|
||||
if (res?.data?.data) {
|
||||
setMonitors(res.data.data);
|
||||
}
|
||||
} catch (error) {
|
||||
setNetworkError(true);
|
||||
@@ -150,18 +128,10 @@ const useFetchMonitorsByTeamId = ({
|
||||
fetchMonitors();
|
||||
}, [
|
||||
types,
|
||||
limit,
|
||||
page,
|
||||
rowsPerPage,
|
||||
filter,
|
||||
field,
|
||||
order,
|
||||
updateTrigger,
|
||||
checkOrder,
|
||||
normalize,
|
||||
status,
|
||||
]);
|
||||
return [monitors, summary, isLoading, networkError];
|
||||
return [monitors, isLoading, networkError];
|
||||
};
|
||||
|
||||
const useFetchStatsByMonitorId = ({
|
||||
|
||||
@@ -34,7 +34,7 @@ const Incidents2 = () => {
|
||||
setUpdateTrigger((prev) => !prev);
|
||||
};
|
||||
|
||||
const [monitors, , isLoadingMonitors, monitorsNetworkError] = useFetchMonitorsByTeamId(
|
||||
const [monitors, isLoadingMonitors, monitorsNetworkError] = useFetchMonitorsByTeamId(
|
||||
{}
|
||||
);
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ const MonitorListItem = ({ monitor, onDelete }) => {
|
||||
|
||||
MonitorListItem.propTypes = {
|
||||
monitor: PropTypes.shape({
|
||||
_id: PropTypes.string.isRequired,
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
@@ -33,7 +33,7 @@ MonitorListItem.propTypes = {
|
||||
const MonitorList = ({ selectedMonitors, setSelectedMonitors }) => {
|
||||
const onDelete = (monitorToDelete) => {
|
||||
const newMonitors = selectedMonitors.filter(
|
||||
(monitor) => monitor._id !== monitorToDelete._id
|
||||
(monitor) => monitor.id !== monitorToDelete.id
|
||||
);
|
||||
setSelectedMonitors(newMonitors);
|
||||
};
|
||||
@@ -47,7 +47,7 @@ const MonitorList = ({ selectedMonitors, setSelectedMonitors }) => {
|
||||
>
|
||||
{selectedMonitors?.map((monitor) => (
|
||||
<MonitorListItem
|
||||
key={monitor._id}
|
||||
key={monitor.id}
|
||||
monitor={monitor}
|
||||
onDelete={onDelete}
|
||||
/>
|
||||
@@ -59,7 +59,7 @@ const MonitorList = ({ selectedMonitors, setSelectedMonitors }) => {
|
||||
MonitorList.propTypes = {
|
||||
selectedMonitors: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
_id: PropTypes.string.isRequired,
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
})
|
||||
).isRequired,
|
||||
|
||||
@@ -22,6 +22,12 @@ const useMaintenanceActions = () => {
|
||||
weekly: MS_PER_DAY * 7,
|
||||
};
|
||||
const handleSubmitForm = async (maintenanceWindowId, form) => {
|
||||
const toStringId = (monitor) => {
|
||||
if (!monitor) {
|
||||
return "";
|
||||
}
|
||||
return monitor._id ?? monitor.id ?? "";
|
||||
};
|
||||
const start = dayjs(form.startDate)
|
||||
.set("hour", form.startTime.hour())
|
||||
.set("minute", form.startTime.minute());
|
||||
@@ -33,7 +39,7 @@ const useMaintenanceActions = () => {
|
||||
const repeat = REPEAT_LOOKUP[form.repeat];
|
||||
|
||||
const submit = {
|
||||
monitors: form.monitors.map((monitor) => monitor._id),
|
||||
monitors: form.monitors.map((monitor) => toStringId(monitor)).filter(Boolean),
|
||||
name: form.name,
|
||||
start: start.toISOString(),
|
||||
end: end.toISOString(),
|
||||
|
||||
@@ -41,7 +41,7 @@ const useMaintenanceData = () => {
|
||||
limit: null,
|
||||
types: ["http", "ping", "pagespeed", "port"],
|
||||
});
|
||||
const fetchedMonitors = response.data.data.monitors;
|
||||
const fetchedMonitors = response.data.data;
|
||||
return fetchedMonitors;
|
||||
};
|
||||
|
||||
@@ -55,7 +55,7 @@ const useMaintenanceData = () => {
|
||||
const endTime = dayjs(end);
|
||||
const durationInMs = endTime.diff(startTime, "milliseconds").toString();
|
||||
const { duration, durationUnit } = getDurationAndUnit(durationInMs);
|
||||
const monitor = monitorList.find((monitor) => monitor._id === monitorId);
|
||||
const monitor = monitorList.find((monitor) => (monitor._id ?? monitor.id) === monitorId);
|
||||
const maintenanceWindowInformation = {
|
||||
name,
|
||||
repeat: REVERSE_REPEAT_LOOKUP[repeat],
|
||||
|
||||
@@ -108,6 +108,7 @@ const CreateMaintenance = () => {
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (hasValidationErrors(form, maintenanceWindowValidation, setErrors)) return;
|
||||
|
||||
const request = handleSubmitForm(maintenanceWindowId, form);
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
@@ -43,7 +43,7 @@ const MonitorListItem = ({
|
||||
const MonitorList = ({ selectedMonitors, setSelectedMonitors }) => {
|
||||
const onDelete = (monitorToDelete) => {
|
||||
const newMonitors = selectedMonitors.filter(
|
||||
(monitor) => monitor._id !== monitorToDelete._id
|
||||
(monitor) => monitor.id !== monitorToDelete.id
|
||||
);
|
||||
setSelectedMonitors(newMonitors);
|
||||
};
|
||||
@@ -80,8 +80,8 @@ const MonitorList = ({ selectedMonitors, setSelectedMonitors }) => {
|
||||
>
|
||||
{selectedMonitors?.map((monitor, index) => (
|
||||
<Draggable
|
||||
key={monitor._id}
|
||||
draggableId={monitor._id}
|
||||
key={monitor.id}
|
||||
draggableId={monitor.id}
|
||||
index={index}
|
||||
>
|
||||
{(provided, snapshot) => (
|
||||
|
||||
@@ -24,7 +24,7 @@ const Content = ({
|
||||
// Handlers
|
||||
const handleMonitorsChange = (selectedMonitors) => {
|
||||
handleFormChange({
|
||||
target: { name: "monitors", value: selectedMonitors.map((monitor) => monitor._id) },
|
||||
target: { name: "monitors", value: selectedMonitors.map((monitor) => monitor.id) },
|
||||
});
|
||||
setSelectedMonitors(selectedMonitors);
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ const useMonitorsFetch = () => {
|
||||
limit: null, // donot return any checks for the monitors
|
||||
types: ["http", "ping", "port", "game"], // include game servers in status page monitor selection
|
||||
});
|
||||
setMonitors(response.data.data.monitors);
|
||||
setMonitors(response.data.data);
|
||||
} catch (error) {
|
||||
setNetworkError(true);
|
||||
createToast({ body: error.message });
|
||||
|
||||
@@ -62,7 +62,6 @@ const CreateStatusPage = () => {
|
||||
useStatusPageFetch(isCreate, url);
|
||||
const [deleteStatusPage, isDeleting] = useStatusPageDelete(fetchStatusPage, url);
|
||||
|
||||
console.log(JSON.stringify(form, null, 2));
|
||||
// Handlers
|
||||
const handleFormChange = (e) => {
|
||||
let { type, name, value, checked } = e.target;
|
||||
@@ -144,6 +143,8 @@ const CreateStatusPage = () => {
|
||||
...form,
|
||||
logo: { type: form.logo?.type ?? null, size: form.logo?.size ?? null },
|
||||
};
|
||||
|
||||
console.log(toSubmit);
|
||||
const { error } = statusPageValidation.validate(toSubmit, {
|
||||
abortEarly: false,
|
||||
});
|
||||
@@ -205,7 +206,7 @@ const CreateStatusPage = () => {
|
||||
companyName: statusPage?.companyName,
|
||||
isPublished: statusPage?.isPublished,
|
||||
timezone: statusPage?.timezone,
|
||||
monitors: statusPageMonitors.map((monitor) => monitor._id),
|
||||
monitors: statusPageMonitors.map((monitor) => monitor.id),
|
||||
color: statusPage?.color,
|
||||
logo: newLogo,
|
||||
showCharts: statusPage?.showCharts ?? true,
|
||||
|
||||
@@ -28,14 +28,14 @@ const MonitorsList = ({
|
||||
const status = determineState(monitor);
|
||||
return (
|
||||
<Stack
|
||||
key={monitor._id}
|
||||
key={monitor.id}
|
||||
width="100%"
|
||||
gap={theme.spacing(10)}
|
||||
margin="0 auto"
|
||||
maxWidth="95%"
|
||||
>
|
||||
<Host
|
||||
key={monitor._id}
|
||||
key={monitor.id}
|
||||
url={monitor.url}
|
||||
title={monitor.name}
|
||||
percentageColor={monitor.percentageColor}
|
||||
|
||||
@@ -144,20 +144,15 @@ class NetworkService {
|
||||
*/
|
||||
|
||||
async getMonitorsByTeamId(config) {
|
||||
const { limit, types, page, rowsPerPage, filter, field, order } = config;
|
||||
const { types, filter } = config ?? {};
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (limit) params.append("limit", limit);
|
||||
if (types) {
|
||||
types.forEach((type) => {
|
||||
params.append("type", type);
|
||||
});
|
||||
}
|
||||
if (page) params.append("page", page);
|
||||
if (rowsPerPage) params.append("rowsPerPage", rowsPerPage);
|
||||
if (filter) params.append("filter", filter);
|
||||
if (field) params.append("field", field);
|
||||
if (order) params.append("order", order);
|
||||
if (filter !== undefined && filter !== null && filter !== "") params.append("filter", filter);
|
||||
|
||||
return this.axiosInstance.get(`/monitors/team?${params.toString()}`, {
|
||||
headers: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
getMonitorByIdQueryValidation,
|
||||
getMonitorsByTeamIdParamValidation,
|
||||
getMonitorsByTeamIdQueryValidation,
|
||||
getMonitorsWithChecksQueryValidation,
|
||||
createMonitorBodyValidation,
|
||||
editMonitorBodyValidation,
|
||||
pauseMonitorParamValidation,
|
||||
@@ -317,10 +318,10 @@ class MonitorController {
|
||||
await getMonitorsByTeamIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorsByTeamIdQueryValidation.validateAsync(req.query);
|
||||
|
||||
let { limit, type, page, rowsPerPage, filter, field, order } = req.query;
|
||||
const { type, filter } = req.query;
|
||||
const teamId = req?.user?.teamId;
|
||||
|
||||
const monitors = await this.monitorService.getMonitorsByTeamId({ teamId, limit, type, page, rowsPerPage, filter, field, order });
|
||||
const monitors = await this.monitorService.getMonitorsByTeamId({ teamId, type, filter });
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
@@ -358,7 +359,7 @@ class MonitorController {
|
||||
getMonitorsWithChecksByTeamId = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
await getMonitorsByTeamIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorsByTeamIdQueryValidation.validateAsync(req.query);
|
||||
await getMonitorsWithChecksQueryValidation.validateAsync(req.query);
|
||||
|
||||
const explain = req?.query?.explain;
|
||||
let { limit, type, page, rowsPerPage, filter, field, order } = req.query;
|
||||
|
||||
@@ -289,47 +289,33 @@ class MonitorModule {
|
||||
}
|
||||
};
|
||||
|
||||
getMonitorsByTeamId = async ({ limit, type, page, rowsPerPage, filter, field, order, teamId }) => {
|
||||
limit = parseInt(limit);
|
||||
page = parseInt(page);
|
||||
rowsPerPage = parseInt(rowsPerPage);
|
||||
if (field === undefined) {
|
||||
field = "name";
|
||||
order = "asc";
|
||||
}
|
||||
// Build match stage
|
||||
const matchStage = { teamId: new this.ObjectId(teamId) };
|
||||
if (type !== undefined) {
|
||||
matchStage.type = Array.isArray(type) ? { $in: type } : type;
|
||||
}
|
||||
|
||||
const summaryResult = await this.Monitor.aggregate(buildMonitorSummaryByTeamIdPipeline({ matchStage }));
|
||||
const summary = summaryResult[0];
|
||||
|
||||
const monitors = await this.Monitor.aggregate(buildMonitorsByTeamIdPipeline({ matchStage, field, order }));
|
||||
|
||||
const filteredMonitors = await this.Monitor.aggregate(
|
||||
buildFilteredMonitorsByTeamIdPipeline({
|
||||
matchStage,
|
||||
filter,
|
||||
page,
|
||||
rowsPerPage,
|
||||
field,
|
||||
order,
|
||||
limit,
|
||||
type,
|
||||
})
|
||||
);
|
||||
|
||||
const normalizedFilteredMonitors = filteredMonitors.map((monitor) => {
|
||||
if (!monitor.checks) {
|
||||
return monitor;
|
||||
getMonitorsByTeamId = async ({ teamId, type, filter }) => {
|
||||
try {
|
||||
const matchStage = { teamId: new this.ObjectId(teamId) };
|
||||
if (type !== undefined) {
|
||||
matchStage.type = Array.isArray(type) ? { $in: type } : type;
|
||||
}
|
||||
monitor.checks = this.NormalizeData(monitor.checks, 10, 100);
|
||||
return monitor;
|
||||
});
|
||||
|
||||
return { summary, monitors, filteredMonitors: normalizedFilteredMonitors };
|
||||
if (filter !== undefined && filter !== null && filter !== "") {
|
||||
matchStage.$or = [{ name: { $regex: filter, $options: "i" } }, { url: { $regex: filter, $options: "i" } }];
|
||||
}
|
||||
const monitors = await this.Monitor.find(matchStage)
|
||||
.sort({ name: 1 })
|
||||
.select({
|
||||
_id: 1,
|
||||
name: 1,
|
||||
type: 1,
|
||||
url: 1,
|
||||
status: 1,
|
||||
isActive: 1,
|
||||
teamId: 1,
|
||||
})
|
||||
.lean();
|
||||
return monitors;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMonitorsByTeamId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
getMonitorsAndSummaryByTeamId = async ({ type, explain, teamId }) => {
|
||||
|
||||
@@ -323,34 +323,10 @@ export class MonitorService implements IMonitorService {
|
||||
return monitor;
|
||||
};
|
||||
|
||||
getMonitorsByTeamId = async ({
|
||||
teamId,
|
||||
limit,
|
||||
type,
|
||||
page,
|
||||
rowsPerPage,
|
||||
filter,
|
||||
field,
|
||||
order,
|
||||
}: {
|
||||
teamId: string;
|
||||
limit?: number;
|
||||
type?: string | string[];
|
||||
page?: number;
|
||||
rowsPerPage?: number;
|
||||
filter?: string;
|
||||
field?: string;
|
||||
order?: "asc" | "desc";
|
||||
}): Promise<any> => {
|
||||
const monitors = await this.db.monitorModule.getMonitorsByTeamId({
|
||||
limit,
|
||||
getMonitorsByTeamId = async ({ teamId, type, filter }: { teamId: string; type?: MonitorType | MonitorType[]; filter?: string }): Promise<any> => {
|
||||
const monitors = await this.monitorsRepository.findByTeamId(teamId, {
|
||||
type,
|
||||
page,
|
||||
rowsPerPage,
|
||||
filter,
|
||||
field,
|
||||
order,
|
||||
teamId,
|
||||
});
|
||||
return monitors;
|
||||
};
|
||||
|
||||
@@ -120,18 +120,30 @@ const getMonitorByIdQueryValidation = joi.object({
|
||||
const getMonitorsByTeamIdParamValidation = joi.object({});
|
||||
|
||||
const getMonitorsByTeamIdQueryValidation = joi.object({
|
||||
limit: joi.number(),
|
||||
type: joi
|
||||
.alternatives()
|
||||
.try(
|
||||
joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port", "game"),
|
||||
joi.array().items(joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port", "game"))
|
||||
),
|
||||
page: joi.number(),
|
||||
rowsPerPage: joi.number(),
|
||||
filter: joi.string(),
|
||||
field: joi.string(),
|
||||
order: joi.string().valid("asc", "desc"),
|
||||
filter: joi.string().allow("", null),
|
||||
});
|
||||
|
||||
const getMonitorsWithChecksQueryValidation = joi.object({
|
||||
limit: joi.number().integer().min(1).max(100).optional(),
|
||||
page: joi.number().integer().min(0).optional(),
|
||||
rowsPerPage: joi.number().integer().min(1).max(100).optional(),
|
||||
filter: joi.string().allow("", null).optional(),
|
||||
field: joi.string().optional(),
|
||||
order: joi.string().valid("asc", "desc").optional(),
|
||||
type: joi
|
||||
.alternatives()
|
||||
.try(
|
||||
joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port", "game"),
|
||||
joi.array().items(joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port", "game"))
|
||||
)
|
||||
.optional(),
|
||||
explain: joi.boolean().optional(),
|
||||
});
|
||||
|
||||
const getCertificateParamValidation = joi.object({
|
||||
@@ -713,6 +725,7 @@ export {
|
||||
getMonitorByIdQueryValidation,
|
||||
getMonitorsByTeamIdParamValidation,
|
||||
getMonitorsByTeamIdQueryValidation,
|
||||
getMonitorsWithChecksQueryValidation,
|
||||
getHardwareDetailsByIdParamValidation,
|
||||
getHardwareDetailsByIdQueryValidation,
|
||||
getCertificateParamValidation,
|
||||
|
||||
Reference in new issue
Block a user