Skip to content

Commit

Permalink
Load plans with redux actions.
Browse files Browse the repository at this point in the history
  • Loading branch information
chmac committed May 28, 2020
1 parent a09c00b commit 36e2328
Show file tree
Hide file tree
Showing 6 changed files with 389 additions and 25 deletions.
2 changes: 2 additions & 0 deletions packages/plans/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"author": "Callum Macdonald",
"module": "dist/plans.esm.js",
"devDependencies": {
"@types/bluebird": "^3.5.32",
"@types/mock-fs": "^4.10.0",
"@types/remote-redux-devtools": "^0.5.4",
"husky": "^4.2.5",
Expand All @@ -47,6 +48,7 @@
},
"dependencies": {
"@reduxjs/toolkit": "^1.3.6",
"bluebird": "^3.7.2",
"gray-matter": "^4.0.2",
"yaml": "^1.10.0"
}
Expand Down
205 changes: 205 additions & 0 deletions packages/plans/src/services/plans/plans.actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import Bluebird from 'bluebird';
import { AppThunk } from '../../store';
import { to } from '../../utils/to.util';
import {
addPlansFolderToPath,
doesDirectoryExist,
FS,
getMessageDataFromPath,
getPlanDataFromIndexFilePath,
getPlanFilesFromDirectory,
getPlanPathsFromUserDirectory,
Message,
Plan,
} from './plans.service';
import { noop, upsertOneMessage, upsertOnePlan } from './plans.state';

export const loadMessageFromPath = ({
fs,
path,
slug,
planId,
}: {
fs: FS;
path: string;
slug: string;
planId: string;
}): AppThunk => async dispatch => {
dispatch(
noop({
code: '#apqZYY',
message: 'Load message from path',
params: { planId, path, slug },
})
);

const response = await to(getMessageDataFromPath({ fs, path }));

if (response.error) {
const { error } = response;
dispatch(
noop({
code: '#BO31C7',
message: 'Error reading message file.',
params: { error, path, planId },
})
);
return;
}

const data = response.result;

const message: Message = { id: path, planId, ...data, slug };

dispatch(upsertOneMessage(message));
};

export const loadPlanFromPath = ({
fs,
path,
userId,
slug,
}: {
fs: FS;
path: string;
userId: string;
slug: string;
}): AppThunk => async dispatch => {
dispatch(
noop({
code: '#FWoxSS',
message: 'Load plan from path',
params: { userId, path, slug },
})
);

const responseOne = await to(getPlanFilesFromDirectory({ fs, path }));

if (responseOne.error) {
const { error } = responseOne;
dispatch(
noop({
code: '#VFlmTq',
message: 'Error getting plan from directory',
params: { error, slug, path },
})
);
return;
}

const { result } = responseOne;
const { indexFile, messageFiles } = result;

const planId = indexFile.path;

const responseTwo = await to(
getPlanDataFromIndexFilePath({
fs,
path: indexFile.path,
})
);

if (responseTwo.error) {
const { error } = responseTwo;
dispatch(
noop({
code: '#scgkS0',
message: 'Error reading plan data.',
params: { indexFile, error },
})
);
return;
}

const { content, data } = responseTwo.result;

const plan: Plan = {
id: planId,
userId,
...data,
descriptionMarkdown: content,
slug,
};

dispatch(upsertOnePlan(plan));

await Bluebird.each(messageFiles, async messageFile => {
const { slug, path } = messageFile;
dispatch(
loadMessageFromPath({
fs,
path,
slug,
planId,
})
);
});

dispatch(
noop({
code: '#iXomhQ',
message: 'loadPlanFromPath() finishd',
params: { userId, path, slug },
})
);
};

