Skip to content

Implement simple API listing #509

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

Merged
merged 10 commits into from
Jun 15, 2021

Conversation

andrewazores
Copy link
Member

@andrewazores andrewazores commented Jun 10, 2021

Tests to come, this is a quick handler to allow Cryostat to give users a rough idea of the shape of the API that is deployed. There is no information about the purpose of a handler per se, but the verb and path give some idea at least.

$ http --verify=false https://0.0.0.0:8181/api | jq
[
  {
    "version": "V1",
    "path": "/api/v1/auth",
    "verb": "POST"
  },
  {
    "version": "V1",
    "path": "/api/v1/clienturl",
    "verb": "GET"
  },
  {
    "version": "V1",
    "path": "/api/v1/grafana_dashboard_url",
    "verb": "GET"
  },
  {
    "version": "V1",
    "path": "/api/v1/grafana_datasource_url",
    "verb": "GET"
  },
  {
    "version": "V1",
    "path": "/api/v1/recordings",
    "verb": "POST"
  },
  {
    "version": "V1",
    "path": "/api/v1/recordings",
    "verb": "POST"
  },
  {
    "version": "V1",
    "path": "/api/v1/recordings",
    "verb": "GET"
  },
  {
    "version": "V1",
    "path": "/api/v1/recordings/:recordingName",
    "verb": "DELETE"
  },
  {
    "version": "V1",
    "path": "/api/v1/recordings/:recordingName",
    "verb": "GET"
  },
  {
    "version": "V1",
    "path": "/api/v1/recordings/:recordingName/upload",
    "verb": "POST"
  },
  {
    "version": "V1",
    "path": "/api/v1/reports/:recordingName",
    "verb": "GET"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets",
    "verb": "GET"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/events",
    "verb": "GET"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/recordingOptions",
    "verb": "PATCH"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/recordingOptions",
    "verb": "GET"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/recordingOptions",
    "verb": "PATCH"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/recordings",
    "verb": "POST"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/recordings",
    "verb": "POST"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/recordings",
    "verb": "GET"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/recordings/:recordingName",
    "verb": "PATCH"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/recordings/:recordingName",
    "verb": "DELETE"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/recordings/:recordingName",
    "verb": "GET"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/recordings/:recordingName",
    "verb": "PATCH"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/recordings/:recordingName/upload",
    "verb": "POST"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/reports/:recordingName",
    "verb": "GET"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/snapshot",
    "verb": "POST"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/templates",
    "verb": "GET"
  },
  {
    "version": "V1",
    "path": "/api/v1/targets/:targetId/templates/:templateName/type/:templateType",
    "verb": "GET"
  },
  {
    "version": "V1",
    "path": "/api/v1/templates",
    "verb": "POST"
  },
  {
    "version": "V1",
    "path": "/api/v1/templates",
    "verb": "POST"
  },
  {
    "version": "V1",
    "path": "/api/v1/templates/:templateName",
    "verb": "DELETE"
  },
  {
    "version": "V2",
    "path": "/api/v2/certificates",
    "verb": "POST"
  },
  {
    "version": "V2",
    "path": "/api/v2/certificates",
    "verb": "POST"
  },
  {
    "version": "V2",
    "path": "/api/v2/targets",
    "verb": "POST"
  },
  {
    "version": "V2",
    "path": "/api/v2/targets",
    "verb": "POST"
  },
  {
    "version": "V2",
    "path": "/api/v2/targets/:targetId",
    "verb": "DELETE"
  },
  {
    "version": "V2",
    "path": "/api/v2/targets/:targetId/eventsSearch/:query",
    "verb": "GET"
  },
  {
    "version": "V2",
    "path": "/api/v2/targets/:targetId/recordingOptionsList",
    "verb": "GET"
  },
  {
    "version": "V2",
    "path": "/api/v2/targets/:targetId/snapshot",
    "verb": "POST"
  }
]

@andrewazores
Copy link
Member Author

As a corollary, should there be a build step that copies HTTP_API.md into src/main/extras/app/resources, similar to how the web-client assets are copied in? This wouldn't be machine processable but it would include nice human descriptions and examples. We could even link from the /api output here to that document, too.

@andrewazores andrewazores force-pushed the api-listing-handler branch from 5189f5d to cc68f45 Compare June 14, 2021 14:49
@andrewazores andrewazores force-pushed the api-listing-handler branch from cc68f45 to ae2fc05 Compare June 14, 2021 14:59
@jan-law
Copy link
Contributor

jan-law commented Jun 14, 2021

{
    "version": "V2",
    "path": "/api/v2/targets",
    "verb": "POST"
  },
  {
    "version": "V2",
    "path": "/api/v2/targets",
    "verb": "POST"
  },

Out of curiosity, for the handlers that have the same path and verb, eg TargetsPostBodyHandler and TargetsPostHandler, why would it be helpful for users to see the same listing twice?

@andrewazores
Copy link
Member Author

Yea, that's a good point - it probably isn't helpful at all. This happens because of *PostBodyHandlers, but those and the *PostHandlers both represent the same logical endpoint.

@andrewazores
Copy link
Member Author

Latest example output:

{
  "meta": {
    "type": "application/json",
    "status": "OK"
  },
  "data": {
    "result": {
      "overview": "https://0.0.0.0:8181/HTTP_API.md",
      "endpoints": [
        {
          "version": "V1",
          "verb": "POST",
          "path": "/api/v1/auth"
        },
        {
          "version": "V1",
          "verb": "GET",
          "path": "/api/v1/clienturl"
        },
        {
          "version": "V1",
          "verb": "GET",
          "path": "/api/v1/grafana_dashboard_url"
        },
        {
          "version": "V1",
          "verb": "GET",
          "path": "/api/v1/grafana_datasource_url"
        },
        {
          "version": "V1",
          "verb": "POST",
          "path": "/api/v1/recordings"
        },
        {
          "version": "V1",
          "verb": "GET",
          "path": "/api/v1/recordings"
        },
        {
          "version": "V1",
          "verb": "DELETE",
          "path": "/api/v1/recordings/:recordingName"
        },
        {
          "version": "V1",
          "verb": "GET",
          "path": "/api/v1/recordings/:recordingName"
        },
        {
          "version": "V1",
          "verb": "POST",
          "path": "/api/v1/recordings/:recordingName/upload"
        },
        {
          "version": "V1",
          "verb": "GET",
          "path": "/api/v1/reports/:recordingName"
        },
        {
          "version": "V1",
          "verb": "GET",
          "path": "/api/v1/targets"
        },
        {
          "version": "V1",
          "verb": "GET",
          "path": "/api/v1/targets/:targetId/events"
        },
        {
          "version": "V1",
          "verb": "PATCH",
          "path": "/api/v1/targets/:targetId/recordingOptions"
        },
        {
          "version": "V1",
          "verb": "GET",
          "path": "/api/v1/targets/:targetId/recordingOptions"
        },
        {
          "version": "V1",
          "verb": "POST",
          "path": "/api/v1/targets/:targetId/recordings"
        },
        {
          "version": "V1",
          "verb": "GET",
          "path": "/api/v1/targets/:targetId/recordings"
        },
        {
          "version": "V1",
          "verb": "DELETE",
          "path": "/api/v1/targets/:targetId/recordings/:recordingName"
        },
        {
          "version": "V1",
          "verb": "PATCH",
          "path": "/api/v1/targets/:targetId/recordings/:recordingName"
        },
        {
          "version": "V1",
          "verb": "GET",
          "path": "/api/v1/targets/:targetId/recordings/:recordingName"
        },
        {
          "version": "V1",
          "verb": "POST",
          "path": "/api/v1/targets/:targetId/recordings/:recordingName/upload"
        },
        {
          "version": "V1",
          "verb": "GET",
          "path": "/api/v1/targets/:targetId/reports/:recordingName"
        },
        {
          "version": "V1",
          "verb": "POST",
          "path": "/api/v1/targets/:targetId/snapshot"
        },
        {
          "version": "V1",
          "verb": "GET",
          "path": "/api/v1/targets/:targetId/templates"
        },
        {
          "version": "V1",
          "verb": "GET",
          "path": "/api/v1/targets/:targetId/templates/:templateName/type/:templateType"
        },
        {
          "version": "V1",
          "verb": "POST",
          "path": "/api/v1/templates"
        },
        {
          "version": "V1",
          "verb": "DELETE",
          "path": "/api/v1/templates/:templateName"
        },
        {
          "version": "V2",
          "verb": "POST",
          "path": "/api/v2/certificates"
        },
        {
          "version": "V2",
          "verb": "POST",
          "path": "/api/v2/targets"
        },
        {
          "version": "V2",
          "verb": "DELETE",
          "path": "/api/v2/targets/:targetId"
        },
        {
          "version": "V2",
          "verb": "GET",
          "path": "/api/v2/targets/:targetId/eventsSearch/:query"
        },
        {
          "version": "V2",
          "verb": "GET",
          "path": "/api/v2/targets/:targetId/recordingOptionsList"
        },
        {
          "version": "V2",
          "verb": "POST",
          "path": "/api/v2/targets/:targetId/snapshot"
        }
      ]
    }
  }
}

Name follows existing handler naming convention
@jan-law
Copy link
Contributor

jan-law commented Jun 15, 2021

now that I moved this over to extending the V2 base handler abstract class,

After extending the V2 base handler, does that mean apiGetHandler's apiVersion is V2 instead of generic?

@andrewazores
Copy link
Member Author

now that I moved this over to extending the V2 base handler abstract class,

After extending the V2 base handler, does that mean apiGetHandler's apiVersion is V2 instead of generic?

In this specific case no/it doesn't quite matter - the apiVersion is used in the RequestHandler interface to determine the basePath(). This particular handler is for an endpoint that intentionally doesn't conform to the typical versioned API hierarchy most of the other handlers use, so the basePath() isn't used. Generally however yes, handlers extending the V2 base handler should return the V2 ApiVersion. In fact, this should probably be added as a default value in the V2 base handler, so that concrete subclasses only need to specify a version by overriding that if they are doing something different (like this handler).

@jan-law
Copy link
Contributor

jan-law commented Jun 15, 2021

Looks good to me. What are your thoughts @hareetd and @ebaron?

Copy link
Contributor

@hareetd hareetd left a comment

Choose a reason for hiding this comment

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

Looks good, although I'm a bit hesitant to approve since I don't fully understand 100% of the syntax/semantics. However, I did give it a good read-through and from what I do understand, the code is logically consistent with respect to what it's trying to do.

Copy link
Member

@ebaron ebaron left a comment

Choose a reason for hiding this comment

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

Looks good to me!

@andrewazores andrewazores merged commit 4dd4f37 into cryostatio:main Jun 15, 2021
@andrewazores andrewazores deleted the api-listing-handler branch June 15, 2021 20:06
andrewazores added a commit to andrewazores/cryostat that referenced this pull request Jun 24, 2021
* Implement simple API listing

* Suppress comparator serialization

* Apply spotless formatting

* Place verb before path in results

Makes for easier/more natural reading for humans

* Migrate to V2 API response format and include HTTP_API.md resource with link

* Add unit test

* Add simple itest

* Do not output duplicate endpoints

* Remove unused field

* Rename ApiListingHandler -> ApiGetHandler

Name follows existing handler naming convention
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants