Skip to content

Commit

Permalink
chore(form-admin-app): use cache service for tags
Browse files Browse the repository at this point in the history
  • Loading branch information
tzuge committed Jan 23, 2025
1 parent 06c60b3 commit 557078c
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 31 deletions.
7 changes: 6 additions & 1 deletion apps/cache-service/src/cache/model/cacheTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,12 @@ export class CacheTarget implements Target {

const deleted = await this.provider.del(invalidateKey);
if (deleted) {
this.logger.info(`Invalidated cache entry for path '${path}' on event ${namespace}:${name}.`, {
this.logger.info(`Invalidated cache entry for path '${cachedPath}' on event ${namespace}:${name}.`, {
context: 'CacheTarget',
tenant: this.tenantId.toString(),
});
} else {
this.logger.debug(`No entries invalidated for path '${cachedPath}' on event ${namespace}:${name}.`, {
context: 'CacheTarget',
tenant: this.tenantId.toString(),
});
Expand Down
6 changes: 2 additions & 4 deletions apps/form-admin-app/src/app/containers/FormDefinitions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,8 @@ export const FormDefinitionRow: FunctionComponent<FormDefinitionRowProps> = ({
const tags = useSelector((state: AppState) => resourceTagsSelector(state, definition.urn));

useEffect(() => {
if (!Array.isArray(tags)) {
dispatch(getResourceTags({ urn: definition.urn }));
}
}, [dispatch, definition, tags]);
dispatch(getResourceTags({ urn: definition.urn }));
}, [dispatch, definition]);

return (
<tr key={definition.id}>
Expand Down
53 changes: 39 additions & 14 deletions apps/form-admin-app/src/app/state/directory.slice.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createAsyncThunk, createSelector, createSlice } from '@reduxjs/toolkit';
import axios from 'axios';
import { DIRECTORY_SERVICE_ID, PagedResults } from './types';
import { CACHE_SERVICE_ID, DIRECTORY_SERVICE_ID, PagedResults } from './types';
import { getAccessToken } from './user.slice';
import type { AppState } from './store';

Expand All @@ -11,6 +11,9 @@ export interface Resource {
name: string;
description: string;
type: string;
_embedded?: {
represents: unknown;
};
}

export interface Tag {
Expand Down Expand Up @@ -78,19 +81,30 @@ export const getTags = createAsyncThunk(

export const getTaggedResources = createAsyncThunk(
'directory/get-tagged-resources',
async ({ value, after }: { value: string; after?: string }, { getState, rejectWithValue }) => {
async (
{
value,
after,
includeRepresents,
type,
}: { value: string; after?: string; includeRepresents?: boolean; type?: string },
{ getState, rejectWithValue }
) => {
try {
const { config } = getState() as AppState;
const directoryServiceUrl = config.directory[DIRECTORY_SERVICE_ID];
const cacheServiceUrl = config.directory[CACHE_SERVICE_ID];
const accessToken = await getAccessToken();

// Request via cache service API.
const { data } = await axios.get<PagedResults<Resource>>(
new URL(`/resource/v1/tags/${value}/resources`, directoryServiceUrl).href,
new URL(`/cache/v1/cache/${DIRECTORY_SERVICE_ID}:resource-v1/tags/${value}/resources`, cacheServiceUrl).href,
{
headers: { Authorization: `Bearer ${accessToken}` },
params: {
top: 50,
after,
includeRepresents,
criteria: type && JSON.stringify({ typeEquals: type }),
},
}
);
Expand All @@ -114,17 +128,23 @@ export const getResourceTags = createAsyncThunk(
async ({ urn, after }: { urn: string; after?: string }, { getState, rejectWithValue }) => {
try {
const { config } = getState() as AppState;
const directoryServiceUrl = config.directory[DIRECTORY_SERVICE_ID];
const cacheServiceUrl = config.directory[CACHE_SERVICE_ID];
const accessToken = await getAccessToken();

const { data } = await axios.get<PagedResults<Tag>>(new URL('/resource/v1/tags', directoryServiceUrl).href, {
headers: { Authorization: `Bearer ${accessToken}` },
params: {
top: 50,
after,
resource: urn,
},
});
// Request via cache service API.
const { data } = await axios.get<PagedResults<Tag>>(
new URL(
`/cache/v1/cache/${DIRECTORY_SERVICE_ID}:resource-v1/resources/${encodeURIComponent(urn)}/tags`,
cacheServiceUrl
).href,
{
headers: { Authorization: `Bearer ${accessToken}` },
params: {
top: 50,
after,
},
}
);

return data;
} catch (err) {
Expand Down Expand Up @@ -231,7 +251,12 @@ const directorySlice = createSlice({
const resourceUrns = payload.results.map((result) => result.urn);
state.results = [...(meta.arg.after ? state.results : []), ...resourceUrns];
for (const result of payload.results) {
state.resources[result.urn] = result;
state.resources[result.urn] = {
urn: result.urn,
type: result.type,
name: result.name,
description: result.description,
};
state.tagResources[meta.arg.value] = [
...(meta.arg.after ? state.tagResources[meta.arg.value] || [] : []),
...resourceUrns,
Expand Down
18 changes: 13 additions & 5 deletions apps/form-admin-app/src/app/state/form.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,22 @@ export const loadDefinitions = createAsyncThunk(

try {
if (tag) {
const { results, page } = await dispatch(getTaggedResources({ value: dashify(tag), after })).unwrap();
const { results, page } = await dispatch(
getTaggedResources({ value: dashify(tag), after, includeRepresents: true, type: 'configuration' })
).unwrap();

const definitions = [];
for (const result of results.filter(({ type }) => type === 'configuration')) {
const [_, definitionId] = result.urn.match(/form-service\/([a-zA-Z0-9-_ ]{1,50})$/);
for (const { urn, _embedded } of results) {
// Note: Not all configuration resources are form definitions.
// Check the URN to confirm it's a form service namespace value.
const [_, definitionId] = urn.match(/form-service\/([a-zA-Z0-9-_ ]{1,50})$/);
if (definitionId) {
const definition = await dispatch(loadDefinition(definitionId)).unwrap();
definitions.push({ ...definition, urn: result.urn });
if (_embedded?.represents?.['latest']?.configuration) {
definitions.push({ ..._embedded?.represents['latest'].configuration, urn });
} else {
const definition = await dispatch(loadDefinition(definitionId)).unwrap();
definitions.push(definition);
}
}
}

Expand Down
1 change: 1 addition & 0 deletions apps/form-admin-app/src/app/state/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const FORM_SERVICE_ID = 'urn:ads:platform:form-service';
export const EXPORT_SERVICE_ID = 'urn:ads:platform:export-service';
export const CALENDAR_SERVICE_ID = 'urn:ads:platform:calendar-service';
export const DIRECTORY_SERVICE_ID = 'urn:ads:platform:directory-service';
export const CACHE_SERVICE_ID = 'urn:ads:platform:cache-service';

export interface PagedResults<T> {
results: T[];
Expand Down
2 changes: 1 addition & 1 deletion apps/form-admin-app/src/app/state/user.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ const userSlice = createSlice({
clearUser: (state) => {
state.user = undefined;
},
setSessionExpiry: (state, { payload }: { payload: { expiresAt: string, alert: boolean } }) => {
setSessionExpiry: (state, { payload }: { payload: { expiresAt: string; alert: boolean } }) => {
state.sessionExpiresAt = payload.expiresAt;
state.alertSessionExpiresAt = payload.alert ? payload.expiresAt : null;
},
Expand Down
13 changes: 7 additions & 6 deletions apps/form-admin-app/src/extension.shim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as ABGovReactComponents from '@abgov/react-components-new';
import axios from 'axios';
import React from 'react';
import * as StyledComponents from 'styled-components';
import { getAccessToken } from './app/state';
// import { getAccessToken } from './app/state';

// Set properties on global for dependencies so that extensions can externalize from their bundle.
// Extension bundles are added dynamically after the app bundles, so global values should be ready when extensions are loaded.
Expand All @@ -14,12 +14,13 @@ globalThis.React = React;
// e.g. config.externals = { 'styled-components': 'StyledComponents', ... }
globalThis.StyledComponents = StyledComponents;

// Note: not used since thunks add token explicitly.
// Prepare an axios interceptor for setting the bearer token in Authorization header.
axios.interceptors.request.use(async function (config) {
const token = await getAccessToken();
config.headers.Authorization = `Bearer ${token}`;
return config;
});
// axios.interceptors.request.use(async function (config) {
// const token = await getAccessToken();
// config.headers.Authorization = `Bearer ${token}`;
// return config;
// });

// e.g. config.externals = { 'axios': 'Axios', ... }
globalThis.Axios = axios;

0 comments on commit 557078c

Please sign in to comment.