NetTalk Central

Author Topic: API Client app question  (Read 180 times)

jking

  • Sr. Member
  • ****
  • Posts: 287
    • View Profile
    • Email
API Client app question
« on: July 13, 2021, 06:23:55 PM »
Hi Bruce,

     I have a NT 12.19 Web App that has an API Client in it (standard windows procedure).  This client, when called from a button in my web app (running in a browser), works fine.  It connects to the remote server and gets data (JSON) as expected.
     I now want to use it to fill in missing data in a tps file.  My plan is to loop through the tps file and check a status field.  If blank, I want to call the API Client and search the remote server for the missing data and place this data in a queue.  After the loop finishes, I'll process the queue to update my target tps file.
     So, I copied the API Client procedure and made some modifications.  First, I added a button the the window so I can call it manually during testing.  I placed the following code in the button embed:

Access:Pathology_MKD.Open()
Access:Pathology_MKD.UseFile()
Set(Pathology_MKD,PatMKD:GUID_KEY)
Loop
Case Access:Pathology_MKD.Next()
    OF Level:Benign   
      If PatMKD:LIS_STATUS = '' OR 'Patient Not Found'
        net.customheader = '' 
        net.ContentType = 'application/json' 
        net.authorization = '****************'
        net.FreeFieldsQueue() 
 
        myURL = 'https://*******************/StudyAPI/api/Study?'&|
        'firstName='&Clip(PatMKD:FirstName)&|
        '&lastName='&Clip(PatMKD:LastName)&|
        '&DateOfBirth='&Format(PatMKD:DateOfBirth, @D02B)&|       
        '&city='&Clip(PatMKD:SubjectCity)&|
        '&country='&Clip(PatMKD:SubjectCountry)       
       
        net.Get(Clip(myURL),'') 
       
       END
ELSE
    BREAK
END
       
END
Access:Pathology_MKD.Close()
!!Post-process queue and update MKD file here...



     To process the incoming JSON data and place in a queue, I have the following code in my PageReceived embed:


net.removeheader()
?response{prop:text} = self.ThisPage.GetValue() 
str.SetValue(?response{prop:text})
json.Start()
json.LoadString(str)
!Free(PreludePathology)
loop X# = 1 to json.Records()
    jsonItem &= json.Get(X#)
   
    PPTH:StudyId = jsonItem.GetValueByName('StudyId')
    PPTH:ReportType = jsonItem.GetValueByName('ReportType')
    PPTH:Focality = jsonItem.GetValueByName('Focality')   
    PPTH:BiopsyType = jsonItem.GetValueByName('BiopsyType')
    PPTH:Grade = jsonItem.GetValueByName('Grade')
    PPTH:TumorSize = jsonItem.GetValueByName('TumorSize')   
    PPTH:SiteOfDCIS = jsonItem.GetValueByName('SiteOfDCIS')   
    PPTH:Palpable = jsonItem.GetValueByName('Palpable')
    PPTH:Necrosis = jsonItem.GetValueByName('Necrosis')
    PPTH:MarginStatus = jsonItem.GetValueByName('MarginStatus')
    PPTH:MarginDistance = jsonItem.GetValueByName('MarginDistance')
    PPTH:Architecture = jsonItem.GetValueByName('Architecture')
    PPTH:ERStatus = jsonItem.GetValueByName('ERStatus')
    PPTH:ERScore = jsonItem.GetValueByName('ERScore')
    PPTH:PRStatus = jsonItem.GetValueByName('PRStatus')
    PPTH:PRScore = jsonItem.GetValueByName('PRScore')
    PPTH:HER2Status = jsonItem.GetValueByName('HER2Status')
    PPTH:HER2Score = jsonItem.GetValueByName('HER2Score')
    PPTH:TotalScore = jsonItem.GetValueByName('TotalScore')
    PPTH:BCSRisk = jsonItem.GetValueByName('BCSRisk')
    PPTH:RTRisk = jsonItem.GetValueByName('RTRisk')       
    PPTH:OrderId = jsonItem.GetValueByName('OrderId')
    PPTH:Guid = jsonItem.GetValueByName('Guid')
    PPTH:ReportId = jsonItem.GetValueByName('ReportId')
    PPTH:InsertDate = jsonItem.GetValueByName('InsertDate')
    PPTH:Accession = jsonItem.GetValueByName('Accession')
    PPTH:MasterAccession = jsonItem.GetValueByName('MasterAccession')
    PPTH:Version = jsonItem.GetValueByName('Version')
    PPTH:AddComments = jsonItem.GetValueByName('AddComments')
    PPTH:PatientAgeAtDCISDx = jsonItem.GetValueByName('PatientAgeAtDCISDx')   
   
    Add(PreludePathology)
   
END
Sort(PreludePathology, +PPTH:StudyId)


     I find I'm only getting the last record in the file, placed into the queue.  I suspect my loop logic is incorrect or there is a timing issue.  Perhaps the loop completes before the API Client data arrives.  Can you suggest where I went wrong here?

Thanks,

Jeff King

Bruce

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 10537
    • View Profile
Re: API Client app question
« Reply #1 on: July 13, 2021, 07:53:48 PM »
you cannot call GET inside a loop like this.

you need to do one GET, then wait until PageReceived is called, then trigger the next GET from there.

Cheers
Bruce

jking

  • Sr. Member
  • ****
  • Posts: 287
    • View Profile
    • Email
Re: API Client app question
« Reply #2 on: July 14, 2021, 12:26:08 PM »
Bruce,

     Thanks.  I still don't understand how to trigger the next get from within the PageReceive embed.  I thought you meant adding the net.Get(myURL, "") call but this leads to a loop.  Can you explain more how to accomplish this?

Thanks,

Jeff

Bruce

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 10537
    • View Profile
Re: API Client app question
« Reply #3 on: July 14, 2021, 10:26:14 PM »
>>  I thought you meant adding the net.Get(myURL, "")

yes, that's exactly what I meant.
consider this basic layout

nextPathology routine
  ! loop until you find just one record from the Pathology_MKD table that you want to update
  ! if no more records then exit
  net.Get(myURL, "")


pagereceived method
  code
  !  process incoming page - presumably updates Pathology_MKD table
  do nextPathology


Cheers
Bruce




bshields

  • Sr. Member
  • ****
  • Posts: 381
    • View Profile
    • Inhabit
    • Email
Re: API Client app question
« Reply #4 on: July 15, 2021, 01:26:47 AM »
Hi Jeff,

I'd suggest creating a separate procedure. I have a bunch of them, the simplest being:

GetWebResource(STRING URL),STRING

Pass the full URL and parameters, and it will return the result. From there you can parse or save the result.

Inside that procedure:

1. Create window
2. Add WebClient extension object
3. At window open, prime WebClient object and call Fetch
4. On PageReceived remove header and return result
5. On ErrorTrap return nothing (yes, you might want to return error messages etc, an empty result may not be sufficent - this is up to you)

Job done, now anytime you want the contents of a web page (or API) 1 call gets it for you and you don't have to worry about sync/async.

Its easily extended to add authentication and POST instead of Get for more complex APIs.

Regards
Bill


Bruce

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 10537
    • View Profile
Re: API Client app question
« Reply #5 on: July 15, 2021, 09:30:25 PM »
Hi Bill,

yes, this approach is possible, but it does have the disadvantage of repeatedly opening and closing a window.
This can mess with the current-window-focus on the machine - not such a big deal on servers, but a pain on desktop.
And a pain on servers if it happens at the wrong time as well.

Cheers
Bruce

jking

  • Sr. Member
  • ****
  • Posts: 287
    • View Profile
    • Email
Re: API Client app question
« Reply #6 on: July 16, 2021, 06:14:14 AM »
Bill and Bruce,

     I have not tried the NextPathology routine mentioned above by Bruce yet.  I have tried the separate procedure approach by Bill.  So, in my NT server app I created the separate procedure as Bill described.  This does indeed work.  But as Bruce indicated, the window of the API Client does open each time a record is processed. 
     My intent is to run the main NT server app as a service on a server.  Then on a timer, I will call the built in API Client to process records.  I have hidden the API Client window and set it to automatically close.  Do you foresee any window focus issues, or other issues when running as a service like described?

Thanks,

Jeff

bshields

  • Sr. Member
  • ****
  • Posts: 381
    • View Profile
    • Inhabit
    • Email
Re: API Client app question
« Reply #7 on: July 18, 2021, 12:48:06 AM »
Hi Bruce,

True it does, I'd forgotten.

I'm running this stuff on servers, often as services (so I don't actually see it).

Regards
Bill


jking

  • Sr. Member
  • ****
  • Posts: 287
    • View Profile
    • Email
Re: API Client app question
« Reply #8 on: July 18, 2021, 08:21:55 AM »
Bill,

     Thanks for the follow up.  I used your technique and it seems to be working well.  In my main NT Server app, I placed a button on the window.  I use this for testing, to manually call the API procedure on my local machine.  I then added a timer (5 minutes) and in the Timer embed I post an event accepted to the button.  This causes the API procedure to run every five minutes as expected.  I then ran this as a service and again it all works as planned.
     Next I moved the main NT server app to my server, Windows Server 2019 Standard.  Here, the main NT Server app won't run as a service.  I'm looking at the Self Service manual and trying some things.  Do you have any thoughts on what to check.

Thanks,

Jeff

bshields

  • Sr. Member
  • ****
  • Posts: 381
    • View Profile
    • Inhabit
    • Email
Re: API Client app question
« Reply #9 on: July 18, 2021, 11:29:11 PM »
Hi Jeff,

I'm still running all my stuff on Windows Server 2012R2 (intentionally).

You could try AlwaysUp (https://www.coretechnologies.com/products/AlwaysUp/).

Its a program that will run your programs as a service.

I have a client who uses it and actually seems pretty good. I've not tried it on Server 2019, but they say they support it.

Might be a solution while you figure out WS2019.

Regards
Bill

Jane

  • Full Member
  • ***
  • Posts: 195
  • Expert on nothing with opinions on everything.
    • View Profile
    • Email
Re: API Client app question
« Reply #10 on: July 19, 2021, 01:40:39 PM »
FWIW, I have a half dozen web servers (NT 12 and NT 11) running without issue as services on Server 2019 on our internal network.

Have you ever made a service that works, Jeff?  (You need to make sure you don't have message boxes popping up, etc.) 

Can you get your program to run as a service on your development machine?  If not, that would be a first step.

Are you running your service as Local System Account or as a Windows user account?

Is the service correctly installed (does it appear in services.msc) ? 

You could pepper some "here I am" debug statements through the startup area of your program and use DebugView++ to see whether you make it to the main window, whether that opens, where you go from there.  Do you see what you expect to see?

No magic bullets.  Follow the instructions and it will work.

Jane



jking

  • Sr. Member
  • ****
  • Posts: 287
    • View Profile
    • Email
Re: API Client app question
« Reply #11 on: July 19, 2021, 06:03:08 PM »
Hello Jane and Bill,

     I did get the service to work by manually installing it with Net Start. 
     I log in with the local system account.  It just would not install and run with the "Install and Start As Service" button of the Self Service control template I added to my app.  However, I am able to use this to Install and Start the service on my local dev machine.

     I'll continue to test...thanks!

Jane

  • Full Member
  • ***
  • Posts: 195
  • Expert on nothing with opinions on everything.
    • View Profile
    • Email
Re: API Client app question
« Reply #12 on: July 19, 2021, 07:12:29 PM »
Jeff,

I assume that when you tried to run with the button, you had started the app using "Run as Administrator"?

UAC requires elevation to install a service.

Jane

jking

  • Sr. Member
  • ****
  • Posts: 287
    • View Profile
    • Email
Re: API Client app question
« Reply #13 on: July 20, 2021, 11:10:56 AM »
Jane,

     I had not thought of that.  I'll give it a try later and let you know.  I have a feeling that was it!.

Thanks,

Jeff

Jane

  • Full Member
  • ***
  • Posts: 195
  • Expert on nothing with opinions on everything.
    • View Profile
    • Email
Re: API Client app question
« Reply #14 on: July 20, 2021, 05:33:50 PM »
Jeff,

Being one who didn't take to Windows until Win 95, I spent many years at the DOS prompt.

It's still the first thing I open on my computer each morning.

My services that I mentioned have all been installed from an AsAdministrator command prompt, running the executable with the /ISS switch.

That may well be the difference.

Cheers,

Jane