Skip to content

Instantly share code, notes, and snippets.

@rhengles
Last active April 24, 2021 23:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rhengles/caee0f177396f0a2151857b1afbf6d3d to your computer and use it in GitHub Desktop.
Save rhengles/caee0f177396f0a2151857b1afbf6d3d to your computer and use it in GitHub Desktop.
Convert JDL to GQL
import jhipster from "jhipster-core";
import url from "url";
import path from "path";
import util from "util";
import * as graphql from "graphql";
import {
LocalDateTypeDefinition as GraphQLLocalDate
} from "graphql-scalars";
const __dirname = url.fileURLToPath(new URL('./', import.meta.url));
console.error(__dirname);
const reRelFieldName = /^\s*(\w+)(?:\s*\(\s*(\w+)\s*\))?\s*$/;
function lcfirst (s) {
return s.replace(/\w/, s => s.toLowerCase());
}
function ucfirst (s) {
return s.replace(/\w/, s => s.toUpperCase());
}
function relFieldName (n) {
return lcfirst(n)+'Id';
}
function relField (from, to, inv) {
const {injectedField} = inv ? to : from;
let name, display;
if (injectedField) {
[, name, display] = injectedField.match(reRelFieldName);
} else {
name = to.name;
}
name = lcfirst(name);
return [name, display];
}
function subEntityFieldSuffix(entity, suffix = 'Children') {
const { injectedField: srcIF } = entity;
let {name} = entity;
let injectedField;
if (srcIF) {
const m = srcIF.match(reRelFieldName);
injectedField = `${m[1]}${suffix}${m[2] ? `(${m[2]})` : ``}`;
} else {
name = `${name}${suffix}`;
}
return {
...entity,
name,
injectedField,
};
}
const queryRootFields = {};
const {
enums,
entities,
relationships,
} = jhipster.parseFromFiles([
path.resolve(__dirname, '../system.jdl')
]);
const enumMap = {};
enums.forEach(({name, values: srcValues}) => {
const values = {};
srcValues.forEach(({key}, value) => {
values[key] = {value};
});
enumMap[name] = new graphql.GraphQLEnumType({
name,
values
});
});
console.log(Object.entries(enumMap).slice(0, 2));
const relParentMap = {};
const relChildMap = {};
function insertRel(from, to, relMap, cardinality, inv) {
const fromName = from.name;
const toName = to.name;
const [field, fieldDisplay] = relField(from, to, inv);
let fieldsMap = relMap[fromName];
if (!fieldsMap) {
fieldsMap = relMap[fromName] = [];
}
fieldsMap.push({
field,
fieldDisplay,
type: toName,
debug: {
from,
to,
cardinality,
},
});
}
relationships.forEach(({from, to, cardinality}) => {
switch (cardinality) {
case 'OneToOne':
insertRel(from, to, relParentMap, cardinality);
break;
case 'ManyToOne':
insertRel(from, to, relParentMap, cardinality);
// to = from.name === to.name
// ? subEntityFieldSuffix(to)
// : to;
to = subEntityFieldSuffix(to);
insertRel(to, from, relChildMap, cardinality, true);
break;
case 'OneToMany':
insertRel(from, to, relChildMap, cardinality);
insertRel(to, from, relParentMap, cardinality);
break;
default:
throw new Error(`Handler not implemented for relationship type (${cardinality})`);
}
});
// console.log(relParentMap);
// console.log(relChildMap);
//*
function getGraphQLType(typeName) {
switch (typeName) {
case 'ID':
return graphql.GraphQLID;
case 'String':
case 'TextBlob':
return graphql.GraphQLString;
case 'Boolean':
return graphql.GraphQLBoolean;
case 'Integer':
return graphql.GraphQLInt;
case 'Float':
return graphql.GraphQLFloat;
case 'LocalDate':
return GraphQLLocalDate;
}
const EnumType = enumMap[typeName];
if (EnumType) return EnumType;
const ObjectType = entityMap[typeName];
if (ObjectType) return ObjectType;
throw new Error(`Unknown type (${typeName})`);
}
function typeRaw(x) { return x; }
function typeNonNull(x) {
return new graphql.GraphQLNonNull(x);
}
function typeList(x) {
return new graphql.GraphQLList(x);
}
function typeComp(list) {
return function composed(x) {
for (var i = list.length - 1; i >= 0; i--) {
x = list[i](x);
}
return x;
};
}
function addField(map, fieldName, typeName, typeWrap = typeRaw, entityName) {
if (fieldName in map) {
throw new Error(`Duplicate field name ${fieldName} (${typeName}) in ${entityName}`);
}
map[fieldName] = typeWrap(getGraphQLType(typeName));
// { type: };
}
const entityMap = {};
const entityMapFields = {};
entities.forEach(({name, body}) => {
const fields = {};
const EntityType = new graphql.GraphQLObjectType({
name,
fields
});
entityMap[name] = EntityType;
entityMapFields[name] = fields;
queryRootFields[name] = EntityType;
// queryRootFields[`all${ucfirst(name)}`] = typeList(typeNonNull(EntityType));
});
entities.forEach(({name, body}) => {
const fields = entityMapFields[name];
addField(fields, 'id', 'ID', typeNonNull, `${name}/id`);
body.forEach(f => addField(
fields,
f.name,
f.type,
f.validations.some(({key}) => 'required' === key)
? typeNonNull
: typeRaw,
`${name}/body`,
));
const relParent = relParentMap[name];
if (relParent) {
relParent.forEach(({field, type}) => addField(
fields,
field,
type,
typeNonNull,
`${name}/relParent`,
));
}
const relChild = relChildMap[name];
if (relChild) {
try {
relChild.forEach(({field, type}) => addField(
fields,
field,
type,
typeComp([typeNonNull, typeList, typeNonNull]),
`${name}/relChild`,
));
} catch (e) {
console.log(`relChild`, name, fields);
relChild.forEach(({field, type, debug}) => {
// if (`PessoaRelacao` !== type) return;
console.log(`rc`, field, debug);
})
throw e;
}
}
});
//console.log(Object.entries(Object.values(queryRootFields)[7]));
const schemaRoot = new graphql.GraphQLSchema({
query: new graphql.GraphQLObjectType({
name: 'Query',
fields: queryRootFields,
})
});
console.log(graphql.printSchema(schemaRoot));
/*
console.error(`-------- Enums --------`);
console.error( enums[0] );
console.error( enums[1] );
console.error( enums[5] );
// console.error( enums[6] );
console.error(`-------- Entities --------`);
console.error( entities[0] );
console.error( util.inspect(entities[7], undefined, 4, true) );
/*
console.error(`-------- Relationships --------`);
relationships
.filter(({from: {name}}) => name === 'Local' || name === 'Pessoa')
.forEach(r => console.error( r ));
*/
application {
config {
baseName system,
applicationType monolith,
packageName com.system,
authenticationType jwt,
prodDatabaseType mysql,
clientFramework react
}
entities *
}
enum TipoLocal {
CONTINENTE,
PAIS,
PAIS_REGIAO,
ESTADO,
ESTADO_MESO,
ESTADO_MICRO,
MUNICIPIO,
DISTRITO,
BAIRRO,
LOGRADOURO,
LOGRADOURO_NUMERO,
COMPLEMENTO
}
enum TipoNome {
NOME_COMPLETO,
NOME_COMUM,
NOME_SOCIAL,
APELIDO,
NOME_FANTASIA,
RAZAO_SOCIAL
}
enum TipoDocumentoPessoa {
CPF,
RG,
CNH,
TITULO_ELEITOR,
SERVICO_MILITAR,
PASSAPORTE,
OUTRO
}
enum TipoDocumentoEmpresa {
CNPJ,
INSCRICAO_ESTADUAL,
INSCRICAO_MUNICIPAL,
NIRE,
ALVARA,
LICENSA_ANVISA,
OUTRO
}
enum TipoDadoContato {
TELEFONE,
EMAIL,
LINK,
FAX
}
enum Sexo {
MASCULINO,
FEMININO
}
enum TipoRelacao {
MARIDO,
ESPOSA,
EX_MARIDO,
EX_ESPOSA,
NAMORADO,
NAMORADA,
EX_NAMORADO,
EX_NAMORADA,
PARCEIRO,
PARCEIRA,
EX_PARCEIRO,
EX_PARCEIRA,
PAI,
MAE,
PADRASTO,
MADRASTA,
FILHO,
FILHA,
AFILHADO,
AFILHADA,
AVO_M,
AVO_F,
NETO,
NETA,
TIO,
TIA,
PRIMO,
PRIMA,
SOBRINHO,
SOBRINHA,
SOGRO,
SOGRA,
NORA,
GENRO,
CUNHADO,
CUNHADA,
PARENTE_OUTRO,
RESPONSAVEL_LEGAL,
AMIGO,
COLEGA_TRABALHO,
COLEGA_ESCOLA,
CONHECIDO
}
enum TipoFuncionario {
SOCIO_DIRETOR,
SOCIO,
RESPONSAVEL_TECNICO,
FUNCIONARIO
}
enum TipoUnidade {
VOLUME,
PESO,
COMPRIMENTO,
UNIDADE,
EMBALAGEM
}
enum UnidadePeriodo {
ANOS,
MESES,
SEMANAS,
DIAS,
HORAS,
MINUTOS
}
entity Observacao {
texto TextBlob required
}
entity UnidadeMedida {
nome String required maxlength(32)
abrev String maxlength(8)
tipo TipoUnidade
}
entity NomeTipo {
nome String required maxlength(64)
validoDe LocalDate
validoAte LocalDate
tipo TipoNome required
}
entity Genero {
nome String required maxlength(64)
}
entity DocumentoPessoa {
numero String required maxlength(32)
tipo TipoDocumentoPessoa required
}
entity DocumentoEmpresa {
numero String required maxlength(32)
tipo TipoDocumentoEmpresa required
}
entity DadoContato {
contato String required maxlength(128)
tipo TipoDadoContato
}
entity Local {
tipo TipoLocal required
sigla String minlength(2) maxlength(3) pattern(/^[a-z]+$/)
nome String required maxlength(64)
codigoPostal String maxlength(12)
}
entity Condominio {
nome String required maxlength(48)
}
entity EntidadeLocais {
validoDe LocalDate
validoAte LocalDate
identificacao String maxlength(32)
cobranca Boolean
servico Boolean
}
entity RegistroConselho {
sigla String required maxlength(8)
}
entity PessoaRegistroConselho {
registro String required maxlength(16)
complemento String maxlength(32)
}
entity Pessoa {
nascimento LocalDate
sexo Sexo
}
entity Empresa {}
entity Funcionario {
admissao LocalDate
demissao LocalDate
cargo String maxlength(32)
tipo TipoFuncionario
}
entity PessoaRelacao {
tipo TipoRelacao
}
entity Publicidade {
nome String required maxlength(64)
}
entity Cliente {
sequencia Integer required unique
}
entity Praga {
nome String required maxlength(32)
nomeCientifico String maxlength(64)
}
entity ProdutoPesticida {
nomeComercial String required maxlength(32)
nomeTecnico String maxlength(64)
grupoQuimico String maxlength(32)
toxicidade String maxlength(32)
acaoToxica String maxlength(32)
antidoto String maxlength(32)
numRegistro String maxlength(24)
}
entity ProdutoUso {
quantidade Float
variacao Boolean
obrigatorio Boolean
}
entity Material {
nome String required maxlength(32)
}
entity MaterialUso {
quantidade Float
variacao Boolean
obrigatorio Boolean
}
entity Equipamento {
nome String required maxlength(64)
}
entity EquipamentoUso {
obrigatorio Boolean
}
entity ServicoAcao {
variacao Boolean
obrigatorio Boolean
}
entity Servico {
nome String required maxlength(64)
garantiaTempo Integer
garantiaUnidade UnidadePeriodo
precoPadrao Integer
}
relationship OneToOne {
DadoContato{obs} to Observacao,
Local{obs} to Observacao,
Condominio{obs} to Observacao,
Publicidade{obs} to Observacao,
Funcionario{obs} to Observacao,
Condominio{local} to Local,
EntidadeLocais{local} to Local,
Pessoa{obs} to Observacao,
Empresa{obs} to Observacao,
Cliente{obs} to Observacao,
DocumentoPessoa{obs} to Observacao,
DocumentoEmpresa{obs} to Observacao,
PessoaRelacao{obs} to Observacao,
ProdutoPesticida{obs} to Observacao,
Material{obs} to Observacao,
Equipamento{obs} to Observacao,
ProdutoUso{instrucoes} to Observacao,
ProdutoUso{obs} to Observacao,
MaterialUso{instrucoes} to Observacao,
MaterialUso{obs} to Observacao,
EquipamentoUso{instrucoes} to Observacao,
EquipamentoUso{obs} to Observacao,
ServicoAcao{instrucoes} to Observacao,
ServicoAcao{obs} to Observacao,
Cliente to Pessoa,
Cliente to Empresa,
Funcionario to Pessoa,
RegistroConselho to Empresa,
PessoaRegistroConselho to RegistroConselho,
ProdutoPesticida{fabricante} to Empresa
}
relationship OneToMany {
Pessoa to NomeTipo,
Pessoa to DadoContato,
Pessoa to DocumentoPessoa,
Pessoa to EntidadeLocais,
Pessoa to PessoaRegistroConselho,
Empresa to NomeTipo,
Empresa to DadoContato,
Empresa to DocumentoEmpresa,
Empresa to EntidadeLocais,
Empresa to Funcionario,
ProdutoPesticida to Praga
}
relationship ManyToOne {
Local{dentroDe(nome)} to Local,
Cliente{publicidade} to Publicidade,
Pessoa{genero} to Genero,
ProdutoPesticida{unidade} to UnidadeMedida,
PessoaRelacao{pessoaDe} to Pessoa,
PessoaRelacao{pessoaPara} to Pessoa,
Material{medida} to UnidadeMedida,
ProdutoUso{produto} to ProdutoPesticida,
ProdutoUso{servicoAcao} to ServicoAcao,
MaterialUso{material} to Material,
MaterialUso{servicoAcao} to ServicoAcao,
EquipamentoUso{equipamento} to Equipamento,
EquipamentoUso{servicoAcao} to ServicoAcao,
ServicoAcao{servico} to Servico
}
// skipClient *
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment