Forum Discussion
Bit of help to identifying a dynamic data source
- 4 months ago
Hey, mike_asplin ,
basically in your case, you can jsut do this:
let BaseUrl = "https://chxxxxd.api.crm11.dynamics.com", Endpoint = "api/data/v9.1/bookableresourcebookings", SelectColumns = "bookableresourcebookingid,_bookingstatus_value,_resource_value,name,_cha_clientid_value,_owningteam_value,duration,starttime,_cha_carercontactid_value,_msdyn_workorder_value,msdyn_totalcost,endtime,statuscode,msdyn_milestraveled,msdyn_actualarrivaltime", FilterDateText = DateTime.ToText(#"Load Date", "yyyy-MM-ddTHH:mm:ssZ"), QueryRecord = [ #"$select" = SelectColumns, #"$filter" = "starttime ge " & FilterDateText ], Source = Json.Document(Web.Contents( BaseUrl, [ RelativePath = Endpoint, Query = QueryRecord ] )), Data = Source[value] in DataThe most important part, and what causes the Dynamic Source flag, is the splitting the host and relative path:
BaseUrl = "https://chxxxxd.api.crm11.dynamics.com",
Endpoint = "api/data/v9.1/bookableresourcebookings",
hey, mike_asplin ,
The LoadDate isn't really the issue.
I would say the first OData query will fail similarly to WebContents when you don't use RelativePath. I assume you tried to use a query parameter for the $select,$filter, but got an error that queries can't use the dollar sign.
Expression.Error: OData.Feed custom query options cannot start with '$'.
Generally, anything after the host in the URL will probably trigger a Dynamic Source issue, like this part:
api/data/v9.1/bookableresourcebookings
I would suggest rewriting this into Web.Contents request with RelativePath, this way it's gonna fix the dynamic source, and you can native still use the Query Parameters.
Explicitly for dynamics, I use this kind of function, could be polished, but good enough:
let
getDynamicsData = (includeIC as logical, tbl as text, fields as list, optional filters as nullable text) =>
let
default = [#"cross-company"=Text.From(includeIC)],
selectExpr = Text.Combine(List.Transform(fields, Text.Trim), ","),
selectQuery = if List.IsEmpty(fields) then [] else [ #"$select" = selectExpr ],
filterQuery =
if filters <> null and Text.Trim(filters) <> "" then
[ #"$filter" = filters ]
else
[],
queryRecord = Record.Combine({ default & selectQuery, filterQuery }),
completePath = tbl & "?" & Uri.BuildQueryString(queryRecord)
in
completePath
in
getDynamicsData
Then I call it like this to get the Path:
= getDynamicsDataPathOnly(false,"ProductionOrderHeaders", columns)
where columns is a list of columns like {"ProductionOrder", "dataAreaId"};
Additionally to pass filter query, you can also use it like this:
= getDynamicsDataPathOnly(false,"ProductionOrderHeaders", columns, " ProductionOrderNumber eq 'ABC'")
And then I call it like this:
= Json.Document( Web.Contents( host, [RelativePath=path] ) )
The authentication method is the native Organization Account window, and that part works flawlessly.
In case you have stuff that needs to be returned with multiple pages, I use a paginator like that:
= List.Generate(
()=> [
request = Json.Document( Web.Contents( host, [RelativePath=path] ) ),
next = request[#"@odata.nextLink"]?,
index = 1
],
each [next] <> null or [index]=1,
each [
request = Json.Document( Web.Contents( host, [RelativePath=Text.AfterDelimiter(next, host)] ) ),
next = [request]?[#"@odata.nextLink"]?,
index = [index]+1
],
each [request]?[value]?
)
btw host is a parameter "https://xx.xx.eu.dynamics.com/data/", yours would be slightly different
I have this method on many semantic models and have no issues, try it and lemme know.
Hi. That looks amazing but I'll be honest and say my code writing skills are nowhere near good enough to understand what any of it means and how I would apply it in my case!!!!
so
is tbl =
"https://chxxxxd.api.crm11.dynamics.com/api/data/v9.1/bookableresourcebookings"
or just
"https://chxxxxd.api.crm11.dynamics.com/api/data/v9.1/
with
bookableresourcebookings
replacing "ProductionOrdersHeaders in?
getDynamicsDataPathOnly(false,"ProductionOrderHeaders", columns)
What is "cross-company" in this bit referring to?
default = [#"cross-company"=Text.From(includeIC)]
much appreciated
- vojtechsima4 months agoSuper User
Hey, mike_asplin ,
basically in your case, you can jsut do this:
let BaseUrl = "https://chxxxxd.api.crm11.dynamics.com", Endpoint = "api/data/v9.1/bookableresourcebookings", SelectColumns = "bookableresourcebookingid,_bookingstatus_value,_resource_value,name,_cha_clientid_value,_owningteam_value,duration,starttime,_cha_carercontactid_value,_msdyn_workorder_value,msdyn_totalcost,endtime,statuscode,msdyn_milestraveled,msdyn_actualarrivaltime", FilterDateText = DateTime.ToText(#"Load Date", "yyyy-MM-ddTHH:mm:ssZ"), QueryRecord = [ #"$select" = SelectColumns, #"$filter" = "starttime ge " & FilterDateText ], Source = Json.Document(Web.Contents( BaseUrl, [ RelativePath = Endpoint, Query = QueryRecord ] )), Data = Source[value] in DataThe most important part, and what causes the Dynamic Source flag, is the splitting the host and relative path:
BaseUrl = "https://chxxxxd.api.crm11.dynamics.com",
Endpoint = "api/data/v9.1/bookableresourcebookings",- mike_asplin4 months agoHelper V
Brilliant let me try that. Thanks again.
- mike_asplin4 months agoHelper V
I got this as an output of the last step? If i click on record i can see the data for 1 record, but how do I convert this into a table with multiple columns please?
- vojtechsima4 months agoSuper User
mike_asplin you can click your way through.
Right click the 'LIST':
and click To Table, then you can expand fields and click
- mike_asplin4 months agoHelper V
Worked it out need
= Table.FromRecords(Source[value])