Sending back the jsdo

Posted by mflanegan on 29-Mar-2016 09:57

Hi There, 

I am building up the jsdo and then saving the changes back to the database. When this method is called an appserver hit is performed for each row in the kendo grid. How would I be able to send back the jsdo once. I am trying to generate an order and this issue is causing multiple orders to be created instead of one order.

I am using jsdo.add and then when I am finished adding my records, I call jsdo.saveChanges().

TIA

All Replies

Posted by Matheus R. Mokwa on 29-Mar-2016 10:03

Think you need the batch propertie in the datasource:

docs.telerik.com/.../datasource

Posted by mflanegan on 29-Mar-2016 10:56

Thanks for the reply but unfortunately this did not work for me.

Posted by egarcia on 29-Mar-2016 11:37

Hello,

>> I am using jsdo.add and then when I am finished adding my records, I call jsdo.saveChanges().

Do you have a Submit method in the Business Entity?

Since you are using the JSDO API directly, then you could use jsdo.saveChanges(true). This would then call the Submit operation in the Business Entity.

I would have expected the batch property to work with the DataSource.

When the batch property is used, the internal code for the JSDO DataSource uses saveChanges(true) instead of saveChanges().

What do you see in the Network tab?

How many requests are sent?

Please let me know how it goes.

Posted by mflanegan on 29-Mar-2016 12:08

Hi Edsel,

The saveChanges accesses my create method, I don't have a submit method. I have never had to use that before.

Without the saveChanges(true), I see an appserver hit for each row being sent back.

If I add saveChanges(true), I get the following error:

Uncaught Error: JSDO: SUBMIT operation is not defined.

Could you please provide me with more info on the submit method?

Thanks in advance.

Posted by egarcia on 29-Mar-2016 12:52

Hello Meyrick,

Support for Submit was added a couple of releases ago.

Are you using the "BusinessEntity" class in your business entities?

The Submit method looks like the following:

    /*------------------------------------------------------------------------------
            Purpose:    Submit a record                                                               
            Notes:                                                                        
    ------------------------------------------------------------------------------*/
    @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true").
    @progress.service.resourceMapping(type="REST", operation="submit", URI="/SubmitCustomer", alias="", mediaType="application/json").
    METHOD PUBLIC VOID SubmitCustomer(INPUT-OUTPUT DATASET dsCustomer):     		       
	
      	SUPER:Submit(DATASET dsCustomer BY-REFERENCE).    	   
    END METHOD.

Please that the annotation for the Submit method uses writeDataSetBeforeImage=true and operation=submit.

You would need to add "BEFORE-TABLE bttCustomer".

You should be able to add this code to an existing Business Entity.

Alternatively, you can generate a new Business Entity with the wizard and specify the usage of the Submit operation.

Information on the Submit operation then would be added to the catalog so that the JSDO would recognize the operation as being defined.

I hope this helps.

Edsel

Posted by mflanegan on 30-Mar-2016 04:14

Hi Edsel,

I have attempted adding the submit operation. If I view my jsdo, the submit operation is there (see below):

"path": "\/SubmitbeOrdLn",

"type": "submit",

"verb": "get",

"params": [{

    "name": "dsrntprod",

    "type": "REQUEST_BODY"

}]

If I attempt to call the method on the client using saveChanges(true) it fires twice. The first time it fires, it looks fine:

  1. Request Method: OPTIONS
  2. Status Code: 200 OK 

Then straight after the first hit, it fires again:

  1. Request Method: GET
  2. Status Code: 404 Not Found

Now im not exactly sure what the parameters are supposed to be,

but can you see where I am going wrong and possibly point me in the right direction?

TIA

Posted by egarcia on 30-Mar-2016 04:48

Hello Meyrick,

Did you regenerate the catalog?

Take a look at the annotations. It should say operation="submit".

The entry in the catalog should say type="submit" verb="put".

PUT is used so that you can send JSON data in the body of the request.

The two entries in the Network tab are expected they are for one saveChanges().

The OPTIONS request is the preflight request to checks if the server can accept the actual request.

I hope this helps.

Posted by mflanegan on 30-Mar-2016 04:54

Yes the the operation is submit yet after the war file is generated the verb is get.

Posted by egarcia on 30-Mar-2016 05:26

What does the annotation for Submit looks like?

What happens if you remove the catalog file (make a backup first) and re-generate it?

You should get an entry for submit like the one in this files: oemobiledemo.progress.com/.../EmployeeSubmitService.json

Posted by mflanegan on 30-Mar-2016 05:35

here is the annotations:

   @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true").

   @progress.service.resourceMapping(type="REST", operation="submit", URI="/SubmitbeOrdLn", alias="", mediaType="application/json").

Posted by mflanegan on 30-Mar-2016 05:47

Hi Edsel,

I have removed the catalog and re-generated it and it is still displaying the same parameters for the operator.

Is there possibly something wrong with the annotations?

Posted by Ruben Dröge on 30-Mar-2016 06:04

Can you add the contents of your .cls file to this thread (not just the submit part)?

Posted by egarcia on 30-Mar-2016 06:13

The annotation looks fine.

What version of OpenEdge / PDSOE are you using? I think that support for Submit was added in 11.4, but it could have been 11.5.

If you use the wizard to create a Business Entity, do you see the option to generate a Submit method?

My guess is that you might be using an old version.

If you are using a version that has support for Submit, then it would need some debugging.

In that case, I would recommend to log a call with Technical Support.

There is a way around... But it is better if you confirm what version you are using and whether you see that it has support for Submit.

I hope this helps.

Posted by mflanegan on 30-Mar-2016 06:42

Thanks Edsel. There is no option to generate a Submit method. We are still using 11.3.2, that is probably my problem. I will try updating the software and take it from there.

Posted by mflanegan on 30-Mar-2016 07:01

 /*------------------------------------------------------------------------
    File        : beOrdLn
    Syntax      : 
    Author(s)   :
    Created     : Fri Jul 04 11:38:13 CAT 2014
    Notes       : 
  ----------------------------------------------------------------------*/
    
