I have a Dto that I want to enable the service layer to filter:
The method selectFields takes an array of field names that should be returned, the other properties will be removed.
What is a short way to enumerate the properties on the class so I can loop through them and set the filtered ones to null?
In the BaseDto I take care of cleaning falsy values (well I need the same function here too as a matter of fact).
class UserServiceDto extends BaseDto {
constructor(userDto) {
super();
this.fbUserId = userDto.fbUserId;
this.fbFirstName = userDto.fbFirstName;
this.fbLastName = userDto.fbLastName;
this.gender = userDto.gender;
this.birthdate = userDto.birthdate;
this.aboutMe = userDto.aboutMe;
this.deviceToken = userDto.deviceToken;
this.refreshToken = userDto.refreshToken;
this.updatedAt = userDto.updatedAt;
this.createdAt = userDto.createdAt;
}
selectFields(fields) {
// --> what's your take?
}
toJson() {
return super.toJson();
}
}
Edit:
The service layer receives a dto from repository layer including all database fields. The ServiceLayerDto aims at filtering out fields that are not required by the web api (or should not be exposed as a security measure e.g. PK field, isDeleted, etc). So the result would I'm looking at the end of a service method for would look something like:
return new UserServiceDto(userDto)
.selectFields('fbUserId', 'fbFirstName', 'fbLastName', 'birthdate', 'aboutMe', 'updatedAt', 'createdAt')
.toJson();
The return value would be a plain json object that the web layer (controller) sends back to the http client.
If you are ok with spread operator, you may try following approach:
class UserServiceDto {
constructor() {
this.a = 1;
this.b = 2;
this.c = 3;
}
selectFields(...fields) {
const result = {};
fields.forEach(key => result[key] = this[key]);
return result;
}
}
new UserServiceDto().selectFields('a', 'c'); // {a: 1, c: 3}
Looking to super.toJson() call, I think that it would not work due to the result of my selectFields() call would not be an instance of UserServiceDto class. There are some possible ways from this point I see:
instantiate new UserServiceDto object inside selectFields() body, remove all fields that not listed in the ...fields array (javascript delete is okey) and return it;
play with UserServiceDto constructor params to save positive logic on selectFields(), and pass to constructor only that props that need to be set up; in this case instantiating a temporary object will not require properties removing;
change the signature of toJson method, or better add a new signature, which would allow to pass fields array and then put current selectFields logic inside toJson method (and remove selectFields method at all): new UserServiceDto().toJson('a', 'c')...
Purely for info, I ultimately changed my app architecture.
The repository returns a Dto to the service layer (dto being mapped directly from the sql queries).
The service builds a static View based on the Dto and returns it to the web layer (represented by a plain json object).
In my directory structure, I have:
- service
-- views
--- index.js
--- UserInfo.js
The view is a simple filter. E.g. UserInfoView:
exports.build = ({ fbUserId, fbFirstName, fbLastName, gender, birthdate, aboutMe, updatedAt, createdAt }) => {
return {
fbUserId,
fbFirstName,
fbLastName,
gender,
birthdate,
aboutMe,
updatedAt,
createdAt,
};
};
Using the view, e.g. UserInfoView in the service looks like this:
const Views = require('../service/views');
exports.findActiveByUserId = async (pUserId) => {
const userDto = await UserRepository.findActiveByUserId(pUserId);
if (!userDto) {
throw new ServiceError(Err.USER_NOT_FOUND, Err.USER_NOT_FOUND_MSG);
}
return Views.UserInfo.build(userDto.toJson());
};
I think that this is much more descriptive compared to my initial take on the problem. Also, it keeps the data objects plain (no additional methods required).
It is unfortunate that I can't require receiving an type (View) in the web layer, I might be able to solve that problem with Typescript later on.
Related
Hello so I am creating a filter search and I 'm trying to collect all the key (tags) that the user press, inside an array, however every time that a new value is push it does override the entire array. So I tried a couple of things, like spread syntax, concat, etc. But with no luck.
So my action looks like this:
const setCurrentFilters = async (context, payload) => {
if (payload) {
context.commit('setCurrentFilter');
}
}
My state
state:{
filters: JSON.parse(sessionStorage.getItem('currentFilters') || '[]'),
}
The mutation
setCurrentFilter(state, payload) {
state.filters.push(payload);
sessionStorage.setItem('currentFilters', JSON.stringify(payload));
}
And my getter
currentFilters(state) {
return state.filters;
},
Thank you in advance for any help : )
This is simply because you set const filters = []; which means that the next condition if (filters.length) will always return false (as you just created this array) and therefore the else statement will execute.
in the else statement you basically push the new payload to the empty array you just initialized - which makes your array always hold only the new value
i believe that you just need to remove the const filters = []; line, and access the filters property that exists in your state
Currently, I have a select element in my html which has a ngModel to the object details:
[ngModel]="details?.publicInformation?.firstname"
However, publicInformation may not exist in that object, or if it does, maybe firstname does not exist. No matter the case, in the end, I want to create the following:
[ngModel]="details?.publicInformation?.firstname" (ngModelChange)="details['publicInformation']['firstname'] = $event"
Basically, if the select is triggered, even if neither of publicInformation nor firstname exist, I would like to create them inside details and store the value from the select.
The issue is that I am getting
Cannot set property 'firstname' of undefined
Can someone explain what I am doing wrong here and how can I achieve the result I desire?
You need to initialize details and publicInformation to empty object
public details = {publicInformation : {}};
You should do that when you load the form data.
For example, you might have something like this:
ngOnInit() {
this._someService.loadForm().then((formData: FormData) => {
this.details = formData;
});
}
Then, you could modify that to fill in the missing empty properties you need:
ngOnInit() {
this._someService.loadForm().then((formData: FormData) => {
this.details = formData || {};
if (!this.details.publicInformation) {
this.details.publicInformation = { firstname: '' };
} else if (!this.details.publicInformation.firstname) {
this.details.publicInformation.firstname = '';
}
});
}
However, it would be better to place this logic in the services, so that they are responsible for adding all the necessary empty properties to the data they load, or if you are using Redux, then it should go into the reducers.
Background
We have a request object that contains information. That specific object has a field called partnerId which determines what we are going to do with the request.
A typical approach would be a gigantic if/then/else:
function processRequest( request ){
if( request.partnerId === 1 ){
//code here
}else if( request.partnerId === 23 ){
//code here
}
//and so on. This would be a **huge** if then else.
}
This approach has two main problems:
This function would be huge. Huge functions are a code smell (explaining why next) but mainly they become very hard to read and maintain very quickly.
This function would do more than one thing. This is a problem. Good coding practices recommend that 1 function should do only 1 thing.
Our solution
To bypass the previous problems, I challenged my co-worker to come up with a different solution, and he came up with a function that dynamically builds the name of the function we want to use and calls it. Sounds complicated but this code will clarify it:
const functionHolder = {
const p1 = request => {
//deals with request
};
const p23 = request => {
//deals with request
};
return { p1, p23 };
};
const processRequest = request => {
const partnerId = request.partnerId;
const result = functionHolder[`p${partnerId}`](request);
return result;
};
Problems
This solution has advantages over the previous one:
There is no main function with an huge gigantic if then else.
Each execution path is not a single function that does one thing only
However it also has a few problems:
We are using an object functionHolder which is in reality useless. p1 and p23 don't share anything in common, we just use this object because we don't know how else we can build the function's name dynamically and call it.
There is no else case. If we get an incorrect parameter the code blows.
Out eslint with rule non-used-vars complains that p1 and p23 are not being used and we don't know how to fix it ( https://eslint.org/docs/rules/no-unused-vars ).
The last problem, gives us the impression that perhaps this solution is not so great. Perhaps this pattern to avoid an if then else has some evil to it that we are yet to find.
Questions
Is there any other pattern we can use to avoid huge if then else statements ( or switch cases )?
Is there a way to get rid of the functionHolder object?
Should we change the pattern or fix the rule?
Looking forward to any feedback!
You can get rid of the unused variables by never declaring them in the first place:
const functionHolder = {
p1: request => {
//deals with request
},
p23: request => {
//deals with request
};
};
const processRequest = request => {
const partnerId = request.partnerId;
const method = functionHolder[`p${partnerId}`]
if(method) // Is there a method for `partnerId`?
return method(request);
return null; // No method found. Return `null` or call your default handler here.
};
To answer your points:
Yeap, as shown above.
Not without some kind of object.
That's up to you. Whatever you prefer.
Perhaps I'm not understanding the question properly, but why not an object to hold the methods?
const functionHolder = {
1: function(request) {
// do something
},
23: function(request) {
// do something
},
_default: function(request) {
// do something
}
}
function processRequest(request) {
(functionHolder[request.partnerId] || functionHolder._default)(request)
}
Explanation:
The object functionHolder contains each of the methods used to deal with a given request.
The keys of functionHolder (e.g. 1) correspond directly to the values of request.partnerId, and the values of these members are the appropriate methods.
The function processRequest "selects" the appropriate method in functionHolder (i.e. object[key]), and calls this method with the request as the parameter (i.e. method(parameter)).
We also have a default method, under the key _default, if request.partnerId does not match any existing key. Given a || b; if a is "falsy", in this case undefined (because there is no corresponding member of the object), evaluate to b.
If you are concerned about making functionHolder "bloated", then you can separate each of the methods:
const p1 = request => {
// do something
}
const p23 = request => {
// do something
}
const _default = request => {
// do something
}
And then combine them into a "summary" object of sorts.
const functionHolder = {
1: p1,
23: p23,
_default: _default
}
processRequest remains the same as above.
This adds a lot of global variables though.
Another advantage is you can import / change / declare methods on the fly. e.g.
functionHolder[1] = p1b // where p1b is another request handler (function) for `request.partnerId` = 1
functionHolder[5] = p5 // where p5 is a request handler (function) that has not yet been declared for `request.partnerId` = 5
Combining the above, without having to declare many global variables while also being able to separate the declaration of each method:
const functionHolder = {}
functionHolder._default = request => {
// do something
}
functionHolder[1] = request => {
// do something
}
functionHolder[23] = request => {
// do something
}
processRequest remains the same as above.
You just have to be sure that the methods are "loaded in" to functionHolder before you call processRequest.
I have an mxml view in flex, and I need to dynamically add data to a DataGrid component.
This is where the DataGrid is initialized:
<mx:DataGrid id="myGrid" width="100%"
dataProvider="{initDG}" >
<mx:columns>
<mx:DataGridColumn dataField="Identifier" />
<mx:DataGridColumn dataField="Name" />
</mx:columns>
</mx:DataGrid>
This is the script part:
private var DGArray:Array = new Array;
[Bindable]
public var initDG:ArrayCollection;
private function onCreation():void{
initData();
}
public function initData():void {
initDG=new ArrayCollection(DGArray);
}
private function onShow():void{
for (var child:Object in children) {
var details:Array = null;
if (child instanceof String) {
var test:String = children[child].toString();
details = test.split(",");
}
//Here I need to make an object like this one:
// record = {Identifier: details[0] , Name: details[1]};
this.DGArray.push(the record created);
}
}
I did this method because it's working if DGArray was a static Array:
private var DGArray:Array = [
{Identifier:'001', Name:'Slanted and Enchanted'},
{Identifier:'002', NAme:'Brighten the Corners'}];
Can anyone tell me how to create the record and add it to DGArray?
Thanks:)
In short
Add or remove items through the ArrayCollection instance instead of through the Array instance.
And here's why
ArrayCollection - as its name suggests - is in fact nothing but a wrapper around Array, adding some functionality to it that comes in handy when working with the Flex framework.
So when you do
initDG.addItem(theNewItem);
that new item will automatically also be added to the underlying Array.
Additionally this function will also dispatch a CollectionEvent, which will notify the DataGrid that the data in its dataProvider has changed and it should be redrawn to reflect those changes.
If on the other hand you do
DGArray.push(theNewItem);
like you did, you directly alter the underlying Array. This doesn't really break anything; you'll still be able to acces the new item through e.g. ArrayCollection.getItemAt() as well, but the DataGrid was never notified of the change, hence it didn't redraw and keeps displaying the old data.
Suppose I need to add unit tests for the following class from legacy code (that has no unit test for now). It is just a simple map or dictionary.
function Map(...) { ... }
Map.prototype.put = function (key, value) {
// associate the value with the key in this map
}
Map.prototype.get = function (key) {
// return the value to which the specified key is mapped, or undefined
// if this map contains no mapping for the key
}
Map.prototype.equals = function (obj) { ... }
// ... and more bound functions
It seems there is no way to test only one function at a time. You cannot test get() without calling put(), for example. How do I unit test this?
If there is a heavy dependancy between the methods you could stub or mock out all the other methods.. Have a look at jsMock for this.
Each method has a contract, explicit or implicit. Map.put() takes some kind of input and mutates something internal or external to Map. In order to test that function, your test needs access to what is mutated. If it is internal and not exposed externally, your test must either exist inside the Map class, the state must be exposed, or the mutateable state structure must be injected into the class in a way that external access remains possible:
ie:
/*Definition*/
function MockRepository() { /*implementation of the repository*/ }
function Map(repository) { /* store the repository */ }
Map.prototype.put = function() { /* mutate stuff in the repository */ }
/*Instantiation/Test*/
var mockRepository = new MockRepository(); /*mock repository has public methods to check state*/
var myMap = new Map(mockRepository);
myMap.put(/*whatever test input*/);
/* here use the mock repository to check that mutation of state occurred as expected based on ititial state of respository and input */
if you are working with data base, for the "get" method you can create DbScripts with inserts in your data base and then get those inserted items.
then you have to create DbScripts for deleting those added items.
for the "put" test you'll have to call the get method to check if it was inserted.
you just have to configure this in your test base class.
[TestFixtureSetUp]
public virtual void InitializeTestData()
{
TestConfiguration.ExecuteSqlFilesInFolder(this.DefaultScriptDirectory + "\\SetUp");
if (!string.IsNullOrEmpty(this.TestFixtureSpecificScriptDirectory))
{
TestConfiguration.ExecuteSqlFilesInFolder(this.TestFixtureSpecificScriptDirectory + "\\SetUp");
}
}
[TestFixtureTearDown]
public virtual void FinalizeTestData()
{
if (!string.IsNullOrEmpty(this.TestFixtureSpecificScriptDirectory))
{
TestConfiguration.ExecuteSqlFilesInFolder(this.TestFixtureSpecificScriptDirectory + "\\TearDown");
}
TestConfiguration.ExecuteSqlFilesInFolder(this.DefaultScriptDirectory + "\\TearDown");
}