<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: chuac</title>
    <description>The latest articles on DEV Community by chuac (@chuac).</description>
    <link>https://dev.to/chuac</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F913324%2F8e4cb737-93e1-4912-96d3-685cafaadcb1.jpeg</url>
      <title>DEV Community: chuac</title>
      <link>https://dev.to/chuac</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chuac"/>
    <language>en</language>
    <item>
      <title>Assigning static IP to AWS Glue jobs</title>
      <dc:creator>chuac</dc:creator>
      <pubDate>Tue, 05 Dec 2023 15:06:57 +0000</pubDate>
      <link>https://dev.to/chuac/assigning-static-ip-to-aws-glue-jobs-300d</link>
      <guid>https://dev.to/chuac/assigning-static-ip-to-aws-glue-jobs-300d</guid>
      <description>&lt;p&gt;Our situation was a desire to run AWS Glue ETL (extract, transform, load) jobs where the data source is SQL Server in Azure that has an IP-whitelist firewall.&lt;/p&gt;

&lt;p&gt;As AWS Glue is a serverless data integration service, it wasn't reasonable to maintain a range of public IP addresses that we should whitelist in the Azure firewall. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8esa24oaxy30o2aqbvpl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8esa24oaxy30o2aqbvpl.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inspired by &lt;a href="https://stackoverflow.com/a/64414639/3024923" rel="noopener noreferrer"&gt;this StackOverflow answer&lt;/a&gt;, it's possible to create a Glue connection where all traffic to external resources (like Azure) will use an assigned Elastic IP address.&lt;/p&gt;

&lt;p&gt;In this post, I'll expand on their workaround with detailed steps and screenshots for the AWS console.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Head to the &lt;a href="https://console.aws.amazon.com/vpcconsole/home" rel="noopener noreferrer"&gt;VPC console&lt;/a&gt; and hit &lt;strong&gt;Create VPC&lt;/strong&gt; in your desired region.&lt;/li&gt;
&lt;li&gt;Look for and select the &lt;strong&gt;VPC and more&lt;/strong&gt; option which will help us to also create necessary subnets, route tables, and network connections - you can see these under the Preview section.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyffcv8r4wqkqah1apyto.png" alt="Image description"&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1c0peqs9v1d3kkr0600t.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Configure other options the way you prefer but most importantly select &lt;strong&gt;1 public subnet and 1 private subnet&lt;/strong&gt;. Hit &lt;strong&gt;Create VPC&lt;/strong&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fui2logl61p1xn12m4o48.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;With the VPC and other essentials created, take note that the subnets, and route tables created for us have a similar name to the VPC name we provided in Step 2 (e.g, &lt;code&gt;glue-demo&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Head to &lt;a href="https://console.aws.amazon.com/vpcconsole/home#CreateNatGateway:" rel="noopener noreferrer"&gt;create a NAT gateway&lt;/a&gt;. Select the &lt;strong&gt;public subnet in which to create the NAT gateway&lt;/strong&gt;, keep &lt;strong&gt;connectivity type as Public&lt;/strong&gt;. Here we will &lt;strong&gt;allocate an Elastic IP&lt;/strong&gt; to the NAT gateway - this will be the IP inserted into our whitelist in Azure!
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fia03czpippra9w5d3u4b.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to &lt;a href="https://console.aws.amazon.com/vpcconsole/home#RouteTables:v=3" rel="noopener noreferrer"&gt;Route tables&lt;/a&gt;, and at this point it's handy to filter resources by VPC.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7xu4f57q77nfsseem329.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Select the route table that is associated with your &lt;strong&gt;private subnet&lt;/strong&gt; - the route table conveniently should've included &lt;strong&gt;private&lt;/strong&gt; in its name tag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edit routes&lt;/strong&gt; for the private route table and add a route with all destinations (AKA &lt;code&gt;0.0.0.0/0&lt;/code&gt;) with target of our NAT gateway created in Step 5.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98nhs2ctpzybcaiujnwr.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;We're now ready to head into AWS Glue and &lt;a href="https://console.aws.amazon.com/gluestudio/home#/connector/create-glue-connection-v2" rel="noopener noreferrer"&gt;create a connection&lt;/a&gt;. We'll select Azure SQL DB as our data source here, but this should work for any other source (e.g, Snowflake).&lt;/li&gt;
&lt;li&gt;I won't get into details of the data source connection (Azure SQL URL, etc) however we'll pay attention the the &lt;strong&gt;Network options&lt;/strong&gt; section.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxl64d2mlsbhw4qknvqd.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;li&gt;Here we'll choose the VPC we created above, and most importantly &lt;strong&gt;select the private subnet&lt;/strong&gt; for this Glue connection. Continue and create the Glue connection for your data source.&lt;/li&gt;
&lt;li&gt;Noting the public IP we allocated in Step 5, we can add it to our whitelist in Azure.&lt;/li&gt;
&lt;li&gt;We're done! Now all AWS Glue jobs using the connection we created above will use the allocated public IP when communicating to external (outside of AWS) resources.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>aws</category>
      <category>azure</category>
      <category>cloud</category>
      <category>networking</category>
    </item>
    <item>
      <title>GA4 Data API with .NET</title>
      <dc:creator>chuac</dc:creator>
      <pubDate>Tue, 25 Jul 2023 08:59:17 +0000</pubDate>
      <link>https://dev.to/chuac/ga4-data-api-with-net-1n00</link>
      <guid>https://dev.to/chuac/ga4-data-api-with-net-1n00</guid>
      <description>&lt;p&gt;Our .NET 7 backend was querying Universal Analytics (UA) using &lt;a href="https://www.nuget.org/packages/Google.Apis.Analytics.v3"&gt;Google.Apis.Analytics.v3&lt;/a&gt; but as we drew closer to UA's shutdown date on July 1st 2023, we had to make the change to start querying our Google Analytics 4 (GA4) properties.&lt;/p&gt;

