John Kumpf · Oct 31, 2019 go to post

Is there a reason it has to be an sql query?  Using object syntax, opening the object whose list you want to check, and then checking it with something like myObj.myListProperty is a very good option if possible.

John Kumpf · Oct 31, 2019 go to post

We now have continuous integration with Jenkins in Angular.

At a high level, the steps are:

1. add a line like ng test --code-coverage --browsers=ChromeHeadless to your build script

2. have Jenkins publish the code coverage HTML report with it's HTML Reports plugin

3. install karma-junit-reporter and have it output the test report to a location on the build machine where it can be globbed together with other junit test reports you want to publish

I'd be happy to talk about these steps more if anyone is doing the same thing.

John Kumpf · Oct 29, 2019 go to post

That makes a lot of sense that you naturally avoid the abbreviations where the English word carries some significant meaning.  Also, it seems like you naturally avoid abbreviations where the abbreviation carries a sort of misleading semantic meaning. With $case vs $c, $c at first glance looks like some sort of character function, whereas $s and $H don't immediately appear to be something that they're not.

And I forgot about horizontal space; on lines that get long it's not trivial.  That said, things like $select, $HOROLOG, and $piece carry a lot of meaning through the full word, and I think there's value in that.

Side note: for some reason I always want to use $g instead of $get (I don't, but it feels natural).  I think that's because, as a single syllable word which is a very simple operation, after seeing the definition for $get once, $g seems to fit it just as well as the full "get".

John Kumpf · Oct 25, 2019 go to post

This is a really good point I hadn't considered.

When I was working with legacy code bases, it was difficult.  However, perhaps I'm blaming the wrong culprit.  Looking back, the main issue is that all the variable names were two random letters (at least to me; by the end I could usually figure out what the two letters were supposed to mean).  I'd be down 15 lines and then have to go back up to a variable's initialization to remember what the heck "cb", "sh", or "ac" actually were, semantically.

That was probably also done to save characters, and, as you said, is far more damaging than set -> s or if -> i.

I also agree that the lack of appropriate spacing makes a huge difference, as appropriate spacing can help show the control flow of the code.  Thanks for commenting this; after reading it, I now think that of all the questionable (in today's world) space saving techniques used in the past, the single letter variable names might be the least offensive.

That said though, with respect to the seeing more at once, s -> set doesn't increase the vertical space at all, so in theory you can see the same number of lines.  Also, I think it's very useful for coding languages to be written in English.  Coding is complex enough as it is, so we might as well take advantage of the fact that we're already completely comfortable with English, and thus we don't need to waste any mental resources during the processing of English string -> semantic meaning.  By using English, we're hijacking our preconceived notions of words like "while" such that the jump to the more complex control flow operator is less severe.  I'd contend it would take far longer than a few hours to be able to really read "w" the way you can read "while".

It's just like learning a new (non-coding) language.  Once I know what the vocabulary words mean, I can competently translate a passage in that language.  But I can't read it yet, with a direct link from the text to the meaning.

John Kumpf · Oct 21, 2019 go to post

From what I understand, the single character command names are an anachronism from a time when the actual space the code took up was more of a limiting factor.  Nowadays, that's not relevant at all, so the added readability of the full words is optimal.

I remember my first time looking at code bases using the single letters.  One of the worst offenders, 'i' instead of 'if' was very annoying; it makes reading the code more like translating and less like reading.

John Kumpf · Oct 17, 2019 go to post

That article about pack and link was very helpful and informative.  Thanks for that.

It didn't address what I'm asking, though (unless it did, and I'm wrong).  I don't really need a css build tool, at least I don't think I do.  I literally just want to know how I can get a scss file from one project into the node_modules directory of another via npm.

When I used npm pack on project1 to create a tarball, then installed that tarball into project2, the css file from the project1 wasn't in tarball2's node_modules, and I'd like it to be (so I can import it somewhere in project2).

John Kumpf · Oct 13, 2019 go to post

Interesting.  We already have all of our Cache code building, running unit tests, and running code coverage on Jenkins.  My current plan is to see how easy/difficult it is to fit Angular build/test/coverage into that architecture, so that we can have everything in once place.

If there are features we can't have with Jenkins, or if it turns out to be so difficult that it may be worth splitting up our continuous integration across multiple platforms, I'll definitely give this stuff a look.  Thanks for the advice.

John Kumpf · Oct 13, 2019 go to post

Would something like that ever make sense if the queries were repeated a lot, across a lot of different applications?  (minus the deleting the query part)

I'd never actually thought of this before.  Have you ever implemented anything like that?  Actually now that I think about it more, it would make more sense to just post the query and have it run (and store if it's not there), rather than getting trying to GET the query first, getting back a 404 or something, and then POSTing it before getting it.

John Kumpf · Oct 13, 2019 go to post

Like others have said, a /search endpoint is inherently non-RESTful.  Are you certain that /search endpoint shouldn't be something else? 

For example, if the search is searching people, then perhaps redesign it as a /person endpoint instead?  If that's the case, then a GET request to /person with URL parameters is definitely the most "RESTful" thing you can do; you're asking for ("GETing") information for a bunch of person resources.

If /search is really what you want, then it's not really following the RESTful architecture anyway, because you aren't asking for information about resources, you're really just asking the server to take an action for you and send you the result.  In this case, you might as well forget about RESTful conventions and just do whatever is most convenient and maintainable. 

I suspect that the user is filling out some sort of a form in the UI with these search parameters.  If that's the case, you might be able to simply POST the object that the form generates to the server, and then pull the parameters out of the request body in your server-side handler method.  In general, whether you're pulling the parameters out of %request.Data or out of  %request.Content, there isn't much code that needs to be written on the server.  So pick the option that makes the client code the most straightforward.

John Kumpf · Oct 9, 2019 go to post

On a related note, if anyone who's involved in the development of the core product sees this, is the * syntax, where we actually get the name of the oref that was invalid (similar to the *variable syntax we get when a reference is undefined) in the plans for the future?

John Kumpf · Oct 1, 2019 go to post

We've been using this tool as the backbone of our REST APIs that serve persistent data to Angular UI's.  It's extremely useful and powerful, glad to see it progressing even more.

John Kumpf · Sep 19, 2019 go to post

As a follow up to this, if you're using the ##class(class name).myMethod(args) syntax and it's not working, it may be that you're trying to call an instance method, a method called on an object instance of your class, rather than a class method, a method called on the class itself.

In that case, you'd want to try something like:

set myObj = ##class(class name).%New()

do myObj.myMethod(args)

Hope this helps!

John Kumpf · Sep 13, 2019 go to post

Can confirm that the %JSON.Adaptor tool is extremely useful!  This was such a great addition to the product.

In Application Services, we've used it to build a framework which allows us to not only expose our persistent classes via REST but also authorize different levels of access for different representations of each class (for example, all the properties, vs just the Name and the Id).  The "Mappings and Parameters" feature is especially useful:

https://irisdocs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Pag…

Also, @Stefan are you writing backwards while you talk?  That's impressive.

John Kumpf · Aug 30, 2019 go to post

A few more details about your exact use case would help me give a better answer here.  As for the exact questions you asked, here's an example of the syntax you can use to write a query as part of a class:

Query Orgs() As %SQLQuery(CONTAINID = 1)
{
SELECT ID As OrgID,Name,DisplayName FROM Org ORDER BY Name
}

As for getting the query result as JSON, I asked a similar question a little while ago, and this discussion was very useful:

https://community.intersystems.com/post/how-do-i-return-json-database-s…

Now, if your real question is something along the lines of "I want to write a REST endpoint that gives me back the result of a class query in Person as JSON", then unfortunately that's a non-trivial problem.  The fastest way would be to write some endpoint, like /basicpersoninfo, and have the REST handler route requests for that endpoint to a method that uses JSON_ARRAYAGG and JSON_OBJECT and dynamic SQL  (discussed in that other thread) to write out the results of that query in JSON.

John Kumpf · Aug 28, 2019 go to post

Oh, okay!  I think the fastest way to get to what you want would be front end form validation.

Whatever you're using to build the UI that leads to this JSON post, have that code mark certain fields as required, and then alert if those aren't filled.  In general, I think in the division of labor that task typically falls to the front end.

In theory, you can write a server method that scans the input before JSON import and then throws a custom exception with all the missing fields, but that's likely more work than doing it on the front end (because the front end will still have to catch that, then assemble the alert message from it anyway).

Also, if you're going to be changing which fields are required a lot, you can (in theory) write a server method which exposes which fields are required, instead of hard-coding that in the front end, but, again, that's likely not worth the effort.

Any of that helpful, or am I misunderstanding the problem?

John Kumpf · Aug 28, 2019 go to post

I believe the %JSON import method returns after hitting an error like that, so it's probably more work than you think to have the error give you all fields that are required and empty.

But could you be a little more specific about your use case and exactly what problem you're trying to solve?  There might be a better way.

John Kumpf · Aug 7, 2019 go to post

You're running into the fact that calling %Get on a dynamic object doesn't return an undefined when data at the index you specify doesn't exist.  Instead, it returns "".

Both the answers you have here are great if you need a generalized solution.  If all you're looking for is a simple work around in a non-general case, all you need to do is check if your %Get returned "",  and if so, kill the value before you pass it in:

 ClassMethod passIncompleteJSON()
{
  set json = ["arg1 val", "arg2 val"]
  set arg1 = json.%Get(0)
  kill:(arg1="") arg1
  set arg2 = json.%Get(1)
  kill:(arg2="") arg2
  set arg3 = json.%Get(2)
  kill:(arg3="") arg3
  write ..threeArgs(.arg1, .arg2, .arg3)
}

ClassMethod threeArgs(a, b, c)
{
  set:($get(c)="") c = "default"
  return a_"."_b_"."_c
}

Just be sure to pass in the parameters by reference since you're going to be passing in undefined variables.

(This does remove the distinction between undefined and "" though; you wouldn't be able to intentionally pass "" with this solution)

John Kumpf · Apr 23, 2019 go to post

I'm building classes which contain representations of REST resources.  For example, a full (likely for administrators) representation of the "person" resource might contain all fields, and all of them are writable.  A limited (maybe authenticated, but non admin) representation might contain most fields, and some like name and email are writable, but others like employment status are not.  A minimal (maybe for unauthenticated users) representation would contain only a few, read-only fields.

The property parameters would be things like the name for the property we use in the JSON, whether the field is writable, other flags regarding how to represent relationships or object properties, etc.

I think what I'm imagining is very similar to the %XML.PropertyParameters class used by %XML.Adaptor

(If you're probably wondering why I didn't include all this in my original post, I've been discussing it with Tim Leavitt and my ideas are a lot more formulated than they were 2 hours ago.  I suspect that an analogous architecture to what's used with XML will work well.)

John Kumpf · Apr 16, 2019 go to post

Oh okay, I see.  JSON_ARRAYAGG takes whatever results it gets back, and puts them into a JSON array.  Since JSON_OBJECT returns a bunch of results that are json objects, using JSON_ARRAYAGG(JSON_OBJECT( says to take all those json objects, and put them into a json array.

As far as the non select part of the query, it looks like you can specify it with a new "select", or without.  I.e.:

SELECT JSON_ARRAYAGG(JSON_OBJECT('state':Home_State)) FROM Sample.Person WHERE Home_State %STARTSWITH 'A'
SELECT JSON_ARRAYAGG(JSON_OBJECT('state':Home_State)) FROM (select state from Sample.Person WHERE Home_State %STARTSWITH 'A')

Thanks a lot for taking the time to explain.  It's very clear to me what is happening now.

John Kumpf · Apr 16, 2019 go to post

Thanks a lot.  This worked.

I found the page about this function for anyone who sees this later:

https://irisdocs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Pag…

Would you mind explaining how exactly this particular line is working?  Why do you have to use both JSON_ARRAYARG and JSON_OBJECT and what does that syntax mean?  It looks like it's essentially "selecting" the data from one result set into another as JSON, but I'm not sure how.  What does the JSON_OBJECT call do here, and what does the JSON_ARRAYAGG call do?

John Kumpf · Apr 12, 2019 go to post

Ah, using base-href is a great idea, thanks.  A few questions about this:

Why did you rename the files to .csp?

Why are you using ahead of time compilation?

By using:

ng build --base-href = /webapp_name/index.html

I was able to access <cname>/webapp_name/index.html successfully; the index.html file could find the scripts like polyfills.js without me modifying those src arguments by hand.

John Kumpf · Feb 13, 2019 go to post

Okay, so if I'm understanding you correctly, even if the user field changes after the initial insertion of the object, the userid field should stay the same?