crossplane-contrib / provider-http

Crossplane Provider designed to facilitate sending HTTP requests as resources.
Apache License 2.0
42 stars 6 forks source link

Chained calls and JSON #19

Closed marcosQuesada closed 2 months ago

marcosQuesada commented 3 months ago

What happened?

I'm implementing a POC with multiple calls, where the first call gets a token, second one uses that token as Authorization Header, I do have doubts about how to handle json responses and patching json body on other requests.

I do have 2 questions:

Glad to hear any indication about how body is intended to be used with JSON data, or if there's any better way of handling authentication or multiple request please let me know.

How can we reproduce it?

In this simple example the DisposableRequest calls an auth endpoint that returns a token as a string, then the token is patched in Authorization header, works perfect. But, how to handle a json response? example: {"token": "ewjhwekjwh..."} .

Example:

apiVersion: http.crossplane.io/v1alpha1
kind: DisposableRequest
metadata:
  name: auth-request
  namespace: default
spec:
  deletionPolicy: Orphan
  forProvider:
    # The 'expectedResponse' field is optional. If used, also set 'rollbackRetriesLimit', which determines the number of HTTP requests to be sent until the jq query returns true.    
    # expectedResponse: '.Body.job_status == "success"'
    insecureSkipTLSVerify: true
    waitTimeout: 5m
    rollbackRetriesLimit: 5
    url:  http://172.17.0.1:8081/api/v1/auth
    method: GET
    body: ""
    headers:
      Content-Type:
      - "application/json"
  providerConfigRef:
    name: http-conf
---
apiVersion: kubernetes.crossplane.io/v1alpha2
kind: Object
metadata:
  name: multi-request
spec:
  references:
  - dependsOn:
      apiVersion: http.crossplane.io/v1alpha1
      kind: DisposableRequest
      name: auth-request
      namespace: default  
  - patchesFrom:
      apiVersion: http.crossplane.io/v1alpha1
      kind: DisposableRequest
      name: auth-request
      namespace: default  
      fieldPath: status.response.body
    toFieldPath: spec.forProvider.headers.Authorization[0]
  forProvider:
    manifest:
      apiVersion: http.crossplane.io/v1alpha1
      kind: Request
      metadata:
        name: cmd-request
      spec:
        forProvider:
          headers:
            Content-Type:
              - "application/json"
            Accept:
              - "application/json"
            Authorization:
              - "Basic BASE64_ENCODED_USER_CREDENTIALS"
          payload:
            baseUrl: http://172.17.0.1:8081/api/v1/pets
            body: |
              {
                "id": 111344321112,
                "name": "zzxxxxxxxxxxxxx",
                "color": "color-12",
                "price": 1232119,
                "state": "foo-state-2"
              }

          mappings:
            - method: "POST"
              url: .payload.baseUrl
              body: |
                {
                  "id": .payload.body.id,
                  "name": .payload.body.name, 
                  "color": .payload.body.color,
                  "price": .payload.body.price,
                  "state": .payload.body.state
                }
            - method: "GET"
              url: (.payload.baseUrl + "/" + .response.body.name)
            - method: "PUT"
              url: (.payload.baseUrl + "/" + .response.body.name)
              body: |
                {
                  "id": .payload.body.id,
                  "name": .payload.body.name, 
                  "color": .payload.body.color,
                  "price": .payload.body.price,
                  "state": .payload.body.state
                }
            - method: "DELETE"
              url: (.payload.baseUrl + "/" + .response.body.name)
        providerConfigRef:
          name: http-conf     
  providerConfigRef:
    name: kubernetes-provider

What environment did it happen in?

Crossplane version: 1.14.0

Using:

arielsepton commented 2 months ago

Regarding your first question, since you're using provider-kubernetes patches-from-resource, you might want to open an issue there, perhaps requesting a "transforms" key similar to what is supported in compositions nowadays. You can refer to this example for context: link to example.

Also, there are plans to add patches-from-secrets feature in both DisposableRequest and Request.

In the meantime, perhaps using compositions (specifically function-go-templating) can help solve your patching problem. Simply create a composition of both requests and patch data from the first request to the second using the following syntax:

token: Bearer {{ (.observed.resources.loginrequest.resource.status.response.body | fromJson).token }}

As for your second question, your implementation looks good and should work fine.