<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Post on Tony McGovern. Hacker. Artist. Storyteller. Data lover.</title>
    <link>/tags/post/index.xml</link>
    <description>Recent content in Post on Tony McGovern. Hacker. Artist. Storyteller. Data lover.</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <atom:link href="/tags/post/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Handling Unsuccessful HTTP Requests in Power Query M</title>
      <link>/blog/web-api-error-handling/</link>
      <pubDate>Wed, 16 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>/blog/web-api-error-handling/</guid>
      <description>&lt;p&gt;Many web APIs implement a standard set of HTTP response codes that indicate whether a request was successful. A request that returns expected data often comes with a successful &lt;code&gt;200&lt;/code&gt; response code. Perhaps the most well-known response code is &lt;code&gt;404&lt;/code&gt;, which tells us a web page doesn&amp;rsquo;t exist. When I request data using the &lt;code&gt;Web.Contents&lt;/code&gt; Power Query M function in Excel or Power BI and that request is unsuccessful, I can often make sense of the response code that comes with it to quickly fix my query. Most other times, I encounter unfamiliar response codes that take me far too long to resolve, if I resolve them at all.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://www.pbiusergroup.com/powerbiug/communities/community-home/digestviewer/viewthread?MessageKey=51aaec6c-1071-4948-9549-59476ba1dfd8&#34;&gt;Inspired by a thread on a Power BI User Group forum&lt;/a&gt;, I&amp;rsquo;d like to share an error handling pattern I&amp;rsquo;ve recently adopted  that helps me quickly resolve unsuccessful HTTP requests using &lt;code&gt;Web.Contents&lt;/code&gt;. This pattern consists of two parts: a) using the ManualStatusHandling option in the &lt;code&gt;Web.Contents&lt;/code&gt; function to catch likely HTTP response codes, and b) creating error tables that explain to the user the likely reasons an HTTP request fails. With this error handling pattern in place, my queries are now more resilient and provide a better user experience.&lt;/p&gt;

&lt;p&gt;So how does it work? Suppose you&amp;rsquo;re interested in the latest population estimates for every state in the United States from the &lt;a href=&#34;https://www.census.gov/data/developers/data-sets.html&#34;&gt;U.S. Census Bureau&amp;rsquo;s API&lt;/a&gt;. A successful request returns the expected data along with a &lt;code&gt;200&lt;/code&gt; response code:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;let
    response = Web.Contents(
        &amp;quot;https://api.census.gov/data/2018/pep/population?get=POP&amp;amp;for=state&amp;quot;
    ),
    json = Json.Document(response)
in
    json
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If I misspell the URL and instead send this request:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;let
    response = Web.Contents(
        &amp;quot;https://api.census.gov/data/2018/BADSPEL/pupuracion?get=POP&amp;amp;for=state&amp;quot;
    ),
    json = Json.Document(response)
in
    json
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I get a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404&#34;&gt;&lt;code&gt;404&lt;/code&gt; response code&lt;/a&gt;, which tells me the page does not exist.
&lt;img src=&#34;../../img/main/404_Response_Code.png&#34; alt=&#34;404&#34; /&gt;&lt;/p&gt;

&lt;p&gt;But what happens when I misspell another part of the URL instead, say one of the parameters?&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;let
    response = Web.Contents(
        &amp;quot;https://api.census.gov/data/2018/pep/population?get=POP&amp;amp;for=states&amp;quot;
    ),
    json = Json.Document(response)
in
    json
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I now get a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400&#34;&gt;&lt;code&gt;400&lt;/code&gt; response code&lt;/a&gt;:
&lt;img src=&#34;../../img/main/400_Response_Code.png&#34; alt=&#34;400&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Why is this? Well, the first unsuccessful query told me I misspelled the location of the resource (the portion of the URL before the &amp;lsquo;?&amp;rsquo;) whereas the second unsuccessful query told me I misspelled one of the paramters in the URL. While this is useful information for me, the Power Query M developer, these sorts of subtleties are lost on an end-user. It&amp;rsquo;s likely your user won&amp;rsquo;t care to know the difference between a &lt;code&gt;400&lt;/code&gt; and &lt;code&gt;404&lt;/code&gt; response code; they just want to know why their request failed. (There&amp;rsquo;s an extra &amp;rsquo;s&amp;rsquo; in the word &amp;lsquo;state&amp;rsquo;, in case you were wondering why the second request failed.)&lt;/p&gt;

