I'm using generator-jhipster and I want to create blueprints for entity-client. After writing entity files, a postWriting function will call addEnitiyToMenu in generator-jhipster/generators/client/needle-api/needle-client-react.js to add new entity generated to file menu/entities.tsx
I need to override this function to write a different entityEntry with original one.
But I can't find the template for it. What should I do?
I found that I can write these function by my own. There is example code if you need
function generateFileModel(aFile, needleTag, ...content) {
return {
file: aFile,
needle: needleTag,
splicable: content,
};
}
function addBlockContentToFile(rewriteFileModel, generator) {
try {
return jhipsterUtils.rewriteFile(rewriteFileModel, generator);
} catch (e) {
console.error(e);
return null;
}
}
function addToMenu() {
if (this.skipClient) return;
if (!this.embedded) {
this.addEntityToModule();
const entityMenuPath = `${this.CLIENT_MAIN_SRC_DIR}app/shared/layout/menus/entities.tsx`;
const entityEntry =
// prettier-ignore
this.stripMargin(`|<Menu.Item key="${this.entityStateName}" icon={<FileOutlined />}>
| <Link to="/${this.entityStateName}">
| ${this.enableTranslation ? `<Translate contentKey="global.menu.entities.${this.entityTranslationKeyMenu}" />` : `${_.startCase(this.entityStateName)}`}
| </Link>
| </Menu.Item>`);
const rewriteFileModel = generateFileModel(entityMenuPath, 'jhipster-needle-add-entity-to-menu', entityEntry);
addBlockContentToFile(rewriteFileModel, this);
}
}
function replaceTranslations() {
if (this.clientFramework === VUE && !this.enableTranslation) {
if (!this.readOnly) {
utils.vueReplaceTranslation(this, [
`app/entities/${this.entityFolderName}/${this.entityFileName}.vue`,
`app/entities/${this.entityFolderName}/${this.entityFileName}-update.vue`,
`app/entities/${this.entityFolderName}/${this.entityFileName}-details.vue`,
]);
} else {
utils.vueReplaceTranslation(this, [
`app/entities/${this.entityFolderName}/${this.entityFileName}.vue`,
`app/entities/${this.entityFolderName}/${this.entityFileName}-details.vue`,
]);
}
}
}
Related
I'm fairly new to Node.js and I am having some issues.
I received error in sonarqube as define a constant instead of duplicating 5 times for "-deleteFlag". how can i resolved this issue.
export class CCGuid {
"-deleteFlag": string;
"#text": string;
constructor(obj: any) {
if (typeof obj === "string") {
this["#text"] = obj;
this["-deleteFlag"] = "N";
} else {
try {
this["-deleteFlag"] = obj["-deleteFlag"];
} catch {
this["-deleteFlag"] = undefined;
}
try {
this["#text"] = obj["#text"];
} catch {
this["#text"] = undefined;
}
}
}
}
export class CCGuid {
"-deleteFlag": string;
"#text": string;
constructor(obj: any) {
const deleteFlag = "-deleteFlag";
if (typeof obj === "string") {
this["#text"] = obj;
this[deleteFlag] = "N";
} else {
try {
this[deleteFlag] = obj[deleteFlag];
} catch {
this[deleteFlag] = undefined;
}
try {
this["#text"] = obj["#text"];
} catch {
this["#text"] = undefined;
}
}
}
}
I think this should do the trick with SQ, at least when it comes to that particular variable. You can do the same with "#text" of course.
After edit: sorry my first answer was broken, I was in a rush and didn't realize what I was really writing down.
Given my updated snippet, you can do the following:
this[deleteFlag']: This will work.
this['-deleteFlag']: This will of course work but Sonar Qube will complain because your use of duplicated string literals.
this.deleteFlag: This won't work because would be looking for a deleteFlag key on the object. Such key doesn't exist, it's '-deleteFlag'.
this['deleteFlag']: this is functionally the same as the line above. Would look for a 'deletFlag' key on the object, which doesn't exist.
Sorry for the confusion! Hope this helps now
I have NodeJS program.
In one class, I have various utility methods. One function, safeGithubPush, calls safeString, another func in the same class
module.exports = {
safeString(stringToCheck) {
console.log(validator.isAscii(stringToCheck), validator.matches(stringToCheck, /^((\w)*[-.]?(\w)*)*$/))
return (
validator.isAscii(stringToCheck) &&
validator.matches(stringToCheck, /^((\w)*[-.]?(\w)*)*$/)
);
},
safeGithubPush(currentJob) {
if (
!currentJob ||
!currentJob.payload ||
!currentJob.payload.repoName ||
!currentJob.payload.repoOwner ||
!currentJob.payload.branchName
) {
this.logIn(
currentJob,
`${' (sanitize)'.padEnd(15)}failed due to insufficient job definition`
);
throw invalidJobDef;
}
if (
this.safeString(currentJob.payload.repoName) &&
this.safeString(currentJob.payload.repoOwner) &&
this.safeString(currentJob.payload.branchName)
) {
return true;
}
throw invalidJobDef;
},
}
While this.logIn(), another func in the utility class, works just fine, I get the error for safeString:
Error caught by first catch: TypeError: this.safeString is not a function
I followed a solution offer by another SO post:
safeString: function(stringToCheck){
...
}
safeGithubPush(currentJob) {
...
if (
this.safeString(currentJob.payload.repoName) &&
this.safeString(currentJob.payload.repoOwner) &&
this.safeString(currentJob.payload.branchName)
) {
return true;
}
}
But this also gets a, TypeError: this.safeString is not a function.
I'm not using arrow functions, which is the explanation for this error on a different SO post
I don't think the reason is determinable with the code you are currently presenting. It likely has something to do with how you are calling safeGithubPush. If you do something that would change the this binding the this.safeString is going to fail.
const foo = {
fizz() {
console.log("fizz");
},
buzz() {
this.fizz();
}
};
// "this" is correct
foo.buzz();
// "this" has no fizz to call
const myFizz = foo.buzz;
myFizz();
Considering you are attaching these to module.exports I am going to guess that you pull these functions off in a require call and then try to use them bare which makes the problem obvious after looking at my example above:
// Ignore these 2 lines, they let this look like node
const module = {};
const require = () => module.exports;
// Ignore above 2 lines, they let this look like node
// Your module "some.js"
module.exports = {
safeString(str) {
return true;
},
safeGithubPush(currentJob) {
if (!this.safeString("some")) {
throw new Error("Not safe");
}
return true;
}
};
try {
// Some consumer module that doesn't work
const {safeGithubPush} = require("./some.js");
const isItSafe = safeGithubPush();
console.log(`Safe? ${isItSafe}`);
} catch (err) {
console.error("Didn't bind right \"this\"");
}
try {
// Some consumer module that DOES work
const someModule = require("./some.js");
const isItSafe = someModule.safeGithubPush();
console.log(`Safe? ${isItSafe}`);
} catch (err) {
console.error(err);
}
I would restructure this code. You say these are utility functions which makes me think you don't really want to have to structure them with this in mind.
Instead of attaching them all to module.exports at their definition, define them outside and directly reference the functions you want to use, then attach them to exports so other modules can use the functions:
function safeString(stringToCheck) {
return true;
}
function safeGithubPush(currentJob) {
if (!safeString("some")) {
throw new Error("Not safe");
}
return true;
}
module.exports = {
safeString,
safeGithubPush
};
I have a website where I can upload a .xlsx file which contains some rows of information for my database. I read the documentation for laravel-excel but it looks like it only works with progress bar if you use the console method; which I don't.
I currently just use a plain HTML upload form, no ajax yet.
But to create this progress bar for this I need to convert it to ajax, which is no hassle, that I can do.
But how would I create the progress bar when uploading the file and iterating through each row in the Excel file?
This is the controller and method where the upload gets done:
/**
* Import companies
*
* #param Import $request
* #return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
*/
public function postImport(Import $request)
{
# Import using Import class
Excel::import(new CompaniesImport, $request->file('file'));
return redirect(route('dashboard.companies.index.get'))->with('success', 'Import successfull!');
}
And this is the import file:
public function model(array $row)
{
# Don't create or validate on empty rows
# Bad workaround
# TODO: better solution
if (!array_filter($row)) {
return null;
}
# Create company
$company = new Company;
$company->crn = $row['crn'];
$company->name = $row['name'];
$company->email = $row['email'];
$company->phone = $row['phone'];
$company->website = (!empty($row['website'])) ? Helper::addScheme($row['website']) : '';
$company->save();
# Everything empty.. delete address
if (!empty($row['country']) || !empty($row['state']) || !empty($row['postal']) || !empty($row['address']) || !empty($row['zip'])) {
# Create address
$address = new CompanyAddress;
$address->company_id = $company->id;
$address->country = $row['country'];
$address->state = $row['state'];
$address->postal = $row['postal'];
$address->address = $row['address'];
$address->zip = $row['zip'];
$address->save();
# Attach
$company->addresses()->save($address);
}
return $company;
}
I know this is not much at this point. I just need some help figuring out how I would create this progress bar, because I'm pretty stuck.
My thought is to create a ajax upload form though, but from there I don't know.
Just an idea, but you could use the Laravel session to store the total_row_count and processed_row_count during the import execution. Then, you could create a separate AJAX call on a setInterval() to poll those session values (e.g., once per second). This would allow you to calculate your progress as processed_row_count / total_row_count, and output to a visual progress bar. – matticustard
Putting #matticustard comment into practice. Below is just sample of how things could be implemented, and maybe there are areas to improve.
1. Routes
import route to initialize Excel import.
import-status route will be used to get latest import status
Route::post('import', [ProductController::class, 'import']);
Route::get('import-status', [ProductController::class, 'status']);
2. Controller
import action will validate uploaded file, and pass $id to ProductsImport class. As it will be queued and run in the background, there is no access to current session. We will use cache in the background. It will be good idea to generate more randomized $id if more concurrent imports will be processed, for now just unix date to keep simple.
You currently cannot queue xls imports. PhpSpreadsheet's Xls reader contains some non-utf8 characters, which makes it impossible to queue.
XLS imports could not be queued
public function import()
{
request()->validate([
'file' => ['required', 'mimes:xlsx'],
]);
$id = now()->unix()
session([ 'import' => $id ]);
Excel::queueImport(new ProductsImport($id), request()->file('file')->store('temp'));
return redirect()->back();
}
Get latest import status from cache, passing $id from session.
public function status()
{
$id = session('import');
return response([
'started' => filled(cache("start_date_$id")),
'finished' => filled(cache("end_date_$id")),
'current_row' => (int) cache("current_row_$id"),
'total_rows' => (int) cache("total_rows_$id"),
]);
}
3. Import class
Using WithEvents BeforeImport we set total rows of our excel file to the cache. Using onRow we set currently processing row to the cache. And AfterReset clear all the data.
<?php
namespace App\Imports;
use App\Models\Product;
use Maatwebsite\Excel\Row;
use Maatwebsite\Excel\Concerns\OnEachRow;
use Maatwebsite\Excel\Events\AfterImport;
use Maatwebsite\Excel\Events\BeforeImport;
use Maatwebsite\Excel\Concerns\WithEvents;
use Illuminate\Contracts\Queue\ShouldQueue;
use Maatwebsite\Excel\Concerns\WithStartRow;
use Maatwebsite\Excel\Concerns\WithChunkReading;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
class ProductsImport implements OnEachRow, WithEvents, WithChunkReading, ShouldQueue
{
public $id;
public function __construct(int $id)
{
$this->id = $id;
}
public function chunkSize(): int
{
return 100;
}
public function registerEvents(): array
{
return [
BeforeImport::class => function (BeforeImport $event) {
$totalRows = $event->getReader()->getTotalRows();
if (filled($totalRows)) {
cache()->forever("total_rows_{$this->id}", array_values($totalRows)[0]);
cache()->forever("start_date_{$this->id}", now()->unix());
}
},
AfterImport::class => function (AfterImport $event) {
cache(["end_date_{$this->id}" => now()], now()->addMinute());
cache()->forget("total_rows_{$this->id}");
cache()->forget("start_date_{$this->id}");
cache()->forget("current_row_{$this->id}");
},
];
}
public function onRow(Row $row)
{
$rowIndex = $row->getIndex();
$row = array_map('trim', $row->toArray());
cache()->forever("current_row_{$this->id}", $rowIndex);
// sleep(0.2);
Product::create([ ... ]);
}
}
4. Front end
On the front-end side this is just sample how things could be handled. Here I used vuejs, ant-design-vue and lodash.
After uploading file handleChange method is called
On successful upload trackProgress method is called for the first time
trackProgress method is recursive function, calling itself on complete
with lodash _.debounce method we can prevent calling it too much
export default {
data() {
this.trackProgress = _.debounce(this.trackProgress, 1000);
return {
visible: true,
current_row: 0,
total_rows: 0,
progress: 0,
};
},
methods: {
handleChange(info) {
const status = info.file.status;
if (status === "done") {
this.trackProgress();
} else if (status === "error") {
this.$message.error(_.get(info, 'file.response.errors.file.0', `${info.file.name} file upload failed.`));
}
},
async trackProgress() {
const { data } = await axios.get('/import-status');
if (data.finished) {
this.current_row = this.total_rows
this.progress = 100
return;
};
this.total_rows = data.total_rows;
this.current_row = data.current_row;
this.progress = Math.ceil(data.current_row / data.total_rows * 100);
this.trackProgress();
},
close() {
if (this.progress > 0 && this.progress < 100) {
if (confirm('Do you want to close')) {
this.$emit('close')
window.location.reload()
}
} else {
this.$emit('close')
window.location.reload()
}
}
},
};
<template>
<a-modal
title="Upload excel"
v-model="visible"
cancel-text="Close"
ok-text="Confirm"
:closable="false"
:maskClosable="false"
destroyOnClose
>
<a-upload-dragger
name="file"
:multiple="false"
:showUploadList="false"
:action="`/import`"
#change="handleChange"
>
<p class="ant-upload-drag-icon">
<a-icon type="inbox" />
</p>
<p class="ant-upload-text">Click to upload</p>
</a-upload-dragger>
<a-progress class="mt-5" :percent="progress" :show-info="false" />
<div class="text-right mt-1">{{ this.current_row }} / {{ this.total_rows }}</div>
<template slot="footer">
<a-button #click="close">Close</a-button>
</template>
</a-modal>
</template>
<script>
export default {
data() {
this.trackProgress = _.debounce(this.trackProgress, 1000);
return {
visible: true,
current_row: 0,
total_rows: 0,
progress: 0,
};
},
methods: {
handleChange(info) {
const status = info.file.status;
if (status === "done") {
this.trackProgress();
} else if (status === "error") {
this.$message.error(_.get(info, 'file.response.errors.file.0', `${info.file.name} file upload failed.`));
}
},
async trackProgress() {
const { data } = await axios.get('/import-status');
if (data.finished) {
this.current_row = this.total_rows
this.progress = 100
return;
};
this.total_rows = data.total_rows;
this.current_row = data.current_row;
this.progress = Math.ceil(data.current_row / data.total_rows * 100);
this.trackProgress();
},
close() {
if (this.progress > 0 && this.progress < 100) {
if (confirm('Do you want to close')) {
this.$emit('close')
window.location.reload()
}
} else {
this.$emit('close')
window.location.reload()
}
}
},
};
</script>
In our code, we repeat the same sequence multiple times: starting a spinner, then execute a spawnSync method and update the spinner depending on result. For example here is one of the method:
cloneSync() {
const spinner = ora({
text: 'Cloning repository',
color: 'cyan',
spinner: 'arrow3'
}).start();
let clone = spawnSync('git', ['clone', repository.url, repository.name]);
if (clone.stderr) {
spinner.fail('Error while cloning repository');
throw new Error(clone.stderr);
} else {
spinner.succeed('Successfully cloned repository');
return clone.stdout;
}
}
Another code example so you can see the logic is almost identical:
parseLatestTagAndTransmitToDocker() {
const spinner = ora({
text: 'Checking latest tag',
color: 'cyan',
spinner: 'arrow3'
}).start();
let tag = spawnSync('git', ['describe', '--abbrev=0']);
if (tag.stderr) {
spinner.fail('Error while fetching latest tag of repository');
throw new Error(tag.stderr);
} else {
spinner.text(`Successfully retrieved latest tag: ${tag.stdout}`);
let docker = spawnSync('docker', ['run', 'myimage:latest', tag.stdout]);
if (docker.stderr) {
spinner.fail('Error while transmitting tag to docker image');
throw new Error(docker.stderr)
} else {
spinner.success('Successfully transmitted tag to docker service');
return docker.stdout;
}
}
}
Is it possible, in node 8+ to wrap this code and make it more reusable. I struggle finding a reusable code without having to trigger spinner and the if/else condition. Doing so with async allow use of try/catch and await/async. But here with sync method, I don't find the proper way to code that kind of behavior.
From the two examples you've provided, I can see a "SpinnerSpawner" function that returns a promise:
function spinnerSpawner(spinnerConfig, cmd, args) {
if (typeof(spinnerConfig) == "string") spinnerConfig = {
text: spinnerConfig,
color: "cyan",
spinner: "arrow3"
}
return new Promise(function(resolve, reject) {
let spinner = ora(spinnerConfig).start,
tag = spawnSync(cmd, args)
if (!tag.stdError) {
resolve(spinner, tag)
} else {
reject(spinner, tag)
}
})
}
cloneSync() {
spinnerSpawner("cloning repository", "git", ["clone", repository.url, repository.name])
.then(function(spinner, proc) {
spinner.succeed('Successfully cloned repository');
return proc.stdout;
}, function(spinner, proc) {
spinner.fail('Error while cloning repository');
throw new Error(proc.stderr);
}
)
}
parseLatestTagAndTransmitToDocker() {
spinnerSpawner("Checking latest tag", "git", ["describe", "--abbrev=0"])
.then(
function(spinner, proc) {
spinner.text(`successfully retrieved latest tag: ${proc.stdout}`)
return spinnerSpawner("checking docker", "docker", ["run", "myimage:latest", proc.stdout])
}
).then(
function(spinner, proc) {
spinner.success("Processing completed")
return proc.stdout
},
function(spinner, proc) {
spinner.fail(`processing error: ${proc.stderr}`);
throw new Error(tag.stderr);
}
)
}
as always, my code is pseudo-code and not fit for execution - let alone production!
I'm writing a simple React application with a Button component, which looks like this:
import React, { Component } from 'react';
// shim to find stuff
Array.prototype.contains = function (needle) {
for (var i = 0; i < this.length; i++) {
if (this[i] == needle) return true;
}
return false;
};
class Button extends Component {
propTypes: {
text: React.PropTypes.string.isRequired,
modifiers: React.PropTypes.array
}
render() {
return(
<span className={this.displayModifiers()}>{this.props.text}</span>
);
}
displayModifiers() {
const modifiers = this.props.modifiers || ["default"];
if (modifiers.contains("default") ||
modifiers.contains("danger") ||
modifiers.contains("success")) {
// do nothing
} else {
// add default
modifiers.push("defualt");
}
var classNames = "btn"
for (var i = 0; i < modifiers.length; i++) {
classNames += " btn-" + modifiers[i]
}
return(classNames);
}
}
export default Button;
I then wrote this to test it:
it("contains the correct bootstrap classes", () => {
expect(mount(<Button modifiers={["flat"]}/>).html()).toContain("<span class=\"btn btn-flat btn-default\"></span>");
});
That code should pass, but I receive the following error message:
expect(string).toContain(value)
Expected string:
"<span class=\"btn btn-flat btn-defualt\"></span>"
To contain value:
"<span class=\"btn btn-flat btn-default\"></span>"
at Object.it (src\__tests__\Button.test.js:42:293)
Any ideas why this is not passing?
From the docs:
Use .toContain when you want to check that an item is in a list.
To test strings you should use toBe or toEqual
it("contains the correct bootstrap classes", () => {
expect(mount(<Button modifiers={["flat"]}/>).html()).toBe("<span class=\"btn btn-flat btn-default\"></span>");
});
But there is a better way of testing the output rendered components: snapshots.
it("contains the correct bootstrap classes", () => {
expect(mount(<Button modifiers={["flat"]}/>).html()).toMatchSnapshot();
});
Note that you will need enzymeToJson for snapshot testing using enzyme.