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

Add module with domain-schema #1128

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions modules/core/common/generatedSchemas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default {};
1 change: 1 addition & 0 deletions modules/core/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export * from './net';
export { default as log } from './log';
export { default as createApolloClient } from './createApolloClient';
export { default as createReduxStore } from './createReduxStore';
export { default as schemas } from './generatedSchemas';
export * from './createReduxStore';
export * from './utils';
2 changes: 1 addition & 1 deletion modules/core/common/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const log = minilog(loggerName);
(log as any).suggest.defaultResult = false;
(log as any).suggest.clear().allow(loggerName, settings.app.logging.level);

if (__DEV__ && __SERVER__ && !__TEST__) {
if (typeof __DEV__ !== 'undefined' && typeof __SERVER__ !== 'undefined' && typeof __TEST__ !== 'undefined') {
const consoleLog = global.console.log;
global.console.log = (...args: any[]) => {
if (args.length === 1 && typeof args[0] === 'string' && args[0].match(/^\[(HMR|WDS)\]/)) {
Expand Down
10 changes: 6 additions & 4 deletions modules/core/common/net.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import url from 'url';
import { PLATFORM } from './utils';

const apiUrlDefine = typeof __API_URL__ !== 'undefined' ? __API_URL__ : '/graphql';

export const serverPort =
PLATFORM === 'server' && (process.env.PORT || (typeof __SERVER_PORT__ !== 'undefined' ? __SERVER_PORT__ : 8080));
export const isApiExternal = !!url.parse(__API_URL__).protocol;
export const isApiExternal = !!url.parse(apiUrlDefine).protocol;

const clientApiUrl =
!isApiExternal && PLATFORM === 'web'
? `${window.location.protocol}//${window.location.hostname}${
__DEV__ ? ':8080' : window.location.port ? ':' + window.location.port : ''
}${__API_URL__}`
: __API_URL__;
}${apiUrlDefine}`
: apiUrlDefine;

const serverApiUrl = !isApiExternal ? `http://localhost:${serverPort}${__API_URL__}` : __API_URL__;
const serverApiUrl = !isApiExternal ? `http://localhost:${serverPort}${apiUrlDefine}` : apiUrlDefine;

export const apiUrl = PLATFORM === 'server' ? serverApiUrl : clientApiUrl;
37 changes: 33 additions & 4 deletions modules/database/server-ts/sql/crud.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import _ from 'lodash';
import uuidv4 from 'uuid';
import { camelize, decamelizeKeys, camelizeKeys } from 'humps';
import { decamelize, decamelizeKeys, camelize, camelizeKeys } from 'humps';
import { log } from '@gqlapp/core-common';
import knexnest from 'knexnest';
import parseFields from 'graphql-parse-fields';

import knex from './connector';

import { orderedFor } from './helpers';

import { selectBy, orderedFor } from './helpers';
import selectAdapter from './select';

export function createWithIdGenAdapter(options) {
Expand Down Expand Up @@ -402,3 +402,32 @@ export function deleteRelationAdapter(options) {
}
};
}

export class Crud {
getTableName() {
return decamelize(this.schema.__.tableName ? this.schema.__.tableName : this.schema.name);
}

getFullTableName() {
return `${this.schema.__.tablePrefix}${this.getTableName()}`;
}

getSchema() {
return this.schema;
}

getBaseQuery() {
return knex(`${this.getFullTableName()} as ${this.getTableName()}`);
}

_findMany(_, info) {
const select = selectBy(this.schema, info, false);
const queryBuilder = select(this.getBaseQuery());

return knexnest(queryBuilder);
}

findMany(args, info) {
return this._findMany(args, parseFields(info));
}
}
130 changes: 129 additions & 1 deletion modules/database/server-ts/sql/helpers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { groupBy } from 'lodash';
import { groupBy, findIndex } from 'lodash';
import { decamelize } from 'humps';

import settings from '@gqlapp/config';

Expand Down Expand Up @@ -30,3 +31,130 @@ export const orderedFor = (rows, collection, field, singleObject) => {
return singleObject ? {} : [];
});
};

