OK thanks for the input, I was able to get everything going.
@pturmel’s pointer to 8.3 File Upload route permissions issue - #8 by pturmel was really key for posting – i.e., whenever you use PermissionType.WRITE. I was able to access the ReduxRootState exactly as he did. Otherwise you will get a non-descript 403 Forbidden error.
For those following along and looking for more examples of what works, here’s what my mountRouteHandlers() looked like in my GatewayHook class:
public void mountRouteHandlers(RouteGroup routes) {
routes.newRoute("/settings")
.type(RouteGroup.TYPE_JSON)
.handler(this::getSettingsJson)
.method(HttpMethod.GET)
.requirePermission(PermissionType.READ)
.mount();
routes.newRoute("/settings")
.type(RouteGroup.TYPE_JSON)
.handler(this::putSettingsJson)
.method(HttpMethod.PUT)
.requirePermission(PermissionType.WRITE)
.mount();
}
and here’s what my Settings.service.ts looks like on the frontend:
export const getSettings = async (): Promise<SeeqSettings> => {
const response = await fetch("/data/seeq/settings", {
method: "GET",
headers: {
Accept: "application/json",
},
});
if (!response.ok) {
throw new Error(`Failed to fetch settings: ${response.statusText}`);
}
const payload: SeeqSettingsPayload = await response.json();
// Flatten the structure for UI convenience
return {
enabled: payload.enabled,
...payload.config,
};
};
export const updateSettings = async (
settings: Partial<SeeqSettings>,
csrfToken: string
): Promise<UpdateSettingsResponse> => {
// Extract enabled and wrap the rest in config
const { enabled, ...config } = settings;
const payload: Partial<SeeqSettingsPayload> = {
enabled,
config: config as IgnitionModuleConfigurationV2,
};
const response = await fetch("/data/seeq/settings", {
method: "PUT",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"X-CSRF-Token": csrfToken,
},
body: JSON.stringify(payload),
});
const data: UpdateSettingsResponse = await response.json();
// Check if the operation was successful
if (!data.success) {
throw new Error(data.message || "Failed to update settings");
}
return data;
};
where csrfToken was retrieved via this code:
const csrfToken = useSelector(
(state: ReduxRootState) => state.userSession.csrfToken || ""
);