-
-
Notifications
You must be signed in to change notification settings - Fork 877
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
ISSUE-1836: Add Dashboard
Support
#1837
ISSUE-1836: Add Dashboard
Support
#1837
Conversation
b91fa90
to
f585a97
Compare
265daf3
to
f58619e
Compare
331b62b
to
9370b18
Compare
31c0b18
to
6a502ba
Compare
40021f5
to
6876c6f
Compare
8e4fc5e
to
b82e326
Compare
6b3a7cb
to
0c9a753
Compare
40cc086
to
ae36f41
Compare
The impetus for these changes is a project I'm working on at work. We want to create a dynamically updating dashboard that helps prioritize maintenance operations. I realize this is a large PR, and one large commit, but I feel like the work in it's entirety makes sense when looked at together. Additionally, there are some changes that were just formatting, in the case where spacing or anything along those lines have changed, that was auto-formatting and accepted by the linting `tox -e lint`. `jira/client.py` ---------------- * Imported `DashboardItemProperty`, `DashboardItemPropertyKey`, and `Gadget` resources to client for use in new methods. * Updated the `dashboards` method to include the `gadgets` that exist on a given dashboard. This is a logical association that makes sense, but isn't directly exposed in the API. * Added `create_dashboard` method. It creates a dashboard via the API and returns a `Dashbord` object. * Added `copy_dashboard` method. * Added `update_dashboard_automatic_refresh_seconds` method. This calls the `internal` API, which is why it's decorated with `experimental` and `cloud`. This might change in the future, but it really is a handy thing to have, otherwise, the user has to configure this in the web interface. --- * Added `dashboard_item_property` method. This is available on both `cloud` and `server`. * Added `dashboard_item_property_keys` method. This is available on both `cloud` and `server`. * Added `set_dashboard_item_property` method. This is available on both `cloud` and `server`. --- ^^ These methods all provide a means of adding arbitrary metadata to `dashboard_items` (`gadgets`) and/or configure them via specific keys. * Added `dashboard_gadgets` method. This returns the gadgets associated with a given dashboard. It also iterates over the `keys` for this `gadget`'s properties, generating a list of `DashboardItemProperty` objects that are associated with each gadget. This makes it really easy for the user to associate which configuration/metadata goes with which gadget. * Added `gadgets` method. This returns a list of from `jira` of all the `gadgets` that are available to add to any dashboard. * Added `add_gadget_to_dashboard` method. This allows the user to add gadgets to a specified dashboard. * Added the protected method `_get_internal_url`. This is very similar to `get_url` or `get_latest` url, where `options` are updated to allow for easy resolution of paths that are on the `internal` `jira` api. * Updated the `_find_for_resource` type hint on `ids` because it is possible that a resource requires more than `2` ids to resolve it's url. jira/resources.py ----------------- * Added the new resources `DashboardItemProperty`, `DashboardItemPropertyKey`, and `Gadget` to the `__all__` list so they are represented. * Updated the typehint on the `find` method for the same reason as the `_find_for_resource` method. * Added a `gadgets` attribute to the `Dashboard` resource to house `gadget` references. * Added `DashbordItemPropertyKey` resource. * Added `DashboardItemProperty` resource. The `update` and `delete` methods are overridden here because it does not have a `self` attribute. This is kind of in an in between space as far as being considered a resource, but for ease of use as an interface, it makes sense for it to be considered. * Added `Gadget` resource. It too has overridden `update` and `delete` methods for the aforementioned reasons. jira/utils/__init__.py ---------------------- * Added `cloud` convenience decorator for client methods that make calls that are only available on the `cloud` api. It checks the `client` instance to see if it `_is_cloud`. If not, it logs a warning and returns `None`. This was the convention seen on other endpoints on the `client`. * Added `experimental` convenience decorator for client methods that make calls that are experimental. It attempts to run the client method, if a `JIRAError` is raised that has a response object, the response is checked for a status code in `[404, 405]` indicating either the path no longer accepts the HTTP verb or no longer exists, and then logs a warning and returns `None`. Otherwise it reraises the error ^^ Regarding these convenience decorators, it makes it really apparent to developers which calls are for the cloud and/or experimental. * Added `remove_empty_attributes` convenience method. I found myself having to remove empty attributes or add a lot of branching in order to accommodate optional payload parameters or path parameters. This function made that easier. Finally I wrote tests to cover these changes. These tests worked for me locally against my docker `jira server` instance. However, most of them didn't run, because the test checks the environment to find out where it is running. For the cloud tests, I signed up for a trial account for `jira cloud` and ran them against my instance. They all panned out okay and cleaned up after themselves appropriately. ISSUE-1836: Address linting ISSUE-1836: Fix CI issue ISSUE-1836: Fix `TypeError` in test ISSUE-1836: Allow `dashboard` tests on cloud ISSUE-1836: Shore Up Testing Refactored the `utils` tests a bit to make them more a little more straight-forward and hit all branches. Added tests for `update` on `dashboard/gadget` resources. ISSUE-1836: Adjust `set_dashboard_item_property` Turns out if you use `set_dashboard_item_property` on an existing dashboard gadget, you get back a `200` response. This makes sense as you're not creating something new. To account for this, rather than looking explicitly for a `201` response, we are now using the `ok` attribute of the response. Updated the test accordingly. ISSUE-1836: Fix `DashboardItemProperty` update method * Found an idiosyncrasy with the `update` method on this resource. The idea is to be able to update the item property value without clobbering it (that's what `set_dashboard_item_property` is for). This change ensures that happens, by only updating the resources current `value` with the user provided input.
* Make method name align with parameters/ui
* Change name of `Gadget` to `DashboarGadget` for specificity and clarity. Could be `gadget`'s of other kinds might exist in the future. * Updated `gadgets` method name to `all_dashboard_gadgets`. More in line with what it does, plus adds the `dashboard` context to the method.
4656115
to
c579fbf
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An excellent quality PR, still a few things that I think should be addressed before merging, but a promising start!
Thanks for taking a look! I'll make another commit with some updates addressing your feedback. |
* Created `NotJIRAInstanceError` exception. This is raised in the case one of the convenience decorators utilized on a client method is improperly applied to some other kind of object. * Moved the `cloud/experimental` decorators into `client`. They are technincally utilites for the `client` so I think this makes sense logically. It also solves the issue of circular imports that came up when attempting to do a type check on the instance that is passed into these decorated functions. * Updated `cloud` decorator name to `cloud_api` to make it clearer. * Updated `experimental` to `experimental_atlassian_api`, to make it apparent to a developer that the `experimental` aspect of the decorated method is on the `atlassian` side of things, not on our side. * Updated docstrings where `ResultList`'s are being returned to include the kind of resource that is stored within the `ResultList`. * Updated `copy_dashboard` and `create_dashboard` function signatures. `edit_permissions` and `share_permissions` default values are no longer `list`'s to avoid the issues with mutable default argument evaluation. Now they are set to `None` by default and within the methods, they are defaulted to `[]` in the case that they evaluate "falsey". * Made `_get_internal_url` docstring more representative of what it does. * Ensured return types on the `update` methods of the `DashboardItemProperty` resource and the `DashboardGadget` is correct. * Added `only_run_on_cloud` marker to skip a test if it should only run during cloud tests to `conftest`. * Updated `test_dashboard` to utilize `only_run_on_cloud`. * Moved tests of `cloud_api/experimental_atlassian_api` decorators into the `test_client` file. Refactored those tests a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, there is one pending thing we need to look at here.
And that is the resource_class_map
in the resources.py
file:
Line 1647 in c51c8a3
r"dashboard/[^/]+$": Dashboard, |
What we are looking at is a regex mapping of the URLs to the resource types.
From what I can see from the REST API reference we are dealing with:
https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-dashboards/#api-rest-api-2-dashboard-get
# DashboardItemProperty
/rest/api/2/dashboard/{dashboardId}/items/{itemId}/properties
# Example regex pattern for the map:
# https://regex101.com/r/1Nw7U6/1
# r"dashboard/[^/]+/items/[^/]+/properties$"
# DashboardItemKey
/rest/api/2/dashboard/{dashboardId}/items/{itemId}/properties/{propertyKey}
# DashboardGadget
/rest/api/2/dashboard/{dashboardId}/gadget/{gadgetId}
Once this is in we can add to your existing tests an isinstance()
check on the class type
* Added necessary path regex/class mapping to `resource_class_map`. * Updated the `test_generic_resource.py` tests to include the resolution of those new paths, their regex, to the appropriate class.
Included some `isinstance` assertions in the `Dashboard` tests per @adehad's request.
@adehad I think I've got it now. I added the paths to the new resources. Added some more data points for the parameterized |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
excellent. I'll need to wait till later to actually run the cloud tests to merge (@studioj if you are able to confirm feel free to squash merge)
Addresses ISSUE-1836
#1836
The impetus for these changes is a project I'm working on at work. We want to create a dynamically updating dashboard that helps prioritize maintenance operations. I realize this is a large PR, and one large commit, but I feel like the work in it's entirety makes sense when looked at together. Additionally, there are some changes that were just formatting, in the case where spacing or anything along those lines have changed, that was auto-formatting and accepted by the linting
tox -e lint
.jira/client.py
DashboardItemProperty
,DashboardItemPropertyKey
, andGadget
resources to client for use in new methods.dashboards
method to include thegadgets
that exist on a given dashboard. This is a logical association that makes sense, but isn't directly exposed in the API.create_dashboard
method. It creates a dashboard via the API and returns aDashbord
object.copy_dashboard
method.update_dashboard_automatic_refresh_seconds
method. This calls theinternal
API, which is why it's decorated withexperimental
andcloud
. This might change in the future, but it really is a handy thing to have, otherwise, the user has to configure this in the web interface.dashboard_item_property
method. This is available on bothcloud
andserver
.dashboard_item_property_keys
method. This is available on bothcloud
andserver
.set_dashboard_item_property
method. This is available on bothcloud
andserver
.^^ These methods all provide a means of adding arbitrary metadata to
dashboard_items
(gadgets
) and/or configure them via specific keys.dashboard_gadgets
method. This returns the gadgets associated with a given dashboard. It also iterates over thekeys
for thisgadget
's properties, generating a list ofDashboardItemProperty
objects that are associated with each gadget. This makes it really easy for the user to associate which configuration/metadata goes with which gadget.gadgets
method. This returns a list of fromjira
of all thegadgets
that are available to add to any dashboard.add_gadget_to_dashboard
method. This allows the user to add gadgets to a specified dashboard._get_internal_url
. This is very similar toget_url
orget_latest
url, whereoptions
are updated to allow for easy resolution of paths that are on theinternal
jira
api._find_for_resource
type hint onids
because it is possible that a resource requires more than2
ids to resolve it's url.jira/resources.py
DashboardItemProperty
,DashboardItemPropertyKey
, andGadget
to the__all__
list so they are represented.find
method for the same reason as the_find_for_resource
method.gadgets
attribute to theDashboard
resource to housegadget
references.DashbordItemPropertyKey
resource.DashboardItemProperty
resource. Theupdate
anddelete
methods are overridden here because it does not have aself
attribute. This is kind of in an in between space as far as being considered a resource, but for ease of use as an interface, it makes sense for it to be considered.Gadget
resource. It too has overriddenupdate
anddelete
methods for the aforementioned reasons.jira/utils/init.py
cloud
convenience decorator for client methods that make calls that are only available on thecloud
api. It checks theclient
instance to see if it_is_cloud
. If not, it logs a warning and returnsNone
. This was the convention seen on other endpoints on theclient
.experimental
convenience decorator for client methods that make calls that are experimental. It attempts to run the client method, if aJIRAError
is raised that has a response object, the response is checked for a status code in[404, 405]
indicating either the path no longer accepts the HTTP verb or no longer exists, and then logs a warning and returnsNone
. Otherwise it reraises the error^^ Regarding these convenience decorators, it makes it really apparent to developers which calls are for the cloud and/or experimental.
remove_empty_attributes
convenience method. I found myself having to remove empty attributes or add a lot of branching in order to accommodate optional payload parameters or path parameters. This function made that easier.Finally I wrote tests to cover these changes. These tests worked for me locally against my docker
jira server
instance. However, most of them didn't run, because the test checks the environment to find out where it is running.For the cloud tests, I signed up for a trial account for
jira cloud
and ran them against my instance. They all panned out okay and cleaned up after themselves appropriately.