export const orderedForArray = (rows, collection, field, arrayElement) => {
// return the rows ordered for the collection
const inGroupsOfField = groupBy(rows, field);
return collection.map(element => {
const elementArray = inGroupsOfField[element];
if (elementArray) {
return inGroupsOfField[element].map(elm => {
return elm[arrayElement];
});
}
return [];
});
};

/**
* Collecting selects and joins
* @param graphqlFields
* @param domainSchema
* @param selectItems
* @param joinNames
* @param single
* @param parentField
* @private
*/
const _getSelectFields = (graphqlFields, domainSchema, selectItems, joinNames, single, parentKey, parentPath) => {
for (const fieldName of Object.keys(graphqlFields)) {
if (fieldName === '__typename') {
continue;
}
const value = domainSchema.values[fieldName];
if (graphqlFields[fieldName] === true) {
if (value && value.transient) {
continue;
}
selectItems.push(_getSelectField(fieldName, parentPath, domainSchema, single, parentKey));
} else {
if (Array.isArray(value.type) || findIndex(joinNames, { fieldName: decamelize(fieldName) }) > -1) {
continue;
}
if (!value.type.__.transient) {
joinNames.push(_getJoinEntity(fieldName, value, domainSchema));
}

parentPath.push(fieldName);

_getSelectFields(
graphqlFields[fieldName],
value.type,
selectItems,
joinNames,
single,
decamelize(fieldName),
parentPath
);

parentPath.pop();
}
}
};

/**
* Computing select field
* @param fieldName
* @param parentField
* @param domainSchema
* @param single
* @returns {string}
* @private
*/
const _getSelectField = (fieldName, parentPath, domainSchema, single, parentKey) => {
const alias = parentPath.length > 0 ? `${parentPath.join('_')}_${fieldName}` : fieldName;
const tableName = `${decamelize(domainSchema.__.tableName ? domainSchema.__.tableName : domainSchema.name)}`;
const fullTableName = parentKey !== null && parentKey !== tableName ? `${parentKey}_${tableName}` : tableName;
// returning object would be array or no
const arrayPrefix = single ? '' : '_';
return `${fullTableName}.${decamelize(fieldName)} as ${arrayPrefix}${alias}`;
};

/**
* Computing join entity object
* @param fieldName
* @param value
* @param domainSchema
* @returns {{fieldName: *, prefix: string, suffix: string, baseTableName: *, foreignTableName: *}}
* @private
*/
const _getJoinEntity = (fieldName, value, domainSchema) => {
return {
fieldName: decamelize(fieldName),
prefix: value.type.__.tablePrefix ? value.type.__.tablePrefix : '',
suffix: value.noIdSuffix ? '' : '_id',
baseTableName: decamelize(domainSchema.name),
foreignTableName: decamelize(value.type.__.tableName ? value.type.__.tableName : value.type.name)
};
};

