Skip to content

Commit

Permalink
Feature/52 stateless apps without login (#75)
Browse files Browse the repository at this point in the history
* typo

* split story-queues for app and user

* Move org call to appTaskQueue

* start handling anonymous access

* moved org in unit-tests (causing them to fail)

* update selector (still mocked the final check)

* update interface

* fix typo

* update fetching of texts to work without profile

* use selector for allowAnonymous for fetchFormData

* add type for IApplicationLogic

* add/update tests

* support submit form data for anonymous

* small updates

* update test for submitFormDataSagas

* update version

* cleanup

* make sure standard texts are always fetched

* Fix small issues

* ImageComponent to use default lang for anonymous

* update selector

* profile icon should not render on anonymous

* code smells

* add tests for fetchTextResourcesSagas

* more tests + code smell

* more test coverage

* fix useless conditional warning

* fix mocking of api calls

* improve tests for fetchFormDataSagas

* Fix for infinite reload loop

* fix failing close modal button on anonymous

* fix method call in test

Co-authored-by: mjulstein <6736558+mjulstein@users.noreply.github.com>
  • Loading branch information
nkylstad and mjulstein authored May 23, 2022
1 parent a745fc4 commit 95cb7be
Show file tree
Hide file tree
Showing 43 changed files with 1,252 additions and 248 deletions.
1 change: 1 addition & 0 deletions src/altinn-app-frontend/__mocks__/initialStateMock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export function getInitialStateMock(customStates?: Partial<IRuntimeState>): IRun
dataTask: null,
infoTask: null,
stateless: null,
userTask: null,
},
textResources: {
resources: [
Expand Down
42 changes: 33 additions & 9 deletions src/altinn-app-frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { AltinnAppTheme } from 'altinn-shared/theme';
import ProcessWrapper from './shared/containers/ProcessWrapper';
import UnknownError from './features/instantiate/containers/UnknownError';
import PartySelection from './features/instantiate/containers/PartySelection';
import { startInitialAppTaskQueue } from './shared/resources/queue/queueSlice';
import { startInitialAppTaskQueue, startInitialUserTaskQueue} from './shared/resources/queue/queueSlice';
import { get } from './utils/networking';
import {
getEnvironmentLoginUrl,
Expand All @@ -14,6 +14,7 @@ import {
import { makeGetHasErrorsSelector } from './selectors/getErrors';
import Entrypoint from './features/entrypoint/Entrypoint';
import { useAppDispatch, useAppSelector } from './common/hooks';
import { makeGetAllowAnonymousSelector } from './selectors/getAllowAnonymous';

const theme = createTheme(AltinnAppTheme);

Expand All @@ -29,6 +30,11 @@ export const App = () => {
);
const lastRefreshTokenTimestamp = React.useRef(0);

const allowAnonymousSelector = makeGetAllowAnonymousSelector();
const allowAnonymous = useAppSelector(allowAnonymousSelector);

const [ready, setReady] = React.useState(false);

React.useEffect(() => {
function setUpEventListeners() {
window.addEventListener('mousemove', refreshJwtToken);
Expand Down Expand Up @@ -62,23 +68,41 @@ export const App = () => {
}
}

refreshJwtToken();
dispatch(startInitialAppTaskQueue());
setUpEventListeners();
if (allowAnonymous !== undefined) {
// Page is ready to be rendered once allowAnonymous value has been determined
setReady(true);
}

if (allowAnonymous === false) {
refreshJwtToken();
dispatch(startInitialUserTaskQueue());
setUpEventListeners();

return () => {
removeEventListeners();
};
}

}, [allowAnonymous, dispatch, appOidcProvider]);

return () => {
removeEventListeners();
};
}, [dispatch, appOidcProvider]);
React.useEffect(() => {
dispatch(startInitialAppTaskQueue());
}, [dispatch]);

if (hasApiErrors) {
return <UnknownError />;
}

if (!ready) {
return null;
}

return (
<MuiThemeProvider theme={theme}>
<Switch>
<Route path='/' exact={true} component={Entrypoint} />
<Route path='/' exact={true}>
<Entrypoint allowAnonymous={allowAnonymous} />
</Route>
<Route
path='/partyselection/:errorCode?'
exact={true}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const useStyles = makeStyles({

export function ImageComponent(props: IImageProps) {
const classes = useStyles();
const language = useAppSelector(state => state.profile.profile.profileSettingPreference.language);
const language = useAppSelector(state => state.profile.profile?.profileSettingPreference.language || 'nb');
const width = props.image.width || '100%';
const align = props.image.align || 'center';
const altText = props.getTextResourceAsString(props.textResourceBindings.altTextImg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,39 @@ describe('features > entrypoint > Entrypoint.tsx', () => {
});
});

it('should show loader while fetching data then start statelessQueue if stateless app with allowAnonymous', async () => {
const statelessApplication: IApplicationMetadata = {
...mockInitialState.applicationMetadata.applicationMetadata,
onEntry: {
show: 'stateless',
},
};
statelessApplication.dataTypes[0].appLogic.allowAnonymousOnStateless = true;
const mockStateWithStatelessApplication: IRuntimeState = {
...mockInitialState,
};
mockStateWithStatelessApplication.applicationMetadata.applicationMetadata =
statelessApplication;
mockStore = createStore(mockReducer, mockStateWithStatelessApplication);
mockStore.dispatch = jest.fn();

const rendered = render(
<Provider store={mockStore}>
<Entrypoint />
</Provider>,
);

const contentLoader = await rendered.findByText('Loading...');
expect(contentLoader).not.toBeNull();

// should have started the initialStatelessQueue
await waitFor(() => {
expect(mockStore.dispatch).toHaveBeenCalledWith({
type: 'queue/startInitialStatelessQueue',
});
});
});

it('should fetch active instances and display InstanceSelection.tsx if select-instance is configured', async () => {
const application: IApplicationMetadata = {
...mockInitialState.applicationMetadata.applicationMetadata,
Expand Down
12 changes: 6 additions & 6 deletions src/altinn-app-frontend/src/features/entrypoint/Entrypoint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ import InstanceSelection from '../instantiate/containers/InstanceSelection';
import MissingRolesError from '../instantiate/containers/MissingRolesError';
import NoValidPartiesError from '../instantiate/containers/NoValidPartiesError';

export default function Entrypoint() {
const applicationMetadata = useAppSelector(
(state) => state.applicationMetadata.applicationMetadata,
);
const selectedParty = useAppSelector((state) => state.party.selectedParty);
export default function Entrypoint({ allowAnonymous }: any) {
const [action, setAction] = React.useState<ShowTypes>(null);
const [partyValidation, setPartyValidation] = React.useState(null);
const [activeInstances, setActiveInstances] =
React.useState<ISimpleInstance[]>(null);
const applicationMetadata = useAppSelector(
(state) => state.applicationMetadata?.applicationMetadata,
);
const selectedParty = useAppSelector((state) => state.party.selectedParty);
const statelessLoading = useAppSelector((state) => state.isLoading.stateless);
const formDataError = useAppSelector((state) => state.formData.error);
const appName = useAppSelector(selectAppName);
Expand Down Expand Up @@ -148,7 +148,7 @@ export default function Entrypoint() {
}

// stateless view
if (partyValidation?.valid && isStatelessApp(applicationMetadata)) {
if (isStatelessApp(applicationMetadata) && (allowAnonymous || partyValidation?.valid)) {
if (statelessLoading === null) {
dispatch(startInitialStatelessQueue());
}
Expand Down
Loading

0 comments on commit 95cb7be

Please sign in to comment.