Hi, On 07/27/2015 11:54 AM, Erik Huelsmann
wrote:
Very cool! Following some links from there led me here: http://www.restdoc.org/
Agreed, sounds good.
Ha. For a while I was using a home-grown Dojo single-page app for testing out APIs, have played around with quite a bit, but it's been a while since I've done a major API project. I've seen some decent browser extensions for some of these kinds of things... The other thing I'm thinking of here is for more light-weight, reporting types of uses. I'm not sure how much control you can get over headers when doing a cross-domain request from a browser -- I'm thinking a lightweight JS app that might want to grab the last 10 sales invoices for a dashboard, or something like that -- with an iframe, for example, you can't necessarily set browser headers but you can easily add a GET parameter. Not a big deal these days, there's so many decent tools for doing it right with a toolkit that we may not need the "lightweight" GET-only approach, but I do think there may be scenarios where it might prove useful...
I think we just define a convention, and describe it. Perhaps make it simply mirror the Json structure with _ separated parts? e.g. debit_1_value=234&debit_1_fx=222&debit_2_value=444&debit_2_fx=400 maps to json as: (intentionally swapping the index to the 2nd position) [{ "debit":{ "value":234, "fx":222 } }, { "debit":{ "value":444, "fx":400 } }] ... I mean we already do this for form posts now, we need to convert it to some sort of data object internally anyway, why not build a library that does this for us, regardless of what format it receives in the request? Might need to change some of the current form field names... This and the previous note does bring up something missing here: response format. Like the Range header, there's the "Accept" header the client can send, and I've also found it useful for very quick browser debugging to allow overriding that with a GET parameter. So we should discuss the formats we support for the response: application/json application/xml text/yaml text/csv text/html application/x-latex application/pdf ... and of course how we handle these. Json, XML, CSV are pretty straightforward (hey, are there any industry-specific XML formats we should leverage/offer?) -- for nested data in CSV I've typically seen Json used... For those last 3, clearly there's a need for templates for each kind of object... If we've done a good job on the API, we should be able to plug in request formatters and response formatters easily -- so we could add text/yaml by writing a new plugin for both response and request handling... Ah, yes, and that's exactly why I think we need to support a GET parameter in addition to Range: header -- then you can simply generate a URL to get a CSV or HTML report of the most recent 10 payments from client X.
Ok. That all sounds fine to me... Yes, that's exactly what I'm envisioning. Although a different "batch" mechanism than our current batches -- something specific to the API that can do all sorts of data changes and then approve in one go... Good questions, and this gets beyond my experience -- I haven't actually done that much transactional programming to know the best practices here... I would think we would expect subsequent calls to have the new values, and I do know that Dojo stores have supported "placeholder" ids that can be replaced with permanent ones after the data is committed, so I would tend to think that pattern should work, a "placeholder" that is returned while the batch is "open", and when the batch is "approved" a set of replacements get returned so the client can update with final IDs. Should we be considering UUIDs here?
Hmm. Goes against REST, but then we are talking about financial systems, practically the definition of transactional logic. It feels like we are reinventing SOAP! I'm thinking about the scenarios here, and the one that comes to mind is "shipping" some products on a sales order. We use this all the time --skipping the shipping screen, we just put in a value in the "ship" box and "Create Invoice." The current LSMB adjusts the sales order line items/totals, and commits that, and then takes you to a create invoice form that is completely open, unsaved, and in my opinion really should be in a transaction -- the sales order qtys shouldn't get updated until the invoice is posted (or at least saved as a draft). That's a scenario I think the current app should do in a transaction, and doesn't. I am also thinking about how you do transactions in a database, that you generally have to start a transaction with a "BEGIN" and otherwise it's not in a transaction. I'm thinking we just model the API the same way, that it's not in a transaction unless it's explicitly called for. I also think this entire transaction functionality can be deferred until a later version, as long as you're thinking about it with the current version so it's something that can be added later...
I think the general approach is a token sent in the body, not a cookie. The browser will send all cookies in any session... You can probably go to some extra steps to isolate sessions with curl, but I mostly just use the "cookiejar" in curl that makes it act like a browser here... IIRC, the Drupal Form API sets a "form-build-id" and a "form-token". The build-id essentially is the session/form sent to the browser, and the token is used to validate and detect replays. Drupal then caches the entire form using the build-id as the cache key.
http://dojotoolkit.org/reference-guide/1.10/dojo/date/locale/parse.html
Well... yes. I think this boils down to a question of "document database" or "relational database". Obviously, we're built on a relational database, and I've never truly warmed up to pure document/object storage, the "NoSQL" movement... At the same time, the structure of an invoice in LSMB is pretty well-defined, and doesn't vary much, so we can present the entire thing as a "document", even though the lines are themselves first-class objects. Maybe this is just force of habit for me, and there may well not be any actual need for it, but I would think that pretty much anything that can be a line on a report or an invoice should be directly addressable. But maybe that's overkill? I've built one very complex system from scratch, and with that one I just made each level of the hierarchy extend the base data object class, and so I did essentially get the basic CRUD APIs for this for free, once I mapped my API layer to the data object -- about the only thing that needed attention at each level were the fields available for index queries -- and then the nesting issues we're discussing. I guess I didn't think that much about whether we *needed* that level of access (though it certainly helped when debugging).
As I think about it, I really only see two levels here: expanded, or condensed. Expanded, for an invoice, the response would include the customer record, each line item detail, each payment line. Condensed, it would only contain references to these other records, which would have to be retrieved separately if they don't exist. How much deeper is useful to go? Would we ever want to load the product from the line item? Perhaps, and then need to look up a pricegroup for a customer for the product... not exactly sure how this is currently modeled. But that really seems as complicated as this system gets. Oh, I guess there's entity/eca/contact method.
Sounds good! Cheers, John |
------------------------------------------------------------------------------
_______________________________________________ Ledger-smb-devel mailing list ..hidden.. https://lists.sourceforge.net/lists/listinfo/ledger-smb-devel