Commit 60606616 authored by Jaden DIEFENBAUGH's avatar Jaden DIEFENBAUGH
Browse files

[js] add more comments to store/helpers and move single use of func to usage location

parent 8d994fe0
// @flow
// API helpers not tied to any specific library
import { pluralize } from './beat.js';
import type { BeatEntity } from './beat.js';
import type { StringObject } from '.';
......@@ -13,6 +14,9 @@ let port = '5000';
const toUrl = (str) => `http://127.0.0.1:${ port }/${ str }`;
// curried func for calling the REST API
// basically:
// <response> = genApiCall(<http rest verb>)(<endpoint>)(<payload if any>)
const genApiCall = (method) => (be: Fetchable) => async (obj: any) => {
const validMethods = ['GET', 'POST', 'PUT', 'DELETE'];
if(!validMethods.includes(method))
......@@ -36,10 +40,12 @@ const genApiCall = (method) => (be: Fetchable) => async (obj: any) => {
return json;
};
// sets the port number for api calls
export const setPort = (newPort: number) => {
port = newPort;
};
// gens all the default API funcs for an endpoint
export const genModuleApiFuncs = (be: Fetchable) => {
return {
get: genApiCall('GET')(be),
......@@ -49,6 +55,7 @@ export const genModuleApiFuncs = (be: Fetchable) => {
};
};
// fetches layout info from graphviz for a toolchain
const layoutUrl = toUrl('layout');
export const fetchLayout = async (tcName: string) => {
const layoutConfig = {
......@@ -64,6 +71,7 @@ export const fetchLayout = async (tcName: string) => {
return JSON.parse(json);
};
// generates a python file from a template given some args payload
const templatesUrl = toUrl('templates');
const generateTemplate = async (be: string, args: {[string]: any}) => {
const templatesConfig = {
......
// @flow
// BEAT-specific helpers
import { jsonClone } from '.';
// all the BEAT entities
export type BeatEntity = 'database' | 'library' | 'dataformat' | 'algorithm' | 'toolchain' | 'experiment' | 'plotter' | 'plotterparameter';
// format of an instance of a beat entity in redux store
export type BeatObject = {|
// name of the object
name: string,
......@@ -11,10 +14,12 @@ export type BeatObject = {|
extraContents?: any,
|};
// format for the settings object
export type BeatSettings = {|
prefix: string
|};
// format for an environment object (info about the available docker envs)
export type BeatEnvironment = {|
name: string,
packages: { [string]: string },
......@@ -44,6 +49,7 @@ export const BEAT_ENTITIES: BeatEntity[] = [
'plotterparameter',
];
// types auto-recognized by beat.core
export const BUILTIN_TYPES = [
'int8',
'int16',
......@@ -63,6 +69,7 @@ export const BUILTIN_TYPES = [
'object',
];
// result types for analyzers
export const ANALYZER_RESULT_TYPES = [
'int32',
'float32',
......@@ -74,12 +81,16 @@ Object.freeze(BEAT_ENTITIES);
Object.freeze(BUILTIN_TYPES);
Object.freeze(ANALYZER_RESULT_TYPES);
// get toolchain name from experiment name
export const getTcFromExpName = (expName: string) => expName.split('/').slice(1, 4).join('/');
// default object for beat entity instances
export const getDefaultEntityObject = () => ({name: '', contents: {}});
// most fields/name of/in beat objects need to conform to this regex
export const rxField = /^[a-zA-Z_][a-zA-Z0-9_-]*$/;
// how many segments (separated by '/') are in an entity's name
export const nameSegmentsForEntity = (entity: BeatEntity) => {
if(entity === 'database')
return 2;
......@@ -88,6 +99,7 @@ export const nameSegmentsForEntity = (entity: BeatEntity) => {
return 3;
};
// pluralizes an entity
export const pluralize = (be: BeatEntity): string => {
switch(be) {
case 'database':
......@@ -111,6 +123,7 @@ export const pluralize = (be: BeatEntity): string => {
}
};
// validates that a name for beat object of an entity is fine, given the names for all objects of that entity
export const nameValidator = (be: BeatEntity, allNames?: string[] = []) => (name: string): boolean => {
if(allNames.includes(name)){
return false;
......@@ -140,12 +153,16 @@ export const nameValidator = (be: BeatEntity, allNames?: string[] = []) => (name
return true;
};
// some editors are prime targets for better/additional validation
export type ValidatorObjectField = boolean | boolean[];
// only algs & libs have this extra validation right now
export type ValidatorObject = AlgorithmValidatorObject | LibraryValidatorObject;
export type ValidatorFunc = (obj: BeatObject, allObjs: BeatObject[]) => ValidatorObject;
// algorithm validator info
export type AlgorithmValidatorObject = {
// is algorithm name valid?
name: ValidatorObjectField,
......@@ -161,6 +178,7 @@ export type AlgorithmValidatorObject = {
result0Exists: ValidatorObjectField,
};
// lots of checks for algs
export const algorithmValidator = (obj: BeatObject, allObjs: BeatObject[]): AlgorithmValidatorObject => {
let name = false;
......@@ -205,6 +223,7 @@ export const algorithmValidator = (obj: BeatObject, allObjs: BeatObject[]): Algo
return valid;
};
// extra validation for libs
export type LibraryValidatorObject = {
// is library name valid?
name: ValidatorObjectField,
......@@ -231,6 +250,10 @@ export const libraryValidator = (obj: BeatObject, allObjs: BeatObject[]): Librar
};
};
// these next several funcs, the "getValid***Obj", returns a properly-formed JSON obj,
// optionally filling in fields with an existing obj.
// basically they exist to keep malformed JSON from breaking the editors.
//
export const getValidAlgorithmObj = (data: BeatObject = {name: '', contents: {}}) => {
const getObj = {
name: '',
......
// @flow
export const objToArr = (data: any): any[] => {
if(!data)
throw new Error(`Invalid data "${ data }"`);
if(Array.isArray(data))
return data;
return Object.entries(data)
.map(([k, obj]) => objToArr(obj))
.reduce((a: any[], objs: any[]) => [...a, ...objs], [])
;
};
// basic helpers - common bits of code refactored to DRY
// changes a field in an object to a different name
// only required because keys are often mutable
export const changeObjFieldName = (obj: {}, oldName: string, newName: string): {} => {
return Object.entries(obj)
.map(([name, val]) => [name === oldName ? newName : name, val])
.reduce((o, [name, val]) => ({...o, [name]: val}), {});
};
// generates a new unique key given a prefix (like "value" or "param") and possibly conflicting keys
export const generateNewKey = (keyPrefix: string = '', existingKeys: string[]) => {
if(!existingKeys.includes(keyPrefix))
return keyPrefix;
......@@ -49,7 +42,8 @@ export const getRandomBrightColor = () => {
};
// recursively sorts an object's keys in alphabetical order
// useful for comparing (deeply nested) objects in stringified form
// useful for comparing (deeply nested) objects in stringified form.
// probably would be better to use another dep specialized in deeply comparing objects?
export const sortObject = (o: any) => Object.entries(o)
.sort(([n1, v1], [n2, v2]) => n1 > n2 ? 1 : -1)
.reduce((o, [n, v]) => ({...o, [n]: Object((v: any)) === v ? sortObject(v) : v}), {});
......
// @flow
// the action types (constants) are defined here
// if you want to add one, youll need to add the constant string to the ActionType below
// and also add a line for the constant
export type ActionType = 'FETCH_ALL'
| 'SAVE_DATABASE' | 'ADD_DATABASE' | 'DELETE_DATABASE' | 'UPDATE_DATABASE'
| 'SAVE_LIBRARY' | 'ADD_LIBRARY' | 'DELETE_LIBRARY' | 'UPDATE_LIBRARY'
......
// @flow
/* Actions for Redux
* all the actions for redux are defined here
* some actions are actually async action creators for async stuff (API calls)
* most are pretty simple and tied to a specific BEAT entity
* each entity has the same actions available, allowing lots of templating stuff
* these actions specify a type, using the constants in the ActionTypes
*/
import type {
ActionType,
} from './actionTypes';
......@@ -7,7 +14,6 @@ import * as Types from './actionTypes.js';
import type { Dispatch } from 'redux';
import { genModuleApiFuncs } from '@helpers/api';
import { BEAT_ENTITIES } from '@helpers/beat';
import { objToArr } from '@helpers';
import type { BeatEntity, BeatObject, BeatSettings, BeatEnvironment } from '@helpers/beat';
......@@ -22,6 +28,19 @@ export type ActionCreator = (any, any) => Action;
export type ActionThunkCreator = (any) => (dispatch: Dispatch<*>) => Promise<*>;
// helper to sort of flatten api response objects to arrays
const objToArr = (data: any): any[] => {
if(!data)
throw new Error(`Invalid data "${ data }"`);
if(Array.isArray(data))
return data;
return Object.entries(data)
.map(([k, obj]) => objToArr(obj))
.reduce((a: any[], objs: any[]) => [...a, ...objs], [])
;
};
export const fetchAllObjects: ActionThunkCreator = () => async (dispatch: Dispatch<*>) => {
//console.log(`fetchAllObjects`);
const getFuncs = [...BEAT_ENTITIES].map(e => genModuleApiFuncs(e).get);
......
// @flow
// builds the store and fetches the objects
import { createStore, applyMiddleware, compose } from 'redux';
import reducer from './reducers';
import thunk from 'redux-thunk';
......
// @flow
/* Reducers for Redux
* all the reducers are here.
* since most actions are similar (all entities have the same basic actions available) there's alot of templating that happens.
*/
import { combineReducers } from 'redux';
import type { Action } from './actions.js';
......
// @flow
/* State selectors for our Redux store via Reselect
* basically a bunch of memoized shortcuts to certain state/derived state
*/
import { createSelector } from 'reselect';
import type { State } from './reducers.js';
import { getTcFromExpName } from '@helpers/beat';
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment