I am using Extractor for querying based on custom attributes. Here is the sample code.
#Override
public void extract(Object arg0, Object arg1, ValueCollector valueCollector) {
//date = domainVO.getDomainDate();
//valueCollector.addObject(age);
ValueReader valueReader = (ValueReader)arg0;
valueReader.read("domain.collectionData[any].status", (ValueCallback<String>)(value) -> {
valueCollector.addObject(value);
});
}
Custom attribute configuration
AttributeConfig attributeConfig = new AttributeConfig().setName("status")
.setExtractorClassName("com.sample.test.extractor.DomainValueExtractor");
Here client receives the entire entry object while querying the data.
Is it possible to fetch the custom attribute 'status' from client ?
Related
SAP Commerce 1811
I have one custom type e.g. "CustomType" which has one media attribute of type MediaModel. My requirement is to upload CSV media using Drag & Drop Editor in Editor Area.
So i used OOTB com.hybris.cockpitng.editor.dndfileupload of type com.hybris.cockpitng.editor.defaultfileupload.FileUploadResult and apply on my media attribute.
<attribute type="com.hybris.cockpitng.editor.defaultfileupload.FileUploadResult" editor="com.hybris.cockpitng.editor.dndfileupload" qualifier="media">
<editor-parameter>
<name>accept</name>
<value>text/csv</value>
</editor-parameter>
</attribute>
Now i created one custom Backoffice action e.g. "UploadMedia" and configured in editorareaactions of "CustomType".
As in when i upload csv media and click on my action, In my Action Controller, I am trying to fetch uploaded FileUploadResult media but its always giving null value.
#Override
public ActionResult<Object> perform(final ActionContext<TargetGroupModel> ctx)
{
final WidgetModel model = (WidgetModel) ctx.getParameter(ActionContext.PARENT_WIDGET_MODEL);
if (model != null)
{
final FileUploadResult result = model.getValue("currentObject.media", FileUploadResult.class);
System.out.println(result.getData()); // result is always NULL
}
return new ActionResult<>(ActionResult.SUCCESS);
}
And the strange thing is, while on debug, if i inspect the WidgetModel, i can see for this key "currentObject.media", value is there.
Any help ?
I solved the issue by just typecast ctx.getParameter(ActionContext.PARENT_WIDGET_MODEL) with Map and get the value with currentObject.media Key.
Working Code
#Override
public ActionResult<Object> perform(final ActionContext<TargetGroupModel> ctx)
{
final Map map = (Map) ctx.getParameter(ActionContext.PARENT_WIDGET_MODEL);
if (map != null)
{
final FileUploadResult result = map.get("currentObject.media");
System.out.println(result.getData());
}
return new ActionResult<>(ActionResult.SUCCESS);
}
I am trying to figure out how to get values back and forth between an external javascript library and the Acumatica back end.
Currently I have 2 fields that are hidden (set to hidden on the PXUIField Attribute) and I am picking up their values successfully as follows:
function doSomething() {
var url = px_alls['txtUrl'].value;
var clientId = px_alls['txtClientID'].value;
}
However I am not having the same luck setting a hidden fields values and then posting the data to the back end in this way:
client.on('someEvent', (data) => {
px_alls['txtId'].value = data.id;
Save();
})
How can I accomplish this?
Thanks
-Kyle
Reading form fields from px_alls works fine but I was not able to automate saving.
I use action callback to send the values from JavaScript to an action defined in the Graph.
var ds = px_alls['ds'];
ds.executeCallback('TestAction', 'parameter value');
Then In the graph you can use the parameter value to update the document:
public PXAction<PrimaryDAC> TestAction;
[PXButton]
[PXUIField(DisplayName = "Test Action")]
public virtual IEnumerable testAction(PXAdapter adapter)
{
string parameterValue = adapter.CommandArguments;
// Update document here with parameter value
return adapter.Get();
}
I've created a custom selector that has logic which depends on the value of a field in a header section of a screen. Since the logic is not in the graph which holds the views, how would I obtain the current value of the cache for this header section? I've set the field I'm referencing in the header to commitchanges=true and I've even put SyncPosition=true in the header section of the page. The following logic does not give me the current value that is (I'm assuming) in the cache:
mh = (xTACMappingHeader)PXSelect< xTACMappingHeader,
Where< xTACMappingHeader.mappingName, Equal<Required<xTACMappingDetail.mappingName>>>>.Select(new PXGraph<FinancialTranslatorMaint>(), md.MappingName);
What's the best way to retrieve the current value of the cache in a graph outside of that graph?
Thanks...
You get hold of graph using CacheAttached event. See example below.
public class YourAttribute : PXEventSubscriberAttribute
{
private PXGraph _Graph = null;
public override void CacheAttached(PXCache sender)
{
_Graph = sender.Graph;
base.CacheAttached(sender);
}
}
PXCache objects never exist outside of graph. You can access current graph through the _Graph field of PXCustomSelectorAttribute:
protected PXGraph _Graph;
something like:
mh = (xTACMappingHeader)PXSelect<…>.Select(_Graph, md.MappingName);
to access current value of the cache:
_Graph.Caches[typeof(YourDAC)].Current
While initializing caches, Acumatica Framework invokes CacheAttached() method on for every field attribute. PXCustomSelectorAttribute assigns value for the _Graph field based on Graph property of the currently initializing PXCache object:
public class PXCustomSelectorAttribute : PXSelectorAttribute
{
...
public override void CacheAttached(PXCache sender)
{
...
_Graph = sender.Graph;
...
}
...
}
I have created a plugin which inspects a param in the query string and loads up a user object based on this ID and populates
any request DTO with it. (All my request DTO's inherit from BaseRequest which has a CurrentUser property)
public class CurrentUserPlugin : IPlugin
{
public IAppHost CurrentAppHost { get; set; }
public void Register(IAppHost appHost)
{
CurrentAppHost = appHost;
appHost.RequestFilters.Add(ProcessRequest);
}
public void ProcessRequest(IHttpRequest request, IHttpResponse response, object obj)
{
var requestDto = obj as BaseRequest;
if (requestDto == null) return;
if (request.QueryString["userid"] == null)
{
throw new ArgumentNullException("No userid provided");
}
var dataContext = CurrentAppHost.TryResolve<IDataContext>();
requestDto.CurrentUser = dataContext.FindOne<User>(ObjectId.Parse(requestDto.uid));
if (requestDto.CurrentUser == null)
{
throw new ArgumentNullException(string.Format("User [userid:{0}] not found", requestDto.uid));
}
}
}
I need to have this User object available in my services but I don't want to inspect the DTO every time and extract from there. Is there a way to make data from plugins globally available to my services? I am also wondering if there is another way of instantiating this object as for my unit tests, the Plugin is not run - as I call my service directly.
So, my question is, instead of using Plugins can I inject a user instance to my services at run time? I am already using IoC to inject different Data base handlers depending on running in test mode or not but I can't see how to achieve this for User object which would need to be instantiated at the beginning of each request.
Below is an example of how I inject my DataContext in appHost.
container.Register(x => new MongoContext(x.Resolve<MongoDatabase>()));
container.RegisterAutoWiredAs<MongoContext, IDataContext>();
Here is an example of my BaseService. Ideally I would like to have a CurrentUser property on my service also.
public class BaseService : Service
{
public BaseService(IDataContext dataContext, User user)
{
DataContext = dataContext;
CurrentUser = user; // How can this be injected at runtime?
}
public IDataContext DataContext { get; private set; }
public User CurrentUser { get; set; }
}
Have you thought about trying to use the IHttpRequest Items Dictionary to store objects. You can access these Items from any filter or service or anywhere you can access IHttpRequest. See the src for IHttpRequest.
Just be mindful of the order that your attributes, services and plugins execute and when you store the item in the Items dictionary.
Adding:
We don't want to use HttpContext inside of the Service because we want use Service in our tests directly.
Advantages for living without it
If you don't need to access the HTTP
Request context there is nothing stopping you from having your same
IService implementation processing requests from a message queue which
we've done for internal projects (which incidentally is the motivation
behind the asynconeway endpoint, to signal requests that are safe for
deferred execution).
http://www.servicestack.net/docs/framework/accessing-ihttprequest
And we don't use http calls to run tests.
So our solution is:
public class UserService
{
private readonly IDataContext _dataContext;
public UserService(IDataContext dataContext)
{
_dataContext = dataContext;
}
public User GetUser()
{
var uid = HttpContext.Current.Request.QueryString["userId"];
return _dataContext.Get<User>(uid);
}
}
and
container.Register(x => new UserService(x.Resolve<IDataContext>()).GetUser()).ReusedWithin(ReuseScope.Request);
This is service signature:
public SomeService(IDataContext dataContext, User user) { }
Any suggestions?
I need to have this User object available in my services but I don't want to inspect the DTO every time and extract from there
How will your application know about the user if you're not passing the 'userid' in the querystring? Could you store the user data in the Session? Using a Session assumes the client is connected to your app and persists a Session Id (ss-id or ss-pid cookie in ServiceStack) in the client that can be looked up on the Server to get the 'session data'. If you can use the Session you can retrieve the data from your service doing something like
base.Session["UserData"] or base.SessionAs<User>();
Note: you will need to save your User data to the Session
Is there a way to make data from plugins globally available to my services? but I can't see how to achieve this for User object which would need to be instantiated at the beginning of each request.
This sounds like you want a global request filter. You're kind of already doing this but you're wrapping it into a Plugin.
I'm working on an API where I'd like to be able to customize the response structure based on a parameter from the client. Response filters seem like a good place to do this in order to avoid doing so in each service or action. The problem is that while I have access to the response DTO returned by the action, and could change its properties, I can't find how or where to replace the object entirely.
Naively replacing the object in the response filter did not work, but this help illustrate what I'm trying to do:
public class ChangeResponseAttribute : ResponseFilterAttribute
{
public override void Execute(IHttpRequest req, IHttpResponse res, object responseDto)
{
var overrideText = req.QueryString["override"];
if (!String.IsNullOrEmpty(overrideText))
responseDto = new { Message = overrideText };
}
}
[ChangeResponse]
public class TodosService : Service
{
public object Get(Todos request)
{
return new object[0];
}
}
It looks like another option would be to write the custom response directly & end the request, but that would bypass any other processing left to do by ServiceStack.
Is there a better place to do this than a response filter? Or do I need to bite the bullet and return the optimal DTO in each action?
You can't change the Response DTO in a filter, but yes one option is to write the response in the filter itself (see this answer for an example of how to do this).
The other option is to use a ServiceRunner and override the OnAfterExecute() custom hook which does let you modify the response returned, e.g:
public class MyServiceRunner<T> : ServiceRunner<T>
{
public override object OnAfterExecute(
IRequestContext requestContext, object response)
{
// Called just after any Action is executed
var overrideText = req.Get<IHttpRequest>().QueryString["override"];
return !string.IsNullOrEmpty(overrideText)
? new { Message = overrideText } : null;
}
}
To get ServiceStack to use it you need to override the CreateServiceRunner method in your AppHost, e.g:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(
ActionContext actionContext)
{
return new MyServiceRunner<TRequest>(this, actionContext);
}