&lt;p&gt;The package you'll need is &lt;a href="https://www.nuget.org/packages/Google.Analytics.Data.V1Beta"&gt;Google.Analytics.Data.V1Beta&lt;/a&gt; - &lt;strong&gt;&lt;em&gt;Not to be confused with&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;&lt;a href="https://www.nuget.org/packages/Google.Apis.AnalyticsData.v1beta"&gt;Google.Apis.AnalyticsData.v1beta&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Authenticating your client
&lt;/h2&gt;

&lt;p&gt;Head &lt;a href="https://developers.google.com/analytics/devguides/reporting/data/v1/quickstart-client-libraries"&gt;here&lt;/a&gt; to follow the steps. After Step 1, you will have a &lt;code&gt;credentials.json&lt;/code&gt; file which we will reference in our code soon.&lt;/p&gt;

&lt;p&gt;Complete Step 2, to add the the service account that looks similar to &lt;code&gt;quickstart@PROJECT-ID.iam.gserviceaccount.com&lt;/code&gt; to your GA4 property.&lt;/p&gt;

&lt;p&gt;Now in your code, we will build the client and authenticate it using the &lt;code&gt;credentials.json&lt;/code&gt; you created above. You are able to simply reference the JSON file from your code by setting &lt;code&gt;CredentialsPath&lt;/code&gt; (and swap it out when deploying to different environments) however, we prefer swapping our environment dependent application settings (or secrets) as strings.&lt;/p&gt;

&lt;p&gt;Luckily, the library accepts the JSON file as a JSON string. Simply JSON stringify the contents of your &lt;code&gt;credentials.json&lt;/code&gt; and pass it to the client builder like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var analyticsDataClient = new BetaAnalyticsDataClientBuilder
{
    JsonCredentials = "JSON_STRINGIFY_YOUR_CREDENTIALS",
}
.Build();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the client to get data
&lt;/h2&gt;

&lt;p&gt;The possibilities are endless as to what dimensions/metrics/filters/date ranges you can do, with &lt;a href="https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema"&gt;documentation of all schema&lt;/a&gt; very good.&lt;/p&gt;

&lt;p&gt;Here's a simple example to get screen page views for particular page paths between "yesterday" and "today". You will need your GA4 Property ID (you can find this in "Property Settings" in GA, remember this is &lt;strong&gt;not&lt;/strong&gt; your Measurement ID)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RunReportRequest request = new ()
{
    Property = "properties/" + "YOUR_GA4_PROPERTY_ID",
    Dimensions = { new Dimension { Name = "pagePath" }, },
    Metrics = { new Metric { Name = "screenPageViews" }, },
    Limit = 10000,
    DateRanges = { new DateRange { StartDate = "yesterday", EndDate = "today", }, },
    DimensionFilter = new FilterExpression()
    {
        Filter = new Filter()
        {
            FieldName = "pagePath",
            StringFilter = new StringFilter()
            {
                MatchType = StringFilter.Types.MatchType.Contains,
                Value = "deals/",
            },
        },
    },
};

