Commit 7772bd37 authored by Bertrand PINEL's avatar Bertrand PINEL

Initial Commit

parent 97ba36d1
import EmberRouter from '@ember/routing/router';
import config from './config/environment';
const Router = EmberRouter.extend({
location: config.locationType,
rootURL: config.rootURL
});
Router.map(function() {
});
export default Router;
This blueprint uses the same signature as the model blueprint for defining the list of attributes of the entity and their type.
The following attribute types are supported: :array :boolean :date :object :number :string :your-custom-transform :belongs-to: :has-many:
ember model <name> <attr:type>
Generates an ember-data model.
You may generate models with as many attrs as you would like to pass. The following attribute types are supported:
<attr-name>
<attr-name>:array
<attr-name>:boolean
<attr-name>:date
<attr-name>:object
<attr-name>:number
<attr-name>:string
<attr-name>:your-custom-transform
<attr-name>:belongs-to:<model-name>
<attr-name>:has-many:<model-name>
For instance: `ember generate model taco filling:belongs-to:protein toppings:has-many:toppings name:string price:number misc`
would result in the following model:
import DS from 'ember-data';
export default DS.Model.extend({
filling: DS.belongsTo('protein'),
toppings: DS.hasMany('topping'),
name: DS.attr('string'),
price: DS.attr('number'),
misc: DS.attr()
});
\ No newline at end of file
import {
Factory,
faker
} from 'ember-cli-mirage';
export default Factory.extend({
<%= fact.length ? '//default generated factory\n\t' + fact + '\n' : '' %>
})
\ No newline at end of file
import {
Model<%= imports.length ? ', '+imports : '' %>
} from 'ember-cli-mirage';
export default Model.extend({<%= rel.length ? rel : '' %>
});
\ No newline at end of file
To be sure that the directory exists...
\ No newline at end of file
To be sure that the directory exists...
\ No newline at end of file
import Controller from '@ember/controller';
import { computed } from '@ember/object';
import { A } from '@ember/array';
export default Controller.extend({
isAddingEntry: false,
<%=ctrlVars%>
<%=entity%>TableColumns: computed(function() {
var col = A([
<%=cols%>
]);
return col;
}),
<%=entity%>TableContent: computed(function() {
return this.get("model");
}),
actions: {
submit() {
this.set('addEntryModal', false);
console.log("Adding new entry");
},
}
});
\ No newline at end of file
import DS from 'ember-data';
export default DS.Model.extend({
<%= attrs.length ? ' ' + attrs : '' %>
});
\ No newline at end of file
import Route from '@ember/routing/route';
export default Route.extend({
model() {
return this.store.findAll('<%=entity%>');
}
});
\ No newline at end of file
{{outlet}}
<div class="container-fluid">
<h2>List of <%=entity%></h2>
{{models-table
data=<%=entity%>TableContent
columns=<%=entity%>TableColumns}}
{{#bs-button onClick=(action (mut addEntryModal) true)}}
Add new entry
{{/bs-button}}
{{#bs-modal open=addEntryModal onSubmit=(action "submit") onHidden=(action (mut addEntryModal) false) as |modal|}}
{{#modal.header}}
<h4 class="modal-title">
Add a new Entry
<div class="badge">2</div>
</h4>
{{/modal.header}}
{{#modal.body}}
<%=form%>
{{/modal.body}}
{{#modal.footer}}
{{#bs-button onClick=(action modal.close)}}Cancel{{/bs-button}}
{{#bs-button type="success" onClick=(action modal.submit)}}Save{{/bs-button}}
{{/modal.footer}}
{{/bs-modal}}
</div>
\ No newline at end of file
const inflection = require('inflection');
const stringUtils = require('ember-cli-string-utils');
const EOL = require('os').EOL;
const path = require('path');
const fs = require('fs');
module.exports = {
description: 'Generates an ember-data model and the associated Mirage couterpart and screens.',
anonymousOptions: ['name', 'attr:type'],
fileMapTokens(options) {
if (options.pod) {
return {
// Never tested. Not used to use pod structure !
__root__() { return 'app'; },
__path__() { return options.blueprintName;},
__name__() { return options.dasherizedModuleName;},
__mirage__() { return '../mirage'},
__cloud__() { return '../cloud'}
}
} else {
return {
__root__() {
if (options.inDummy) {
return 'tests/dummy/app';
} else {
return 'app';
}
},
__path__() { return 'models';},
__name__() { return options.dasherizedModuleName;},
__mirage__() { return '../mirage'},
__cloud__() { return '../cloud'}
}
}
},
locals(options) {
let attrs = [], cols = [], needs = [], fact = [], form = [], imports = {}, ctrlVars= [], paramObject = [];
let entityOptions = options.entity.options;
let entityName = options.entity.name;
let capitalizeEntityName = entityName.capitalize() ;
const rel = Object.keys(entityOptions)
.filter(key => (/has-many/.test(entityOptions[key]) || /belongs-to/.test(entityOptions[key])))
.map(key => '\n\t\t' + stringUtils.camelize(key) + relFunction(entityOptions[key]))
Object.values(entityOptions).forEach(value => {
if (/has-many/.test(value)) {
imports.hasMany = true
}
if (/belongs-to/.test(value)) {
imports.belongsTo = true
}
})
for (let name in entityOptions) {
let type = entityOptions[name] || '';
let foreignModel = name;
if (type.indexOf(':') > -1) {
foreignModel = type.split(':')[1];
type = type.split(':')[0];
}
let dasherizedName = stringUtils.dasherize(name);
let camelizedName = stringUtils.camelize(name);
let dasherizedType = stringUtils.dasherize(type);
let dasherizedForeignModel = stringUtils.dasherize(foreignModel);
let dasherizedForeignModelSingular = inflection.singularize(dasherizedForeignModel);
let attr;
if (/has-many/.test(dasherizedType)) {
let camelizedNamePlural = inflection.pluralize(camelizedName);
attr = dsAttr(dasherizedForeignModelSingular, dasherizedType);
attrs.push(camelizedNamePlural + ': ' + attr);
} else if (/belongs-to/.test(dasherizedType)) {
attr = dsAttr(dasherizedForeignModel, dasherizedType);
attrs.push(camelizedName + ': ' + attr);
} else {
attr = dsAttr(dasherizedName, dasherizedType);
attrs.push(camelizedName + ': ' + attr);
cols.push("Ember.Object.create({\n\tpropertyName: \""+camelizedName+"\",\n\ttitle: \""+name+"\"\n}),");
}
if (/has-many|belongs-to/.test(dasherizedType)) {
needs.push("'model:" + dasherizedForeignModelSingular + "'");
}
if (!/has-many/.test(dasherizedType) && !/belongs-to/.test(dasherizedType)) {
fact.push('\n\t' + camelizedName + fakerize(dasherizedType));
// todo Generate input form for creating entity (without any relationship)
form.push('{{input type="text" name="'+camelizedName+'" value='+camelizedName+' placeholder="'+camelizedName+'"}}');
ctrlVars.push(camelizedName+': null,');
}
}
let needsDeduplicated = needs.filter(function(need, i) {
return needs.indexOf(need) === i;
});
attrs = attrs.join(',' + EOL + ' ');
needs = ' needs: [' + needsDeduplicated.join(', ') + ']';
cols = cols.join(EOL);
return {
entity: entityName,
attrs: attrs,
cols: cols,
needs: needs,
fact: fact,
rel: rel,
ctrlVars: ctrlVars.join(EOL),
form: form.join(EOL),
imports: Object.keys(imports).map(relation => '\n\t' + relation)
};
},
afterInstall(options) {
let blueprintName = options.originBlueprintName;
let entityName = options.entity.name;
// Check that /mirage/serializers/applications.js is existing
let mirageSerializerPath = (options.dummy) ? "tests/dummy/mirage/serializers/application.js" : "mirage/serializers/application.js";
if (!fs.existsSync(mirageSerializerPath)) {
this.ui.writeLine("Creating file "+mirageSerializerPath);
let serializerContent = "import { JSONAPISerializer } from 'ember-cli-mirage';\n\n\texport default JSONAPISerializer.extend({\n});\n";
fs.writeFileSync(mirageSerializerPath, serializerContent, 'utf-8', 'w+');
}
// Complete /mirage/scenarios/default.js
let mirageScenarioPath = (options.dummy) ? "tests/dummy/mirage/scenarios/default.js" : "mirage/scenarios/default.js";
let scenarioLine = '\tserver.createList\(\''+entityName+'\', 15\);\n';
if (!fs.existsSync(mirageScenarioPath)) {
this.ui.writeLine("Creating file "+mirageScenarioPath);
let scenarioContent = "export default function(server) {\n"+scenarioLine+"}\n";
fs.writeFileSync(mirageScenarioPath, scenarioContent, 'utf-8', 'w+');
} else {
addLineToFile(this, mirageScenarioPath, /export default function\(server\) {/, scenarioLine);
}
// Complete /mirage/config.js
let mirageConfigPath = (options.dummy) ? "tests/dummy/mirage/config.js" : "mirage/config.js";
let configLine = "this.get('/"+inflection.pluralize(entityName)+"', '"+inflection.pluralize(entityName)+"');\n"
if (!fs.existsSync(mirageConfigPath)) {
this.ui.writeLine("Creating file "+mirageConfigPath);
let configContent = "export default function() {\n\tthis.namespace = '';\n"+configLine+"}\n";
fs.writeFileSync(mirageConfigPath, configContent, 'utf-8','w+');
} else {
addLineToFile(this, mirageConfigPath, /this\.namespace = \'\';/, configLine);
}
// Add route in router.js for the entity page
let routerPath = (options.dummy) ? "tests/dummy/app/router.js" : "app/router.js";
let routeName = '\tthis.route\(\''+blueprintName+'/'+entityName+'\'\);';
addLineToFile(this, routerPath, /Router\.map\(function\(\) {/, routeName);
}
};
function addLineToFile(ctx, filePath, markerString, addedLine) {
let fileContents = fs.readFileSync(filePath, 'utf-8');
if (fileContents.indexOf(addedLine) === -1) {
ctx.ui.writeLine("\tAdd line "+addedLine+" to file "+filePath);
fileContents = fileContents.replace(markerString, [
'$&',
addedLine,
].join('\n'));
}
if (fileContents.indexOf(addedLine) === -1) {
ctx.ui.writeWarnLine(
'Unable to update '+filePath+'. You should update this file manually.');
} else {
fs.writeFileSync(filePath, fileContents, 'utf-8');
}
}
function dsAttr(name, type) {
switch (type) {
case 'belongs-to':
return "DS.belongsTo('" + name + "')";
case 'has-many':
return "DS.hasMany('" + name + "')";
case '':
//"If you don't specify the type of the attribute, it will be whatever was provided by the server"
//https://emberjs.com/guides/models/defining-models/
return 'DS.attr()';
default:
return "DS.attr('" + type + "')";
}
}
const fakerize = (type) => {
let typeFake
switch (type) {
case 'boolean':
typeFake = 'faker.random.boolean()'
break
case 'date':
typeFake = 'faker.date.past()'
break
case 'number':
typeFake = 'faker.random.number({min: 0,max: 10000})'
break
case 'string':
typeFake = 'faker.name.findName()'
break
default:
return null
}
return ' () {\n\t\treturn ' + typeFake + '\n\t}'
}
const relFunction = (str) => {
if (/has-many/.test(str)) {
return ': hasMany()'
}
if (/belongs-to/.test(str)) {
return ': belongsTo()'
}
}
\ No newline at end of file
/* eslint-env node */
const fs = require('fs-extra');
const jhipster = require('jhipster-core');
module.exports = {
name: 'jdlimporter',
fileName: '',
description: '',
SEPARATOR: ';',
entities: {},
normalizeEntityName(entityName) {
// Normalize and validate entity name here.
this.fileName = entityName;
return entityName;
},
beforeInstall(options) {
this.ui.writeLine("afterInstall processing for jdlimporter module with file "+this.fileName);
// TODO Check options contains a link on a JDL file
// Parse provided JDL file
var files= this.fileName.split(this.SEPARATOR);
var jdlModel = jhipster.parseFromFiles(files);
//this.ui.writeLine(JSON.stringify(jdlModel));
entities = jdlModel.entities;
for (var i=0; i<entities.length;i++) {
// Add an array field to further hold relationships
entities[i].relationships = [];
}
let relationships = jdlModel.relationships;
this.ui.writeLine("many-to-one");
relationships.filter(relation => /many-to-one/.test(relation.cardinality)).forEach(relation => {
// DS.belongsTo + DS.hasMany
this.ui.writeLine("\t"+JSON.stringify(relation));
let fromObject = relation.from;
let toObject = relation.to;
entities.filter(entity => fromObject.name === entity.name).forEach(entity => {
//BelongsTo
let rel = {type: "DS.belongsTo", injectedField: fromObject.injectedField, required: fromObject.required};
entity.relationships.push(rel);
});
entities.filter(entity => toObject.name === entity.name).forEach(entity => {
//hasMany
let rel = {type: "DS.hasMany"};
entity.relationships.push(rel);
});
});
this.ui.writeLine("one-to-many");
relationships.filter(relation => /one-to-many/.test(relation.cardinality)).forEach(relation => {
// DS.hasMany + DS.belongsTo
this.ui.writeLine("\t"+JSON.stringify(relation));
});
this.ui.writeLine("many-to-many");
relationships.filter(relation => /many-to-many/.test(relation.cardinality)).forEach(relation => {
// DS.hasMany
this.ui.writeLine("\t"+JSON.stringify(relation));
});
this.ui.writeLine("one-to-one");
relationships.filter(relation => /one-to-one/.test(relation.cardinality)).forEach(relation => {
// DS.belongsTo
this.ui.writeLine("\t"+JSON.stringify(relation));
});
},
locals: function(options) {
const templateAws = fs.readFileSync('./template.json', 'utf-8'); //require('json:./template.json').default(options.target);
const usingProxy = "const usingProxy = () => {\n var usingProxyArg = !!process.argv.filter(function(arg) {\n return arg.indexOf('--proxy') === 0 || arg.indexOf('-pr') === 0 || arg.indexOf('-pxy') === 0;\n }).length;\n\n var hasGeneratedProxies = false;\n var proxiesDir = process.env.PWD + '/server/proxies';\n try {\n fs.lstatSync(proxiesDir);\n hasGeneratedProxies = true;\n } catch (e) {}\n\n return usingProxyArg || hasGeneratedProxies;\n}";
},
afterInstall(options) {
this.ui.writeLine("Real stuff begins here !");
// Check ENV value to add AWS stuff
// Perform extra work here.
}
};
const getAllMapping = (conf) => {
const json = {
"TableName": conf.pluralName
}
return JSON.stringify(json)
}
const getMapping = (conf) => {
const json = {
"TableName": conf.pluralName,
"KeyConditionExpression": "id = :v1",
"ExpressionAttributeValues": {
":v1": {
"N": "$input.params('id')"
}
}
}
return JSON.stringify(json)
}
const postMapping = (conf) => {
const json = {
"TableName": conf.pluralName,
"Item": {
"id": {
"N": "$input.path('$.id')"
}
}
}
Object.keys(conf.options).forEach(attr => {
const inputPath = "$input.path('$." + attr + "')"
let type
if (conf.options[attr] === "string") {
type = {
"S": inputPath
}
} else if (conf.options[attr] === "number") {
type = {
"N": inputPath
}
}
json.Item[attr] = type
})
return JSON.stringify(json)
}
const configureTemplate = (confs, template) => {
if (!confs.length > 0) return template
let ret = JSON.parse(JSON.stringify(template))
const resources = ret.Resources
resources.Api.Properties.Name = 'emberBackendEntity'
confs.forEach(conf => {
resources.Resource.Properties.PathPart = conf.pluralName;
resources.myDynamoDBTable.Properties.TableName = conf.pluralName;
resources.AllTemplateRequestGET.Properties.Integration.RequestTemplates["application/json"] = getAllMapping(conf);
resources.AllTemplateRequestGET.Properties.ResourceId.Ref = conf.name + "AllResource";
resources.TemplateRequestGET.Properties.Integration.RequestTemplates["application/json"] = getMapping(conf);
resources.TemplateRequestGET.Properties.ResourceId.Ref = conf.name + "Resource";
resources.TemplateRequestPOST.Properties.Integration.RequestTemplates["application/json"] = postMapping(conf);
resources.TemplateRequestPOST.Properties.ResourceId.Ref = conf.name + "Resource";
resources.AllResource.DependsOn = conf.name + "Resource";
resources.AllResource.Properties.ParentId.Ref = conf.name + "Resource";
resources[conf.name + "AllResource"] = JSON.parse(JSON.stringify(resources.AllResource));
resources[conf.name + "Resource"] = JSON.parse(JSON.stringify(resources.Resource));
resources[conf.name + "DynamoDBTable"] = JSON.parse(JSON.stringify(resources.myDynamoDBTable));
resources["All" + conf.name + "RequestGET"] = JSON.parse(JSON.stringify(resources.AllTemplateRequestGET));
resources[conf.name + "RequestGET"] = JSON.parse(JSON.stringify(resources.TemplateRequestGET));
resources[conf.name + "RequestPOST"] = JSON.parse(JSON.stringify(resources.TemplateRequestPOST));
resources.ApiDeployment.DependsOn = [
"All" + conf.name + "RequestGET",
conf.name + "RequestGET",
conf.name + "RequestPOST"
];
})
delete resources.Resource
delete resources.AllResource
delete resources.myDynamoDBTable
delete resources.TemplateRequestGET
delete resources.TemplateRequestPOST
delete resources.AllTemplateRequestGET
return ret
}
//const generateTemplate = (url) => {
const generateTemplate = (data) => {
//const data = require('../../helpers/JdlJSONParser').default(url + '/.jdl')
let entities = data.entities
const template = require('./template.json')
const rel = data.relations.map(rel => {
if (/one-to-many/.test(rel.relationshipType)) {
return [{
entity: rel.otherEntityName,
otherEntityName: rel.entityName,
relationName: stringUtils.camelize('belongs-to-' + rel.relationshipName + '-' + rel.otherEntityName)
}]
}
if (/many-to-one/.test(rel.relationshipType)) {
return [{
entity: rel.entityName,
otherEntityName: rel.otherEntityName,
relationName: stringUtils.camelize('belongs-to-' + rel.relationshipName + '-' + rel.entityName)
}]
}
if (/one-to-one/.test(rel.relationshipType)) {
return [{
entity: rel.entityName,
otherEntityName: rel.otherEntityName,
relationName: stringUtils.camelize('belongs-to-' + rel.relationshipName + '-' + rel.entityName)
},
{
entity: rel.otherEntityName,
otherEntityName: rel.entityName,
relationName: stringUtils.camelize('belongs-to-' + rel.relationshipName + '-' + rel.otherEntityName)
}
]
}
}).reduce((acc, el) => acc.concat(el), [])
rel.forEach(relation => {
let index = entities.findIndex(ent => relation.entity.localeCompare(ent.name) == 0)
if ((index > 0)) {
entities[index].options[relation.relationName] = 'number'
}
})
const generatedFile = configureTemplate(entities, template)
return {
template: generatedFile,
entities
}
}
'use strict';
const AWS = require('aws-sdk');
// BEGIN : To be removed from lambda deployment
AWS.config.update({region:'us-east-1'});
const msgPut = {
"body-json": {
"data": {
"type": "photos",
"attributes": {
"title": "Ember Hamster",
"src": "http://example.com/images/productivity.png"
},
"relationships": {
"photographer": {
"data": { "type": "people", "id": "9" }
}
}
}
},
"params": {
"path": {
"type": "photos"
},
"querystring": {},
"header": {
"Accept": "*/*",
"accept-encoding": "gzip, deflate",
"cache-control": "no-cache",
"Content-Type": "application/json",
"Host": "9o6yk638sk.execute-api.us-east-1.amazonaws.com",
"Postman-Token": "ca9ccefd-42e1-4bad-85bd-620050ef1770",
"User-Agent": "PostmanRuntime/7.4.0",
"X-Amz-Date": "20181122T091255Z",
"X-Amzn-Trace-Id": "Root=1-5bf67317-3e732e202ed67512573c7ca4",
"X-Forwarded-For": "195.5.224.170",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
}
},
"stage-variables": {},
"context": {
"account-id": "",
"api-id": "9o6yk638sk",
"api-key": "",
"authorizer-principal-id": "",
"caller": "",
"cognito-authentication-provider": "",
"cognito-authentication-type": "",
"cognito-identity-id": "",
"cognito-identity-pool-id": "",
"httpMethod": "POST",
"stage": "default",
"source-ip": "195.5.224.170",
"user": "",
"user-agent": "PostmanRuntime/7.4.0",
"user-arn": "",
"request-id": "cc5001d5-ee36-11e8-8ae7-273051a62acd",
"resource-id": "anwfak",
"resource-path": "/EmberDataServerless/{type}"
}
};
// END
const dynamo = new AWS.DynamoDB.DocumentClient();
const tableName = 'EmberDataServerlessTable';
const EPOCH = 1300000000000;
// Instagram inspired --> https://instagram-engineering.com/sharding-ids-at-instagram-1cf5a71e5a5c
function generateRowId(subid) {
var ts = new Date().getTime() - EPOCH; // limit to recent
// 41 bits for time in milliseconds (gives us 41 years of IDs with a custom epoch
var randid = Math.floor(Math.random() * 512);
ts = (ts * 64); // bit-shift << 6
// Given shard (if any...)
ts = ts + subid;
// random value
return (ts * 512) + (randid % 512);
}
const createObject = (obj) => {
let objout = { "type": obj.ObjectType,
"id": obj.Id,
"attributes": {},
"relationships": {}
};
for (var attr in obj) {
if (attr !== 'ObjectType' && attr !== 'Id') {
if(!attr.endsWith('_id')) {
objout.attributes[attr] = obj[attr];
} else {
let relationDetails = attr.split('_');
let relationId = Number(obj[attr]);
objout.relationships[relationDetails[0]] = {
"links": {