@program FILE(name="beOrdLn.cls", module="AppServer").
@openapi.openedge.export FILE(type="REST", executionMode="singleton", useReturnValue="false", writeDataSetBeforeImage="false").
@progress.service.resource FILE(name="beOrdLn", URI="/beOrdLn", schemaName="dsrntprod", schemaFile="elcbFgr/Inc/dsPartDet.i").
USING Progress.Lang.*.
ROUTINE-LEVEL ON ERROR UNDO, THROW.
CLASS beOrdLn INHERITS beInherit:
    {Inc\var.i}
{"Inc\dsPartDet.i"} 
   
    DEFINE VARIABLE phDataset AS HANDLE NO-UNDO.
    @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="false").
    @progress.service.resourceMapping(type="REST", operation="read", URI="?filter=~{filter~}", alias="", mediaType="application/json"). 
    METHOD PUBLIC VOID ReadbeOrdLn(
    INPUT filter AS CHARACTER, 
    OUTPUT DATASET dsrntprod):
    END METHOD.
    @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true").
    @progress.service.resourceMapping(type="REST", operation="submit", URI="/SubmitbeOrdLn", alias="", mediaType="application/json").
    METHOD PUBLIC VOID SubmitbeOrdLn(INPUT-OUTPUT DATASET dsrntprod):                    
        FOR EACH ttrntprod.
            MESSAGE "DATASET" ttrntprod.part "|" ttrntprod.contract
        END.
        
        FOR EACH B_ttrntprod.
            MESSAGE "BEFORE DATASET" ttrntprod.part "|" ttrntprod.contract.
        END.
                
    END METHOD.
 
    @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="false").
    @progress.service.resourceMapping(type="REST", operation="create", URI="", alias="", mediaType="application/json").
    METHOD PUBLIC VOID CreatebeOrdLn(INPUT-OUTPUT DATASET dsrntprod):
        DEFINE VARIABLE scr       AS INTEGER   NO-UNDO.
        DEFINE VARIABLE selprt    AS LOGICAL   NO-UNDO.
        DEFINE VARIABLE cono      AS CHARACTER NO-UNDO.
        DEFINE VARIABLE mailret   AS CHARACTER NO-UNDO.
        DEFINE VARIABLE cnt       AS INTEGER   NO-UNDO.
        DEFINE VARIABLE pso-id    AS INTEGER   NO-UNDO.
        DEFINE VARIABLE mytime    AS INTEGER   NO-UNDO.
        DEFINE VARIABLE connumber AS CHARACTER NO-UNDO.
        DEFINE VARIABLE ipcuser   AS CHARACTER NO-UNDO.
        DEFINE VARIABLE opcError  AS CHARACTER NO-UNDO.
        DEFINE VARIABLE proemail  AS CHARACTER NO-UNDO.
        DEFINE VARIABLE reason    AS INTEGER   NO-UNDO.
        DEFINE VARIABLE intorder  AS CHARACTER NO-UNDO.
        DEFINE VARIABLE cOutput   AS CHARACTER NO-UNDO.
        
        DEFINE VARIABLE hProc     AS HANDLE    NO-UNDO.
        ASSIGN mytime    = TIME
               proemail  = "".
               
        FOR EACH ttrntprod.
            MESSAGE "FULL DATASET" ttrntprod.cono "|" ttrntprod.part "|" ttrntprod.contract "|" ttrntprod.empno "|" ttrntprod.cono "|" ttrntprod.usramend "|"
                                   ttrntprod.oldpart "|" ttrntprod.totgar "|"  ttrntprod.qtychg "|" ttrntprod.price "|" ttrntprod.wkcharge.
        END.       
               
        RUN VALUE("beRntprod.p") PERSISTENT SET hProc.
        IF VALID-HANDLE(hProc) THEN DO: 
            FOR EACH  ttrntprod WHERE ttrntprod.part NE "TOTALS":
               IF ttrntprod.delmarker = 'D' THEN DO: 
                       MESSAGE "Deleting part" ttrntprod.part "|" ttrntprod.contract "|" ttrntprod.empno "|" ttrntprod.cono "|" ttrntprod.usramend.
                       RUN deleteRntprod IN hProc (INPUT  ttrntprod.usramend,
                                                   INPUT  ttrntprod.empno,
                                                   INPUT  ttrntprod.contract,
                                                   INPUT  ttrntprod.part,                                               
                                                   OUTPUT cOutput).
               END.
               ELSE DO:
                       MESSAGE "Creating part" ttrntprod.part "|" ttrntprod.contract "|" ttrntprod.empno "|" ttrntprod.cono "|" ttrntprod.usramend "|"
                                               ttrntprod.oldpart "|" ttrntprod.totgar "|"  ttrntprod.qtychg "|" ttrntprod.price "|" ttrntprod.wkcharge.
                       RUN CreateRntprod IN hProc (INPUT  ttrntprod.usramend,
                                               INPUT  ttrntprod.cono,
                                               INPUT  ttrntprod.empno,
                                               INPUT  ttrntprod.contract,
                                               INPUT  ttrntprod.oldpart,
                                               INPUT  ttrntprod.part,
                                               INPUT  ttrntprod.totgar,
                                               INPUT  ttrntprod.qtychg,
                                               INPUT  ttrntprod.price,
                                               INPUT  ttrntprod.wkcharge,
                                               OUTPUT cOutput).
                   
               END.                              
            END.                           
        END.
        DELETE PROCEDURE hProc.  
        DO TRANSACTION:
            FIND FIRST ttrntprod WHERE ttrntprod.part NE "TOTALS".
            IF AVAILABLE ttrntprod THEN DO:
                
                ASSIGN reason    = ttrntprod.reason
                       connumber = ttrntprod.contract
                       ipcuser   = ttrntprod.usramend
                       cono      = ttrntprod.cono  
                       intorder  = ttrntprod.intorder.
                       
                FIND FIRST rntcon WHERE rntcon.rc-number = ttrntprod.contract NO-LOCK NO-ERROR.
                IF NOT AVAILABLE rntcon THEN RETURN ERROR "Contract does not exist.".
                FIND rntreas WHERE rntreas.sys-cono = ttrntprod.cono
                               AND rntreas.rr-type  = rntcon.rc-contype
                               AND rntreas.rr-code  = INTEGER(ttrntprod.reason) NO-LOCK NO-ERROR.
                IF NOT AVAILABLE rntreas THEN RETURN ERROR "Invalid reason code.".
                FIND LAST rntsord USE-INDEX rs-pso-i NO-LOCK NO-ERROR.
        
                IF NOT AVAILABLE rntsord THEN
                    ASSIGN pso-id = 1.
                ELSE
                    ASSIGN pso-id = rntsord.pso-id + 1.
                MESSAGE "before create " ttrntprod.intorder.
                CREATE rntsord.
                ASSIGN rntsord.rc-number     = connumber
                       rntsord.dm-accno      = rntcon.dm-accno
                       rntsord.pso-id        = pso-id /*Marked back down to zero before created!!!*/
                       rntsord.rs-datecreate = TODAY
                       rntsord.rs-usrcreate  = ipcuser
                       rntsord.rs-duedate    = TODAY
                       rntsord.rs-timeamend  = mytime
                       rntsord.rr-code       = reason
                       rntsord.pki-order     = intorder.
                
                MESSAGE "AFTER CREATE " rntsord.pki-order ttrntprod.intorder.
                
                FIND FIRST drsmas WHERE drsmas.dm-accno = rntcon.dm-accno NO-LOCK NO-ERROR.
                IF AVAILABLE drsmas THEN DO:
                    FOR EACH drsemail WHERE drsemail.dm-accno = drsmas.dm-accno
                                        AND drsemail.dem-type = "SOG"
                                        AND drsemail.sys-cono = cono NO-LOCK:
                         
                        IF proemail = "" THEN DO:
                             
                            ASSIGN  proemail = drsemail.dem-email.
                        END.      
                        ELSE DO: 
                           
                            ASSIGN  proemail = proemail + ";" + drsemail.dem-email.
                        END.      
                        MESSAGE "here1 " proemail.
                    END.                     
                END. 
            END. /*FIRST ttrntprod*/
            cnt = 0.
            FOR EACH ttrntprod WHERE ttrntprod.part NE "TOTALS":                 
                FIND FIRST rntprod WHERE rntprod.rc-number = connumber
                                     AND rntprod.pm-partno = ttrntprod.part
                                     AND rntprod.re-number = ttrntprod.empno
                                     AND rntprod.pso-id    = 0
                                   EXCLUSIVE-LOCK NO-ERROR.
                IF AVAILABLE rntprod THEN DO:
                    ASSIGN rntprod.pso-id       = pso-id
                           rntprod.rp-dateamend = TODAY
                           rntprod.rp-timeamend = mytime
                           rntprod.rp-usramend  = ipcuser
                           cnt = cnt + 1.
                END.
            END. /*FOR EACH tt*/
            IF cnt = 0 THEN
              DELETE rntsord.
            ELSE
              RELEASE rntsord.
            RELEASE rntprod.
        END. /*DO TRANSACTION*/
        IF cnt > 0 THEN DO:
            ASSIGN scr    = 99
                   selprt = FALSE.
    
            OUTPUT TO VALUE("C:\AppServerTemp\email" + connumber + "_" + STRING(pso-id) + ".txt").
     
            IF NOT scr EQ 0 THEN DO:
                MESSAGE "BEFORE SYSPROFSO" connumber "," pso-id "," reason "," ipcuser "," scr "," selprt. 
                RUN VALUE(LC("sysprofso.p")) (INPUT connumber, INPUT pso-id, INPUT reason, INPUT ipcuser, INPUT scr, INPUT selprt).
            END.
    
            OUTPUT CLOSE.
     
            MESSAGE "BEFORE DOCTOPDF" INTEGER(SUBSTRING(connumber,1,2)) "," ipcuser. 
            RUN docToPDF.p(INPUT  INTEGER(SUBSTRING(connumber,1,2)),
                           INPUT  ipcuser,
                           "C:\AppServerTemp\email" + connumber + "_" + STRING(pso-id) + ".txt",
                           "C:\AppServerTemp\email" + connumber + "_" + STRING(pso-id) + ".pdf",
                           "pdfprofso.p",
                           2,
                           OUTPUT opcError).
    
            FIND FIRST sysuser WHERE sysuser.user-id = ipcuser NO-LOCK NO-ERROR.
            IF AVAILABLE sysuser THEN DO:
     
                IF proemail = "" THEN DO:
                    ASSIGN  proemail = sysuser.user-email.
                END.
                ELSE DO:
                    ASSIGN  proemail = proemail + ";" + sysuser.user-email.
                END.
            END.
            
            FIND FIRST rntreas WHERE rntreas.sys-cono = cono
                                 AND rntreas.rr-code  = reason
                               NO-LOCK NO-ERROR.
            IF AVAILABLE rntreas THEN DO: 
                ASSIGN mailret = rntreas.rr-email.     
            END.   
              
            IF System.Convert:IsDBNull(mailret) THEN  
                ASSIGN mailret = "". 
           
            IF mailret = ? THEN   
                ASSIGN mailret = "".  
             
            IF mailret NE "" THEN DO:  
                IF proemail = "" THEN DO: 
                    ASSIGN  proemail = mailret.
                END. 
                ELSE DO:
                    ASSIGN  proemail = proemail + ";" + mailret.
                END.
            END.
           
            IF proemail NE "" THEN 
                clEmail:Emailing ("Proforma Sales Order",
                                  "There are Proforma Sales Orders for contract " + connumber + " waiting to be approved.",
                                  proemail,"C:\AppServerTemp\email" + connumber + "_" + STRING(pso-id) + ".pdf").
         
        END. /*cnt > 0*/ 
    END METHOD.
    @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="false").
    @progress.service.resourceMapping(type="REST", operation="update", URI="", alias="", mediaType="application/json").
    METHOD PUBLIC VOID UpdatebeOrdLn(INPUT-OUTPUT DATASET dsrntprod):    
    END METHOD.
    @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="false").
    @progress.service.resourceMapping(type="REST", operation="delete", URI="", alias="", mediaType="application/json").
    METHOD PUBLIC VOID DeletebeOrdLn(INPUT-OUTPUT DATASET dsrntprod):           
    END METHOD.