&lt;p&gt;So how does this relate to error tables? Like most well-documented APIs, the U.S. Census Bureau API has a page devoted to listing and describing all the &lt;a href=&#34;https://factfinder.census.gov/service/ErrorCodes.html&#34;&gt;possible response codes that can be returned by their service&lt;/a&gt;. I take this information and build an internal table within the query that defines and describes these response codes in my own words. I&amp;rsquo;m now able to throw custom messages that make the difference between a &lt;code&gt;400&lt;/code&gt; response code and a &lt;code&gt;404&lt;/code&gt; response code more obvious.&lt;/p&gt;

&lt;p&gt;For example, in the code below, I use the &lt;code&gt;Error.Record&lt;/code&gt; function to create individual records that allow me to catch these unsuccessful requests and throw my own custom error messages to the user. I then create an extra field in each record called &amp;lsquo;Status&amp;rsquo;, which maps each HTTP response code returned by the API to a corresponding error message of my choosing:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;let
    // define possible errors
    errors.badRequest = Record.AddField(Error.Record(&amp;quot;Bad Request&amp;quot;, &amp;quot;this dataset does not exist. It&#39;s most likely a misspelled parameter. Double check the spelling of the geography and variables you&#39;re passing as parameters.&amp;quot;), &amp;quot;Status&amp;quot;, 400),
    errors.invalidAccessKey = Record.AddField(Error.Record(&amp;quot;Invalid Access Key&amp;quot;, &amp;quot;Make sure the provided access exists and is entered correctly.&amp;quot;), &amp;quot;Status&amp;quot;, 401),
    errors.insufficientPermissions = Record.AddField(Error.Record(&amp;quot;Insufficient Permissions&amp;quot;, &amp;quot;you don&#39;t have the right permissions to access this dataset.&amp;quot;), &amp;quot;Status&amp;quot;, 403),
    errors.resourceNotFound = Record.AddField(Error.Record(&amp;quot;Page Does Not Exist&amp;quot;, &amp;quot;this dataset does not exist. It&#39;s most likely a misspelling somewhere in the URL. Double check the URL and make sure it&#39;s spelled correctly.&amp;quot;), &amp;quot;Status&amp;quot;, 404),
    errors.tooManyRequests = Record.AddField(Error.Record(&amp;quot;Too Many Requests&amp;quot;, &amp;quot;you&#39;ve reached your daily limit. Come back in 24 hours and try again.&amp;quot;), &amp;quot;Status&amp;quot;, 429),
    errors.backendError = Record.AddField(Error.Record(&amp;quot;Server Error&amp;quot;, &amp;quot;there is an unknown server error preventing this request from being executed. You&#39;ve done nothing wrong. Just try again in a bit.&amp;quot;), &amp;quot;Status&amp;quot;, 500),
    errors.serviceUnavailable = Record.AddField(Error.Record(&amp;quot;Service Unavailable&amp;quot;, &amp;quot;the server is too busy right now and needs a short break. Don&#39;t worry, you can try again in a bit.&amp;quot;), &amp;quot;Status&amp;quot;, 503),
    // build an error table
    errors.table = Table.FromRecords({errors.badRequest, errors.invalidAccessKey, errors.insufficientPermissions, errors.resourceNotFound, errors.tooManyRequests, errors.backendError, errors.serviceUnavailable})
in
    errors.table
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;../../img/main/Error_Table.png&#34; alt=&#34;ErrorTable&#34; /&gt;&lt;/p&gt;

&lt;p&gt;I then combine this with the ManualStatusHandling option in &lt;code&gt;Web.Contents&lt;/code&gt;, where I list all the HTTP response codes provided by the API. So, if the API returns one of those response codes, ManualStatusHandling tells my request to fail silently, thus allowing me to trap the unsuccessful request in a subsequent step. Chris Webb has a &lt;a href=&#34;https://blog.crossjoin.co.uk/2016/08/09/handling-404-not-found-errors-with-web-contents-in-power-query-and-power-bi&#34;&gt;great explainer on what the ManualStatusHandling option does and provides examples on how to use it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With both an error table and the ManualStatusHandling option implemented, my query now handles unsuccessful API requests. Here&amp;rsquo;s an example where I throw a custom error message to handle a &lt;code&gt;404&lt;/code&gt; response code (the &amp;lsquo;state&amp;rsquo; parameter still has that extra &amp;rsquo;s&amp;rsquo;):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;let
    // define possible errors
    errors.badRequest = Record.AddField(Error.Record(&amp;quot;Bad Request&amp;quot;, &amp;quot;this dataset does not exist. It&#39;s most likely a misspelled parameter. Double check the spelling of the geography and variables you&#39;re passing as parameters.&amp;quot;), &amp;quot;Status&amp;quot;, 400),
    errors.invalidAccessKey = Record.AddField(Error.Record(&amp;quot;Invalid Access Key&amp;quot;, &amp;quot;Make sure the provided access exists and is entered correctly.&amp;quot;), &amp;quot;Status&amp;quot;, 401),
    errors.insufficientPermissions = Record.AddField(Error.Record(&amp;quot;Insufficient Permissions&amp;quot;, &amp;quot;you don&#39;t have the right permissions to access this dataset.&amp;quot;), &amp;quot;Status&amp;quot;, 403),
    errors.resourceNotFound = Record.AddField(Error.Record(&amp;quot;Page Does Not Exist&amp;quot;, &amp;quot;this dataset does not exist. It&#39;s most likely a misspelling somewhere in the URL. Double check the URL and make sure it&#39;s spelled correctly.&amp;quot;), &amp;quot;Status&amp;quot;, 404),
    errors.tooManyRequests = Record.AddField(Error.Record(&amp;quot;Too Many Requests&amp;quot;, &amp;quot;you&#39;ve reached your daily limit. Come back in 24 hours and try again.&amp;quot;), &amp;quot;Status&amp;quot;, 429),
    errors.backendError = Record.AddField(Error.Record(&amp;quot;Server Error&amp;quot;, &amp;quot;there is an unknown server error preventing this request from being executed. You&#39;ve done nothing wrong. Just try again in a bit.&amp;quot;), &amp;quot;Status&amp;quot;, 500),
    errors.serviceUnavailable = Record.AddField(Error.Record(&amp;quot;Service Unavailable&amp;quot;, &amp;quot;the server is too busy right now and needs a short break. Don&#39;t worry, you can try again in a bit.&amp;quot;), &amp;quot;Status&amp;quot;, 503),
    // build an error table
    errors.table = Table.FromRecords({errors.badRequest, errors.invalidAccessKey, errors.insufficientPermissions, errors.resourceNotFound, errors.tooManyRequests, errors.backendError, errors.serviceUnavailable}),
    // get response
    response = Web.Contents(
        &amp;quot;https://api.census.gov/data/2018/pep/population?get=POP&amp;amp;for=states&amp;quot;, 
        [
            ManualStatusHandling = {400,401,403,404,429,500,503}
        ]
    ),
    responseMetadata = Value.Metadata(response),
    responseCode = responseMetadata[Response.Status],
    responseHeaders = responseMetadata[Headers],
    json = if responseCode &amp;lt;&amp;gt; 200 then error errors.table{List.PositionOf(errors.table[Status],responseCode)} else Json.Document(response)
in
    json
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;../../img/main/Custom_Error_Message.png&#34; alt=&#34;ErrorMessage&#34; /&gt;&lt;/p&gt;

&lt;p&gt;This error handling pattern provides a more resilient query that catches most HTTP response codes I&amp;rsquo;ll likely encounter. Moreover, I get to describe these responses in my own words, allowing me to better communicate to my users unsuccessful &lt;code&gt;Web.Contents&lt;/code&gt; requests.&lt;/p&gt;

&lt;p&gt;What error handling techniques do you use in Power Query M? Comment below &amp;ndash; I&amp;rsquo;m interested to hear about your patterns.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Map of Wisconsin midterm election results for 2018</title>
      <link>/blog/wisconsin_midterms_shapefile/</link>
      <pubDate>Wed, 12 Dec 2018 00:00:00 +0000</pubDate>
      
      <guid>/blog/wisconsin_midterms_shapefile/</guid>
      <description>

&lt;h2 id=&#34;wisconsin-midterm-election-results-for-2018&#34;&gt;Wisconsin Midterm Election Results for 2018&lt;/h2&gt;

&lt;p&gt;When voters in the state of Wisconsin cast ballots for national and state elections, they do so at the &amp;ldquo;ward&amp;rdquo;-level, an administrative boundary created to conduct elections. Yet when it comes to tabulating and reporting election results, Wisconsin officials combine wards into &amp;ldquo;reporting units&amp;rdquo;, larger administrative boundaries comprised of one or more wards. But there is no authoritative body that produces shapefiles for these reporting unit boundaries, leaving analysts, journalists, and the public little opportunity to visualize Wisconsin election results in any meaningful way.&lt;/p&gt;

&lt;p&gt;For the 2018 midterm elections, John Johnson, Research Fellow at the Marquette Law School, filled in that gap and created a &lt;a href=&#34;https://johndjohnson.info/2018/12/10/a-shapefile-of-wisconsin-november-2018-reporting-units/&#34;&gt;shapefile that provides high-quality, consistent boundaries and election results for Wisconsin&lt;/a&gt;. Expanding on his work, I used his shapefile &amp;ndash; which also contains election data &amp;ndash; and visualized the results.
&lt;img src=&#34;../../img/main/Wisconsin_2018_Midterms.gif&#34; alt=&#34;Wisconsin Midterm Results&#34; /&gt;&lt;/p&gt;

&lt;p&gt;I created the map in Power BI using the Mapbox Visual. I converted the shapefile to geojson with &lt;a href=&#34;https://mapshaper.org/&#34;&gt;mapshaper&lt;/a&gt; and simplified its features using &lt;a href=&#34;https://github.com/mbloch/mapshaper/wiki/Command-Reference&#34;&gt;mapshaper&amp;rsquo;s command line tools&lt;/a&gt;. I applied the Visvalingam simplification method through the following statement:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-simplify 10%
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I converted the geojson into a tileset using &lt;a href=&#34;https://www.mapbox.com/studio/tilesets/&#34;&gt;Mapbox Studio&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Power Query M Version Control using GitHub</title>
      <link>/blog/power-query-version-control/</link>
      <pubDate>Mon, 12 Nov 2018 00:00:00 +0000</pubDate>
      
      <guid>/blog/power-query-version-control/</guid>
      <description>

&lt;h2 id=&#34;power-query-m-version-control-using-github&#34;&gt;Power Query M Version Control using GitHub&lt;/h2&gt;

&lt;p&gt;There is currently no obvious version control process to see a history of changes made to Power Query M functions within Excel or Power BI. The Advanced Editor in the Power Query Editor has no built-in function to capture changes made to the query. In other words, it&amp;rsquo;s very difficult to roll back queries to earlier versions once they&amp;rsquo;re executed.&lt;/p&gt;

&lt;p&gt;I tend to have a fairly involved data preparation process that I build in to my Power Query M functions. It&amp;rsquo;s not uncommon for my queries to &lt;a href=&#34;https://github.com/tonmcg/powersocrata/blob/master/M/Socrata.ReadData.pq&#34;&gt;contain hundreds of lines&lt;/a&gt;. Once they become this large, managing changes to my queries starts to become onerous. It&amp;rsquo;s at this point I often switch from writing Power Query M functions in the Advanced Editor to writing them in a text editor like Notepad++. One of the benefits of separating the functions from the Power BI or Excel file is that I can store the functions in a .pq file on GitHub and &lt;a href=&#34;https://github.com/tonmcg/powersocrata/commit/52fd8905c9cbe4d1c7143dd61264af0c6f7b3a50#diff-64eaca6fc062ee30a8d65f09d2beb4aa&#34;&gt;see the entire timeline of changes made to the file&lt;/a&gt;. Storing .pq files on GitHub becomes especially &lt;a href=&#34;https://guides.github.com/introduction/git-handbook/&#34;&gt;useful in collaborative envrionments&lt;/a&gt; where team members work on the same set of Power Query M functions. And even if you&amp;rsquo;re working by yourself, GitHub is still useful for those projects that contain a number of queries, like this library of M functions that &lt;a href=&#34;https://github.com/tonmcg/powersocrata&#34;&gt;prepare data from the Socrata Open Data API&lt;/a&gt;, for example.&lt;/p&gt;

&lt;p&gt;So if I&amp;rsquo;m not writing these functions in the Advanced Editor, how does the Power Query Editor know they exist? How do we execute Power Query M functions that reside somewhere else?&lt;/p&gt;

&lt;h3 id=&#34;expression-evaluate-and-the-shared-environment&#34;&gt;Expression.Evaluate() and the &lt;code&gt;#shared&lt;/code&gt; Environment&lt;/h3&gt;

&lt;p&gt;Consider the Power Query M code below:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;let
  Source = 
    Expression.Evaluate(
      Text.FromBinary(
        Web.Contents(
          &amp;quot;https://raw.githubusercontent.com/tonmcg/powersocrata/master/M/Socrata.ReadData.pq&amp;quot;
        )
      ),
      #shared
    )
in
  Source
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If we copy the code into the Advanced Editor, we should see the following function appear:
&lt;img src=&#34;../../img/main/Excel_GitHub_Query.png&#34; alt=&#34;Excel&#34; /&gt;&lt;/p&gt;

&lt;p&gt;The screenshot shows the Power Query Editor rendering a function with multiple parameters. Not anywhere in the code above have we defined parameters. So where did they come from? The answer is I created a custom Power Query M function called &lt;code&gt;Socrata.ReadData()&lt;/code&gt; and stored it in a repository on GitHub. I&amp;rsquo;m using the &lt;code&gt;Web.Contents()&lt;/code&gt; function to return the &lt;a href=&#34;https://raw.githubusercontent.com/tonmcg/powersocrata/master/M/Socrata.ReadData.pq&#34;&gt;contents of the file&lt;/a&gt; and then the &lt;code&gt;Text.FromBinary()&lt;/code&gt; function to render the text contained within. The parameters are defined within the function itself, which again, is stored on GitHub.&lt;/p&gt;

&lt;p&gt;The most interesting part of the function is the use of &lt;code&gt;Expression.Evaluate()&lt;/code&gt;. At its simplest, it &lt;a href=&#34;https://docs.microsoft.com/en-us/powerquery-m/expression-evaluate&#34;&gt;evaluates text and returns an evaluated value&lt;/a&gt;. In our case, the evaluated text is the text within the &lt;code&gt;Socrata.ReadData()&lt;/code&gt; function. The evaluated value is the actual function itself. Put another way, &lt;code&gt;Expression.Evaluate()&lt;/code&gt; allows us to make a call to GitHub and return the function&amp;rsquo;s contents, render the text of the function, and tell the Power Query engine to treat the rendered text as an M function.&lt;/p&gt;

&lt;p&gt;But that&amp;rsquo;s only half of the answer. While the first parameter of the &lt;code&gt;Expression.Evaluate()&lt;/code&gt; function requires an expression as text, the second parameter asks for the &lt;em&gt;environment&lt;/em&gt; in which the text resides. In our example, I&amp;rsquo;ve defined the &lt;em&gt;envrionment&lt;/em&gt; as the &lt;code&gt;#shared&lt;/code&gt; variable. This pattern is what allows a Power Query M function that resides on GitHub to be executed in our Power Query Editor.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll not go into it here, but if you&amp;rsquo;re interested in understanding the &lt;em&gt;environment&lt;/em&gt; parameter and the &lt;code&gt;#shared&lt;/code&gt; variable, here is a list of resources that should get you started:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Lars Schreiber and Imke Feldmann have done &lt;a href=&#34;https://ssbi-blog.de/technical-topics-english/the-environment-concept-in-m-for-power-query-and-power-bi-desktop-part-3/&#34;&gt;incredible work simplifying these concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Chris Webb provides simple examples that help &lt;a href=&#34;https://blog.crossjoin.co.uk/2015/02/06/expression-evaluate-in-power-querym/&#34;&gt;motivate understanding in this area&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Finally, Imke Feldmann uses this pattern often and provides numerous examples on her blog. Here&amp;rsquo;s &lt;a href=&#34;https://www.thebiccountant.com/2018/05/17/automatically-create-function-record-for-expression-evaluate-in-power-bi-and-power-query/&#34;&gt;one example&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&#34;making-it-work-in-power-bi-dataflows&#34;&gt;Making it Work in Power BI Dataflows&lt;/h3&gt;

&lt;p&gt;The most exciting part, however, is that this pattern works not only in Excel and PowerBI&lt;sup id=&#34;a1&#34;&gt;&lt;a href=&#34;#warning&#34;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, but through the browser as well &lt;a href=&#34;https://www.tonymcgovern.com/blog/power-bi-dataflows/&#34;&gt;with Power BI Dataflows&lt;/a&gt;. We can get the &lt;code&gt;Socrata.ReadData()&lt;/code&gt; function to work in dataflows by using the Power Query M code above in the Power BI dataflows Query Editor:
&lt;img src=&#34;../../img/main/Dataflows_GitHub_Query.png&#34; alt=&#34;Dataflows&#34; /&gt;&lt;/p&gt;

&lt;p&gt;If you want to see what this looks like in action, I &lt;a href=&#34;https://twitter.com/tonmcg/status/1060617265501126656&#34;&gt;tweeted a GIF of this working&lt;/a&gt; in Power BI dataflows.&lt;/p&gt;

&lt;p&gt;There are boundless opportunities here to use GitHub&amp;rsquo;s version control system to manage the entire timeline of changes made to projects that use Power Query M functions. Distributed teams around the world can simultaneously work on the code, understand what changes have been made, and collaborate at any time while maintaining source code integrity.&lt;/p&gt;

&lt;p&gt;Do you use this pattern as well? What else have you found? Let me know in the comments section below.&lt;/p&gt;

&lt;h3 id=&#34;update-13-november-2018&#34;&gt;Update 13 November 2018&lt;/h3&gt;

&lt;p&gt;Matt Masson offers a comment in response to this blog post:
&lt;blockquote class=&#34;twitter-tweet&#34; data-lang=&#34;en&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;Well the gateway infrastructure doesn&amp;#39;t support dynamic data sources, and I&amp;#39;m not sure if / when that will happen. I&amp;#39;m also not sure it is wise to ever pull dynamic code from web locations.&lt;/p&gt;&amp;mdash; Matt Masson (@mattmasson) &lt;a href=&#34;https://twitter.com/mattmasson/status/1062066012298842118?ref_src=twsrc%5Etfw&#34;&gt;November 12, 2018&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;&lt;/p&gt;

&lt;p&gt;I think Matt is suggesting this pattern of storing M code on a separate server could open up your application or dataflow to an exploit. If so, he&amp;rsquo;s absolutely correct. The code above is stored on a public GitHub repository, which means anyone can submit a pull request and sereptitiously inject code that can cause a security vulnerability.&lt;/p&gt;

&lt;p&gt;This solution is far from ideal. But the question remains: is there a secure version control system that allows me to see changes to my M code over time? If you have ideas, feel free to leave them in the comments below.&lt;/p&gt;

&lt;ol&gt;&lt;li id=&#34;warning&#34;&gt;&lt;b&gt;Be aware&lt;/b&gt;: the use of the &lt;code&gt;#shared&lt;/code&gt; variable within the &lt;code&gt;Expression.Evaluate()&lt;/code&gt; function is not currently a supported feature, which means it can &lt;a href=&#34;https://ssbi-blog.de/technical-topics-english/the-environment-concept-in-m-for-power-query-and-power-bi-desktop-part-3/#comment-134&#34;&gt; go away at any time&lt;/a&gt;.&lt;/li&gt;&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>Power BI Dataflows</title>
      <link>/blog/Power-BI-Dataflows/</link>
      <pubDate>Sun, 11 Nov 2018 00:00:00 +0000</pubDate>
      
      <guid>/blog/Power-BI-Dataflows/</guid>
      <description>

&lt;h2 id=&#34;a-spectacular-power-bi-update&#34;&gt;A Spectacular Power BI Update&lt;/h2&gt;

&lt;p&gt;The November 2018 update of Power BI has been nothing short of spectacular. Among other things, this update introduces us to dataflows, one of the most anticpated features to come out in a while. In essence, dataflows allows us to perform complex data preparation steps within the Power BI cloud service. It&amp;rsquo;s now easier to build, standardize, and manage data preparation workflows across datasets stored on the service, all through the browser. In the coming months, dataflows will also let us leverage Azure Data Services like Azure Machine Learning for advanced analytics and AI.&lt;/p&gt;

&lt;p&gt;Adi Regev has an excellent &lt;a href=&#34;https://powerbi.microsoft.com/en-us/blog/introducing-power-bi-data-prep-wtih-dataflows/&#34;&gt;introduction to dataflows&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Matthew Roche provides a more in-depth look at Power BI dataflows, complete with hands-on tutorials, worked-out examples, and links to essential resources. His &lt;a href=&#34;https://ssbipolar.com/2018/10/23/dataflows-in-power-bi/&#34;&gt;six-part blog series&lt;/a&gt; is a probably the best place to start.&lt;/p&gt;

&lt;p&gt;For me, the ability to write Power Query M in the browser is the most exciting part of dataflows. In an upcoming post, I&amp;rsquo;ll explore how to build a series of data preparation steps in dataflows referencing Power Query M code stored somewhere else, like on a GitHub repo or a file server. I tweeted about &lt;a href=&#34;https://twitter.com/tonmcg/status/1060617265501126656&#34;&gt;this ability just the other day&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>