var response = await analyticsDataClient.RunReportAsync(request);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Note: Limitations with Realtime reports
&lt;/h2&gt;

&lt;p&gt;There isn't 100% feature parity between UA Data API and this newer GA4 Data API. One dimension we were dependent on for Realtime reports was &lt;code&gt;pagePath&lt;/code&gt;. You can recall above that it exists for normal reports &lt;strong&gt;but is missing for Realtime&lt;/strong&gt;. There is an &lt;a href="https://issuetracker.google.com/issues/235416504"&gt;existing issue&lt;/a&gt; logged but with most recent activity being in 2022, I wouldn't count on that feature coming in anytime soon.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>dotnet</category>
      <category>google</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Updating Angular routes at runtime</title>
      <dc:creator>chuac</dc:creator>
      <pubDate>Tue, 07 Feb 2023 06:34:42 +0000</pubDate>
      <link>https://dev.to/chuac/updating-angular-routes-at-runtime-ja1</link>
      <guid>https://dev.to/chuac/updating-angular-routes-at-runtime-ja1</guid>
      <description>&lt;p&gt;There may be times where you need to update your Angular routes dynamically depending on the result of an API call, cookies, etc.&lt;/p&gt;

&lt;p&gt;Realistically, this should be done during &lt;a href="https://angular.io/api/core/APP_INITIALIZER" rel="noopener noreferrer"&gt;Angular app initialization&lt;/a&gt; (especially so if the routes are dependent on an API call) but we simply choose the &lt;code&gt;AppModule&lt;/code&gt; constructor for this quick example.&lt;/p&gt;

&lt;p&gt;Say you have a bunch of routes that have been registered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const routes: Routes = [
    { path: 'about-us', component: AboutUsComponent },
    { path: '', component: HomeComponent },
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and depending on some condition at runtime, you would like to swap the base path &lt;code&gt;''&lt;/code&gt; route to point to another component like &lt;code&gt;NewHomeComponent&lt;/code&gt;, you can do like below and access &lt;code&gt;resetConfig()&lt;/code&gt; on the Angular &lt;code&gt;Router&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export class AppModule {
    constructor(
        private readonly router: Router,
    ) {
        if (someCondition) {
            const configRoutes = this.router.config;

            const index = configRoutes.findIndex((route: Route) =&amp;gt; route.path === '');
            configRoutes[index] = { path: '', component: NewHomeComponent };

            this.router.resetConfig(configRoutes);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you're done!&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>Specify Geolocation for Cypress tests - LambdaTest</title>
      <dc:creator>chuac</dc:creator>
      <pubDate>Thu, 03 Nov 2022 00:00:07 +0000</pubDate>
      <link>https://dev.to/chuac/specify-geolocation-for-cypress-tests-lambdatest-28j7</link>
      <guid>https://dev.to/chuac/specify-geolocation-for-cypress-tests-lambdatest-28j7</guid>
      <description>&lt;p&gt;If you're using a tool like &lt;a href="https://www.lambdatest.com/cypress-testing"&gt;LambdaTest Cypress Automation&lt;/a&gt; to execute your Cypress tests as part of your pipelines, you may eventually require the environment your tests are running on to mock certain Geolocations which your tests are dependent on.&lt;/p&gt;

&lt;p&gt;Luckily, with LambdaTest, &lt;a href="https://www.lambdatest.com/list-of-geolocation"&gt;this is possible&lt;/a&gt; however it is not clear in their documentation on how to turn this on!&lt;/p&gt;

&lt;p&gt;After consulting with their handy tech support, the following is how we can specify Australia as the Geolocation of the machine LambdaTest runs our Cypress tests on:&lt;/p&gt;

&lt;p&gt;Inside &lt;code&gt;lambdatest-config.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"run_settings": {
    ...
    "geo_location": "AU",
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is unclear what ISO standard that LambdaTest uses for their &lt;code&gt;geo_location&lt;/code&gt; field, but &lt;a href="https://gist.github.com/tadast/8827699"&gt;ISO 3166-1 (Alpha-2)&lt;/a&gt; is a good bet.&lt;/p&gt;

</description>
      <category>cypress</category>
      <category>testing</category>
      <category>e2e</category>
      <category>lambdatest</category>
    </item>
  </channel>
</rss>
