I'm trying to associate tags to an organization via sequelize's set<Model>(...) method and it seems to be setting the tags correctly in the db but the updated org with these new tags does not reflect this.
organization model
'use strict';
module.exports = (sequelize, DataTypes) => {
var organization = sequelize.define('organization', {
...
}, {});
organization.associate = function (models) {
// associations can be defined here
organization.hasMany(models.tag)
};
return organization;
};
tag model
'use strict';
module.exports = (sequelize, DataTypes) => {
...
tags.associate = function(models) {
// associations can be defined here
tags.belongsTo(models.organization)
};
return tags;
};
method
/**
* Update organization
* #param {object} db - The db handle
* #param {object} stats - The datadog client
*/
function updateOrganization(db, stats) {
return function (req, res) {
let tagsToAdd = []
let tagsToDelete = []
let myOrg
db.organization.findOne({
where: {
id: req.params.id
},
include: ['tags', 'users', 'retrievals']
})
.then(org => {
myOrg = org
return org.getTags()
})
.then(tags => {
let promises = []
...a bunch of irrelevant logic
// Add all the new tags
tagsToAdd.forEach(tag => {
promises.push(myOrg.createTag({ name: 'newTag' }))
})
return Promise.all(promises)
})
.then(updatedOrg => {
console.log('updatedOrg = ', updatedOrg) <-- Does NOT have tags in updated org output even though they actually exist in db. Why not???
return res.status(HttpStatus.OK).send(updatedOrg)
})
}
}
After countless hours of smashing my skull against anything near me i finally figured out that i needed to call reload()
.then(updatedOrg => {
console.log('updatedOrg = ', updatedOrg)
return res.status(HttpStatus.OK).send(updatedOrg)
})
should be
.then(updatedOrg => {
return myOrg.reload()
})
.then(updatedOrg => {
return res.status(HttpStatus.OK).send(updatedOrg)
})
Related
I'm facing a weird situation where I need to call a bunch of my CMS API routes from my server in order to use their response.data into an object that will then be passed to my client side.
This is the code that caches my data: meaning that when I change a field on my CMS, that data that is being pulled is not updated.
The code is:
let baseUrl = "https://sismographie-cms.herokuapp.com/api/"
let locales = ["?locale=en", "?locale=fr"]
let links = [
"contact-page",
"keywords",
"podcasts",
"random-question-arrays",
"featured-entries-headlines-anims",
"main-text",
"headline",
"cookie-setting",
"header-info-array",
"random-question-prompt",
"contact-page",
"map-entry-right-text",
"map-entry-left-text",
"sponsor-logos",
"credit",
"projects-about-texts"
].map((ele, index) => {
return {
en: `${baseUrl + ele + locales[0]}`,
fr: `${baseUrl + ele + locales[1]}`,
}
});
let objectKeys = [
"info",
"keywords",
"podcasts",
"randomQuestions",
"featuredEntries",
"balladosSubtitle",
"balladosTitles",
"cookiesSetting",
"headerInfoArray",
"randomQuestionPrompt",
"conctactPage",
"mapEntryRightText",
"mapEntryLeftText",
"sponsorLogos",
"credit",
"ProjectsAboutText"
];
let getAxiosRequests = (locale) => {
return links
.map((ele, index) => {
return axios.get(ele[locale])
})
};
axios.all(getAxiosRequests("fr"))
.then(axios.spread((...responses) => {
let cmsObjFr = mapToObject(objectKeys, responses);
axios.all(getAxiosRequests("en"))
.then(axios.spread(
(...responses) => {
let cmsObjEn = mapToObject(objectKeys, responses);
console.log(cmsObjEn);
app.get('/cms-routes', (req, res) => {
res.json({fr: cmsObjFr, en: cmsObjEn})
})
})).catch(errors => {
console.error(errors);
});
})).catch(errors => {
console.error(errors);
});
const mapToObject = (objectKeys, responses) => {
return objectKeys.reduce(
(sum, key, index) => Object.assign(
sum, { [key]: responses[index].data.data}),{}
);
};
When I access the json object, I see that the field I just changed did not update.
When I individually call that same field's CMS route, however, the response contains the updated version of the data:
app.get("/credits", (req, res ) => {
console.log("/credits' call");
axios.get("https://sismographie-cms.herokuapp.com/api/credit?locale=en")
.then(data => res.json(data.data))
})
For, let's say, the credit field, this method will give me the updated version I don't have access when I'm using the axios.spread method.
The problem is that because you create your route handler (app.get("/cms-routes")) after retrieving data, the data it responds with is fixed and will never change.
You need to move the data retrieval logic into the route handler.
Also, as mentioned above axios.all() and axios.spread() are deprecated and should not be used.
const links = {
info: "contact-page",
keywords: "keywords",
podcasts: "podcasts",
randomQuestions: "random-question-arrays",
featuredEntries: "featured-entries-headlines-anims",
balladosSubtitle: "main-text",
balladosTitles: "headline",
cookiesSetting: "cookie-setting",
headerInfoArray: "header-info-array",
randomQuestionPrompt: "random-question-prompt",
conctactPage: "contact-page",
mapEntryRightText: "map-entry-right-text",
mapEntryLeftText: "map-entry-left-text",
sponsorLogos: "sponsor-logos",
credit: "credit",
ProjectsAboutText: "projects-about-texts",
};
const baseURL = "https://sismographie-cms.herokuapp.com/api/";
/**
* Resolves with an array of single property objects, eg
* [
* {
* info: {...}
* },
* {
* keywords: {...}
* },
* ...
* ]
*/
const getAll = (locale) =>
Promise.all(
Object.entries(links).map(async ([key, link]) => ({
[key]: (await axios.get(link, { baseURL, params: { locale } })).data.data,
}))
);
app.get("/cms-routes", async (req, res) => {
const [fr, en] = await Promise.all([getAll("fr"), getAll("en")]);
res.json({ fr: Object.assign(...fr), en: Object.assign(...en) });
});
I've taken the liberty of simplifying your data structures so your links and object keys are tightly coupled.
I'm new to nextjs and I'm creating API on next.js to perform db update using the pg-promise. However, it always hit the WARNING: Creating a duplicate database object for the same connection on console when the app is calling the API.
I tried browsing the docs but couldn't find a solution. I also tried solution (update-2) mentioned on stackoverflow page below, but the warning still exists.
Where should I initialize pg-promise
I think the problem is on the method I used to set the columnset. However I can't find proper way to do it. How should I fix it with pg-promise ?
Db setting code:
import ConfigEnv from 'utils/configuration';
import * as pgLib from 'pg-promise';
const initOptions = {
capSQL: true,
};
const pgp = require('pg-promise')(initOptions);
interface IDatabaseScope {
db: pgLib.IDatabase<any>;
pgp: pgLib.IMain;
}
export function createSingleton<T>(name: string, create: () => T): T {
const s = Symbol.for(name);
let scope = (global as any)[s];
if (!scope) {
scope = {...create()};
(global as any)[s] = scope;
}
return scope;
}
export function getDB(): IDatabaseScope {
return createSingleton<IDatabaseScope>('my-app-db-space', () => {
return {
db: pgp(ConfigEnv.pgp),
pgp
};
});
}
API code:
import {getDB} from 'db/pgpdb';
const {db, pgp} = getDB();
const cs = new pgp.helpers.ColumnSet([
'?detail_id',
'age',
'name'
// 'last_modified_date',
], {
table: 'user_detail',
})
export default async (req, res) => {
try {
// generating the update query where it is needed:
const update = pgp.helpers.update(req.body.content, cs) + ` WHERE v.detail_id = t.detail_id`;
// executing the query
await db
.none(update)
.then(() => {
return res.status(200).end();
})
.catch((error) => {
console.log('error', error);
return res.status(500).send(error);
});
} catch (error) {
console.log(error);
}
};
I need to access a "random string generator function" in my spec file and call the function and be able to access the returned random value in all the tests within the spec file.
What would be the recommended way to do this?
Edit: bit more about what I want to do :
at the start of the spec I generate a random number,
us that as id to create an entry in 1st test
search for it and edit it in the 2nd test
delete it in the 3rd ...
It's even easier if your random function is synchronous, you can just add it to the Cypress object and use it tests directly.
Place this in cypress/support/index.js or at the top of the test.
Cypress.userName = () => `User-${Math.floor(Math.random() * 100000)}`;
In the test
describe('add, edit, delete a User', () => {
const userName = Cypress.userName(); // same random name for all tests in this block
it('add a user', () => {
cy.get('.username').type(userName);
cy.get('Submit').click();
})
it('search for the user', () => {
cy.get('.search').type(userName);
cy.get('.found-user').should('contain', userName);
})
it('rejects an unknown user', () => {
const anotherUser = Cypress.userName(); // new name provided here
cy.get('.search').type(anotherUser);
cy.get('.found-user').should('not.contain', anotherUser); // not added yet
})
})
As a bonus you don't have to be extra careful to use it('...', function() { all the time, it works with arrow function format it('...', () => {.
You can get random strings that are more appropriate to the usage by using Faker.js.
Sample taken from this article Using Faker to generate data for your Cypress tests
/cypress/plugins/index.js
const faker = require("faker");
module.exports = (on, config) => {
on("task", {
freshUser() {
user = {
username: faker.name.firstName(),
email: faker.internet.email(),
password: "SuperSecret",
};
return user;
},
});
};
In the test
/// <reference types="Cypress" />
let user;
describe("Docket Post Test", () => {
before(function () {
cy.task("freshUser").then((object) => {
user = object;
});
});
it("Register a new user", () => {
cy.apiRegister({
username: user.username,
email: user.email,
password: user.password,
});
});
});
Kevin's full repo is here.
I had a similar requirement for my Tests, the way I am doing it is:
First I created a file data.json under the fixtures folder with the content:
username: "user-102020"
Then I am generating my random string and then saving this value in the fixtures file. I am writing all this in the before() block of my first test since I want to pass on the same random value to all my tests.
before(function() {
const uniqueUsername = `User-${Math.floor(Math.random() * 100000)}`
cy.readFile("cypress/fixtures/data.json", (err, data) => {
if (err) {
return console.error(err);
};
}).then((data) => {
data.username = uniqueUsername
cy.writeFile("cypress/fixtures/data.json", JSON.stringify(data))
})
})
Then in the remaining tests, I am using the username from the data.json fixtures file. With every run, a new random value will be generated and this will be replaced in the data.json file and then used throughout.
describe('Random Test', function() {
before(function() {
cy.visit(url)
cy.fixture('data').then(function(data) {
this.data = data
})
})
it('Validate successful Login', function() {
cy.get('#txtUsername').type(this.data.username)
//Rest of the test
})
})
================
Now as per your question:
Create a custom command. Go to cypress/support/commands.js and write:
Cypress.Commands.add('updateRandomValueToFixtures', (uniqueUsername) => {
cy.readFile("cypress/fixtures/data.json", (err, data) => {
if (err) {
return console.error(err);
};
}).then((data) => {
data.username = uniqueUsername
cy.writeFile("cypress/fixtures/data.json", JSON.stringify(data))
})
})
Create a file data.json under the fixtures folder with the content:
username: "user-102020"
For the first test use:
//Generate a random value
const uniqueUsername = `User-${Math.floor(Math.random() * 100000)}`
//Update the random value in the fixture file
cy.updateRandomValueToFixtures(uniqueUsername)
//Use the uniqueUsername to search
describe('Random Test', function() {
before(function() {
cy.fixture('data').then(function(data) {
this.data = data
})
})
it('Some test', function() {
//this.data.username has the random value, use it to search
})
})
For Second Test use:
//Edit previously created random value and save it in a variable
const uniqueUsername = newValue
//Update the random value in the fixture file
cy.updateRandomValueToFixtures(uniqueUsername)
For the third test:
describe('Random Test', function() {
before(function() {
cy.fixture('data').then(function(data) {
this.data = data
})
})
it('Some test', function() {
//this.data.username has the updated random value, use it to delete
})
})
I have a test file with a single describe that I want to run it twice but each time I want to pass different parameters into it.
My code looks like that:
MochaRunner.js
'use strict';
const fs = require('fs');
const path = require('path');
const Mocha = require('mocha');
const uuidv1 = require('uuid/v1');
class MochaRunner {
/**
* #param {Object} options
*/
constructor(options = {}) {
this._tmps = [];
this._mocha = new Mocha(options);
}
/**
* Run tests
* #param {Array} tests
* #returns {Promise}
*/
run(tests) {
return new Promise((resolve, reject) => {
const Config = require('../config').create();
let counter = 0;
tests.forEach(test => {
const testDir = path.dirname(test.testPath);
const tmpTest = path.join(testDir,`${uuidv1()}.spec.js`);
fs.writeFileSync(tmpTest, fs.readFileSync(test.testPath));
this._tmps.push(tmpTest);
this._mocha.addFile(tmpTest);
Config.setOptions('var ' + counter++);
});
this._mocha.run((err) => {
return err ? reject(err) : resolve();
});
}).catch(() => {
return this.cleanup();
});
}
/**
* Get mocha instance
* #returns {Mocha}
*/
getMocha() {
return this._mocha;
}
/**
* Remove tmp test files
* #returns {Promise}
*/
cleanup() {
this._tmps.forEach(tmpTest => {
fs.unlinkSync(tmpTest);
});
return Promise.resolve();
}
}
module.exports = MochaRunner;
Config.js
class Config {
constructor() {
this.options = {};
}
setOptions(options){
this.options = options;
}
}
module.exports = {
_instance: null,
create: function(){
if (this._instance){
return this._instance;
}
this._instance= new Config();
return this._instance;
}
};
test.e2e.test.js
const chai = require('chai');
const Config = require('./config').create();
const assertionStyles = {
assert: chai.assert,
expect: chai.expect,
should: chai.should,
};
setImmediate(async () => {
describe('describe blah blha', function () {
console.log(Config.options);
it('test case', function () {
assertionStyles.expect(true).to.be.true;
});
});
run();
});
I do this when I want to run the above code:
let tests = ['test.e2e.test.js', 'test.e2e.test.js'];
let mocha = new MochaRunner({});
await mochaRunner.run(tests).then(() => {
mochaRunner.cleanup();
});
In the above example, I want to pass different data when the test will run for second time.
For example, I want the first file read the param from Config with the value:
'var 0'
and the second file read the param with the value:
'var 1'
Any ideas please?
Any help would be greatly appreciated!
I'm building a GraphQL Server where I need to do some sort of validation before committing data to database (MongoDB and Mongoose).
One of these checks is related to unique fields. So, a model may have one or more unique fields and I need to be able to check for that before saving into database.
So, I have build some helper functions to do it and the code is below:
Helper code:
import mongoose from "mongoose";
const isFieldUnique = (modelName, fieldName, fieldValue) => {
let model = mongoose.model(modelName);
let query = {};
query[fieldName] = fieldValue;
return model.findOne(query).exec();
};
const executeUniquePromises = (uniques, modelName, data) => {
let promises = [];
uniques.map(name => {
let value = data[name];
if (!value)
throw new Error("Cannot test uniqueness for a null field.");
promises.push(
isFieldUnique(modelName, name, value)
.then(value => {
if (value) {
let error = name + ' is not unique';
console.log(error);
return error;
}
console.log(name + ' is unique');
return null;
})
.catch(error => {
throw new Error(error);
})
)
});
return Promise.all(promises);
};
export const checkUniqueness = (uniques, modelName, data) => {
return new Promise((resolve, reject) => {
executeUniquePromises(uniques, modelName, data).then(result => {
let errors = [];
// Check for errors
result.map((error) => {
if (error)
errors.push(error);
});
if (errors.length > 0)
return reject(errors);
else
resolve();
});
});
}
Mongoose static create function:
import * as helper from './helper';
schema.statics.create = function (data) {
let uniques = ['name', 'email'];
helper.checkUniqueness(uniques,'Company', data)
.then(result => {
let user = new this(data);
return company.save();
})
.catch(error => {
throw new Error(error);
});
}
GraphQL code:
const createUser = {
type: UserType,
description: "Create a user",
args: {
data: {
name: "user",
type: new GraphQLNonNull(UserInputType)
}
},
resolve(root, args) {
return UserModel.create(args.data);
}
};
The helper code seens to be confused and I´m not using my usage of promises with other promises are the correct way of doing it.
Remember that I may need to check several fields for uniqueness, so that is why I´ve created the promise array.
One problem is that when I´m inserting data where there are not uniques matching I get no return in my GraphQL Server.
I want to find out a better way of doing it and discover why I´m not getting back the saved object.
MongoDB already handles unique out of the box. Set the field to unique: true in the Mongoose schema. You can use mongoose-beautiful-unique to make the error messages similar to the validation error messages. And finally, read this when you can't get unique: true to work.