Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React router 6 #53

Merged
merged 3 commits into from
Jan 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ It is dedicated to be deployed as a module of [openimis-fe_js](https://github.co

Note: depends on the selected calendar (Gregorian vs. Nepali)

### history
### navigation

- `withHistory`: helper to inject history to any openIMIS component (allow navigation)
- `historyPush`: helper to push a new route to openIMIS navigation
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
},
"peerDependency": {
"react-intl": "^5.8.1",
"react-helmet": "*"
"react-helmet": "*",
"react-router-dom": "^6.2.1",
"react-router": "^6.2.1"
},
"devDependencies": {
"@babel/cli": "^7.8.4",
Expand All @@ -37,7 +39,8 @@
"prettier": "^2.3.2",
"prop-types": "^15.7.2",
"react-autosuggest": "^10.0.2",
"react-router-dom": "^5.2.0",
"react-router-dom": "^6.2.1",
"react-router": "^6.2.1",
"redux": "^4.0.5",
"redux-api-middleware": "^3.2.1",
"rollup": "^2.10.0"
Expand Down
259 changes: 206 additions & 53 deletions src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const ROLE_FULL_PROJECTION = () => [
"validityTo",
];

import { getRefreshToken, setRefreshToken } from "./helpers/auth";

const ROLERIGHT_FULL_PROJECTION = () => ["rightId"];

const LANGUAGE_FULL_PROJECTION = () => ["name", "code", "sortOrder"];
Expand All @@ -33,10 +35,6 @@ export function apiHeaders() {
let headers = {
"Content-Type": "application/json",
};
if (process.env.NODE_ENV === "development") {
headers["remote-user"] = "Admin";
// headers['remote-user'] = "user";
}
return headers;
}

Expand All @@ -53,7 +51,7 @@ export function journalize(mutation, meta) {
};
}