/**
* Computing query with selects and joins
* @param schema
* @param fields
* @param single
* @returns {function(*): *}
*/
export const selectBy = (schema, fields, single = false) => {
// select fields and joins
const parentPath = [];
const selectItems = [];
const joinNames = [];
_getSelectFields(fields, schema, selectItems, joinNames, single, null, parentPath);

return query => {
// join table names
joinNames.map(({ fieldName, prefix, suffix, baseTableName, foreignTableName }) => {
// if fieldName (schema key) diff with table name than make proper table alias
const tableNameAlias =
fieldName !== null && fieldName !== foreignTableName ? `${fieldName}_${foreignTableName}` : foreignTableName;
query.leftJoin(
`${prefix}${foreignTableName} as ${tableNameAlias}`,
`${tableNameAlias}.id`,
`${baseTableName}.${fieldName}${suffix}`
);
});

return query.select(selectItems);
};
};
1 change: 1 addition & 0 deletions modules/database/server-ts/sql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { default as knex } from './connector';
export { default as populateTestDb } from './populateTestDb';
export { default as createTransaction } from './createTransaction';
export * from './helpers';
export * from './crud';
5 changes: 5 additions & 0 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@
"dependencies": {
"@babel/preset-env": "^7.0.0",
"@babel/register": "^7.0.0",
"@domain-schema/core": "^0.0.34",
"@domain-schema/graphql": "^0.0.33",
"@domain-schema/knex": "^0.0.35",
"@gqlapp/core-common": "^0.1.0",
"@gqlapp/database-server-ts": "^0.1.0",
"@gqlapp/testing-server-ts": "^0.1.0",
Expand Down Expand Up @@ -88,6 +91,7 @@
"graphql": "^14.1.1",
"graphql-auth": "0.2.6",
"graphql-iso-date": "^3.5.0",
"graphql-parse-fields": "^1.2.0",
"graphql-resolve-batch": "^1.0.2",
"graphql-subscriptions": "^1.1.0",
"graphql-tag": "^2.6.0",
Expand All @@ -98,6 +102,7 @@
"isomorphic-fetch": "^2.2.1",
"jsonwebtoken": "^8.1.0",
"knex": "^0.14.2",
"knexnest": "^1.0.0",
"lerna": "^2.5.1",
"lint-staged": "^7.0.4",
"lodash": "^4.17.4",
Expand Down
10 changes: 8 additions & 2 deletions tools/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ const addModuleCommand = require('./cli/commands/addModule');
const deleteModuleCommand = require('./cli/commands/deleteModule');
const chooseTemplateCommand = require('./cli/commands/chooseTemplate');
const deleteStackCommand = require('./cli/commands/deleteStack');
const updateSchemaCommand = require('./cli/commands/updateSchema');

const CommandInvoker = require('./cli/CommandInvoker');

const commandInvoker = new CommandInvoker(
addModuleCommand,
deleteModuleCommand,
chooseTemplateCommand,
deleteStackCommand
deleteStackCommand,
updateSchemaCommand
);

prog
Expand Down Expand Up @@ -55,6 +57,10 @@ List of technologies [react, angular, vue, scala, node]`

.action(({ stackList }, { list }, logger) => {
commandInvoker.runDeleteStack(stackList, logger, list);
});
})
// Update schema
.command('updateschema', 'Update Module Schema')
.argument('<moduleName>', 'Module name')
.action((args, options, logger) => commandInvoker.runUpdateSchema(args, options, logger));

prog.parse(process.argv);
11 changes: 10 additions & 1 deletion tools/cli/CommandInvoker.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ class CommandInvoker {
* @param chooseStack - The function for choosing stack of technologies.
* @param deleteStack - The function for delete stack of technologies.
*/
constructor(addModule, deleteModule, chooseStack, deleteStack) {
constructor(addModule, deleteModule, chooseStack, deleteStack, updateSchema) {
this.addModule = addModule;
this.deleteModule = deleteModule;
this.chooseStack = chooseStack;
this.deleteStack = deleteStack;
this.updateSchema = updateSchema;
}

/**
Expand All @@ -30,6 +31,7 @@ class CommandInvoker {

if (location === 'both') {
runFunc('client');
runFunc('common');
runFunc('server');
} else {
runFunc(location);
Expand Down Expand Up @@ -63,6 +65,13 @@ class CommandInvoker {
runDeleteStack(args, logger, isShowStackList) {
this.deleteStack(args, logger, isShowStackList);
}

/**
* Runs operation (function) for updating existing module schema.
*/
runUpdateSchema(args, options, logger) {
runOperation(this.updateSchema, args, options, logger);
}
}

function runOperation(operation, args, options, logger) {
Expand Down
Loading