Guardian Component - getTradingWindows - Is this a GET or POST action?

The Bet Angel API makes it easy for you to further enhance your betting and trading by integrating your own code into Bet Angel
Post Reply
RobberBaron
Posts: 16
Joined: Wed Dec 02, 2020 10:14 pm

Hi,

I'm doing an implementation of the API in C# .NET using HTTPClient to call the API. I've implemented all the ENUMS and Types and I'm just going through tests for all the Component EndPoints to make sure I've not messed anything up.

The 2 endpoints I've tested so far applyCoupons & removeMarkets from the Guardian component, work fine using POST requests. I'm getting back the results I'm expecting.

The issue I have is with the getTradingWindows endpoint. According to the documentation there's no Instructions to send in the POST request like all the other endpoints in the API.

Is the call to this endpoint a POST request with an empty body or is it a simple GET REQUEST?

The Test I did was:

1) Add markets to Guardian (applyCoupon)
2) I opened a market in the main-window of BetAngel, opened a new window (by right clicking on a Guardian Row) - So I believe I have 2 trading windows open.
3) Send Get request to open trading windows (getTradingWindows) - I get back "Error: NotFound"
4) Both trading windows still open and active. I sent a POST request with an empty body to the getTradingWindows. Got nothing

Here's the BetAngel Api Log file contents ( I refreshed this after each step of the above)
08/07/2024 12:10:15.151:APIServerManager: Activating Server
08/07/2024 12:10:15.209:APIServer: StartAPIHost url=http://localhost:9000/
08/07/2024 12:10:15.736:APIServer: Start of Configuration
08/07/2024 12:10:15.893:APIServer: End of Configuration
08/07/2024 12:10:15.994:APIServerManager: Server Activated
08/07/2024 12:11:11.876:APIServerMessageHandler: Url = http://localhost:9000/api/guardian/v1.0/applyCoupon
08/07/2024 12:11:11.877:APIServerMessageHandler: Data = {"couponName":"Horse Racing - Test","clearOption":"CLEAR_GUARDIAN_AND_WATCH_LIST","watchListNumber":1}
08/07/2024 12:11:14.843:APIServerMessageHandler: Response Data = {"status":"OK","marketIdsAdded":["1.230465751","1.230465752","1.230465747","1.230465749","1.230465757","1.230465753","1.230465754","1.230465760","1.230465763","1.230465764","1.230465761","1.230465766","1.230465768","1.230465776","1.230465777","1.230465772","1.230465774","1.230465782","1.230465783","1.230465779","1.230465780","1.230465994","1.230465990","1.230465991","1.230465997","1.230466001","1.230466002","1.230465999","1.230466004","1.230466008","1.230466009","1.230466006","1.230466014","1.230466015","1.230466011","1.230466012","1.230466018","1.230466022","1.230466023","1.230466020","1.230466028","1.230466029","1.230466025","1.230466026","1.230468752","1.230468754","1.230468753","1.230468757","1.230468759","1.230468758","1.230468762","1.230468764","1.230468763","1.230468771","1.230468767","1.230468769","1.230468768","1.230468772","1.230468776","1.230468774","1.230468773","1.230468781","1.230468777","1.230468779","1.230468778","1.230468782","1.230468784","1.230468783","1.230466032","1.230466036","1.230466037","1.230466034","1.230466039","1.230466043","1.230466044","1.230466041","1.230466049","1.230466050","1.230466046","1.230466047","1.230466056","1.230466057","1.230466053","1.230466054","1.230466062","1.230466063","1.230466059","1.230466060","1.230466068","1.230466069","1.230466065","1.230466066","1.230466075","1.230466076","1.230466072","1.230466073","1.230466082","1.230466083","1.230466079","1.230466080","1.230466088","1.230466085","1.230466089","1.230466086","1.230466095","1.230466092","1.230466096","1.230466093","1.230466103","1.230466099","1.230466104","1.230466101","1.230466109","1.230466106","1.230466110","1.230466107","1.230466115","1.230466112","1.230466116","1.230466113","1.230466122","1.230466119","1.230466123","1.230466120","1.230466129","1.230466126","1.230466130","1.230466127"]}
08/07/2024 12:18:47.457:APIServerMessageHandler: Url = http://localhost:9000/api/guardian/v1.0 ... ingWindows
08/07/2024 12:18:47.474:APIServerMessageHandler: Data = null
08/07/2024 12:18:47.477:APIServerMessageHandler: Response Data = {"Message":"No HTTP resource was found that matches the request URI 'http://localhost:9000/api/guardian/v1.0 ... etail":"No route data was found for this request."}
The log only shows the API responded to steps 1 and 4. Nothing was recorded for step 3 (GET request).

The code I use to send HTTP requests that works
string postData = JsonSerializer.Serialize(Instruction);
var data = new StringContent(postData, Encoding.UTF8, "application/json");

return client.PostAsync(uri, data);
The different versions of code I've tried for the getTradingWindows issue, see below
1) // Attempting a POST with no content in the POST body using null
var data = new StringContent(null, Encoding.UTF8, "application/json");
return client.PostAsync(uri, data);

2) // Attempting a POST with no content in the POST body using null
return client.PostAsync(uri, null);


3) // A GET request
return client.GetAsync(uri);

I've tried looking at the Bet Angel API Tests that BetAngel API provides, but there's none for Trading Windows.

Any help clarification or help is appreciated.

Thank you in advance
User avatar
jimibt
Posts: 4197
Joined: Mon Nov 30, 2015 6:42 pm

I just looked at this in fiddler and it's a POST request with an empty json body i.e.

Code: Select all

string body = @"{}";

return client.PostAsync(uri, body);
hope this helps...
RobberBaron
Posts: 16
Joined: Wed Dec 02, 2020 10:14 pm

Thanks jimibt, that did give me a few ideas to forcing an empty body in a POST request. using C# .Net

Tried:

1) return client.PostAsync(uri, null);

2) string postData = JsonSerializer.Serialize(string.Empty);
var data = new StringContent(postData, Encoding.UTF8, "application/json");
return client.PostAsync(uri, data);

3) string postData = JsonSerializer.Serialize(@"{}");
var data = new StringContent(postData, Encoding.UTF8, "application/json");

return client.PostAsync(uri, data);


However, according to the C# help i can find, the null option for PostAsync should force an empty Post Body. Although a string literal of curly braces should do the same thing.

However, the API is responding with "Not Found" as the Status error code.

Here's the new log file from BetAngel app. You can see I've sent "", {}, null and is received in the Data portion of the log message, however, the API seems to be having issues the the URL routing of getTradingWindows. I've tripled checked the spelling and even deliberately put a wrong URL path (getTradingWindowsTestForClobbers) for a test to check if I would get the same response and I did.

For the BetAngel software team I'm using version 1.61.1 in practice Mode.

Log file:
08/07/2024 13:15:29.591:APIServerManager: Activating Server
08/07/2024 13:15:29.596:APIServer: StartAPIHost url=http://localhost:9000/
08/07/2024 13:15:29.670:APIServer: Start of Configuration
08/07/2024 13:15:29.706:APIServer: End of Configuration
08/07/2024 13:15:29.718:APIServerManager: Server Activated
08/07/2024 13:16:27.222:APIServerMessageHandler: Url = http://localhost:9000/api/guardian/v1.0/applyCoupon
08/07/2024 13:16:27.223:APIServerMessageHandler: Data = {"couponName":"Horse Racing - Test","clearOption":"CLEAR_GUARDIAN_AND_WATCH_LIST","watchListNumber":1}
08/07/2024 13:16:29.564:APIServerMessageHandler: Response Data = {"status":"OK","marketIdsAdded":["1.230465751","1.230465752","1.230465747","1.230465749","1.230465757","1.230465753","1.230465754","1.230465760","1.230465763","1.230465764","1.230465761","1.230465766","1.230465768","1.230465776","1.230465777","1.230465772","1.230465774","1.230465782","1.230465783","1.230465779","1.230465780","1.230465994","1.230465990","1.230465991","1.230465997","1.230466001","1.230466002","1.230465999","1.230466004","1.230466008","1.230466009","1.230466006","1.230466014","1.230466015","1.230466011","1.230466012","1.230466018","1.230466022","1.230466023","1.230466020","1.230466028","1.230466029","1.230466025","1.230466026","1.230468752","1.230468754","1.230468753","1.230468757","1.230468759","1.230468758","1.230468762","1.230468764","1.230468763","1.230468771","1.230468767","1.230468769","1.230468768","1.230468772","1.230468776","1.230468774","1.230468773","1.230468781","1.230468777","1.230468779","1.230468778","1.230468782","1.230468784","1.230468783","1.230466032","1.230466036","1.230466037","1.230466034","1.230466039","1.230466043","1.230466044","1.230466041","1.230466049","1.230466050","1.230466046","1.230466047","1.230466056","1.230466057","1.230466053","1.230466054","1.230466062","1.230466063","1.230466059","1.230466060","1.230466068","1.230466069","1.230466065","1.230466066","1.230466075","1.230466076","1.230466072","1.230466073","1.230466082","1.230466083","1.230466079","1.230466080","1.230466088","1.230466085","1.230466089","1.230466086","1.230466095","1.230466092","1.230466096","1.230466093","1.230466103","1.230466099","1.230466104","1.230466101","1.230466109","1.230466106","1.230466110","1.230466107","1.230466115","1.230466112","1.230466116","1.230466113","1.230466122","1.230466119","1.230466123","1.230466120","1.230466129","1.230466126","1.230466130","1.230466127"]}
08/07/2024 13:18:28.078:APIServerMessageHandler: Url = http://localhost:9000/api/guardian/v1.0 ... ingWindows
08/07/2024 13:18:28.078:APIServerMessageHandler: Data = "{}"
08/07/2024 13:18:28.084:APIServerMessageHandler: Response Data = {"Message":"No HTTP resource was found that matches the request URI 'http://localhost:9000/api/guardian/v1.0 ... etail":"No route data was found for this request."}
08/07/2024 13:21:54.525:APIServerMessageHandler: Url = http://localhost:9000/api/guardian/v1.0 ... ingWindows
08/07/2024 13:21:54.526:APIServerMessageHandler: Data = "{}"
08/07/2024 13:21:54.526:APIServerMessageHandler: Response Data = {"Message":"No HTTP resource was found that matches the request URI 'http://localhost:9000/api/guardian/v1.0 ... etail":"No route data was found for this request."}
08/07/2024 13:28:54.132:APIServerMessageHandler: Url = http://localhost:9000/api/guardian/v1.0 ... ingWindows
08/07/2024 13:28:54.172:APIServerMessageHandler: Data = ""
08/07/2024 13:28:54.172:APIServerMessageHandler: Response Data = {"Message":"No HTTP resource was found that matches the request URI 'http://localhost:9000/api/guardian/v1.0 ... etail":"No route data was found for this request."}
08/07/2024 13:39:36.641:APIServerMessageHandler: Url = http://localhost:9000/api/guardian/v1.0 ... ingWindows
08/07/2024 13:39:36.641:APIServerMessageHandler: Data = ""
08/07/2024 13:39:36.642:APIServerMessageHandler: Response Data = {"Message":"No HTTP resource was found that matches the request URI 'http://localhost:9000/api/guardian/v1.0 ... etail":"No route data was found for this request."}
08/07/2024 13:43:30.225:APIServerMessageHandler: Url = http://localhost:9000/api/guardian/v1.0 ... ingWindows
08/07/2024 13:43:30.225:APIServerMessageHandler: Data = "{}"
08/07/2024 13:43:30.225:APIServerMessageHandler: Response Data = {"Message":"No HTTP resource was found that matches the request URI 'http://localhost:9000/api/guardian/v1.0 ... etail":"No route data was found for this request."}
08/07/2024 13:51:53.115:APIServerMessageHandler: Url = http://localhost:9000/api/guardian/v1.0 ... orClobbers
08/07/2024 13:51:53.125:APIServerMessageHandler: Data = "{}"
08/07/2024 13:51:53.521:APIServerMessageHandler: Response Data = {"Message":"No HTTP resource was found that matches the request URI 'http://localhost:9000/api/guardian/v1.0 ... etail":"No route data was found for this request."}
08/07/2024 13:57:58.516:APIServerMessageHandler: Url = http://localhost:9000/api/guardian/v1.0 ... ingWindows
08/07/2024 13:57:58.534:APIServerMessageHandler: Data = "{}"
08/07/2024 13:57:58.535:APIServerMessageHandler: Response Data = {"Message":"No HTTP resource was found that matches the request URI 'http://localhost:9000/api/guardian/v1.0 ... etail":"No route data was found for this request."}
08/07/2024 13:59:18.854:APIServerMessageHandler: Url = http://localhost:9000/api/guardian/v1.0 ... ingWindows
08/07/2024 13:59:18.855:APIServerMessageHandler: Data = "{}"
08/07/2024 13:59:18.855:APIServerMessageHandler: Response Data = {"Message":"No HTTP resource was found that matches the request URI 'http://localhost:9000/api/guardian/v1.0 ... etail":"No route data was found for this request."}
And a screen shot of the Trading Windows that I had open when doing these tests, expecting info about them to be returned in the post response, but none was received.

Image

Is there an internal URL routing issue with BetAngelAPI for getTradingWindows?

Cheers.
You do not have the required permissions to view the files attached to this post.
User avatar
jimibt
Posts: 4197
Joined: Mon Nov 30, 2015 6:42 pm

I have noticed that all of the other api classes have an instruction class attached, i.e.

GetMarketPricesInstruction
GetInstancesInstruction
GetMarketsInstruction

etc, etc

There is no defined class called "GetTradingWindowsInstruction" (we only have GetTradingWindowsResult and GetTradingWindowsResponse). This exclusion of the Instruction class doesn't follow the pattern in the exposed api, so maybe there IS something under the covers that isn't being seen.
RobberBaron
Posts: 16
Joined: Wed Dec 02, 2020 10:14 pm

Hi Jimibt,

When I was implementing the all the enums and types for all endpoints, I noticed that getTradingWindows was the odd one out. In that it didn't take an Instruction like all the others.

So i assumed it was a GET call or a POST with an empty body. A GET Call does nothing and nothing is logged by BetAngelApi in the log file when doing a GET call. That's why I'm struggling with an empty body POST call.

It's this one Endpoint is giving me problems, all the other EndPoints work like a charm. I probably won't use this getTradingWindows at all, but I would like it working for completeness, if I were to offer my C# BetAngelApi library free of charge on here.

I have used C# in the past to do web development but this one has exhausted my knowledge of POST, GET, DELETE, PUT, PATCH calls. C# used to use WebRequest which has been supplanted by HTTP Client.
User avatar
jimibt
Posts: 4197
Joined: Mon Nov 30, 2015 6:42 pm

likewise, i've not implemented this one as my library is purely for personal use (also c#) and I'm not using this endpoint.

If you do stumble on the solution, would be great to see.. good luck!
Bet Angel
Bet Angel
Bet Angel
Posts: 4031
Joined: Tue Apr 14, 2009 3:47 pm

RobberBaron wrote:
Mon Jul 08, 2024 2:22 pm

For the BetAngel software team I'm using version 1.61.1 in practice Mode.
That'll be the issue. getTradingWindows was added to the API as part of the v1.62 beta that can be downloaded from this forum.
User avatar
jimibt
Posts: 4197
Joined: Mon Nov 30, 2015 6:42 pm

RobberBaron wrote:
Mon Jul 08, 2024 3:24 pm
Hi Jimibt,

When I was implementing the all the enums and types for all endpoints, I noticed that getTradingWindows was the odd one out. In that it didn't take an Instruction like all the others.

So i assumed it was a GET call or a POST with an empty body. A GET Call does nothing and nothing is logged by BetAngelApi in the log file when doing a GET call. That's why I'm struggling with an empty body POST call.

It's this one Endpoint is giving me problems, all the other EndPoints work like a charm. I probably won't use this getTradingWindows at all, but I would like it working for completeness, if I were to offer my C# BetAngelApi library free of charge on here.

I have used C# in the past to do web development but this one has exhausted my knowledge of POST, GET, DELETE, PUT, PATCH calls. C# used to use WebRequest which has been supplanted by HTTP Client.

don't know if it'll help, but here's my calling method against the api:

Screenshot 2024-07-08 191338.png

Screenshot 2024-07-08 190836.png

and the implemented api method looks like this:

Screenshot 2024-07-08 200301.png

using the implementation above, i was able to get it to work by adding a class to BetfairWS as per below:

public class GetTradingWindowsInstruction { }

this then feeds into the api call:

Code: Select all

public GetTradingWindowsResponse? GetTradingWindows()
{
    // setup body request object
    var getTradingWindowsInstruction = new GetTradingWindowsInstruction();


    // get the open trading windows
    var getTradingWindowsResponse = ba.CallApi<GetTradingWindowsResponse, GetTradingWindowsInstruction>(getTradingWindowsInstruction);

    return getTradingWindowsResponse;
}
You do not have the required permissions to view the files attached to this post.
RobberBaron
Posts: 16
Joined: Wed Dec 02, 2020 10:14 pm

Upgrading to 1.62 beta 3, resolved the getTradingWindows issue.

Jimibt, I might refactor my code with ideas from yours on handling the HTTPClient side of things.

Here's how I've got my project organised at the moment.

Example Instruction class

Code: Select all

internal class ApplyCouponInstruction
{
    // The name of the coupon(from the list of Coupon filters on the Coupon tab of Guardian) that you wish to use to load markets into Guardian and the Watch List.
    // Required: Yes
    [JsonPropertyName("couponName")]
    public string? CouponName { get; set; }

    // The choice of what should be cleared before adding the new markets
    // Required: Yes
    [JsonConverter(typeof(JsonStringEnumConverter))]
    [JsonPropertyName("clearOption")]
    public CouponClearOptions? ClearOption { get; set; }

    // The watch list that will contain the coupon.Value must be in the range [1 to 5]
    // Required: Yes
    [JsonPropertyName("watchListNumber")]
    public int? WatchListNumber { get; set; }
}
Current HTTP sending class, need to re-write to handle things better

Code: Select all

internal class Send<T,V>
{
    private string? OperationName;
    private string? EndPoint;
    private T? Instruction;
   
    public Send(string operationName, string endPoint, T instruction)
    {
        this.OperationName = operationName;
        this.EndPoint = endPoint;
        this.Instruction = instruction;
    }

    public async Task<V> SendAsync()
    {
        var uri = EndPointInfo.Url + this.EndPoint + this.OperationName;

        var client = new HttpClient();

        // Send an empty POST Body if there is no instruction passed
        string postData = (this.Instruction is not null) ? JsonSerializer.Serialize(this.Instruction) :
                                                           JsonSerializer.Serialize(@"{}");

        var data = new StringContent(postData, Encoding.UTF8, "application/json");

        var response = await client.PostAsync(uri, data);

        V responseObject = default(V);

        if (response.IsSuccessStatusCode)
        {
            string responseBody = await response.Content.ReadAsStringAsync();
            try
            {
                responseObject = JsonSerializer.Deserialize<V>(responseBody);
            }
            catch (Exception e)
            {
                Console.WriteLine("Error: " + e.Message);
            }

            Console.WriteLine("response: " + responseBody);
        }
        else
        {
            Console.WriteLine("Error: " + response.StatusCode);
        }

        return responseObject;  
    }
}
And here's how I currently call and initiate the Send class.

