Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dhis2: Implement http namespace, a generic request function, and remove all callbacks #1046

Open
wants to merge 10 commits into
base: epic/release-dhis2
Choose a base branch
from

Conversation

hunterachieng
Copy link
Contributor

@hunterachieng hunterachieng commented Mar 10, 2025

Summary

Move all the generic functions into a http namespace, remove callbacks and add a generic request function.

Fixes #978

Details

All the functions such as post, get, and patch are moved to their own http name-spaced file to separate them from the other specific functions, and a generic request function was added as well.

All callbacks are being removed since the same behaviour can be achieved with promise chaining

AI Usage

Please disclose how you've used AI in this work (it's cool, we just want to
know!):

  • Code generation (copilot but not intellisense)
  • Learning or fact checking
  • Strategy / design
  • Optimisation / refactoring
  • Translation / spellchecking / doc gen
  • Other
  • I have not used AI

You can read more details in our
Responsible AI Policy

Review Checklist

Before merging, the reviewer should check the following items:

  • Does the PR do what it claims to do?
  • If this is a new adaptor, added the adaptor on marketing website ?
  • If this PR includes breaking changes, do we need to update any jobs in
    production? Is it safe to release?
  • Are there any unit tests?
  • Is there a changeset associated with this PR? Should there be? Note that
    dev only changes don't need a changeset.
  • Have you ticked a box under AI Usage?

Signed-off-by: Hunter Achieng <[email protected]>
@hunterachieng hunterachieng changed the base branch from main to epic/release-dhis2 March 10, 2025 15:23
Signed-off-by: Hunter Achieng <[email protected]>
Copy link
Collaborator

@josephjclark josephjclark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am having a hard time seperating "move http methods out into a clean namespace" from "fix all the problems with DHIS2 http APIs"

Docs are worrying me, base64 is worrying me, and I wonder if handleResponse needs to move into here.

I need to break here today but I'll think about this stuff more tomorrow.

It doesn't technically matter which PR fixes these problems - but it does matter what's in the next release. Dhis2 7 really needs to have a clean, well documented API. And do get to there we'll need more work.

I'll come back with a fresh brain.

return update(resourceType, path, data, options)(state);
}
});
promise = http
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ooh this is really annoying

Because we're using the http.get() operation directly, we have to feed state back into the call here.

it's an antipattern really. We certainly don't want to encourage it, and this is a big, brand-defining adaptor.

We don't have to do this in this PR, but what's the cost in using like util.request instead of http.request? Of using a function rather than an operation?

Also: if asbase64 is true, I think this stuff might be broken? Because response.data is a string and all these tests will fail 😬


/**
* Options object
* @typedef {Object} RequestOptions
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worried about these docs but they're not technically part of this PR. Let me think about it. Referenced here: #975 (comment)

* @example <caption>a dataElement</caption>
* http.patch('dataElements', 'FTRrcoaog83', { name: 'New Name' });
*/
// TODO: @Elias, can this be deleted in favor of update? How does DHIS2 handle PATCH vs PUT?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mtuchi can you help resolve this? Should we just remove patch?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hiya @josephjclark this is what i was able to gather regarding PATCH method in DHIS2

The DHIS2 API supports the PATCH method for various metadata endpoints.

For example, you can use PATCH to:

  • Update properties of metadata objects
  • Add or remove items from collections
  • Change domain types and value types
  • Remove specific organization units from groups

When using the PATCH method with any of these endpoints, you must use the content type application/json-patch+json in your request headers as per the JSON patch specification. See DHIS2 Documentation - Partial Updates DHIS2 Documentation - Bulk Sharing

Meanwhile, you can use PUT on:

That being said, I have never used the API endpoints in this discovery but i guess it doesn't hurt to maintain both patch and put or if we decide to remove them we should have a http.request function which will still allow me to specify patch or put when there is a need for that

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the thorough report @mtuchi!

So this must be a JSON patch. That's interesting.

If we wanted, we could sit down a workout a nice easy JSON patch API. But we should not do that for a function that no-one is using.

How about:

  • We keep the patch function
  • We default the content type to application/json-patch+json
  • We document that data must be in JSON Patch format and link to DHIS2 docs

That's a reasonable position to take right now. Maybe later we can make this a bit slicker.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am happy with that, and since the link to DHIS2 docs is subjected to either metadata apis or sharing apis maybe we add an example with JSON Patch format. For example we should convert this example 👇🏽 to openfn job code

PATCH /api/dataElements/{id}

[
  {"op": "add", "path": "/name", "value": "New Name"},
  {"op": "add", "path": "/valueType", "value": "INTEGER"}
]

@hunterachieng hunterachieng changed the title dhis2: Implement http namespace dhis2: Implement http namespace, a generic request function, and remove all callbacks Mar 11, 2025
@josephjclark
Copy link
Collaborator

@hunterachieng after you've addressed my comments, you'll want to rebase this on top of #1059 (or maybe merge is easier). It might be a hard rebase so it might be easier to start again? You'll need to take a look and decide.

@hunterachieng
Copy link
Contributor Author

@josephjclark I have re-worked this. Do you mind taking a look?

…feature/978-dhis2-namespace

Signed-off-by: Hunter Achieng <[email protected]>
Copy link
Collaborator

@josephjclark josephjclark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, now the problem with this is is that there's no basic get() function to retrieve resources. This is partly my fault for not thinking this through.

What I'm looking for is something like OpenMRS, where we have a basic HTTP namespace and a fancier main rest namespace which doesn't use HTTP semantics. This is actually the focus of OpenMRS 5 (which isn't ready for release yet but see the PR)

The HTTP namespace is pretty good. I like that it's lower level - no clever tracker stuff. That's great. I've left a few comments but it's broadly right.

Let's finish the http namespace, but then we need to think a bit more about the main namespace. What should get() look like? Maybe we should just restore the old get() for now?

EDIT: Yes, looking back, I'm fairly sure that we should just restore original get() and have a more lightweight one in http (different docs, fewer examples). Maybe we'll tweak it before release, but not in this PR

@josephjclark
Copy link
Collaborator

Sorry @hunterachieng I posted this review a bit early - I'll update it in the morning :)

@@ -253,7 +251,7 @@ export function create(resourceType, data, options = {}, callback = s => s) {
const { location } = response.headers;
if (location) console.log(`Record available @ ${location}`);

return handleResponse(response, state, callback);
return handleResponse(response, state);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think - and I'll come back to you on this - that in the main namespace here, we don't include response in the result. We just return to state.data.

The http methods should surface the response, but the resource helpers should not

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@josephjclark What should we do here? I have fixed the other comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: New Issues
Development

Successfully merging this pull request may close these issues.

dhis2: move http helpers into http. namespace
3 participants