Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

  • 6.1p: viewRequires + velocity problem with empty template variable and therefore broken viewRequires

    Hi Isomorphic,

    I'm trying to use viewRequires as you describe it in the docs:
    Indicates that the specified VelocityExpression must evaluate to true if values for the field are to be fetched. If the specified expression does not evaluate to true, the field will be dropped as described for viewRequiresAuthentication.

    In addition to the normal context variables available to Velocity expressions in Smart GWT, expressions you write for field-level requires clauses - viewRequires, editRequires, initRequires and updateRequires - can reference two additional variables: $fieldName and $dsName. These are the names of the dataSource and field currently undergoing requires checks. They are helpful because they allow you to write a generic checker function that can be used to handle requires checks for multiple fields and dataSources.
    I do have a problem with a template variable I need in velocity to evaluate the value, though. To me it seems that this template variable is never set - the breakpoint for it is never hit for a fetch nor for DataSourceLoader (which might be correct, but then I'd need to know how to add those here). Please see this BuiltInDS-based sample (v11.1p_2018-03-17) and select "Animals":

    animals.ds.xml:
    Code:
    <DataSource
      ID="animals"
        serverType="sql"
        tableName="animals"
        testFileName="animals.data.xml" serverConstructor="com.smartgwt.sample.server.listener.MySQLDataSource">
        <fields>
            <field name="commonName"      title="Animal"             type="text"/>
            <field name="scientificName"  title="Scientific Name"    type="text"  primaryKey="true"  required="true"/>
            <field name="lifeSpan"        title="Life Span"          type="integer"/>
            <field name="status"          title="Endangered Status"  type="text">
                <valueMap>
                    <value>Threatened</value>
                    <value>Endangered</value>
                    <value>Not Endangered</value>
                    <value>Not currently listed</value>
                    <value>May become threatened</value>
                    <value>Protected</value>
                </valueMap>
            </field>
           <field name="diet"            title="Diet"               type="text"                viewRequires="$t_config.getVisible(&quot;diet&quot;, $servletRequest)" />
            <field name="information"     title="Interesting Facts"  type="text"  length="1000" viewRequires="$t_config.getVisible(&quot;diet&quot;)" /> 
              <!--
                    <field name="diet"            title="Diet"               type="text"  />
            <field name="information"     title="Interesting Facts"  type="text"  length="1000" />
            -->
            <field name="picture"         title="Picture"            type="image" detail="true"
                   imageURLPrefix="/isomorphic/system/reference/inlineExamples/tiles/images/"/>
        </fields>
    </DataSource>
    MySQLDataSource.java:
    Code:
    package com.smartgwt.sample.server.listener;
    
    import com.isomorphic.datasource.DSRequest;
    import com.isomorphic.datasource.DSResponse;
    import com.isomorphic.sql.SQLDataSource;
    
    public class MySQLDataSource extends SQLDataSource {
    
        private static final long serialVersionUID = 1L;
    
        public static DSRequest addDefaultTemplateContext(DSRequest request) throws Exception {
            request.addToTemplateContext("t_config", T_CONFIG.getInstance());
            return request;
        }
    
        @Override
        public DSResponse executeFetch(DSRequest request) throws Exception {
            // Not hit during DataSourceLoader - most likely OK
            addDefaultTemplateContext(request);
            DSResponse fetchResponse = super.executeFetch(request);
            return fetchResponse;
        }
    }
    T_CONFIG.java - I add all template classes like this as singleton with a timed data reload (no data here):
    Code:
    package com.smartgwt.sample.server.listener;
    
    import javax.servlet.http.HttpServletRequest;
    
    public class T_CONFIG {
        private static T_CONFIG instance = new T_CONFIG();
    
        public static T_CONFIG getInstance() {
            return instance;
        }
    
        public boolean getVisible(String fieldName, HttpServletRequest servletRequest) {
            return true;
        }
    
        public boolean getVisible(String fieldName) {
            return true;
        }
    
        public String getVisible2(String fieldName, HttpServletRequest servletRequest) {
            return "true";
        }
    
        public String getVisible2(String fieldName) {
            return "true";
        }
    }
    If you look at the DataSourceLoader-result, you'll see a canView:false for both edited fields in the .ds.xml. I assume that when I know where to inject the template variable here, everything will be good.
    It is OK that the function then will rely on user grants and on database data to return either true or false, correct?
    So differing results for different users are OK, like it is the case with viewRequires, correct? (This basically means, viewRequires is an boilerplate-free version of canView?)

    This is an important one for me, as a customer of mine wants to see extra fields, but I want to show those fields only there and not for other customers.

    Best regards
    Blama

  • Isomorphic
    replied
    If those are the ways you invoke DataSource operations, then you are covered.

    As we explained, there is also Standalone Usage. In that case, there isn't even a servlet, so your DataSource cannot work in that mode. If you are not using that mode, there's no reason to care.

    Second question: the XML is on disk and/or returned from your own DataSourceGenerator code, so you already have that. Then, as far as the created DataSource instance, there are server-side APIs such as DataSource.getFieldNames() / getField().

    Leave a comment:


  • Blama
    replied
    Hi Isomorphic,

    OK, understood.
    So for me, DataSourceLoader is just the way to transport the .ds.xml config relevant on the client side for the logged in user to the browser.

    DataSourceLoader and IDACall and RESTHandler requests:
    • httpServletRequest.setAttribute() in a DataSourceLoader-subclass.doGet() for automatic GUI changes because of canView:false.
    • httpServletRequest.setAttribute() in a IDACall-subclass.handleDSRequest() for SQLs created for an incoming IDACall-request
    • httpServletRequest.setAttribute() in a RESTHandler-subclass.handleDSRequest() for SQLs created for an incoming RESTHandler-request
    What about all other cases, like the one you mention in #23. I think this applies to every request created in DMI with new DSRequest(), no matter if I use the incoming RPCManager (for requests additional to an incoming IDACall request) or created RequestContext.instance() for server initiated requests (e.g. servlet hit from cron job).

    You write "then you will need to make sure those variables are defined in all the ways you might invoke it". How to do that? I have the strong feeling this is somehow to be done in SQLDataSource-subclass, but in #19 you write this is too late. Do I need it additionally there?


    Additional question for better debugging: Is there some way for me to get the DataSource, it's fields etc you are using to generate an SQL?
    So this means the "XML" (most likely not possible) or at least the DataSource instance after Dynamic DataSource modification and after Velocity declarative security evaluation, so that I can use getters on it? Can/Should I put a breakpoint in SQLDataSouce.execute() for this?
    Or just DataSourceManager.get()? But how to inject then velocity-variables then?

    Thank you & Best regards
    Blama

    Leave a comment:


  • Isomorphic
    replied
    Can't quite figure out the question here, but if we have it right, no, code in a DataSourceLoader servlet will not be invoked if processing starts from IDACall (or a subclass).

    If you've written your DataSource so that it requires certain variables to be present when created, then you will need to make sure those variables are defined in all the ways you might invoke it, including in standalone (non-servlet-triggered) usage if you are using the DataSource that way too.

    Leave a comment:


  • Blama
    replied
    Hi Isomorphic,

    I do have a question w.r.t. to this:
    Originally posted by Blama View Post
    httpServletRequest.setAttribute() in a DataSourceLoader-subclass.doGet() for automatic GUI changes because of canView:false.
    If I have a viewRequires="$t_config.getVisible(&quot;diet&quot;, $servletRequest)" with a variable I need to inject like the one here, I do need to inject it in a DataSourceLoader subclass
    Code:
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.isomorphic.servlet.DataSourceLoader;
    import com.lmscompany.lms.server.worker.T_SETTINGS;
    
    public class MyDataSourceLoader extends DataSourceLoader {
        private static final long serialVersionUID = 616663945156810788L;
    
        @Override
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            request.setAttribute("t_config", T_CONFIG.getInstance());
            super.doGet(request, response);
        }
    }
    I also need to register MyDataSourceLoader like this in web.xml:
    Code:
        <servlet>
            <servlet-name>DataSourceLoader</servlet-name>
            <servlet-class>com.lmscompany.lms.server.LMSDataSourceLoader</servlet-class>
        </servlet>
    Now I know that you load and perhaps cache DataSource yourself quite often in your server side processing.
    Does this internal "get DataSource" also use the class (=the servlet) from web.xml?
    Somehow I can't imagine it is that way, as there is not always a HTTP request and therefore I think you'll do this somehow differently.
    Can I (and must I) also inject my Velocity variables there?

    Thank you & Best regards
    Blama

    Leave a comment:


  • Isomorphic
    replied
    As written your requires expression would crash, and be ignored. You could write it differently so that it doesn't crash, and does whatever is the right thing for server-initiated requests.

    Leave a comment:


  • Blama
    replied
    OK, but doesn't that mean that requests that don't go through IDACall (=all server initiated requests) don't have that variable for viewRequires-calculation? Is this then just ignored and therefore true?

    Best regards
    Blama

    Leave a comment:


  • Isomorphic
    replied
    No, because there are multiple kinds of DataSources that can use Velocity, and because DMI and other kinda of business logic execute before DataSource-specific logic becomes involved.

    Leave a comment:


  • Blama
    replied
    For other finding this thread:

    You need to inject the class with:
    • httpServletRequest.setAttribute() in a IDACall-subclass.handleDSRequest() for SQLs
    • httpServletRequest.setAttribute() in a DataSourceLoader-subclass.doGet() for automatic GUI changes because of canView:false.

    The only thing not working as of today is that joins for not needed includeFrom fields are still being generated. This is not a security issue, but results in SQLs more complicated than necessary.

    Isomorphic: If I understand correctly, the IDACall-subclass only affects incoming GUI requests. Therefore I also need this in my RESTHandler-subclass, correct? What about server initiated requests? Wouldn't it be better this this were in my SQLDataSource-subclass instead of IDACall and RESTHandler?

    Best regards
    Blama

    Leave a comment:


  • Blama
    replied
    Hi Isomorphic,

    as followup: It is working as expected with handleDSRequest().

    Thank you & Best regards
    Blama

    Leave a comment:


  • Isomorphic
    replied
    Sorry, you're correct that handleDSRequest() is too late, however there is still no need to worry about doGet or doPost or know anything about the encoding of the request, as APIs such as processRequest() or processRPCTransaction() are both early enough.
    Last edited by Isomorphic; 10th Apr 2018, 09:36.

    Leave a comment:


  • Blama
    replied
    Hi Isomorphic,

    that's why I was asking. I expected it should be handleDSRequest(), but that does not work - breakpoint in T_CONFIG.getVisible() not hit on fetch, diet- and information-fields missing in SQL.

    not working MyIDACall.java:
    Code:
    package com.smartgwt.sample.server.listener;
    
    import com.isomorphic.datasource.DSRequest;
    import com.isomorphic.datasource.DSResponse;
    import com.isomorphic.rpc.RPCManager;
    import com.isomorphic.servlet.IDACall;
    import com.isomorphic.servlet.RequestContext;
    
    public class MyIDACall extends IDACall {
        private static final long serialVersionUID = -2103887355803373948L;
    
        @Override
        public DSResponse handleDSRequest(DSRequest arg0, RPCManager arg1, RequestContext arg2) throws Exception {
            arg0.addToTemplateContext("t_config", T_CONFIG.getInstance()); // breakpoint hit, if set
            return super.handleDSRequest(arg0, arg1, arg2);
        }
    
        /*
        @Override
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // request.setAttribute("t_config", T_CONFIG.getInstance()); // breakpoint hit, if set
            super.doGet(request, response);
        }
    
        @Override
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            request.setAttribute("t_config", T_CONFIG.getInstance()); // breakpoint hit, if set
            super.doPost(request, response);
        }
        */
    }
    Best regards
    Blama

    Leave a comment:


  • Isomorphic
    replied
    There are obscure cases where doGet() would also need to be overridden. However, there's no need to know this, as handleDSRequest() would also work.

    Leave a comment:


  • Blama
    replied
    Hi Isomorphic,

    this is working like this. I get this SQL (expected, with diet and without information) and on FETCH my breakpoint in T_CONFIG.getVisible() is hit twice (expected). I do have a question though (see below).

    Code:
    SELECT LIMIT 0 75  animals.commonName, animals.scientificName, animals.lifeSpan, animals.status, animals.diet, animals.picture FROM animals WHERE ('1'='1')
    Code:
    package com.smartgwt.sample.server.listener;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.isomorphic.servlet.IDACall;
    
    public class MyIDACall extends IDACall {
        private static final long serialVersionUID = -2103887355803373948L;
    
        /*
        @Override
        public DSResponse handleDSRequest(DSRequest arg0, RPCManager arg1, RequestContext arg2) throws Exception {
            // arg0.addToTemplateContext("t_config", T_CONFIG.getInstance()); // breakpoint hit, if set
            return super.handleDSRequest(arg0, arg1, arg2);
        }
    
        @Override
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // request.setAttribute("t_config", T_CONFIG.getInstance()); // breakpoint hit, if set
            super.doGet(request, response);
        }
        */
    
        @Override
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            request.setAttribute("t_config", T_CONFIG.getInstance()); // breakpoint hit, if set
            super.doPost(request, response);
        }
    }
    See comments, only doPost() overwritten. Is this what you meant? Not an override of handleDSRequest()? Will this also work for queues?

    Can you add this to the documentation of viewRequires? I'm pretty sure noone could figure this out on his or her own currently.

    Best regards
    Blama

    Leave a comment:


  • Isomorphic
    replied
    Declarative Security checks are actually run before DMI, well before DataSource.execute(). Sorry, this was obvious in retrospect. This means you'll need to inject custom context at the servlet level, with an override of IDACall.

    Leave a comment:

Working...
X