Code: Select all

           // Adds markets to Guardian via an existing coupon
           var instruction = new ApplyCouponInstruction
           {
               CouponName = "Horse Racing - Test",
               ClearOption = CouponClearOptions.CLEAR_GUARDIAN_AND_WATCH_LIST,
               WatchListNumber = 1
           };

           var send = new Send<ApplyCouponInstruction, ApplyCouponResponse>(EndPointInfo.ApplyCouponOperation,
                                                                                                                                            EndPointInfo.GuardianEndPoint,
                                                                                                                                             instruction);
           send.SendAsync().Wait();
 
User avatar
jimibt
Posts: 4197
Joined: Mon Nov 30, 2015 6:42 pm

RobberBaron wrote:
Mon Jul 08, 2024 8:32 pm
Upgrading to 1.62 beta 3, resolved the getTradingWindows issue.

Jimibt, I might refactor my code with ideas from yours on handling the HTTPClient side of things.

[snip]...
we're not 100 miles away in our approaches. a small thing i would add is that my api convention always applies the Response followed by the instruction in the generic methods. just means that the left hand side always points to the result and the right hand side, the instruction.

Screenshot 2024-07-08 211851.png

Also, i used a small FRIG to use the GetInstruction type to obtain the endpointUrl. i.e.:

Code: Select all

        private string? GetApiEndPointForInstruction<T2>()
        {
            string? apiEndPoint;

            if (typeof(T2) == typeof(GetMarketPricesInstruction))
            {
                apiEndPoint = GET_MARKET_PRICES;
            }
            else if (typeof(T2) == typeof(GetMarketsInstruction))
            {
                apiEndPoint = GET_MARKETS;
            }
            else if (typeof(T2) == typeof(GetMarketBetsInstruction))
            {
                apiEndPoint = GET_MARKET_BETS;
            }
            else if (typeof(T2) == typeof(RemoveMarketsInstruction))
            {
                apiEndPoint = REMOVE_ALL_MARKETS;
            }
            else if (typeof(T2) == typeof(ApplyCouponInstruction))
            {
                apiEndPoint = APPLY_COUPON;
            }
            else if (typeof(T2) == typeof(ApplyRulesInstruction))
            {
                apiEndPoint = APPLY_RULES;
            }
            else if (typeof(T2) == typeof(PlaceBetsInstruction))
            {
                apiEndPoint = PLACE_BETS;
            }
            else if (typeof(T2) == typeof(CancelBetsInstruction))
            {
                apiEndPoint = CANCEL_BETS;
            }
            else if (typeof(T2) == typeof(ModifyBetsInstruction))
            {
                apiEndPoint = MODIFY_BETS;
            }
            else if (typeof(T2) == typeof(CloseTradeInstruction))
            {
                apiEndPoint = CLOSE_TRADE;
            }
            else if (typeof(T2) == typeof(GetStoredValuesInstruction))
            {
                apiEndPoint = GET_STORED_VALUES;
            }
            else if (typeof(T2) == typeof(ClearStoredValuesInstruction))
            {
                apiEndPoint = CLEAR_STORED_VALUES;
            }
            else if (typeof(T2) == typeof(GetTradingWindowsInstruction))
            {
                apiEndPoint = GET_TRADING_WINDOWS;
            }
            else
            {
                apiEndPoint = null;
            }

            return apiEndPoint;
        }
a typical example of the endpoint constant being:

Code: Select all

private string GET_MARKET_PRICES = "markets/v1.0/getMarketPrices";
just my 2c ;)
You do not have the required permissions to view the files attached to this post.
Post Reply

Return to “Bet Angel - API”