Skip to content

Commit

Permalink
feat: support offline context
Browse files Browse the repository at this point in the history
Support offline getting, patching and posting app instance
resources without breaking online support.

closes #8
  • Loading branch information
juancarlosfarah committed May 22, 2019
1 parent 516e436 commit 7faf977
Show file tree
Hide file tree
Showing 12 changed files with 287 additions and 42 deletions.
24 changes: 21 additions & 3 deletions src/actions/appInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import {
DEFAULT_GET_REQUEST,
DEFAULT_PATCH_REQUEST,
} from '../config/api';
import { flag, getApiContext, isErrorResponse } from './common';
import { flag, getApiContext, isErrorResponse, postMessage } from './common';
import {
FLAG_GETTING_APP_INSTANCE,
FLAG_PATCHING_APP_INSTANCE,
GET_APP_INSTANCE_FAILED,
GET_APP_INSTANCE_SUCCEEDED,
PATCH_APP_INSTANCE_FAILED,
PATCH_APP_INSTANCE_SUCCEEDED,
GET_APP_INSTANCE,
PATCH_APP_INSTANCE,
} from '../types';

const flagGettingAppInstance = flag(FLAG_GETTING_APP_INSTANCE);
Expand All @@ -19,7 +21,15 @@ const flagPatchingAppInstance = flag(FLAG_PATCHING_APP_INSTANCE);
const getAppInstance = async () => async (dispatch, getState) => {
dispatch(flagGettingAppInstance(true));
try {
const { appInstanceId, apiHost } = getApiContext(getState);
const { appInstanceId, apiHost, offline } = getApiContext(getState);

console.log(offline); // todo
// if offline send message to parent requesting resources
if (offline) {
return postMessage({
type: GET_APP_INSTANCE,
});
}

const url = `//${apiHost + APP_INSTANCES_ENDPOINT}/${appInstanceId}`;

Expand Down Expand Up @@ -51,7 +61,15 @@ const patchAppInstance = async ({ data } = {}) => async (
) => {
dispatch(flagPatchingAppInstance(true));
try {
const { appInstanceId, apiHost } = getApiContext(getState);
const { appInstanceId, apiHost, offline } = getApiContext(getState);

console.log(offline); // todo
// if offline send message to parent requesting resources
if (offline) {
return postMessage({
type: PATCH_APP_INSTANCE,
});
}

const url = `//${apiHost + APP_INSTANCES_ENDPOINT}/${appInstanceId}`;
const body = {
Expand Down
83 changes: 78 additions & 5 deletions src/actions/appInstanceResources.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ import {
FLAG_DELETING_APP_INSTANCE_RESOURCE,
DELETE_APP_INSTANCE_RESOURCE_FAILED,
DELETE_APP_INSTANCE_RESOURCE_SUCCEEDED,
GET_APP_INSTANCE_RESOURCES,
POST_APP_INSTANCE_RESOURCE,
PATCH_APP_INSTANCE_RESOURCE,
DELETE_APP_INSTANCE_RESOURCE,
} from '../types';
import { flag, getApiContext, isErrorResponse } from './common';
import { flag, getApiContext, isErrorResponse, postMessage } from './common';
import { showErrorToast } from '../utils/toasts';
import { MISSING_APP_INSTANCE_RESOURCE_ID_MESSAGE } from '../constants/messages';
import { APP_INSTANCE_RESOURCE_FORMAT } from '../config/formats';
Expand All @@ -42,7 +46,26 @@ const getAppInstanceResources = async ({
} = {}) => async (dispatch, getState) => {
dispatch(flagGettingAppInstanceResources(true));
try {
const { appInstanceId, apiHost } = getApiContext(getState);
const {
appInstanceId,
apiHost,
offline,
spaceId,
subSpaceId,
} = getApiContext(getState);

// if offline send message to parent requesting resources
if (offline) {
return postMessage({
type: GET_APP_INSTANCE_RESOURCES,
payload: {
type,
spaceId,
subSpaceId,
appInstanceId,
},
});
}

let url = `//${apiHost +
APP_INSTANCE_RESOURCES_ENDPOINT}?appInstanceId=${appInstanceId}`;
Expand Down Expand Up @@ -84,7 +107,29 @@ const postAppInstanceResource = async ({ data, userId, type } = {}) => async (
) => {
dispatch(flagPostingAppInstanceResource(true));
try {
const { appInstanceId, apiHost } = await getApiContext(getState);
const {
appInstanceId,
apiHost,
offline,
spaceId,
subSpaceId,
} = await getApiContext(getState);

// if offline send message to parent requesting to create a resource
if (offline) {
return postMessage({
type: POST_APP_INSTANCE_RESOURCE,
payload: {
data,
type,
spaceId,
subSpaceId,
format: APP_INSTANCE_RESOURCE_FORMAT,
appInstanceId,
userId,
},
});
}

const url = `//${apiHost + APP_INSTANCE_RESOURCES_ENDPOINT}`;

Expand Down Expand Up @@ -128,7 +173,27 @@ const patchAppInstanceResource = async ({ id, data } = {}) => async (
) => {
dispatch(flagPatchingAppInstanceResource(true));
try {
const { apiHost } = await getApiContext(getState);
const {
appInstanceId,
apiHost,
offline,
spaceId,
subSpaceId,
} = await getApiContext(getState);

// if offline send message to parent requesting to patch resource
if (offline) {
return postMessage({
type: PATCH_APP_INSTANCE_RESOURCE,
payload: {
data,
spaceId,
subSpaceId,
appInstanceId,
id,
},
});
}

if (!id) {
return showErrorToast(MISSING_APP_INSTANCE_RESOURCE_ID_MESSAGE);
Expand Down Expand Up @@ -167,7 +232,15 @@ const patchAppInstanceResource = async ({ id, data } = {}) => async (
const deleteAppInstanceResource = async id => async (dispatch, getState) => {
dispatch(flagDeletingAppInstanceResource(true));
try {
const { apiHost } = await getApiContext(getState);
const { apiHost, offline } = await getApiContext(getState);

console.log(offline); // todo
// if offline send message to parent requesting to delete a resource
if (offline) {
return postMessage({
type: DELETE_APP_INSTANCE_RESOURCE,
});
}

if (!id) {
return showErrorToast(MISSING_APP_INSTANCE_RESOURCE_ID_MESSAGE);
Expand Down
68 changes: 59 additions & 9 deletions src/actions/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import {
MISSING_SPACE_ID_MESSAGE,
UNEXPECTED_ERROR_MESSAGE,
} from '../constants/messages';
import {
GET_APP_INSTANCE_RESOURCES_SUCCEEDED,
GET_APP_INSTANCE_SUCCEEDED,
PATCH_APP_INSTANCE_RESOURCE_SUCCEEDED,
POST_APP_INSTANCE_RESOURCE_SUCCEEDED,
} from '../types';

const flag = type => payload => dispatch =>
dispatch({
Expand All @@ -28,17 +34,61 @@ const isErrorResponse = async response => {

const getApiContext = getState => {
const { context } = getState();
const { apiHost, appInstanceId, spaceId, userId } = context;
if (!apiHost) {
throw Error(MISSING_API_HOST_MESSAGE);
const {
apiHost,
appInstanceId,
spaceId,
userId,
offline,
subSpaceId,
} = context;

// these bits of context are needed when running online
if (!offline) {
if (!apiHost) {
throw Error(MISSING_API_HOST_MESSAGE);
}
if (!appInstanceId) {
throw Error(MISSING_APP_INSTANCE_ID_MESSAGE);
}
if (!spaceId) {
throw Error(MISSING_SPACE_ID_MESSAGE);
}
}
if (!appInstanceId) {
throw Error(MISSING_APP_INSTANCE_ID_MESSAGE);
return { apiHost, appInstanceId, spaceId, userId, offline, subSpaceId };
};

const postMessage = data => {
const message = JSON.stringify(data);
if (window.parent.postMessage) {
window.parent.postMessage(message, '*');
} else {
console.error('unable to find postMessage');
}
if (!spaceId) {
throw Error(MISSING_SPACE_ID_MESSAGE);
};

const receiveMessage = dispatch => event => {
const { data } = event;
try {
const message = JSON.parse(data);

const { type, payload } = message;
switch (type) {
case GET_APP_INSTANCE_RESOURCES_SUCCEEDED:
case GET_APP_INSTANCE_SUCCEEDED:
case PATCH_APP_INSTANCE_RESOURCE_SUCCEEDED:
case POST_APP_INSTANCE_RESOURCE_SUCCEEDED:
return dispatch({
type,
payload,
});
default:
return false;
}
} catch (err) {
console.error(err);
return false;
}
return { apiHost, appInstanceId, spaceId, userId };
};

export { flag, isErrorResponse, getApiContext };
export { flag, isErrorResponse, getApiContext, postMessage, receiveMessage };
10 changes: 9 additions & 1 deletion src/actions/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
GET_CONTEXT_FAILED,
GET_CONTEXT_SUCCEEDED,
} from '../types';
import { flag } from './common';
import { flag, receiveMessage } from './common';
import { DEFAULT_API_HOST, DEFAULT_MODE } from '../config/settings';
import { DEFAULT_VIEW } from '../config/views';

Expand All @@ -28,6 +28,7 @@ const getContext = () => dispatch => {
subSpaceId = null,
userId = null,
sessionId = null,
offline = false,
} = Qs.parse(window.location.search, { ignoreQueryPrefix: true });
const context = {
mode,
Expand All @@ -39,7 +40,14 @@ const getContext = () => dispatch => {
sessionId,
spaceId,
subSpaceId,
offline,
};

// if offline, we need to set up the listeners here
if (offline) {
window.addEventListener('message', receiveMessage(dispatch));
}

dispatch({
type: GET_CONTEXT_SUCCEEDED,
payload: context,
Expand Down
12 changes: 10 additions & 2 deletions src/actions/users.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { flag, getApiContext, isErrorResponse } from './common';
import { flag, getApiContext, isErrorResponse, postMessage } from './common';
import {
FLAG_GETTING_USERS,
GET_USERS,
GET_USERS_FAILED,
GET_USERS_SUCCEEDED,
} from '../types';
Expand All @@ -15,7 +16,14 @@ const flagGettingUsers = flag(FLAG_GETTING_USERS);
const getUsers = async () => async (dispatch, getState) => {
dispatch(flagGettingUsers(true));
try {
const { spaceId, apiHost } = getApiContext(getState);
const { spaceId, apiHost, offline } = getApiContext(getState);

// if offline send message to parent requesting resources
if (offline) {
return postMessage({
type: GET_USERS,
});
}

const url = `//${apiHost + SPACES_ENDPOINT}/${spaceId}/${USERS_ENDPOINT}`;

Expand Down
2 changes: 1 addition & 1 deletion src/components/common/Loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const Loader = ({ classes }) => (
);

Loader.propTypes = {
classes: PropTypes.shape({ root: PropTypes.object }).isRequired,
classes: PropTypes.shape({ root: PropTypes.string }).isRequired,
};

const StyledComponent = withStyles(styles)(Loader);
Expand Down
Loading

0 comments on commit 7faf977

Please sign in to comment.