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

API Enhancement to support fine-grained settings whitelisting #13948

Merged
merged 4 commits into from
Mar 16, 2017

Conversation

abellotti
Copy link
Member

@abellotti abellotti commented Feb 16, 2017

  • Adding support for arbitrary path categories, can include top level categories like it does today like product, authentication, server, but also sub pathing, server/role, session/timeout even lower level entries like server/worker_monitor/start_algorithm/value

  • Exposes the subset of the settings as driven by the new categories

  • Can fetch top level categories or any setting entry path:

    GET /api/settings/product, or GET /api/settings/server/role (if exposed)

  • Added rspecs

@abellotti
Copy link
Member Author

Marked as WIP

  • prototype
  • requires tests

@abellotti
Copy link
Member Author

/cc @Fryguy @skateman

This is a prototype for the enhancement I briefly referred to. It allows the API to expose a subset of the settings tree, down to an individual entry, and allows the users to be able to fetch any particular entry and not just top level category (i.e. GET /api/settings/product) with is a current limitation.

For David's use, we can update the config/api.yml to expose the additional entries he needs as well as others as follows:

:collections:
  :settings:
    :categories:
      - product
      - server/role
      - server/timezone
      - server/asynchronous_notifications

And with this PR: a GET of the settings collection:

GET /api/settings

will return the exposed settings subtree as follows:

{ 
  "product": {
    "maindb": "ExtManagementSystem",
    "container_deployment_wizard": false,
    "datawarehouse_manager": false
  },
  "server": {
    "role" : "database_operations,event,reporting,scheduler,smartstate,ems_operations,ems_inventory,user_interface,websocket,web_services,automate"
    "timezone" : "UTC",
    "asynchronous_notifications" : false
  }
}

One can fetch just the server subtree:

GET /api/settings/server
{
  "server": {
    "role" : "database_operations,event,reporting,scheduler,smartstate,ems_operations,ems_inventory,user_interface,websocket,web_services,automate"
    "timezone" : "UTC",
    "asynchronous_notifications" : false
  }
}

Or simply the entry of interest:

GET /api/settings/server/asynchronous_notification
{
  "server": {
    "asynchronous_notifications" : false
  }
}

@abellotti abellotti force-pushed the api_fine_grained_settings branch 2 times, most recently from 41ca011 to c22ac82 Compare March 8, 2017 21:47
@abellotti abellotti changed the title [WIP] API Enhancement to support fine-grained settings exposure API Enhancement to support fine-grained settings whitelisting Mar 8, 2017
@abellotti abellotti removed the wip label Mar 8, 2017
@abellotti
Copy link
Member Author

@imtayadeway @gtanzillo please review. Thanks!!

run_get settings_url

expect(response.parsed_body).to match(
"product" => normalize_settings(Settings.product),
Copy link
Contributor

Choose a reason for hiding this comment

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

I think if we stubbed the Settings constant as below, we'd have more control over this. Then you could see in these tests that you're getting back the values you expected. This test gives me low confidence, because it's verifying against something that's not specified (Settings.product etc.., whatever these values may be), and it's also munging those values with untested code (normalize_hash).

Copy link
Member Author

Choose a reason for hiding this comment

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

correct, if we have a declared Settings hash (without symbol or nil data), the normalize_hash wouldn't be needed. It was written as to make sure the Settings returned is how the API would have rendered it.

end

private

def exposed_settings
ApiConfig.collections[:settings][:categories]
def whitelist_settings(settings)
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems (a) sufficiently complicated, and (b) already abstracted away from details of the request to warrant a service object that could be perhaps better tested in isolation. WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

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

This method is coded as it's needed as such by a following PR. Some of this code will be moving to an API Settings mixin, will be used by servers, regions and zones.

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure I follow. Is there a problem with delegating?

@imtayadeway
Copy link
Contributor

Not sure I get the workflow around configurable categories. Is it designed to be configurable by the end user? How would they configure it, by updating the yml file by hand?

def settings_entry_to_hash(path, value)
path_elements = path.split("/")
result_hash = {path_elements.last => value}
path_elements[0..-2].reverse.each { |entry| result_hash = { entry => result_hash } } if path_elements.length > 1
Copy link
Member

Choose a reason for hiding this comment

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

This might be cleaner if you just start with the value and iterate from there.

    def settings_entry_to_hash(path, value)
      result = value
      path.split("/").reverse.each { |entry| result = {entry => result} }
      result
    end

Copy link
Member

Choose a reason for hiding this comment

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

Actually, now that I think about it, just use store_path

def settings_entry_to_hash(path, value)
  {}.tap { |h| h.store_path(path.split("/"), value) }
end

Example

path = "foo/bar/baz"
value = 1234
{}.tap { |h| h.store_path(path.split("/"), value) }
# => {"foo"=>{"bar"=>{"baz"=>1234}}}

Copy link
Member

Choose a reason for hiding this comment

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

Additionally, if you're just using store_path, then you can avoid the create-entry-and-deep-merge-loop I think.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ahh, didn't know about store_path, NICE !! I'll update.

Copy link
Member Author

Choose a reason for hiding this comment

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

deep_merge was still needed with categories like server/role, server/worker_monitor, ...

@abellotti
Copy link
Member Author

@imtayadeway as discussed yesterday, see updated tests, much simpler. Thanks!!

  categories like it does today like product, authentication, server,
  but also sub pathing, server/role, session/timeout even lower level
  entries like server/worker_monitor/start_algorithm/value

- whitelists a subset of the settings as driven by the new categories

- Can fetch top level categories or any setting entry path:
  GET /api/settings/product, or GET /api/settings/server/role (if exposed)

- Added rspecs
and thus queried by the API eliminating the need for Settings
normalization on the responses in the tests.
  settings categories via the method stub_api_settings_categories.
- Moved the method to within the settings_spec.rb's tests
@miq-bot
Copy link
Member

miq-bot commented Mar 15, 2017

Checked commits abellotti/manageiq@7df0015~...86cd8ff with ruby 2.2.6, rubocop 0.47.1, and haml-lint 0.20.0
2 files checked, 0 offenses detected
Everything looks good. 🏆

@gtanzillo gtanzillo added this to the Sprint 57 Ending Mar 27, 2017 milestone Mar 16, 2017
@gtanzillo gtanzillo merged commit 7af6fcd into ManageIQ:master Mar 16, 2017
@abellotti abellotti deleted the api_fine_grained_settings branch March 17, 2017 19:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants