I am using ember 2.18 and ember-data-factory-guy 3.3.0.
I want to test an action inside a component. In the action, I am using makeList in order to create and return a list of models. In my ember app, I pass the store to the component and I am doing a query when I a call the action.
Here is my Factory:
import FactoryGuy from 'ember-data-factory-guy';
FactoryGuy.define('contact', {
sequences: {
first_name: (num) => `FirstName${num}`,
last_name: (num) => `LastName${num}`,
email: (num) => `contact${num}#example.com`,
phone: (num) => `01234567${num}`,
linkedin: (num) => `samplelinkedinurl${num}`,
},
default: {
first_name: FactoryGuy.generate('first_name'),
last_name: FactoryGuy.generate('last_name'),
email: FactoryGuy.generate('email'),
phone: FactoryGuy.generate('phone'),
linkedin: FactoryGuy.generate('linkedin'),
position: 'CEO',
client_office: FactoryGuy.belongsTo('office')
},
traits: {
withClient: {
client: FactoryGuy.belongsTo('client')
},
withOffice: {
client_office: FactoryGuy.belongsTo('office')
}
}
});
Additionaly, my action is the followin:
_this.store.query('contact', {term: searchTerm}).then(function(contacts) { ... })
I have create a TestStore class in order to test actions like these. It is a minimal replication of the querying methods of ember data store. The query method looks like that:
query(modelName, attributes, trait=null) {
console.log('ATTRIBUTES PASSED TO TEST STORE QUERY: ', attributes)
// make a new record of selected model with factory guy
let _this = this,
records = Ember.isEmpty(trait) ? makeList(modelName, 4) : makeList(modelName, 4, trait);
// Push it to test-store
if (!this.models[modelName]) {
this.models[modelName] = Ember.A([]);
}
records.forEach(function(record) {
_this.models[modelName].pushObject(record); })
// Create promise to return
let recordPromise = new Ember.RSVP.Promise((resolve) => {
resolve(records);
});
return DS.PromiseArray.create({promise: recordPromise});
}
In rendering tests it is working just fine. If I am to run the action manually (e.g. like in unit tests ), I get the following error:
FactoryGuy does not have the application's
manualSetup is not working as well, as the store service is stuck in the "willDestroy()" of the store and _pushedInternalModels is null so no Models can be pushed to the store.
Any ideas/suggestions on that?
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
I have a API than return a user list /api/user :
[{
id: 1,
firstname: 'toto',
lastname: 'titi'
},
{
...
}]
Now I want add a pagination informations from my UserRepository
{
result: [{
id: 1,
firstname: 'toto',
lastname: 'titi'
},
{
...
}],
pagination: {
totalPages: 75,
page: 2,
limit: 25
}
}
My controller (interface layer)
...
router.get('/users', ctx => {
getUsersUseCase
.execute(ctx.state.limit, ctx.state.offset)
.then(data => {
ctx.response.status = Status.OK;
ctx.response.body = data;
})
.catch(error => {
ctx.response.status = Status.INTERNAL_SERVER_ERROR;
ctx.response.body = error;
});
});
...
In my application layer, I have a getUsers use case :
// app/getUsers.js
module.exports = () => {
const execute = (limit, offset) => {
const userListPaginated = userRepository.find(limit, offset);
return userListPaginated; // before it returned just a User Array
};
return {
execute,
};
};
My domain User model:
// domain/User.js
module.exports = {
id: Integer,
username: String,
email: String,
firstName: String,
lastName: String
}
My domain UserListPaginated model:
// domain/UserListPaginated.js
module.exports = {
result: [], // User list
pagination: {}, // pagination informations
}
And in my UserRepository (I using https://github.com/aravindnc/mongoose-paginate-v2 for the pagination information):
class UserRepository {
constructor({ model }) {
this.userModel = model; // mongoose model
}
async find(limit, offset) {
var userListPaginated;
this.userModel.paginate({}, { offset, limit }).then(function(result) {
userListPaginated = {
result: result.docs,
pagination: {
totalPages: result.totalPages
limit: result.limit - 10
offset: result.offset
}
});
return userListPaginated; // before returned just a array of user model
}
My problem, I think, is that the pagination informations not concern the Domain (in the UserListPaginated.js).
It is just here to be return to the API controller. But I need to return this information from the repository and cross the domain layer.
What is the good DDD pratrice ?
Thanks
It can be a good idea to by-pass the domain model entirely for queries (CQRS). Let the domain model focus on writes/commands and deal with reads/queries elsewhere. For instance, you could have an IUserQueryService interface in the application layer which gets implemented in the infrastructure layer (it could even be implemented by the same repository class).
It's important to be pragmatic though. If you feel comfortable using domain entities directly in query results then polluting the repository's interface with query concerns such as pagination state could be acceptable. It's always a question of trade-offs and you shouldn't strive for pureness when it's not practical.
Thanks for your answer. For the moment I not use CQRS in my Clean Architecture.
In my UserController if I call two use case, do you think it is a better idea ? getUserCountUseCase is it a really good use case ?
router.get('/users', ctx => {
const usersPromise = getUsersUseCase // Old use case (just a users array)
.execute(ctx.state.limit, ctx.state.offset)
const userCountPromise = getUserCountUseCase
.execute(ctx.state.limit, ctx.state.offset)
Promise.all([userCountPromise, usersPromise])
.then((values) => {
const [count, users] = values;
const userListPaginated = {
result: users,
pagination: {
totalPages: count,
page: Math.ceil((offset + 1) / limit),
limit: ctx.state.limit
}
}
ctx.response.status = Status.OK;
ctx.response.body = userListPaginated;
})
});
I'm new using VUE.JS and I'm in love with it! I love the vue-router and router-link! They are awesome!
Now I have a table populated by data coming from axios and I would like to build a link using this data in a custom method to have the team name clickable.
Here the template:
<BootstrapTable :columns="table.columns" :data="table.data" :options="table.options"></BootstrapTable>
Axios returns ID, name and other data used to update the table as here
Basically, I need to update the values in my table using the axios's received data. Something like:
team: '<a v-bind:href="club/'+team.id+'">'+team.team+'</a>',
or
team: '<router-link :to="club/'+team.id+'">'+team.team+'</router-link>',
But obviously it dosn't works...
How can a build a link?
I fixed it using custom column event and formatter in columns table setting:
{
field: 'match',
title: 'Match',
formatter (value, row) {
return `${value}`
},
events: {
'click a': (e, value, row, index) => {
e.preventDefault();
this.$router.push(`/matches/${row.pos}`)
}
}
},
Another solution:
Just in case of JSON code having links instead of table config is adding click listener in mounted() and a well formatted dataset in JSON HTML link:
team: "<a href=\"/club/"+team.id+"\" data-to='{\"name\": \"team\",\"params\":{\"teamId\":"+ team.id+"}}'>"+ team.team+"</a> "+userCode
Here the listener:
mounted() {
window.addEventListener('click', event => {
let target = event.target;
if (target && target.href && target.dataset.to) {
event.preventDefault();
const url = JSON.parse(target.dataset.to);
//router.push({ name: 'user', params: { userId: '123' } })
this.$router.push(url);
}
});
}
This might be shorter solution for your issue :
routes = [
{
component : 'club',
name : 'club',
path : '/club/:teamid'
}
]
<a #click="$router.push({ name: 'club', params: { teamid: team.id}})">team.team</a>
I have tried searching everywhere, from stackoverflow to GitHub but i can get a solution. I am trying to get list of users by using their userid that I get from a collection of businesses. What Am i doing wrong?
componentWillMount() {
//Loading all the business collections.
firebase.firestore().collection("business").onSnapshot((snapshot) => {
var bizs = [];
snapshot.forEach((bdt) => {
var userdt = [];
//get document id of a certain user in the business collections
firebase.firestore().collection('users').where("userid", "==", bdt.data().userid).get()
.then((snap) => {
snap.forEach(dc => {
//loading details of the user from a specific ID
firebase.firestore().collection("users").doc(dc.id).onSnapshot((udt) => {
userdt.push({
name: udt.data().fullname,
photourl: udt.data().photoURL,
location: bdt.data().location,
openhrs: bdt.data().openHrs,
likes: '20',
reviews: '3002',
call: bdt.data().contacts
});
console.log(userdt); //this one works
})
console.log(userdt); // but this one doesnt diplay anything just []
})
}).catch((dterr) => {
console.log(dterr)
})
});
this.setState({bizdata: bizs,loading: false
});
});
}
I am using react-native and firestore
Put log with some number,
like,
console.log('1',userdt);
console.log('2',userdt);
and check weather which one is appearing first, Maybe '2' is executing before updating the data
I have to say HttpClient Observables, subscriptions etc are pretty hard/time consuming to get right.
I have been working on a problem for a while now and tearing my hair out. I have a service that I need to be able to perform a mapping function on.
loadAllSummary(organisationId: number) {
return this.http.get('/api/aircrafts/organisations/' + organisationId)
.pipe(
map(data => data.forEach(datum => {
console.log('why am i not getting here! ' + JSON.stringify(data));
return this.mapToSummary(datum);
}))
);
}
with the mapToSummary() method:
private mapToSummary(aircraft: Aircraft): IAircraftSummary {
const lastDate: Date = new Date(Math.max.apply(null, aircraft.workorders.map(function(e) {
return new Date(e.date);
})));
return new AircraftSummary({
lastWork: lastDate,
rego: aircraft.registration,
make: aircraft.make,
model: aircraft.model,
contact: (aircraft.owner.type.endsWith('primary')) ? aircraft.owner.principal : aircraft.operator.principal,
phone: (aircraft.owner.type.endsWith('primary')) ? aircraft.owner.contact.phone : aircraft.operator.contact.phone
});
}
Now, I need these summaries as input data to a view, so I borrowed code from the interwebs and created this ResolverService:
#Injectable()
export class AircraftsResolverService implements Resolve<IAircraftSummary[]> {
constructor(private service: AircraftService,
private router: Router) { }
resolve(route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<IAircraftSummary[]> {
console.log('called AircraftsResolverService')
const id = route.params['id'];
if (isNaN(+id)) {
console.log(`Organisation id was not a number: ${id}`);
this.router.navigate(['/login']);
return Observable.of(null);
}
return this.service.loadAllSummary(+id)
.map(summaries => {
console.log(summaries)
if (summaries) {
return summaries;
}
console.log(`Summaries were not found: ${id}`);
this.router.navigate(['/organisations/', +id]);
return null;
})
.catch(error => {
console.log(`Retrieval error: ${error}`);
this.router.navigate(['/organisations/', +id]);
return Observable.of(null);
});
}
}
Which I then refer to in the ngOnInit call...
ngOnInit() {
this.currentUser = this.authenticationService.returnCurrentUser();
this.route.data
.subscribe(({aircrafts}) => {
this.aircrafts = aircrafts;
const id = +this.route.snapshot.paramMap.get('id');
console.log(' where are my aircraft!' + JSON.stringify(aircrafts));
this.ELEMENT_DATA = aircrafts;
this.displayedColumns = ['Last Work', 'Rego', 'Make', 'Model', 'Contact', 'Phone'];
this.dataSource = new MatTableDataSource(this.ELEMENT_DATA);
this.dataSource.sort = this.sort;
console.log(id);
if (id) {
this.organisationService.getById(id).subscribe(org => {
this.organisation = org;
});
} else {
console.log('its bad');
}
});
console.log(this.dataSource);
}
The console log under the subscribe is undefined and the console.logs under the service never get triggered. So once again, I find myself not understanding why subscription fire or not fire, or whatever it is that they do.
How do I get past this? thanks everyone.
EDIT: appears that the problem is actually in the ResolverService, I have been able to determine that the data service is getting the results and that they are correct. For some reason, the resolver service can't see them.
The answer was in the route resolver, or rather the app-routing-module. I should have included it in the question, because some of the angular saltys would have picked it up
I was trying to do this:.
{ path: 'organisations/:orgId/aircrafts/:id', component: AircraftsComponent, resolve: {aircrafts : AircraftsResolverService}, canActivate: [AuthGuard] },
But you can't, you have to do this:
{ path: 'organisations/aircrafts/:orgId/:id', component: AircraftsComponent, resolve: {aircrafts : AircraftsResolverService}, canActivate: [AuthGuard] },
results in very non-resty urls, but, hey, whatever works, right?
I am learning GraphQL so I built a little project. Let's say I have 2 models, User and Comment.
const Comment = Model.define('Comment', {
content: {
type: DataType.TEXT,
allowNull: false,
validate: {
notEmpty: true,
},
},
});
const User = Model.define('User', {
name: {
type: DataType.STRING,
allowNull: false,
validate: {
notEmpty: true,
},
},
phone: DataType.STRING,
picture: DataType.STRING,
});
The relations are one-to-many, where a user can have many comments.
I have built the schema like this:
const UserType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: {
type: GraphQLString
},
name: {
type: GraphQLString
},
phone: {
type: GraphQLString
},
comments: {
type: new GraphQLList(CommentType),
resolve: user => user.getComments()
}
})
});
And the query:
const user = {
type: UserType,
args: {
id: {
type: new GraphQLNonNull(GraphQLString)
}
},
resolve(_, {id}) => User.findById(id)
};
Executing the query for a user and his comments is done with 1 request, like so:
{
User(id:"1"){
Comments{
content
}
}
}
As I understand, the client will get the results using 1 query, this is the benefit using GraphQL. But the server will execute 2 queries, one for the user and another one for his comments.
My question is, what are the best practices for building the GraphQL schema and types and combining join between tables, so that the server could also execute the query with 1 request?
The concept you are refering to is called batching. There are several libraries out there that offer this. For example:
Dataloader: generic utility maintained by Facebook that provides "a consistent API over various backends and reduce requests to those backends via batching and caching"
join-monster: "A GraphQL-to-SQL query execution layer for batch data fetching."
To anyone using .NET and the GraphQL for .NET package, I have made an extension method that converts the GraphQL Query into Entity Framework Includes.
public static class ResolveFieldContextExtensions
{
public static string GetIncludeString(this ResolveFieldContext<object> source)
{
return string.Join(',', GetIncludePaths(source.FieldAst));
}
private static IEnumerable<Field> GetChildren(IHaveSelectionSet root)
{
return root.SelectionSet.Selections.Cast<Field>()
.Where(x => x.SelectionSet.Selections.Any());
}
private static IEnumerable<string> GetIncludePaths(IHaveSelectionSet root)
{
var q = new Queue<Tuple<string, Field>>();
foreach (var child in GetChildren(root))
q.Enqueue(new Tuple<string, Field>(child.Name.ToPascalCase(), child));
while (q.Any())
{
var node = q.Dequeue();
var children = GetChildren(node.Item2).ToList();
if (children.Any())
{
foreach (var child in children)
q.Enqueue(new Tuple<string, Field>
(node.Item1 + "." + child.Name.ToPascalCase(), child));
}
else
{
yield return node.Item1;
}
}}}
Lets say we have the following query:
query {
getHistory {
id
product {
id
category {
id
subCategory {
id
}
subAnything {
id
}
}
}
}
}
We can create a variable in "resolve" method of the field:
var include = context.GetIncludeString();
which generates the following string:
"Product.Category.SubCategory,Product.Category.SubAnything"
and pass it to Entity Framework:
public Task<TEntity> Get(TKey id, string include)
{
var query = Context.Set<TEntity>();
if (!string.IsNullOrEmpty(include))
{
query = include.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Aggregate(query, (q, p) => q.Include(p));
}
return query.SingleOrDefaultAsync(c => c.Id.Equals(id));
}