export const loadPlansFromUserPath = ({
fs,
userId,
userDirectoryPath,
}: {
fs: FS;
userId: string;
userDirectoryPath: string;
}): AppThunk => async dispatch => {
dispatch(
noop({
code: '#wi9aR7',
message: 'loadUserPlansAction called',
params: { userName: userId, userDirectoryPath },
})
);

const plansPath = addPlansFolderToPath({ path: userDirectoryPath });

// If the user does not have a `plans` folder, there's nothing to do here.
if (!(await doesDirectoryExist({ fs, path: plansPath }))) {
dispatch(
noop({
code: '#nMlg40',
message: 'loadUserPlansAction directory not found',
params: { userId, userDirectoryPath },
})
);
return;
}

const plansPaths = await getPlanPathsFromUserDirectory({
fs,
path: plansPath,
});

await Bluebird.each(plansPaths, async ({ path, slug }) => {
dispatch(
loadPlanFromPath({
fs,
path,
slug,
userId,
})
);
});

dispatch(
noop({
code: '#i0W2Yp',
message: 'loadUserPlansAction finished',
params: {
userId,
userDirectoryPath,
plansPaths,
},
})
);
};
103 changes: 100 additions & 3 deletions packages/plans/src/services/plans/plans.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,28 @@ import fsNode from 'fs/promises';
import { join } from 'path';
import matter from 'gray-matter';

type FS = {
const PLANS_FOLDER_NAME = 'plans';

export type FS = {
readdir: typeof fsNode.readdir;
readFile: typeof fsNode.readFile;
stat: typeof fsNode.stat;
};

export type Message = {
id: string;
planId: string;
slug: string;
sender: string;
contentMarkdown: string;
};

export type Plan = {
id: string;
userId: string;
slug: string;
name: string;
descriptionMarkdown: string;
messages: Message[];
};

type PlanFrontMatter = {
Expand Down Expand Up @@ -64,13 +70,14 @@ export const parseMessageFromMarkdown = async (input: string) => {
return { contentMarkdown: content, ...data };
};

// DEPRECATED
export const readPlansFromUserPlansDirectory = async ({
fs,
directoryPath,
}: {
fs: FS;
directoryPath: string;
}): Promise<Plan[]> => {
}) => {
const dir = await fs.readdir(directoryPath, { withFileTypes: true });
const subDirectories = dir.filter(entry => entry.isDirectory());
const plans = await Promise.all(
Expand Down Expand Up @@ -119,3 +126,93 @@ export const readPlansFromUserPlansDirectory = async ({

return plans;
};

export const addPlansFolderToPath = ({ path }: { path: string }): string => {
return join(path, PLANS_FOLDER_NAME);
};

export const doesDirectoryExist = async ({
fs,
path,
}: {
fs: FS;
path: string;
}) => {
const stat = await fs.stat(path);
return stat.isDirectory();
};

export const getPlanPathsFromUserDirectory = async ({
fs,
path,
}: {
fs: FS;
path: string;
}) => {
const dir = await fs.readdir(path, {
encoding: 'utf-8',
withFileTypes: true,
});

const plansPaths = dir
.filter(plan => plan.isDirectory())
.map(plan => ({ path: join(path, plan.name), slug: plan.name }));

return plansPaths;
};

export const getPlanFilesFromDirectory = async ({
fs,
path,
}: {
fs: FS;
path: string;
}) => {
const planFiles = await fs.readdir(path, {
withFileTypes: true,
});

const index = planFiles.find(file => file.name === 'index.md');

if (typeof index === 'undefined') {
throw new Error(`Plan does not have index file. #yJtokv`);
}

const indexFile = { path: join(path, index.name), slug: index.name };

const messageFiles = planFiles
.filter(file => file.name !== 'index.md')
.map(file => ({ path: join(path, file.name), slug: file.name }));

return { indexFile, messageFiles };
};

export const getPlanDataFromIndexFilePath = async ({
fs,
path,
}: {
fs: FS;
path: string;
}) => {
const text = await fs.readFile(path, { encoding: 'utf-8' });

const planData = planMarkdownToData(text);

return planData;
};

export const getMessageDataFromPath = async ({
fs,
path,
}: {
fs: FS;
path: string;
}) => {
const markdown = await fs.readFile(path, {
encoding: 'utf-8',
});

const data = await parseMessageFromMarkdown(markdown);

return data;
};
Loading

0 comments on commit 36e2328

Please sign in to comment.