I have a registry with an asset. I need to add a new asset, and then update the registry. The assets are an apportionment, where you send a value from one to the other one. If the receiving asset doesn't exist it's created with the new value assigned, and then the value it's taken from the sending one. When I have all the assets modified though the transaction .js and I want to update the registry, I do:
if(exists==false){
return getAssetRegistry('org.basetis.bonusetis.apportionment')
.then(function (apportionmentRegistry) {
return apportionmentRegistry.addAll([apportionment1]);
})
.then(function () {
return getAssetRegistry('org.basetis.bonusetis.apportionment');
})
.then(function (apportionmentRegistry) {
return apportionmentRegistry.updateAll(apportionments);
})
which if I debug, goes through the addAll that adds the new created asset, but then, when it tries to update it with updateAll it gives me an error saying that the new added asset does not exist. If I delete the updateAll function, the new asset does get added.
Why is it not able to update the asset if it has added it before in the code? Is there anything wrong in the code?
In Hyperledger Fabric (which Hyperledger Composer is currently using), you are not able to read your own writes from within a transaction, meaning that you cannot add something to a registry and then read it, in the same transaction.
Paul is right. And I think this has something to how a new asset is not really fully committed to the registry until the transaction is successful:
Transaction processor functions will fail and roll back any changes
already made an error is thrown. The whole transaction fails, not just
the transaction processing, and anything changed by the transaction
processor function before the error occurred will be rolled back.
From: https://hyperledger.github.io/composer/reference/js_scripts.html
Related
I am writing a Post PLugin changing the owner. When the owner has a substitution manager, the owner is changed to the substitution manager. I tried a service.Update and an AssignRequest, but these throw an exception.
When I post the request my entity cannot update (and then throws "The request channel time out while waiting for reply after 10:00:00"). But like I see there is no recursion, because when I logged it I have only one repetition of log and it has stopped before or on line with update.
var assignedIncident = new AssignRequest
{
Assignee = substManagerRef, //get it throw another method, alreay checked in test it`s correct
Target = new EntityReference ("incident", incedentId)
};
service.Execute(assignedIncident);
I tried to write target in another way
Target = postEntityImage.ToEntityReference()
I tried to write simple update but the problem is the same.
Entity incident = new Entity("incident" , incidentId);
incident["ownerid"] = substManagerRef:
service.Update(incident);
Can somebody help me with that? Or maybe show the way to solve it)
The plugin is triggering itself and gets in a loop. The system only allows a maximum of 8 calls deep within plugins and that is the reason it throws an error and rolls back the transaction.
To solve this issue redesign your plugin in the following way:
Register your plugin on the Update message of your entity in the PreValidation stage.
In the plugin pick up the Target property from the InputParameters collection.
This is an Entity type. Modify or set the ownerid attribute on this Entity object.
That is all. You do not need to do an update, your modification is now passed into the plugin pipeline.
Note: for EntityReference attributes this technique only works when your plugin is registered in the PreValidation stage. For other regular attributes it also works in the PreOperation stage.
I have a list of tasks in vuex. I have a button in a component that will add a task. I also have a backend server that will store the task in a DB.
Right now, I have it set up like this:
Button->Ajax->Backend Response->Update Vuex->Vue Updates Frontend
What I would like is:
Button->Update Vuex->Ajax/Vue each do their thing.
The issue I have is that I don't have an id for the item until the DB creates it. I could create a temporary id locally and send that to the server as a temporary id, but that seems messy. I wouldn't store it, I would just use it in vuex to update the object when it comes back from the server.
For example, let's say the database table tasks looks like this:
id | title
How would I know that the response from the server corresponds to the object I just pushed into Vuex? I can't do a find by title because it's not unique. I can't do a find by id == null because I might be creating more than one task before I get a response from the server.
I could do this in Vuex:
id | title | vuex_id
And I could use the response from the server to find the task with vuex_id that I set and update it with the id from the database.
Feels like there should be a cleaner way to make the the frontend react instantly.
Your first approach is the default (and for me the right one), go to the server and return (if it was successfully save) the id, add it to the task object and update the store.
Your second approach it's not appropriated (also for me), what if the save fails? you already have the task on the store and updated the UI, and then the task should be removed or implement some retry request method, to maintain the task in the UI. It will be harder to test and debug, and also harder to understand if you are note familiar with the code.
Button->Update Vuex->Ajax/Vue is possible, hence the reason we have Actions to perform an async operation.
That said, you can always chain actions , if the first one returns a promise say it triggers a db save, waits for it to save successfully gets back the id and then trigger a subsequent action, if it fails you may want to handle it with retries and or whatever is appropriate, something along the lines of,
actions: {
saveInDb ({ commit }) {
return new Promise((resolve, reject) => {
// set time out below is your call to save into your Db
setTimeout(() => {
// save in db
resolve(yourId)
}, 1000)
})
},
updateEverythingElse ({ dispatch, commit }) {
return dispatch('SaveInDb').then((myNewId) => {
commit('mutationToSaveId')
}).catch((e) => {})
}
}
I may not have captured everything you said completely, but just gave a brief idea how it can possibly be done from the little i have done.
Based on the edited question
This is the reason guids exist, and hence frontend/client sends in the Id, that said, looks like it might be an identity column for Id from your question, so there is then lesser options, of which you mentioned one with vuexid , the other would be combinations of the column values like a composite key could be as simple ${title}_${you_other_column}, other than this you are probably left with option 1 of your doing.
Food for thought
Along those lines, my guess is, you are doing a batch update of Array<Tasks>, if am not mistaken, so i don't see the reason for your Vuex's mutations to be per row (per task object) and that it needs to be the entire collection , you may need to rethink that portion of it as i dont know the context and the reasoning behind updating one row at a time.
I've got the task of updating a CRM plugin for a system migrating from cm 2013 to 2016. The plugin fails because it tries to set the opportunity state to won, simply by updating the field. And you need to use the WinOpporunityRequest to do so.
The logic is as follows:
When the opportunity is won the plugin executes and runs on the opportunityclose entity
The plugin creates a new custom entity record (project) and updates several other records.
It gets the current opportunity by using the opportunityid of the opportunityclose entity
It updates a field on the opportunity with a reference to the newly created project record.
That update is done through the Update() method.
On 5 it fails because when at 3 it gets the current opportunity it already has the state of won. And if you try to update the record with a new state it fails.
My question is, how can I get the opportunity when acting on the opportunityclose entity and update only the one single field. I do not need to set the state as this is done in the standard CRM flow.
--Edit
The line of code that fetches the opportunity:
Xrm.Opportunity currentOpportunityObjectToUpdate = serviceContext.CreateQuery<Xrm.Opportunity>().First(x => x.Id == entityRef.Id);
The platform allows you to update closed opportunities, I just tried it to verify. What is the error you are getting?
In step #5, make sure you're only sending the attributes you're trying to update (opportunityid and lookup to project). So, when you issue the update, don't use any preexisting opportunity object that you either retrieved or created...doing so sends all attributes that are on the object and the platform will process each attribute as if it were being changed even if the value is unchanged. Instead, create a new opportunity object with just the id and project specified, something like this:
context.AddObject(new Opportunity() {
Id = idOfOpportunity, // you may have to specify id both here...
OpportunityId = idOfOpportunity, // ...and here, can never remember. Doesn't hurt to specify in both places.
new_ProjectId = idOfProject
});
context.SaveChanges();
If you get stuck, you always have an easy workaround option: take the logic from #4 and move it to an async plugin on create of project (even a workflow should work).
I'm having an issue when running a function in Cloud Code. It is supposed to check the existence of an object and, if it does exist, create a new user:
Parse.Cloud.define("createUser", function(request, response) {
// Query the existing company by id
var query = new Parse.Query(Parse.Object.extend("Company"));
query.equalTo("objectId", request.params.company.existing.id);
query.find().then(function(result){
var user = new Parse.User();
user.set("username", request.params.username);
user.set("password", request.params.username);
user.set("email", request.params.email);
user.set("permissions", ["User"]);
var company = result[0];
user.signUp(null, {
success: function(user){
// Asign company ACL for User write permission
var cACL = company.getACL();
cACL.setWriteAccess(user.id, true);
company.setACL(cACL);
// Save company
company.save();
console.log(company);
// Establish user-company relationship
var cRelation = user.relation("associated");
cRelation.add(company);
// Save user
user.save();
console.log(user);
// Finish
response.success(user);
},
error: function(user, error){
response.error(JSON.stringify({code: -8000, message: "User creation failed"}));
}
});
}, function(error){
response.error(JSON.stringify({code: -8001, message: "Invalid company"}));
});
});
I first query Parse for the existence of said object. If it does exist I create a new user with the parameters received. In the completion block of the user creation I assign the proper ACLs (to the company object) and later on save them. That's when I encounter the first issue: the ACLs are not saved (checked in the dashboard). I console.log the company for debugging purposes and it shows the ACLs are correctly set. So I assume it must be a saving problem.
NOTE: The user is created, but whatever I try to do later doesn't work.
Later on I add this object to a relationship previously defined in the dashboard, but I have the same problem with that: the relationship does not come up in the dashboard, even though when I console.log the object it shows that the relationship was properly set.
I'm lost here. I don't understand why this isn't working and I've read tons of online documentation and still can't find the answer.
Okay, after a day of work I finally found out my problem. I had ACLs set everywhere and I had no privilege for saving the objects I was trying to save. So saving was indeed failing.
I should note that if you are having the same problem I did, you can easily solve it using the Master Key. To do so, you need to call Parse.Cloud.useMasterKey() before executing any requests that must be authenticated.
This only works in Cloud Code, and you should definitely know what you are doing when you use the Master Key because it basically gives read and write privileges to anyone, everywhere, for everything. So make sure your logic is flawless because you might get big security problems if it's not used wisely. As Uncle Ben said: With great power comes great responsibility.
Hope this helps someone.
In my application, I have cross-entity validation logic that requires me to look at the entire change set and I'm doing this using the BeforeSaveEntities override.
I can construct the right logic by examining the saveMap parameter, but what am I supposed to do if I find something invalid?
If I throw an exception, like I would for single entity validation in the BeforeSaveEntity override, the whole save is aborted and the error is reported to the client. But some of the entities might be valid so I would want to save those and only abort the invalid parts.
Because BeforeSaveEntities returns a saveMap, I think I should be able to remove the invalid entities from the change set and continue to save the valid entities, but then how do I report the invalid parts to the client?
Is it possible to do a partial save of only the valid entities and at the same time, report a sensible error to the client to describe the parts of the save that failed?
Jay told you the way it is.
I wouldn't hold my breath waiting for Breeze to change because I think yours is a rare scenario and it isn't one we would want to encourage anyway.
But I'm weird and I can't stop thinking what I'd do if were you and I absolutely HAD to do it. I might try something like this.
Warning: this is pseudo-code and I'm making this up. I do not recommend or warrant this
Create a custom MyCustomEFContextProvider that derives from EFContextProvider.
Give it an ErrorEntities property to hold the error object
Override (shadow) the SaveChanges method with another that delegates to the base
public new CustomSaveResult SaveChanges(JObject saveBundle,
TransactionSettings transactionSettings = null) {
var result = base.SaveChanges(saveBundle, transactionSettings);
// learn about CustomSaveResult below
return new CustomSaveResult(this.ErrorEntities, result);
}
Catch an invalid entity inside BeforeSaveEntities
Pass it with error message to your custom ErrorEntities property
You get to that property via the EntityInfo instance as in
((MyCustomEFContextProvider) info.ContextProvider).ErrorEntities.Add(new ErrorEntity(info, message));
Remove the invalid entity from the SaveMap so it won't be included in the actual save
Let the save continue
The second line of your override SaveChanges method creates a new instance of your CustomSaveResult from the standard one and returns that to the caller.
public class CustomSaveResult : SaveResult {
public List ErrorEntities;
public CustomSaveResult(List errorEntities, SaveResult result){
// copy over everything
this.Entities = result.Entities;
this.KeyMappings = result.KeyMappings;
this.Errors = this.Errors;
// and now your error stuff
this.ErrorEntities = errorEntities;
}
}
Let's assume the caller is your Web API controller's SaveChanges method. Well you don't have to change a thing but you might make it clear by explicitly returning your custom SaveResult:
readonly MyCustomEFContextProvider _contextProvider = new MyCustomEFContextProvider();
...
[HttpPost]
public CustomSaveResult SaveChanges(JObject saveBundle) {
return _contextProvider.SaveChanges(saveBundle);
}
JSON.Net will happily serialize the usual material + your custom ErrorEntities property (be sure to make it serializable!) and send it to the Breeze client.
On the Breeze client you write your own variation on the stock Breeze Web API data service adapter. Yours does almost exactly the same thing as the Breeze version. But, when processing the save payload from the server, it also extracts this extra "error entities" material in the response and does whatever you want to do with it.
I don't know what that will be but now you have it.
See how easy that was? LOL.
Breeze does not currently support a save mechanism that both saves and returns an error at the same time. While possible this seems a bit baroque.
As you pointed out, you can
1) Throw an exception inside of the BeforeSaveEntities and fail the save. You can even specify which specific entity or entities caused the failure and why. In this case the entire save is aborted.
or
2) Remove 'bad' items from the saveMap within the BeforeSaveEntities and save only a subset of what was passed in. In this case you are performing a partial save.
But we don't support a hybrid of these two. Please add this to the Breeze User Voice if you feel strongly and we can see if other members of the community feel that this would be useful.