Coder Social home page Coder Social logo

PATCH Request not passing Body about kitura-net HOT 16 OPEN

kitura avatar kitura commented on May 20, 2024
PATCH Request not passing Body

from kitura-net.

Comments (16)

ianpartridge avatar ianpartridge commented on May 20, 2024

Hmm it looks like we do set up the curl handle to upload on PATCH: see https://github.com/IBM-Swift/Kitura-net/blob/master/Sources/KituraNet/ClientRequest.swift#L419

Can you provide some example code showing the problem?

from kitura-net.

laurentades avatar laurentades commented on May 20, 2024

See code below

No issue when
httpMethod = "POST"
or
httpMethod = "PUT"

However when
httpMethod = "PATCH"
The body does not come through

I have to confess that being very new to all this my code is very made up from posts all around GitHub, StackOverflow and other blogs... I would not be fussed that you find something blatantly wrong in there.... :-S

Thanks

`
import SwiftyJSON
import KituraNet
func httpCall(httpUri: String, httpMethod: String, httpBody: String, queryDict: [String:String], httpHeaders: [String:String]) -> [String:Any] {

	var returnDict = [String:Any]()
	var queryString: String = ""
		var i: Int = 0
		for (key, value) in queryDict{
			i+=1
			queryString = queryString + key + "=" + value
			if i != queryDict.count {
				queryString = queryString + "&"
			}
		}
	let httpQueryEncoded = queryString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)

	var httpRequestOptions:[ClientRequest.Options] = [
		.schema(""),
		.method(httpMethod),
		.hostname(httpUri + "?" + httpQueryEncoded!),
		.headers(httpHeaders)
	]

	var httpStatus: Int = 0
	var httpResp: String = ""

	let httpRequest = HTTP.request(httpRequestOptions) { response in
		do {
			httpResp = try response!.readString()!
			print("httpResp: \(httpResp)")
			httpStatus = try response!.status

			if let jsonDictionary = JSON.parse(string: httpResp).dictionaryObject {
				returnDict = jsonDictionary 
				print("jsonDictionary: \(jsonDictionary)")
			} else {
				returnDict = ["error": "Unknown data format returned"]
			}
		} catch {
			httpResp = "error: http request returned an error"
			//returnDict["seqHalt"] = "true"
		}
	}

	httpRequest.write(from: httpBody)

	httpRequest.end()

	return returnDict
}`

from kitura-net.

DunnCoding avatar DunnCoding commented on May 20, 2024

Hi @laurentades :)

I've been looking into this issue for you, thanks for providing your example code!
I was wondering if you could also share the code where you call this function?

Basically I'm interested to know what parameters you're passing through to your httpCall(...) function :)

Thanks!

from kitura-net.

laurentades avatar laurentades commented on May 20, 2024

Hi @ddunn2
Sorry for long awaited reply.

The full code is below:

`/*
*/

import SwiftyJSON
import KituraNet

func httpCall(httpUri: String, httpMethod: String, httpBody: String, queryDict: [String:String], httpHeaders: [String:String]) -> [String:Any] {

var returnDict = [String:Any]()

var queryString: String = ""
	var i: Int = 0
	for (key, value) in queryDict{
		i+=1
		queryString = queryString + key + "=" + value
		if i != queryDict.count {
			queryString = queryString + "&"
		}
	}
print("httpUri: \(httpUri)")
print("queryString: \(queryString)")
print("httpHeaders: \(httpHeaders)")
print("httpBody: \(httpBody)")

let httpQueryEncoded = queryString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)

var httpRequestOptions:[ClientRequest.Options] = [
	.schema(""),
	.method(httpMethod),
	.hostname(httpUri + "?" + httpQueryEncoded!),
	.headers(httpHeaders)
]

var httpStatus: Int = 0
var httpResp: String = ""

let httpRequest = HTTP.request(httpRequestOptions) { response in
	do {
		httpResp = try response!.readString()!
		print("httpResp: \(httpResp)")
		httpStatus = try response!.status

		if let jsonDictionary = JSON.parse(string: httpResp).dictionaryObject {
			returnDict = jsonDictionary // we got back JSON
			print("jsonDictionary: \(jsonDictionary)")
		} else {
			returnDict = ["error": "Unknown data format returned"]
		}

	} catch {
		httpResp = "error: http request returned an error"
		//returnDict["seqHalt"] = "true"
	}
}

httpRequest.write(from: httpBody)

httpRequest.end()

return returnDict

}

func main(args: [String:Any]) -> [String:Any] {

var returnDict = [String:Any]()

let seqHalt: String? = args["seqHalt"] as! String
let passThruData: [String:Any]? = args["passThruData"] as! [String:Any]

if seqHalt == "false" {
	
	var httpResponse = [String:Any]()
	    
    let httpUri: String? = args["httpUri"] as! String // String
    let httpMethod: String? = args["httpMethod"] as! String // String
    let httpBody: String? = args["httpBody"] as!  String // Dictionary
    var httpHeaders: [String:String]? = args["httpHeaders"] as! [String:String]   //Dictionary
    var queryDict: [String:String]? = args["queryDict"] as! [String:String]
    let pagingMode: String? = args["pagingMode"] as! String // String
    let pageSize: String? = args["pageSize"] as! String // String
    let jsonCombinePathString: String? = args["jsonCombinePathString"] as! String // String
    
    let jsonCombinePath = jsonCombinePathString!.components(separatedBy: ",")
    
	if pagingMode! == "none" {
		
		returnDict = httpCall(httpUri: httpUri!, httpMethod: httpMethod!, httpBody: httpBody!, queryDict: queryDict!, httpHeaders: httpHeaders!)
		
	} else {
		
		var loopHalt: String = "false"
		var loopCount: Int = 0
		
		var dataArray = [[String:Any]]()
		
		repeat {
			loopCount += 1
			
			switch pagingMode! {

				case "recordRange":
					queryDict!["fromIndex"] = String((loopCount - 1) * (Int(pageSize!)!) + 1)
					queryDict!["toIndex"] = String(loopCount * Int(pageSize!)!)
					
					httpResponse = httpCall(httpUri: httpUri!, httpMethod: httpMethod!, httpBody: httpBody!, queryDict: queryDict!, httpHeaders: queryDict!)
			
					//print("httpResponse: \(httpResponse)")
                    var httpResponseJson = JSON(httpResponse)
                    let response = httpResponseJson["response"].dictionaryValue
					let responseError = response["error"]
					let responseNoData = response["nodata"]
					let responseResult = response["result"]

					if responseError != nil {
						loopHalt = "true"

					} else if responseNoData != nil {
						loopHalt = "true"

					} else {
					    dataArray = jsonCombine(baseArray: dataArray, addData: httpResponse, jsonCombinePathString: jsonCombinePathString!)
					    
					}
		
				case "pageIndex":
					print("pagingMode: \(pagingMode!)")
				case "offset":
					print("pagingMode: \(pagingMode!)")
				default:
					print("pagingMode: \(pagingMode!)")
			}
		
		} while loopHalt == "false"
		
	}
	
    
}else{
    returnDict["seqHalt"] = "true"
}

returnDict["passThruData"] = passThruData

return returnDict
}`

from kitura-net.

laurentades avatar laurentades commented on May 20, 2024

Hi,
Quick update.
Looks lie it is actually the target service acting up with the PATCH request rather than OpenWhisk struggling.
Will confirm further on.

from kitura-net.

laurentades avatar laurentades commented on May 20, 2024

Hi,
Eventually this is not something happening on the target API side: trying the patch method via Postman works just fine with the same parameters..
Any idea ?

from kitura-net.

DunnCoding avatar DunnCoding commented on May 20, 2024

Hi @laurentades
Thanks for providing the code and the updates!
I did some testing with this and also confirmed the patch method seems to be working when I setup my own project and passed through some custom parameters.

I'm going to attempt to recreate the problem you're running into using the code you've provided, once I have more information I'll update here as well.

from kitura-net.

DunnCoding avatar DunnCoding commented on May 20, 2024

@laurentades What parameters are you passing to your func main(args: [String:Any]) -> [String:Any] {...} function?

from kitura-net.

laurentades avatar laurentades commented on May 20, 2024

Hi, the parameters passed to the main func is basically a json set of values which are extracted and passed to variables pretty much just after the beginning of the function:

httpUri: Base target API url
httpMethod: HTTP method (GET,POST,PUT,PATCH...)
httpBody: request body as a string
httpHeaders: Dictionary formatted set of values later-on processed into a request header
queryDict: Dictionary formatted set of values later-on processed into a request query string
pagingMode : text value used for specific API functionality (irrelevant here)
pageSize : numeric value used for specific API functionality (irrelevant here)
jsonCombinePathString : text value used for specific API functionality (irrelevant here)

Hope that helps.... :-)
Thanks !

from kitura-net.

DunnCoding avatar DunnCoding commented on May 20, 2024

Thanks for that! :)
Do you have the values you're passing in? Mainly I want to know the values you're passing into httpBody, httpHeaders and queryDict, this will allow me to recreate your exact issue :)

from kitura-net.

laurentades avatar laurentades commented on May 20, 2024

ah yes sure:
httpHeaders: ["Authorization": "Bearer keyDbQyg9ApRFyffG", "Content-type": "application/json; charset=utf-8", "Accept": "application/json"]

httpBody: {"fields":{"targetGP":0,"channelDiscount":0,"importUplift":0,"brandId":"1804391000001949436","brandName":"Generic Local"},"typecast":true}

In this case queryDict is empty

from kitura-net.

DunnCoding avatar DunnCoding commented on May 20, 2024

Thanks for providing that information!

So I've put together a little test scenario, and just want to confirm we're on the same page.

I created a simple Kitura server using the kitura init command, added all necessary packages and then I edited the postInit() function to look like this:

func postInit() throws {
        // Capabilities
        initializeMetrics(app: self)

        // Endpoints
        initializeHealthRoutes(app: self)
        router.all(middleware: BodyParser())
        router.patch("/") { request, response, next in
            print(request.body)
            response.send("OK")
            next()
        }
    }

I then created a project that includes all the code you've provided and made the necessary call, the code looks like this:

import KituraNet
import SwiftyJSON
import Foundation

func httpCall(httpUri: String, httpMethod: String, httpBody: String, queryDict: [String:String], httpHeaders: [String:String]) -> [String:Any] {
    
    var returnDict = [String:Any]()
    
    var queryString: String = ""
    var i: Int = 0
    for (key, value) in queryDict{
        i+=1
        queryString = queryString + key + "=" + value
        if i != queryDict.count {
            queryString = queryString + "&"
        }
    }
    print("httpUri: \(httpUri)")
    print("queryString: \(queryString)")
    print("httpHeaders: \(httpHeaders)")
    print("httpBody: \(httpBody)")
    
    let httpQueryEncoded = queryString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
    
    var httpRequestOptions:[ClientRequest.Options] = [
        .schema(""),
        .method(httpMethod),
        .hostname(httpUri + "?" + httpQueryEncoded!),
        .headers(httpHeaders)
    ]
    
    var httpStatus: Int = 0
    var httpResp: String = ""
    
    let httpRequest = HTTP.request(httpRequestOptions) { response in
        do {
            httpResp = try response!.readString()!
            print("httpResp: \(httpResp)")
            httpStatus = try response!.status
            
            if let jsonDictionary = JSON.parse(string: httpResp).dictionaryObject {
                returnDict = jsonDictionary // we got back JSON
                print("jsonDictionary: \(jsonDictionary)")
            } else {
                returnDict = ["error": "Unknown data format returned"]
            }
            
        } catch {
            httpResp = "error: http request returned an error"
            //returnDict["seqHalt"] = "true"
        }
    }
    
    httpRequest.write(from: httpBody)
    
    httpRequest.end()
    
    return returnDict
}


func main(args: [String:Any]) -> [String:Any] {
    
    var returnDict = [String:Any]()
    
    let seqHalt: String? = args["seqHalt"] as! String
    let passThruData: [String:Any]? = args["passThruData"] as! [String:Any]
    
    if seqHalt == "false" {
        
        var httpResponse = [String:Any]()
        
        let httpUri: String? = args["httpUri"] as! String // String
        let httpMethod: String? = args["httpMethod"] as! String // String
        let httpBody: String? = args["httpBody"] as!  String // Dictionary
        var httpHeaders: [String:String]? = args["httpHeaders"] as! [String:String]   //Dictionary
        var queryDict: [String:String]? = args["queryDict"] as! [String:String]
        let pagingMode: String? = args["pagingMode"] as! String // String
        let pageSize: String? = args["pageSize"] as! String // String
        let jsonCombinePathString: String? = args["jsonCombinePathString"] as! String // String
        
        let jsonCombinePath = jsonCombinePathString!.components(separatedBy: ",")
        
        if pagingMode! == "none" {
            
            returnDict = httpCall(httpUri: httpUri!, httpMethod: httpMethod!, httpBody: httpBody!, queryDict: queryDict!, httpHeaders: httpHeaders!)
            
        } else {
            
            var loopHalt: String = "false"
            var loopCount: Int = 0
            
            var dataArray = [[String:Any]]()
            
            repeat {
                loopCount += 1
                
                switch pagingMode! {
                    
                case "recordRange":
                    queryDict!["fromIndex"] = String((loopCount - 1) * (Int(pageSize!)!) + 1)
                    queryDict!["toIndex"] = String(loopCount * Int(pageSize!)!)
                    
                    httpResponse = httpCall(httpUri: httpUri!, httpMethod: httpMethod!, httpBody: httpBody!, queryDict: queryDict!, httpHeaders: queryDict!)
                    
                    //print("httpResponse: \(httpResponse)")
                    var httpResponseJson = JSON(httpResponse)
                    let response = httpResponseJson["response"].dictionaryValue
                    let responseError = response["error"]
                    let responseNoData = response["nodata"]
                    let responseResult = response["result"]
                    
                    if responseError != nil {
                        loopHalt = "true"
                        
                    } else if responseNoData != nil {
                        loopHalt = "true"
                        
                    } else {
//    I commented this line out as I assume jsonCombine is a custom function you've created
//                        dataArray = jsonCombine(baseArray: dataArray, addData: httpResponse, jsonCombinePathString: jsonCombinePathString!)
                        
                    }
                    
                case "pageIndex":
                    print("pagingMode: \(pagingMode!)")
                case "offset":
                    print("pagingMode: \(pagingMode!)")
                default:
                    print("pagingMode: \(pagingMode!)")
                }
                
            } while loopHalt == "false"
            
        }
        
        
    }else{
        returnDict["seqHalt"] = "true"
    }
    returnDict["passThruData"] = passThruData
    
    return returnDict
}


let result = main(args: ["httpUri": "http://localhost:8080",
            "httpMethod": "PATCH",
            "httpBody": "{\"fields\":{\"targetGP\":0,\"channelDiscount\":0,\"importUplift\":0,\"brandId\":\"1804391000001949436\",\"brandName\":\"Generic Local\"},\"typecast\":true}",
            "queryDict": [:],
            "httpHeaders": ["Authorization": "Bearer keyDbQyg9ApRFyffG", "Content-type": "application/json; charset=utf-8", "Accept": "application/json"],
            "seqHalt": "false",
            "pagingMode": "none",
            "passThruData": ["": ""],
            "pageSize": "",
            "jsonCombinePathString": ""
            ])

print(result)

When I start up the server and run the code, I get this output on the server side:

Optional(Kitura.ParsedBody.json(["fields": {
    brandId = 1804391000001949436;
    brandName = "Generic Local";
    channelDiscount = 0;
    importUplift = 0;
    targetGP = 0;
}, "typecast": 1]))

To me this suggests the body is being passed through? Is this the issue you're referring to? Or have I made a mistake in my recreation?

from kitura-net.

laurentades avatar laurentades commented on May 20, 2024

Hi,
What you are doing seems right. However that does not match what i am experiencing. As i wante to make sure the target API is no the issue, I use a generic request bin service to check data stream.

Here is that comes into this bin for the PUT request:

http://requestbin.fullcontact.com
PUT /11njq5e1  application/json; charset=utf-8
 132 bytes 7s ago  
From 158.175.125.86, 52.46.38.68
FORM/POST PARAMETERS
None
HEADERS
Cloudfront-Is-Tablet-Viewer: false

Connect-Time: 0

Host: requestbin.fullcontact.com

Total-Route-Time: 0

Content-Length: 132

Authorization: Bearer keyDbQyg9ApRFyffG

Cloudfront-Forwarded-Proto: http

X-Amz-Cf-Id: NYikN7fDMNoxRt9K6Ru7VU_mxWY5Eav1IdY1IuOIcoFCELbQxMq21Q==

Cloudfront-Viewer-Country: US

Cloudfront-Is-Smarttv-Viewer: false

Cloudfront-Is-Mobile-Viewer: false

Cloudfront-Is-Desktop-Viewer: true

X-Request-Id: d6ca222d-b816-41ba-b58c-2eb72fb9abbe

Via: 1.1 b8c7ca6d200a4b87d5b25c2d6f760752.cloudfront.net (CloudFront), 1.1 vegur

Accept: application/json

Connection: close

Content-Type: application/json; charset=utf-8

RAW BODY
{"fields":{"importUplift":0,"channelDiscount":0,"targetGP":-12,"brandName":"Adobe","brandId":"1804391000000697017"},"typecast":true}

and the PATCH request:

http://requestbin.fullcontact.com
PATCH /11njq5e1  application/json; charset=utf-8
 0 bytes 1m ago  
From 158.175.125.86, 52.46.38.43
FORM/POST PARAMETERS
None
HEADERS
Cloudfront-Is-Tablet-Viewer: false

Connect-Time: 0

Host: requestbin.fullcontact.com

Total-Route-Time: 0

Authorization: Bearer keyDbQyg9ApRFyffG

Cloudfront-Forwarded-Proto: http

Content-Type: application/json; charset=utf-8

X-Amz-Cf-Id: XrxWO0jz5l_bAYzbsqz7rI4eCctYxmxMYBZ2vqReC7tvrah46XNo_Q==

Cloudfront-Is-Smarttv-Viewer: false

Cloudfront-Is-Mobile-Viewer: false

Cloudfront-Is-Desktop-Viewer: true

Content-Length: 0

Via: 1.1 fff10bd55076b11530aa8b26ee2c0b65.cloudfront.net (CloudFront), 1.1 vegur

Accept: application/json

X-Request-Id: 698511af-8546-4593-98dd-419893da5813

Cloudfront-Viewer-Country: US

Connection: close

RAW BODY
None

That is consistent with what i am experiencing in my test environment to my target API.

Does that help ?

from kitura-net.

DunnCoding avatar DunnCoding commented on May 20, 2024

Hi,
Apologies for the delay. Thanks for providing that information it's very helpful!
I'll review what you've provided and see if I can find the cause of the issue.

from kitura-net.

DunnCoding avatar DunnCoding commented on May 20, 2024

Hey!
Can you provide the code for your router.patch(...) method on your server?

Thanks :)

from kitura-net.

laurentades avatar laurentades commented on May 20, 2024

errr... no idea what this is :-)
I am just using KituraNet as a 'Import' in IBM Bluemix cloud functions. What I have shared so far is basically all i have written for this function...
Hope that's not a show-stopper...!
Thanks

from kitura-net.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.