API 2 using Power Query

This is a basic connector to connect Excel (or PowerBI) to Manager api2.

let
    _ExpandCols = (TableToExpand as table, optional ColumnName as text) => let
        ListAllColumns = Table.ColumnNames(TableToExpand),
        ColumnsTotal = Table.ColumnCount(TableToExpand),
        CurrentColumnIndex = if (ColumnName = null) then 0 else  List.PositionOf(ListAllColumns, ColumnName),
        CurrentColumnName = ListAllColumns{CurrentColumnIndex},
        CurrentColumnContent = Table.Column(TableToExpand, CurrentColumnName),
        IsExpandable = if List.IsEmpty(List.Distinct(List.Select(CurrentColumnContent, each _ is record))) then false else true,
        FieldsToExpand = if IsExpandable  then Record.FieldNames(List.First(List.Select(CurrentColumnContent, each _ is record))) else {},
        ColumnNewNames = List.Transform(FieldsToExpand, each  CurrentColumnName & "." & _),
        ExpandedTable = if IsExpandable  then Table.ExpandRecordColumn(TableToExpand, CurrentColumnName, FieldsToExpand, ColumnNewNames) else TableToExpand,
        NextColumnIndex = CurrentColumnIndex + 1,
        NextColumnName = ListAllColumns{NextColumnIndex}
    in
        if NextColumnIndex > ColumnsTotal-1 then ExpandedTable else @_ExpandCols(ExpandedTable, NextColumnName),

    _KebabToCamel = (string as text) => let
        camelCaseString = Text.Replace(
            Text.ReplaceRange(
                Text.Proper(
                    Text.Replace(string,"-"," ")
                ),
            0,1,Text.Lower(Text.At(string,0))
        )," ","")
    in
        camelCaseString,

    _SingularKebab = (string as text) => let
        singularString = Text.Replace(Text.Start(string,Text.Length(string)-1),"-and-","-or-")
    in
        singularString,
    
    _ManagerApi2_Connect = (#"Source Path" as text, #"Request Headers" as record) => let
        result = Json.Document(Web.Contents(#"Source Path" , [Headers = #"Request Headers"]))
    in
        result,

    _ManagerApi2_GetRecords = (
        #"Manager Server Address" as text, 
        #"Access Token Secret" as text,
        Route as text, 
        #"Expand Object" as logical,
        #"Page Size" as nullable number,
        #"Field List" as nullable text
    ) => let
    
        source = #"Manager Server Address" & "/" & Route,
        headers = [
            #"accept" = "*/*",
            #"content-type" = "application/json",
            #"x-api-key" = #"Access Token Secret"
        ],
        singularKebab = _SingularKebab(Route),
        pathSummary = _ManagerApi2_Connect(source, headers),
        recordField = _KebabToCamel(Route),
        pageSize = if #"Page Size" = 0 or #"Page Size" = null then pathSummary[totalRecords] else #"Page Size",
        params1 = Record.AddField([], "pageSize", Text.From(pageSize)),
        params2 = try Record.AddField(params1, "fields", Text.Split(#"Field List",",")) otherwise params1, 
        tabView = Table.FromRecords(
            Record.Field(Json.Document(Web.Contents(source, [Headers = headers, Query = params2])), recordField)
        ),
        result = try 
            _ExpandCols(_ExpandCols(Table.AddColumn(tabView,Text.Proper(Text.Replace(singularKebab,"-"," ")), 
                each Json.Document(Web.Contents(#"Manager Server Address" & "/" & singularKebab & "-form/" & [key], [Headers = headers]))
            ))) otherwise tabView
       
    in
        if #"Expand Object" = true then result else tabView
        
in
    _ManagerApi2_GetRecords

Make sure that the authentication for this source is set to “Anonymous”.

It’s quite basic, but it gives the user a starting point where he/she can use Power Query Navigation to arrive at his destination.

I was planning to release a full connector function with dynamic routes, however, I’m facing an authorization issue with Power Query, since logging in to “/api2” requires basic credentials, while accessing anything beyond that requires the x-api-key.

Power Query cannot handle that gracefully and it will cause it to prompt the user to set credentials to anonymous everytime the route changes. This results in an annoying user experience, so I would leave the full connector until I can sort this issue.

For now, this should do just fine and everyone is welcome to contribute to this connector!

Enjoy :slightly_smiling_face:

6 Likes

Update 1: Added _ManagerApi2_Records_Get_All function to automatically get all records. This saves the user some navigation steps.

6 Likes

@Ealfardan Very much appreciate sharing your knowledge here.

1 Like

Thanks for the connector.
I managed to pull receipts and payments using: …/api2/receipts and …/api2/payments, respectively.
However when trying http://127.0.0.1:55667/api2/bank-and-cash-accounts I get:

Any idea of the issue and/or has the connector been updated in the last year?

Welcome to the forum @odiseo123,

I’m aware of this, in fact, I already have made an updated connector which I will post it shortly.

1 Like

Power query connector was updated:

The following issues were solved:

  • Simplified the options to retrieve the object, instead of changing the output function from the code editor, now you can simply set Expand Object to true.

  • Automatically get field path and field names from the Route argument. This solves the issue of having to type in inventory-items then inventoryItems and then inventory-item/[key]. Now all of this is being handled by the connector

  • Added fields selection. This should be entered as a coma separated list, e.g. ItemCode, ItemName. Leave blank for default field list.

    To learn more on the available fields, you can use this connector, which will give you a navigable documents revealing all paths and fields:

let
    Source = Json.Document(Web.Contents("https://{your subdomain}.manager.io/api2"))
in
    Source
  • Added pageSize. In order to retrieve all records, you can set this to 0 or null. Be warned though that when you try set your pageSize to anything other than 50, you must make a fields selection

  • Added the ability to retrieve the objects, expand it and expand custom field object. Manual expansion is no longer required.

1 Like

Thanks.
No luck though:

I would venture it is due to the fact of how the URL is built.
The connector endpoint seems to be formed by appending “-form” but the URL in manager uses “or” instead of “and”:
http://127.0.0.1:55667/bank-or-cash-account-form?

This error occurs when setting “Expand Object” to true. A valid table is returned when set to false.
Setting the call to “true” works as intended with the “payments” and “receipts” tables, correctly breaking up columns into Payment.Date, Payment.PaidFrom, Payment.Description.
It does not work with tables “bank-and-cash-accounts”, “chart-of-accounts”, “investments”.

Do you have any custom fields set on those objects that fail to expand?

This just clicked with me. The singular form of bank-AND-cash-accounts is actually bank-OR-cash-account.

Now it works for bank-and-cash-accounts.

As far as investments, it’s working fine with me, so please share the error you are getting for that.

Finally, As far as “chart-of-accounts”, it is nothing like other tabs. Meaning, it is a collection of different object types like profit-and-loss-statement-account-form and balance-sheet-account-form including different Control Accounts of all sorts and that’s why this isn’t expandable.

However, I still don’t believe that a wrong #“Expand Object” parameter value should result in a catastrophic failure. I made sure in the latest version that this error is handled silently.

Thanks @Ealfardan
bank-and-cash-accounts and investments are working for me. You are probably right that investments was working all along and it was just a copy/paste error on my last message.

I understand the challenge with chart-of-accounts. In the current version I get a flat list of all accounts and their keys. Is there then another table I can pull that captures the hierarchy, namely that X and Y account are children of Z?

If you prefer me to create a separate thread for this question, let me know and I will edit and tag you there.

Thanks for your help.

It looks like you’re using this connector to generate new transaction entries. I will base my answer on this assumption.

I personally never thought of replicating the COA for this particular purpose – or any other purpose really – because I didn’t have to.

My go to solution is to query each individual type of Control Account separately.

Another solution is to use a Custom Control account for every sub account and this will appear inside of its object.

Yet another solution is to affix an identifier to the names or codes of Control Accounts, this way you will have this information on your Chart of Accounts