export function graphql(payload, type, params = {}) {
export function graphql(payload, type = "GRAPHQL_QUERY", params = {}) {
let req = type + "_REQ";
let resp = type + "_RESP";
let err = type + "_ERR";
Expand All @@ -62,11 +60,10 @@ export function graphql(payload, type, params = {}) {
}
return async (dispatch) => {
try {
const response = await dispatch({
[RSAA]: {
const response = await dispatch(
fetch({
endpoint: `${baseApiUrl}/graphql`,
method: "POST",
headers: apiHeaders(),
body: JSON.stringify({ query: payload }),
types: [
{
Expand All @@ -82,8 +79,8 @@ export function graphql(payload, type, params = {}) {
meta: params,
},
],
},
});
}),
);
if (response.error) {
dispatch(coreAlert(formatServerError(response.payload)));
}
Expand All @@ -94,44 +91,38 @@ export function graphql(payload, type, params = {}) {
};
}

export function graphqlWithVariables(operation, variables, type, params = {}) {
let req = type + "_REQ";
let resp = type + "_RESP";
let err = type + "_ERR";
export function graphqlWithVariables(operation, variables, type = "GRAPHQL_QUERY", params = {}) {
let req, resp, err;
if (Array.isArray(type)) {
[req, resp, err] = type;
} else {
req = type + "_REQ";
resp = type + "_RESP";
err = type + "_ERR";
}
return async (dispatch) => {
try {
const response = await dispatch({
[RSAA]: {
endpoint: `${baseApiUrl}/graphql`,
method: "POST",
headers: apiHeaders(),
body: JSON.stringify({ query: operation, variables }),
types: [
{
type: req,
meta: params,
},
{
type: resp,
meta: params,
},
{
type: err,
meta: params,
},
],
},
});
if (response.error) {
dispatch(coreAlert(formatServerError(response.payload)));
}
return response;
} catch (err) {
console.error(err);
}
const response = await dispatch(
fetch({
endpoint: `${baseApiUrl}/graphql`,
method: "POST",
body: JSON.stringify({ query: operation, variables }),
types: [
{
type: req,
meta: params,
},
{
type: resp,
meta: params,
},
{
type: err,
meta: params,
},
],
}),
);
return response;
};
}

Expand All @@ -150,27 +141,189 @@ export function prepareMutation(operation, input, params = {}) {
return { operation, variables, clientMutationId: params.clientMutationId };
}

export function graphqlMutation(mutation, variables, type = "CORE_TRIGGER_MUTATION", params = {}) {
export function waitForMutation(clientMutationId) {
return async (dispatch) => {
let attempts = 0;
let res;
do {
if (res) {
await new Promise((resolve) => setTimeout(resolve, 100 * attempts));
}
const response = await dispatch(
graphqlWithVariables(
`
query ($clientMutationId: String) {
mutationLogs (clientMutationId: $clientMutationId) {
edges {
node {
status
clientMutationId
jsonContent
error
}
}
}
}
`,
{ clientMutationId },
),
);
if (response.error) {
return null;
}
res = response.payload.data.mutationLogs?.edges[0]?.node;
} while ((!res || res.status === 0) && attempts++ < 10);
if (res && res.status === 1 && res.error) {
res.error = JSON.parse(res.error);
}
return res;
};
}

export function graphqlMutation(mutation, variables, type = "CORE_TRIGGER_MUTATION", params = {}, wait = true) {
let clientMutationId;
if (variables?.input) {
clientMutationId = uuid.uuid();
variables.input.clientMutationId = clientMutationId;
}
return async (dispatch) => {
const response = await dispatch(graphqlWithVariables(mutation, variables, type, params));
if (clientMutationId) dispatch(fetchMutation(clientMutationId));
if (clientMutationId) {
dispatch(fetchMutation(clientMutationId));
if (wait) {
return dispatch(waitForMutation(clientMutationId));
} else {
return response?.payload?.data;
}
}
return response;
};
}

export function auth() {
export function fetch(config) {
return async (dispatch, getState) => {
const state = getState();
const token = state.core.auth.token;
const headers = {};
if (token) {
headers.Authorization = `Bearer ${token}`;
}

return dispatch({
[RSAA]: {
...config,
headers: {
"Content-Type": "application/json",
...config.headers,
...headers,
},
},
});
};
}

export function loadUser() {
return fetch({
endpoint: `${baseApiUrl}/core/users/current_user/`,
method: "GET",
types: ["CORE_USERS_CURRENT_USER_REQ", "CORE_USERS_CURRENT_USER_RESP", "CORE_USERS_CURRENT_USER_ERR"],
});
}

export function login(credentials) {
return async (dispatch, getState) => {
if (credentials) {
// We log in the user using the credentials
const mutation = `mutation authenticate($username: String!, $password: String!) {
tokenAuth(username: $username, password: $password) {
token
refreshToken
refreshExpiresIn
}
}`;

await dispatch(
graphqlMutation(mutation, credentials, ["CORE_AUTH_LOGIN_REQ", "CORE_AUTH_LOGIN_RESP", "CORE_AUTH_ERR"]),
);
const state = getState();
setRefreshToken(state.core.auth.refreshToken);
} else {
// We try to login using the refresh token if any
await refreshAuthToken();
}
const isAuthenticated = getState().core.auth.isAuthenticated;
if (isAuthenticated) {
await dispatch(loadUser());
}
return isAuthenticated;
};
}

export function refreshAuthToken() {
return async (dispatch, getState) => {
const { refreshToken, isAuthenticated, isAuthenticating } = getState().core.auth;
const storedToken = getRefreshToken();
if (!refreshToken && !storedToken) {
console.warn("No refresh token found");
return;
}

if (!isAuthenticated && isAuthenticating) {
console.warn("User is currently logging in.");
return;
}

const mutation = `
mutation refreshAuthToken ($refreshToken: String) {
refreshToken (refreshToken: $refreshToken) {
refreshToken
refreshExpiresIn
token
}
}
`;
const action = await dispatch(
graphqlMutation(mutation, { refreshToken: refreshToken ?? storedToken }, "CORE_AUTH_REFRESH_TOKEN"),
);
const state = getState();

if (state.core.auth.isAuthenticated) {
await dispatch(loadUser());
}
setRefreshToken(state.core.auth.refreshToken);
return action;
};
}

export function initializeAuth() {
return async (dispatch) => {
await dispatch(refreshAuthToken());
return dispatch({ type: "CORE_AUTH_INITIALIZED" });
};
}

export function authError(error) {
return {
[RSAA]: {
endpoint: `${baseApiUrl}/core/users/current_user/`,
method: "GET",
headers: apiHeaders(),
types: ["CORE_USERS_CURRENT_USER_REQ", "CORE_USERS_CURRENT_USER_RESP", "CORE_USERS_CURRENT_USER_ERR"],
},
type: "CORE_AUTH_ERR",
payload: error,
};
}

export function logout() {
return async (dispatch, getState) => {
const state = getState();
const { refreshToken } = state.core.auth;

const mutation = `
mutation revokeToken($refreshToken: String) {
revokeToken(refreshToken: $refreshToken) {
revoked
}
}
`;
setRefreshToken(null);
await dispatch(graphqlMutation(mutation, { refreshToken }));
return dispatch({ type: "CORE_AUTH_LOGOUT" });
};
}

Expand Down
Loading