Forum Discussion

emikelsoft's avatar
emikelsoft
Helper I
2 years ago
Solved

Incremental refresh for custom connector with call to REST API: RangeStart and RangeEnd

Hi,

I have developed a custom connector for Power BI with NavigationTables as shown in the sample of Power Query SDK.
How can I pass RangeStart and RangeEnd Parameters to my Rest API?
Where and when does the incremental refresh Logic set these parameters in the Power BI Service?

My Function:

[DataSource.Kind = "Foo", Publish = "Foo.Publish"]
shared Foo.Contents = Value.ReplaceType(FooImplementation, type function (Url as Uri.Type) as any);

I already tried:
shared Foo.Contents = Value.ReplaceType(FooImplementation, type function (Url as Uri.Type, optional RangeStart as nullable datetime, optional RangeEnd as nullable datetime) as any);

But RangeStart and RangeEnd are always empty. If I specifiy the values when I connect to the datasource, I always get the specified values and not the overwritten ones from the incremental refresh logic.

Any Ideas?
BR, Michael


21 Replies

  • RangeStart and RangeEnd must be included in the construction of the URL. Note that if your query doesn't fold (which is rather likely to happen here) then you will not get any performance gains from Incremental Refresh.

    • emikelsoft's avatar
      emikelsoft
      Helper I

      I include the parameters in the url. The problem is that the parameters are always empty. Is it the right way to defne them as I did in the original post?

       

      • lbendlin's avatar
        lbendlin
        Super User

        Sounds like you didn't mark the table as incremental refresh yet and didn't publish the pbix?

  • Here  is the full code:

    [Version = "1.0.0"]
    section Foo;

    [DataSource.Kind = "Foo", Publish = "Foo.Publish"]
    shared Foo.Contents = Value.ReplaceType(FooNavTable, type function (Url as Uri.Type, optional RangeStart as nullable datetime, optional RangeEnd as nullable datetime) as any);

    DefaultRequestHeaders = [
    #"Content-Type" = "application/json",
    #"api-key" = Extension.CurrentCredential()[Key]
    ];


    FooNavTable = (url as text, optional rangeStart as nullable datetime, optional rangeEnd as nullable datetime) as table =>
    let
    source = Json.Document(Web.Contents(url, [Headers = DefaultRequestHeaders, RelativePath = "navTable"])),
    tablesList = List.Transform(source, each
    [
    Name = _[Name],
    KeyColumn = _[KeyColumn], // Der Schlüssel sollte eindeutig sein
    Data = GetTable(url, Name, KeyColumn, _[Schema], rangeStart, rangeEnd),
    ItemKind = "Table",
    ItemName = "Table",
    IsLeaf = true
    ]),

    tablesTable = Table.FromRecords(tablesList),

    navigationTable = Table.ToNavigationTable(tablesTable, {"Name"}, "Name", "Data", "ItemKind", "ItemName", "IsLeaf")
    in
    navigationTable;

    GetTable = (url as text, tableName as text, keyColumn as text, schema as record, optional rangeStart as nullable datetime, optional rangeEnd as nullable datetime) as table =>
    let
    startDate = if rangeStart = null then "" else DateTime.ToText(rangeStart, "yyyy-MM-dd"),
    endDate = if rangeEnd = null then "" else DateTime.ToText(rangeEnd, "yyyy-MM-dd"),
    source = Json.Document(Web.Contents(url, [Headers = DefaultRequestHeaders, Query=[rangeStart=startDate, rangeEnd=endDate], RelativePath = tableName])),
    toTable = Table.FromRecords(source),
    typedTable = SetDataTypes(toTable, schema),
    typedTableWithKey = Table.AddKey(typedTable, { keyColumn }, true)
    in
    typedTableWithKey;

    SetDataTypes = (table as table, schema as record) as table =>
    let
    schemaTable = Record.ToTable(schema),
    transformationList = List.Transform(Table.ToRows(schemaTable),
    each {_{0}, GetTypeFromString(_{1})
    }),
    transformedTable = Table.TransformColumnTypes(table, transformationList)
    in
    transformedTable;

    GetTypeFromString = (typeString as text) as type =>
    let
    result = if typeString = "number" then Int64.Type
    else if typeString = "string" then Text.Type
    else if typeString = "boolean" then Logical.Type
    else if typeString = "Date" then Date.Type
    else if typeString = "DateTime" then DateTime.Type
    else if typeString = "Time" then Time.Type
    else if typeString = "Decimal" then Decimal.Type
    else Any.Type
    in result;

    • lbendlin's avatar
      lbendlin
      Super User

      Everything in Power Query is case sensitive.  "rangeStart" and "rangeEnd" will not work.

       

      You must hand over the URL already precompiled. The Power BI service must be able to inject the RangeStart and RangeEnd values directly.

      • emikelsoft's avatar
        emikelsoft
        Helper I

        RangeStart and RangeEnd is upercase at the shared function. Do you mean it should be upercase on the other functions as well?

        Or can you give an example to see where exactly those params get injected?

        By the way, the url can not be precompiled because we need our customer to connect to his individual endpoint.

        Many thanks for your help so far.

  • I found that query folding might be the issue. Currently I could make some steps into the right direction: Implementing paging and Table.View.

    The missing link is still to get the RangeStart and RangeEnd params into the connector. The implementation of my connector is more or less the same as the Tripin Sample with Folding.

    Has anybody a sample for doing it in a custom connector?

    br, Michael 

  • Finally we found the solution:
    It is all up to query folding. The article helped a lot:
    https://bengribaudo.com/blog/2022/01/20/6500/power-query-m-primer-part-23-query-folding-i

    One does not need to define RangeStart nor RangeEnd parameters anywhere in the connector.
    Instead one should implement Table.View as shown here:
    https://learn.microsoft.com/en-us/power-query/samples/trippin/10-tableview1/readme

    The only missing information is that one need to implement  OnSelectRows.
    Further information: 
    https://community.fabric.microsoft.com/t5/Power-Query/Query-folding-Table-View-handler-function-to-filter-rows-add/td-p/3154149

    br, Michael

    • lbendlin's avatar
      lbendlin
      Super User
      One does not need to define RangeStart nor RangeEnd parameters anywhere in the connector.

      They need to be used in the Power Query that calls the connector.

       

      How are you planning to do the partition refresh? All manually?

      • emikelsoft's avatar
        emikelsoft
        Helper I

        It works manually and scheduled.
        We do it scheduled (once at night)