END CLASS.

Posted by mflanegan on 30-Mar-2016 07:28

This may be a stupid question but is there no way that I can create my own method and call it on the client?

If not, would there not be a way to invoke the update method to do what I need it to do? if so, is there an example for me to look at?

Is there any means possible to for me to do what im wanting to do without upgrading to 11.5?

Posted by Ruben Dröge on 30-Mar-2016 09:26

Yes that is possible. As long as the method is available in your catalog you can invoke it.

See community.progress.com/.../22773

Posted by egarcia on 30-Mar-2016 13:34

The best would be to use Submit because it uses the JSON before-image format and you can send different types of operations: creates, updates, and deletes.

The approach that Ruben mentioned would be to call an invoke the method as an invoke operation.

Another approach would be to try to use the method that you defined as a Submit.

There is a recent option in the JSDO where you can use saveChanges(true) with a backend that does not have the JSON before-image format.

documentation.progress.com/.../

You would need to write the code for method.

For this you would need to manually change get to put in the catalog.

Please give this a try to see if it works with your 11.3. Still, you might want to try upgrading to a more recent version.

(Take a look at the new WebHandlers support in WebSpeed if you upgrade to 11.6.)

Posted by mflanegan on 31-Mar-2016 09:19

I have tried editing the jsdo file and changing the parameters of the submit operation. When I ran it, I could see the request was built up but a 404 error was returned. So now I have tried creating an invoke method. When I run this method on the application, the appserver returns an error and I can see that my invoke is trying to access the Update Method.

Would there need to be something special set up in the business entity?

   @openapi.openedge.export(type="REST", useReturnValue="false", writeDataSetBeforeImage="true").

   @progress.service.resourceMapping(type="REST", operation="invoke", URI="", alias="", mediaType="application/json").

   METHOD PUBLIC VOID LoadbeOrdLn(INPUT-OUTPUT DATASET dsrntprod):                    

       MESSAGE "Invoke Method".

   END METHOD.    

Posted by mflanegan on 31-Mar-2016 09:35

I'm accessing the method from the client by doing this:

var jsdo = new progress.data.JSDO('beOrdLn');

jsdo.invoke("LoadbeOrdLn",{});

Posted by egarcia on 01-Apr-2016 05:33

The invoke approach should work fine. Support for invoke is available since 11.2.

The approach with making the invoke operation work as a Submit could work... but you would be trying to do something that was not available with the 11.3 release.

It would be better to just focus on doing invoke operations.

You would need to update the catalog file to say invoke.

The following thread related to invoke operations might be useful to you:

   community.progress.com/.../24084

This thread is closed