GCast

Unofficial, machine-generated summaries, as of 2026-11-09.

Ingesting Into an ADX Table From an Azure Storage Blob [GCast 123]
  • One .ingest into table command loads a blob into ADX. The syntax is .ingest into table <table name>, then a parenthesized list of blobs, each written h"<blob URL>?<SAS token>", with an optional with clause setting the format. Giard notes the full syntax is in the help under the ingest-into command.
  • The blob's columns must match the target table's structure. His demo table, customers, has full name, last order date, sales, expenses, city and postal code. The comma-delimited .csv file he uploads to a blob container has about 10 rows in the same layout (a string, a date, then numbers), and he calls the match important.
  • Scope the SAS token narrowly: one container, read-only, short expiry. He takes the blob URL from the file's Properties, then uses Generate SAS on the container rather than the storage account. He grants only read permission on the blobs and sets the token to expire in about an hour.
  • Several blobs can go in one command, but not a whole container. Multiple blobs are listed comma-separated inside the parentheses. Giard says there is no command to ingest everything in a container, so each blob has to be listed individually.
  • The format clause is optional when the file extension shows it. Rerunning the command without it still ingested the .csv, and the table went from 10 to 20 rows, the same rows repeated. He says to state the format explicitly when the extension does not make it obvious, and points to the ADX supported-formats documentation, which lists JSON, multi-JSON and Parquet among others.
Azure Data Explorer Materialized Views [GCast 122]
  • Materialized views trade slower inserts for faster aggregate queries. An ADX materialized view stores a query's already-summarized result, so the count, min, max and average are computed in advance rather than at query time; each insert takes a performance hit to redo the aggregation. Giard says it is meant for aggregations run repeatedly, especially slow ones over millions or hundreds of millions of rows, not his 10-row demo table.
  • The view must be an aggregation query, or creation throws. The syntax is .create async materialized view with (...), then the view name, the source table and the query; Giard always creates them asynchronously. His demo summarizes a customers table's year-to-date sales by postal code (10 rows, six postal codes).
  • Backfill defaults to false, so existing rows are skipped. With backfill=false only data arriving after creation is summarized; Giard generally sets backfill=true to cover the whole table, warning that a large table with heavy calculations can take a long time to backfill. Other properties he points to are a docstring for a longer description and a folder for organizing many views.
  • The view is queried like a table and altered or dropped by command. It can be ordered, filtered and further summarized just like a table, and appears in the database's object list. .alter materialized view takes near-identical syntax to change the query (he drops the min column), and .drop materialized view removes it, after which querying it errors.
Analyzing Geographic Data with KQL [GCast 121]
  • geo_distance_2points gives the distance between consecutive positions. It takes the longitude and latitude of two points and returns meters. Giard sorts a vehicle-location table by vehicle ID and timestamp, pulls the previous row's coordinates with prev(), and gets distances such as 1,659 m and 260 m between readings; he notes this is straight-line distance, not the route actually travelled.
  • geo_point_in_polygon tests whether a point lies inside a shape. A polygon is a dynamic object built from an array of longitude/latitude pairs, with the first point repeated to close it; the function returns true or false. He drew a rectangle in Manhattan at geojson.io to get the coordinates, and two of one vehicle's readings (10:45 and 11:30) fell inside. He suggests uses such as detecting a restricted zone or leaving an area.
  • S2 cells divide the globe into rectangles at chosen precision levels. Per the s2geometry.io material he shows, level 0 is six cells about 7,800 km across and level 30 is about 7×10^18 cells of 8–9 mm; level 9 is roughly 18–20 km. geo_point_to_s2cell takes longitude, latitude and level and returns a cell token, so asking whether something stayed in the same cell can be faster than measuring how far it moved. At level 9 one vehicle's readings all shared a cell; at level 30 every reading was unique.
  • Pre-filtering by S2 cells speeds point-in-polygon queries on large data. geo_polygon_to_s2cells returns the minimum set of cells at a given level that covers a polygon; Giard filters rows to those whose level-12 cell is in that set before running geo_point_in_polygon. The result matched the unfiltered query, and he says it should run much faster on millions of points scattered worldwide, though his nine-row demo cannot show that.
  • Giard calls the geospatial function family a KQL strength. He points to related functions: distance from a point to a polygon, point-in-circle tests, and converting an S2 cell back to its central point. He says other query languages generally lack these.
Analyzing Time Series Data with KQL [GCast 120]
  • The prev function reads a value from the preceding row. It works only after an order by; the first row returns null. On drone telemetry logged about every 15 minutes, Giard extends a battery-life-change column as battery life minus prev(battery life), exposing how much charge dropped in each interval, which he says could be plotted against other factors. He says this is hard in SQL and most query languages; KQL also has a next function and a cumulative-sum function.
  • The bin function rolls rows up into fixed-size buckets. Used inside summarize ... by, bin takes a column and a size: a time span for time series, or a number such as every 5 or 10 for numeric data. Giard bins 15-minute readings by one hour to get one row per hour, as a way to avoid showing millions of rows.
  • arg_max keeps the whole last row of each bucket. Rolling up forces a choice of which value represents the bucket; besides averages, arg_max and arg_min return all the values of the row with the highest or lowest value, here the last reading in each hour. Giard's hourly results were not exactly on the hour because some sample data was missing.
Kusto Query Language Basics [GCast 119]
  • KQL chains operators with pipes, starting from the table name. A table name alone returns every row and column; a | feeds that output into the next operator, as in a bash script. take 5 returns just a sample, which Giard uses to inspect a table's structure quickly, noting ADX limits how much data it will return. order by followed by take 3 gives a top-N query, here the top three customers by year-to-date sales.
  • order by sorts descending by default, unlike SQL. Ascending needs asc added. Giard says he writes desc explicitly anyway, because he expected SQL's ascending default when he started.
  • project limits columns, and later operators only see projected ones. where filters rows, for example last order date greater than a datetime of 2022-01-09. Because each step's output is the next step's input, a where on a column left out of a preceding project fails with an unknown-column error.
  • extend adds calculated columns without dropping the rest. A calculated column can be written inside project (profit as year-to-date sales minus year-to-date expenses) or with extend. Giard says extend is needed when one calculated column feeds another, and when you want every column plus the new one without listing them.
  • summarize aggregates, and explain translates SQL into KQL. summarize with the sum of year-to-date sales by postal code returns one row per postal code. Prefixing a SQL query with explain returns an equivalent KQL query, which Giard calls a great tool for SQL users learning KQL, though he says it is not one-to-one and suits simple queries.
Managing Azure Data Explorer Tables with ADX Commands [GCast 118]
  • Production tables should be scripted with commands, not wizards. That is Giard's case for this episode's Azure Data Explorer commands. He adds a database (gcastdb, all defaults) from the cluster's overview blade, then works in the fuller web UI at dataexplorer.azure.com, reached from the database's query blade via "Open in Web UI". Commands run with the Run button or Shift-Enter. He mentions ARM templates and the Azure CLI as other ways to automate, possibly for a later video.
  • Creating a table means declaring an enforced schema. .create table takes comma-separated name:type pairs, here string, datetime and decimal columns for customers. Giard says ADX resembles a NoSQL store in not enforcing relationships or primary keys, but each table's schema is enforced.
  • Inline ingestion takes comma-delimited rows in column order. .ingest inline into table followed by one row per line inserts data typed in directly; values must match the column types, using the built-in datetime() function for dates. Giard pastes 10 rows of well-known technologists as sample customers.
  • Re-running create table on an existing table silently changes nothing. Adding a column to the .create table statement and running it again appears to work, but the column is not added because the table already exists. .create-merge table, with almost the same syntax, creates the table if absent or else adds the new columns.
  • Renaming a column does not break positional ingestion. .rename column changed postal code to zip code, and a later inline ingest still loaded correctly because the rows do not name their columns.
Getting Started with Azure Data Explorer [GCast 117]
  • Azure Data Explorer starts with a cluster of virtual machines. Giard describes ADX as an Azure service for managing very large amounts of structured, unstructured or semi-structured data at scale and analyzing it in real time. In the portal it is created through Create a Resource; the cluster belongs to a resource group, which he creates new for the demo. Deploying his cluster took about 13 minutes, and he expects larger clusters to take longer.
  • The workload choice sets the cluster's scale and isolation. The categories are development, standard and specialized. Development is a single virtual machine, which Giard calls good for learning but not very scalable and maybe not right for production, and it offers only two machine sizes. Standard options are weighted toward storage, computation or heavy computation. The specialized "isolated compute" option guarantees that only your data runs on the machine. He picks the cheapest dev/test option.
  • Availability zones protect against one data center failing. Giard explains that the zones are isolated from shared points of failure and physically far enough apart that a flood or earthquake should not hit more than one, so another data center can take over and reduce downtime. He uses a single zone for the demo. Review and Create validates the settings before deployment.
  • Each database sets how long data is retained and cached. Retention defaults to one year (365 days) and can be set to any period or to forever. Cached data is served without reading from disk, so queries run faster; the cache period defaults to 31 days and can also be set to forever. Giard keeps both defaults, and the database is created in a few seconds.
  • Ingesting a CSV creates a table with an inferred schema. From "ingest new data", Giard names a new table, then picks a source: Azure Blob Storage, a container or a local file. He uploads a 5,000-row sample sales CSV that he found online. ADX takes column names from the header row and infers data types, and each one can be renamed or retyped, such as changing an ID column from long to string. It detects that the file is an uncompressed CSV and shows a preview of the data before import.
Azure Active Directory B2C Token Generation With No User Interaction [GCAST 116]
  • The ROPC user flow gets a token with no user interaction. Giard says every other Azure AD B2C user-flow template requires the user to interact, while the resource owner password credentials (ROPC) template does not. He gives the use case as automated integration or end-to-end tests that call a secured back-end API, for example inside a DevOps pipeline. The episode assumes a B2C tenant already exists, from a previous video.
  • The app registration needs token settings and two saved values. Under App registrations he creates a new registration with the default account type and a redirect URL of https://jwt.ms. On the Authentication blade he checks both "access tokens" and "ID tokens". From the Overview blade he copies the application (client) ID and, from the endpoints list, the "Azure AD B2C OAuth 2.0 token endpoint", which has a policy-name placeholder in brackets to fill in later.
  • The identity-provider choice determines how local users sign in. B2C supports identity providers such as GitHub, Facebook and LinkedIn because they share common protocols such as OAuth, but he uses local accounts only. Local accounts can sign in by username, email or phone number, and the options enabled there set the sign-in methods offered when he creates a new local user, here with a username and an auto-generated password.
  • A user flow is a policy that names the token endpoint. He creates an ROPC flow named B2C_1_GCAST and picks the claims the token should carry, such as display name, email, given name and surname. Giard says a token can carry facts about the user as well as validity, which an API can use for authorization, such as restricting access by region or city. The flow's name replaces the bracketed policy-name placeholder in the token endpoint URL.
  • An HTTP POST of form data returns the access token. The body is sent as form data, not JSON or XML, with username, password, grant_type "password", scope of openid plus offline_access plus the client ID, client_id, and response_type "token". He says this can be done in code, curl or Postman, and demonstrates it with the Boomerang plug-in for Edge, also naming YARC for Chrome; the request returned a 200 with an access token.
Registering an Azure Account Subscription [GCast 115]
  • Creating a B2C tenant can fail on an unregistered subscription. The error reads that the subscription is not registered to use namespace Microsoft.AzureActiveDirectory. Giard says he got it in both of his subscriptions and infers it is probably a common issue.
  • Azure CLI commands in Cloud Shell register the provider. He runs them in the portal's Cloud Shell (bash), which skips the az login a local command prompt would need. az account list lists the accounts as JSON, which he redirects to a file and downloads when there are many; az account set --subscription <name> selects the one he wants; az provider register --namespace Microsoft.AzureActiveDirectory starts the registration.
  • Registration is not instant, but its status can be checked. He says it may take a couple of minutes, and az provider show -n Microsoft.AzureActiveDirectory shows the registration state, which in the demo read as registered.
  • After registering, the same B2C tenant creation goes through. Rerunning the create step for a new Azure Active Directory B2C resource no longer showed the error, and he contrasts the screen before and after the fix.
Creating an Azure Active Directory B2C Tenant [GCast 114]
  • A separate B2C tenant isolates security for external users. Giard describes Azure Active Directory B2C (business to consumers) as the tool for very large user populations from outside the organization, possibly signing in through providers other than your own Active Directory. His reasons for creating a separate tenant are: building for a customer whose subscription you can't access, keeping dev, QA, user-testing and production environments apart with their own users and policies, or having a sandbox. That last one is his own reason, because the B2C tenant his company set up for him is controlled by them.
  • Creating a new tenant now links it automatically. In the Azure portal (portal.azure.com) he goes to create a resource, searches for B2C, and gets two options: create a new Azure AD B2C tenant, or link an existing one to the subscription. Giard says the link option is left over from when tenants had to be created and then linked separately. New tenants are now linked by default, so linking is needed only for an old tenant that was never linked.
  • The create dialog asks for name, domain, region and resource group. Giard enters an organization name, an initial domain name, the United States as country, a subscription, a new resource group and East US as its location, then runs Review and create. He suggests placing the tenant near where your users are. Creation took about two minutes.
  • An unregistered namespace error is fixed from the CLI. Giard says he first got "Subscription is not registered to use namespace Microsoft.AzureActiveDirectory". The fix, from a blog article he found by searching, is to open a CLI prompt, set your subscription and register the namespace before creating the tenant.
  • You must switch directories before you can use the tenant. The new directory may not appear in Switch directory right away; Giard had to refresh the page before his tenant's onmicrosoft.com domain showed up. From inside it he opens Azure AD B2C, where users, policies and app registrations are set up, which he leaves for the next video.
Passing parameters to xUnit net tests with the ClassData attribute [GCAST 113]
  • InlineData couples test data to the test and accepts only primitives. Giard says the coupling matters little for simple cases but can for complex ones, and that InlineData offers no way to pass a whole object as a parameter. ClassData is xUnit's answer to both.
  • The test data lives in its own class implementing IEnumerable<object[]>. His class holds a read-only Data property, a list of object arrays (15, 30, 45; -15, -30, -45; -15, 30, 15). The generic GetEnumerator returns this.Data.GetEnumerator(), and the non-generic one returns this.GetEnumerator(). Each array must match the test method's parameters in number, data type and meaning. He notes the entries could be full objects with properties and sub-objects.
  • [ClassData(typeof(...))] replaces InlineData on the test method. The class's type is passed to the attribute, and each array becomes one run, with its three values as the method's three parameters. He describes this as "three times three". In Test Explorer the new class shows one method with three tests under it, all passing.
  • Debugging shows the runs do not follow the data's listed order. With a breakpoint set and that one test debugged, the first pass received -15, -30, -45, the second -15, 30, 15, and the third 15, 30, 45.
Passing parameters to xUnit.net tests with the InlineData attribute [GCAST 112 ]
  • A Theory replaces near-duplicate Fact tests with one parameterized method. The three tests from the previous episode (positive, negative and mixed inputs to an add function) repeat most of their code. Marking one method [Theory] instead of [Fact] lets it take int firstNumber, int secondNumber and int expectedResult as parameters and assert the expected result against the actual one.
  • Each [InlineData] attribute supplies one test case's values. The number of arguments must equal the number of method parameters, so here each holds three numbers: 15, 30, 45; -15, -30, -45; and -15, 30, 15. He keeps the empty "arrange" comment as a reminder that the inputs arrive as arguments.
  • Test Explorer adds a level showing each case's result. Under the class and method sit the three individual runs, each with its own green check mark. Giard recommends running all tests often, and says that is an incentive to keep tests short in running time.
  • InlineData works only for primitive types like strings and numbers. He says passing objects needs another approach, covered in the next episode.
Writing unit tests with xUnit.net [GCAST 111]
  • Visual Studio's xUnit template sets up the test project; no download needed. xUnit.net is a free, open-source framework for testing .NET applications, found at xunit.net. Giard picks the C# template, names the project DemoXunit.Tests (one test project per project tested, and one test class per class), and uses .NET 3.1, which he calls the current long-term-support version. The test project needs a reference to the project under test and a using for its namespace.
  • Giard structures each test as arrange, act, assert. Arrange sets up variables or objects, act calls the method under test, and assert checks the outcome; he types these as comments first and favours long, descriptive test-method names. xUnit's Assert class offers checks for equality, true or false, and not null, and Assert.Equal takes the expected value first, then the actual.
  • The [Fact] attribute is what makes a method a test. Once it is added, the test appears in Test Explorer (Test > Test Explorer), listed under its class, project and solution. Tests can be run or debugged at any of those levels, or from the code: right-clicking inside a method runs that test, and inside the class but outside a method runs the whole class. Debugging adds breakpoints and stepping.
  • A deliberately broken function shows a failing test's report. With the add method changed to a bug, the test failed and reported it expected 45 but got -15. Mid-demo, unexplained build errors cleared after he closed and reopened Visual Studio, cleaned and rebuilt, which he puts down to cached results.
  • Test edge cases beyond the happy path. He copies the test for negative inputs (-15 + -30 = -45) and mixed inputs (-15 + 30 = 15), and suggests zero or unexpected formats as other cases worth checking. A blue exclamation point in Test Explorer marks a test never yet run.
Renaming Projects and Folders in a Visual Studio Solution [GCAST 110]
  • Renaming a project in a .NET solution is non-trivial. Other projects reference it, and Giard's own conventions mean the namespace and the project's folder should change to match. Renaming the folder breaks the solution's path to it. He warns that some files, such as JavaScript or Blazor files, sometimes have trouble being renamed automatically.
  • Rename in Visual Studio, then close it and rename the folder. His steps: rename the project (F2), replace every occurrence of the old name, close Visual Studio, rename the folder, then fix the project's path in the .sln file in a text editor. He knows of no way to change the project's location from inside Visual Studio. In the demo a project became VideoManager.Lib.
  • Replace occurrences one at a time, not globally. He uses Ctrl+H across the entire solution with match case and whole word on, but says even that is not enough, so he reviews each hit and replaces it (Alt+R) or skips it. That way he avoids renaming similarly named libraries he has not yet handled.
  • After reopening, clean the solution before rebuilding. He waits for background restore tasks to finish, then cleans the solution, because cached build output can get in the way after radical changes. The rebuild is the first clue that something broke.
  • A successful build does not prove nothing broke. Giard runs a manual smoke test of the app and says a change this large warrants full regression: integration, automated, unit and manual tests. He notes the rename makes source-control history look odd, which he says cannot be avoided, and advises making a backup and committing to source control first.
Using the Video Indexer AI Tool [GCast 109]
  • Video Indexer analyzes video with AI and needs no code. It is a Microsoft tool at videoindexer.ai, signed into with a Microsoft account; Giard says it integrates with Azure Media Services and Cognitive Services, and that he first used it about four years before this recording.
  • An upload takes a source, title, language, visibility and agreement. The video can come from a web URL (a YouTube video, for example) or the local machine; the language helps its speech-to-text, and Giard keeps his video private. Upload stays disabled until a checkbox is ticked; Giard says Microsoft currently does not allow police departments to use the technology, and the uploader must have rights to the video.
  • Indexing took longer than the video itself ran. Giard's low-quality 2010 charity-event video, 744 MB and 16 minutes long, took about a minute to upload and about 19 minutes to index; he offers that as a gauge for other videos.
  • The results break the video into people, topics, emotions and scenes. It tracked each face across every appearance, even in the background, without naming non-famous people, and matched two of his friends to celebrities they are not (one at 70.5% certainty). It also listed topics, keywords, labels and named entities from the transcript; emotions from facial expressions; sentiment from speech on a 0-to-1 scale; and 12 scenes, each split into shots with key frames usable as thumbnails.
  • Captions and insights download for use in other applications. Closed captions come in standard formats, VTT among them, optionally with audio effects, and each line carries a confidence score (about 78–84% in his file); the insights download as JSON with start and end times for every emotion, scene and the like. Giard lists uses: better video search, highlight reels, closed captioning, content moderation, pointing to specific moments, and feeding the data to other machine-learning models; he says much of it works on audio too.
Managing Deleted Facebook Posts [GCast 108]
  • Deleting a Facebook post only moves it to Trash. Choosing "Move to Trash" from the post's three-dot menu removes it from the timeline, but Facebook's own message says items in trash are deleted after 30 days. Giard says he only recently learned this.
  • The Trash is buried deep in account settings. Giard calls its location unintuitive: the triangle at the top right (labelled Account), then Settings and Privacy, Settings, Your Facebook Information, Activity Log, and Trash.
  • Trash lets you delete a post permanently without waiting. Giard suggests this for sensitive material you don't want to exist anywhere. Each item's ellipsis menu has Delete, and checking boxes applies the same action to several posts at once; once deleted this way, he says, a post cannot be recovered.
  • Archive and Restore to Profile are the other options. Giard says archiving keeps a post past the 30 days, which suits something you want kept for yourself but not seen by others. Restore to Profile undoes a mistaken deletion, and in the demo the restored post reappeared on his profile after a brief "Restoring" message.
Live Streaming with Azure Media Services [GCast 107]
  • One live stream reaches many viewers and is archived at once. Giard describes Azure Media Services taking camera, screen or slide input and sending it to clients in near real time (a short delay depending on latency), broadcast to many devices worldwide, while saving the footage to Azure Storage so people can watch it later.
  • A live event needs a Media Services account and an RTMP input. In the Azure portal he creates a Media Services account with a new resource group, the East US region (near him, to cut upload latency, assuming his viewers are there too) and a new storage account, and ticks the box promising not to broadcast content he does not own the rights to; it takes about 30 seconds. On the account's live streaming blade he adds a live event, keeps the defaults with no encoding and RTMP input, starts it, and copies the input URL it gives him.
  • Local broadcasting software pushes the feed to that input URL. He uses the free version of Telestream's Wirecast, which he says is fine for experimenting but not commercial use because it watermarks the video and periodically announces "this is Wirecast". He adds a webcam layer (screen capture is another option, and inputs can be switched live), moves it from preview to live, pastes the input URL as the RTMP server in the output settings, and starts broadcasting; the portal preview shows the feed a few seconds behind, and refreshing the page cleared an error it threw.
  • Public viewing needs an output, streaming locator and streaming endpoint. He creates an output and a streaming locator with the defaults, starts the streaming endpoint, and pastes the streaming URL as the source of a video tag in a plain HTML page (the same sample from an earlier episode, without the track or captions), which then played the live feed.
  • Stop the live event and streaming when done to avoid compute charges. Giard warns that you are charged while the event runs, citing the cloud rule that "storage is cheap, compute is expensive"; once stopped, only the small charges for configuration and storage remain.
Audio Transcription and Captioning with Azure Media Services [GCast 106]
  • An Audio Transcription transform runs only the audio analysis. Giard uses it instead of the full video analyzer from the previous episode, which also spent time on face and object detection. He adds a new job on the uploaded asset with transform type Audio Transcription, turns off automatic language detection and specifies US English, names the output asset, picks the storage account and sets high priority; the job took about a minute.
  • The output asset is a set of analysis files, not video. Its container held emotions.json, insights.json, lid.json, metadata.json, transcript.ttml and transcript.vtt. Emotions came back empty, which Giard puts down to the clip being so short; lid.json reported English (US) at 100% confidence and would list percentages for other candidate languages; metadata.json describes the file itself.
  • The TTML and VTT transcripts carry timed, confidence-scored caption text. Both hold the same content in different formats, broken out by time range (the first sentence spanning 0 to 7 seconds) with a confidence level per segment. Giard notes both are standards usable on the web and calls them the most interesting of the outputs.
  • Captions need the transcript in the same container as the video. Giard points an HTML video tag's source at the streaming manifest from View Locator, uploads the VTT and TTML files into the container holding the adaptive streaming videos, and sets a track element's source to the VTT file using that container's URL. The player then showed captions; with several caption files a viewer could switch language or turn captions off.
Video Files and Azure Media Services [GCast 102]
  • A Media Services account bundles a resource group and storage account. From portal.azure.com Giard creates Media Services with a new resource group, an account name, East US as the location and a new storage account, which he prefers to an existing one to keep things together. He must check a box promising not to share anything he lacks rights to; creation took about 30 seconds.
  • Giard is remaking his early Media Services videos. He says he covered the service a couple of years earlier in the series, but the interface and the capabilities have changed a lot since.
  • Media enters the service as an asset backed by blob storage. On the Assets blade he uploads a local MP4 as an input asset, renames the suggested asset name and again affirms he owns the rights. The asset's details link to a blob container in the storage account; an asset maps to one container, which can hold one or more files, here just the uploaded MP4.
Analyzing a Video with Azure Media Services [GCast 105]
  • Analysis is a job run with the Video and Audio Analyzer transform. Giard says analysis ranges from file properties to machine-learning detection of objects, faces, on-screen text and speech. Narrower transforms do only audio transcription or face detection; he picks the full analyzer for both video and audio with the language set to US English. The run took about 1 minute 14 seconds on a clip of about seven seconds, and he warns longer videos take longer.
  • The output is a set of data files, not a stream. The output asset's container held JSON files, a JPEG and zip files, which he downloads with the Azure Storage Explorer. The annotations file tags each time interval with confidence scores; it was 99% sure of a human face, a person, text, clothing, a man and a beard.
  • Moderation scores let a site screen uploaded videos. Each interval gets an adult score, a racy score and a review-recommended flag; Giard's understanding is that adult means pornography and racy means suggestive. He suggests setting your own rules, for example rejecting above 80%, sending 30–80% to a human reviewer and accepting below 30%.
  • Emotion and face files score faces and voice separately. The face emotion scores put joy highest at 69%, then sad and angry, while the vocal analysis read sad ("I looked happy, but I sounded sad"). faces.json gives each face's position, width and height, roll, pitch and yaw, and a 99.99% face confidence; the generated thumbnail was cropped to the face.
  • OCR and transcription ran with mixed results. OCR picked up a poster in the background but classed it as Chinese Traditional, which Giard thinks a language hint might have fixed. The TTML and VTT transcripts caught the one sentence he spoke.
Sharing a Video Online with Azure Media Services [GCast 104]
  • Sharing starts with a streaming locator on the encoded asset. On the adaptive-streaming output asset (four MP4s at different sizes plus metadata) Giard creates a new streaming URL. The streaming policy offers stream only, stream and download, or download only, with optional DRM; he chooses stream and download without DRM. The locator's expiration defaults to 100 years out and can be shortened, to three days for example.
  • A running streaming endpoint costs extra, so stop it afterwards. Giard starts the endpoint to serve the video and, at the end, stops it from the Streaming Endpoints tab. After that nobody can reach the video, but he is no longer paying for it.
  • View Locator lists direct download links for each file. The Downloads section has URLs for the JSON, the JPEG thumbnail and each MP4, whose names give their resolution; pasted into a browser tab, the JPEG renders and the MP4 plays.
  • The smooth streaming manifest URL gives an adaptive web player. Giard pastes the link ending in /manifest into the source element of an HTML video tag, from a template on his blog post of January 20, 2021 on adding an adaptive Azure Media Services video to a web page. The player auto-detected his wired connection and played a high resolution, let him pick a lower one, and its default controls can be restyled.
Encode a Video with Azure Media Services [GCast 103]
  • Encoding runs as a job that executes a reusable transform. Giard describes the transform as the recipe, the steps to do a particular thing, and the job as the execution of it. A job is added from the uploaded asset's page; the first time there is only "create new" for the transform, but once created it can be reused for later jobs. Priority can be set to high, normal or low; he uses normal.
  • Giard picks the adaptive streaming preset to serve unknown viewers. The preset list covers AAC, adaptive streaming and various bit rates. He chooses adaptive streaming because it encodes the video in multiple formats, and he cannot know whether a viewer is on a phone or a computer, or on fast or slow internet.
  • Encoding time trades clip length against the number of formats. His very short clip went from queued to processing to finished in about a minute and a half, visible under transforms and jobs. He notes a longer video would take much longer, and asking for adaptive streaming adds work.
  • The job produces a separate output asset holding several renditions. The output asset sits in a different storage container from the input asset. It holds four MP4s whose file names show AAC audio and the resolution (1024x576, 320x180, 480x270 and so on), plus a thumbnail screenshot and metadata files (JSON, and ISM and ISMC manifests), which he says different clients use. He downloads them with Azure Storage Explorer; the renditions grow in size and sharpness from the grainy smallest to the sharp largest.
Azure Resource Groups [GCast 101]
  • A resource group's region does not bind what goes in it. Giard says he is unsure how much the group's location matters, since resources in other regions can be placed in it; his demo group is in East US and he notes a web app could go in Central US. He typically puts a group near himself, his users or the resources it will use.
  • Resource groups gather an application's parts in one place. A web app's app service plan, and any SQL Server, Cosmos DB or functions it uses, logically belong in the same group, so opening the group shows "this is my application" at a glance.
  • Deleting the group removes everything in it at once. Giard uses this to clear multi-part demo apps rather than deleting resources one by one. Because it is destructive, the portal makes him type the group's name before the delete button is enabled.
  • Resources can move between groups and now between subscriptions. Moving some or all resources to another group can merge groups, or to a new one can effectively rename a group. Moving to another subscription "didn't used to be possible", he says, and matters when handing work to a customer or changing billing.
  • Export template generates an ARM template for the whole group. The template automates creating all the group's resources, and can be deployed with PowerShell or the CLI, with parameters changed for different names or locations. It can be added to a library or downloaded as separate files.
Creating a Screencast [GCast 100]
  • A mid-range microphone is enough; avoid the laptop's built-in one. Giard uses a Microsoft headset with a boom mic and says about $50 buys good enough quality, with no need to spend hundreds. He turns the mic level up and makes sure Camtasia records from the headset, not the webcam mic.
  • Giard captures the screen in Camtasia, splitting long waits into clips. Camtasia Studio from TechSmith costs a couple hundred dollars, with a free trial he thinks lasts 30 days; he recommends trying it before buying. F10 stops recording; he records separate clips to skip a step that takes, say, five minutes, and notes Camtasia does not add later clips to the timeline automatically, so he drags them in, adding a transition such as page roll to signal time passing. He exports a best-quality WMV at 1080p with no watermark; episodes run about 7 to 25 minutes, and longer topics get split into two or more videos to stay consumable.
  • Intro and credits come from re-saving the last episode in Premiere Elements. Adobe Premiere Elements costs under $100. He opens the previous episode, saves it under the new name, deletes the old screencast, edits only the episode number, title and recording date in the intro and outro, drops in the new WMV, and raises clip audio gain by about 5 or 10 if it sounds quiet before exporting.
  • Recording date goes in the YouTube tags because software changes. He switches to the GCast channel's own account (each of his three shows has one), titles the upload "<title> [GCast NNN]", marks it not made for kids, and tags the recording date so viewers can judge whether a demo is still relevant; he sometimes re-records a demo years later. He usually publishes public, and sometimes schedules uploads days or a week apart when he records several in one day.
  • Giard replaced YouTube's auto-thumbnails with a standard PowerPoint title card. YouTube picks three frames pseudo-randomly, and he found them not indicative of the content. He duplicates a slide in a PowerPoint deck, changes the episode number and title, swaps in a different photo of himself (using the selection pane to reach the picture behind other elements), and saves just that slide as a PNG to upload as the thumbnail.
PowerPoint Animations [GCast 99]
  • Picking from the ribbon replaces an object's animation rather than adding one. PowerPoint's four categories are entrance (green in the animation pane), emphasis (yellow), exit (red) and motion paths. To stack several on one object Giard uses Add Animation, and reorders them by dragging in the animation pane; emphasis effects are grouped as basic, subtle, moderate and exciting.
  • Start settings chain animations onto a single click. Each animation defaults to "on click"; "with previous" and "after previous" make them run together or in sequence. Giard sets a fly-in, a wipe and a float-in to run after one another so one click plays the whole sequence.
  • Effect options and timing tune each animation. The options depend on the object: Giard sets a wipe to run from the left to match an arrow's direction and adds a half-second bounce at the end of a float-in. Timing sets a delay and a duration, here waiting half a second and taking two seconds.
  • Motion paths and text effects add movement and staged reveals. Motion paths include lines, arcs, turns, shapes and loops, with more such as a curvy star. Text can be revealed one line per click, or animated by word or by letter.
  • Giard's advice: add animation last, and sparingly. He builds the content first, then looks for places animation helps, such as revealing a flow chart in the direction of flow as he talks. Too much of it, he says, makes the audience stop listening and watch the slides.
Using the Azure Storage Explorer [GCast 98]
  • Azure Storage Explorer is a free Microsoft desktop tool. Giard downloads it from azure.microsoft.com/features/storage-explorer and installs it with the defaults; the version shown came out in December 2020. It can connect to a local storage emulator or to storage accounts in the cloud.
  • A connection string is Giard's simplest way to connect. Of the several connection methods, he copies a connection string from the storage account's Access keys in the portal and gives it a display name. The account then shows nodes for blob containers, file shares, queues and tables, and changes made in the tool show up in the portal's in-browser storage explorer, and vice versa.
  • Blob containers support uploads of files or whole folders. Giard creates a container, uploads a file and then a folder of two text files; a refresh may be needed to see them. Opening a blob downloads it and opens it in the program registered for its extension, and blobs can be multi-selected for deletion, copied, and inspected for history and statistics.
  • Table entities need a partition key and row key. The combination must be unique within the table, and other properties can be typed, such as integer or Boolean. Entities can be added one at a time or imported from a CSV, whose rows need not share the same properties, then edited, deleted or exported back to CSV.
  • Its main advantage is juggling several storage accounts. Giard says the portal means separate tabs or constant switching, while in the Explorer multiple accounts are each a click away.
Accessing MinIO with the AWS S3 SDK [GCast 97]
  • The captions are a different episode from the one titled. This text never mentions the AWS S3 SDK. It is the same walkthrough as "Using the MinIO Java Client SDK [GCast 96]", step for step: MinIO's own Java SDK in a Spring Boot app, uploading and downloading the same test.txt, with the same "the last two videos" opening.
Using the MinIO Java Client SDK [GCast 96]
  • The MinIO Java SDK comes in as a Maven dependency. Giard runs a MinIO server in Docker on port 9000, with his own access key and secret key as environment variables (set up as in episode 94). He then generates a Spring Boot project with no dependencies from Spring Initializr and adds the MinIO SDK dependency to pom.xml. He says he learned the approach from the Java Client API Reference and the Java Client Quick Start Guide under Docs at min.io.
  • A service class wraps the MinIO client for upload and download. The client is built from the endpoint, access key and secret key. The code checks whether the bucket exists, creates it if not, and uploads with the client's upload-object method, passing args built with the bucket and file name. Download works the same way with download-object args and saves into the same local folder with a "d_" prefix so the original is not overwritten. Giard notes that hard-coding the credentials "isn't the most secure thing" and that they probably belong in environment variables, but this is a demo.
  • A component plus getBean connects the static main method to the service. Giard says it is difficult in Java to call a non-static class from a static method. So a component class autowires the service and holds one read-write method. In main he captures the ApplicationContext that the run call returns, calls getBean for the component, and adds the required throws declarations.
  • The first run failed because the service lacked the @Service annotation. The exception said the service could not be found for autowiring, and adding @Service fixed it. Stepping through, the new bucket appeared in the MinIO browser, then the uploaded file appeared in it, and then a d_test.txt identical to the original appeared locally.
Creating a MinIO Agent for Azure Blob Storage [GCast 95]
  • A MinIO gateway fronts another storage service instead of holding data itself. Giard calls it a MinIO agent and contrasts it with the server from the previous episode. The MinIO docs list five gateways, and he uses the one for Azure. It is a docker run command exposing port 9000 under the name "azure-s3", with the argument "minio gateway azure" where the server used "minio server".
  • The gateway's access and secret keys must be the Azure storage credentials. In server mode you invent your own keys. Here the access key is the Azure storage account's name and the secret key is the storage account key (he uses key 1 and says key 2 works too). The prerequisites are an Azure account (he thinks the free trial is 30 days) and a storage account created at portal.azure.com.
  • Every change made in one interface appears in the other. A bucket created in the MinIO browser shows up as a blob container in Azure Storage Explorer, and a container created in Azure shows up as a bucket in MinIO. Uploads and deletions made on either side are reflected on the other.
Creating a MinIO Server [GCast 94]
  • MinIO is object storage comparable to Azure Blob Storage or Amazon S3. It stores objects and other semi-structured data. In server mode it creates a storage repository plus a browser interface for uploading, listing, downloading and managing objects. Giard points to the quick start guide under Docs at min.io, which he says got him running in a hurry.
  • Server mode is one docker run command from PowerShell. Docker must be installed (on Windows that needs a reboot) and running. Giard says that if the command fails, Docker may not be set to auto-start or may still be starting up. The container is exposed on port 9000 and needs two environment variables, an access key and a secret key. The defaults work, but he says you probably want your own, and he uses "access key 2" and "secret key 2".
  • The MinIO browser organises objects into buckets. Giard logs in at the endpoint with the keys he set, in a private window in case credentials are cached. Buckets are MinIO's term for what he calls containers from Azure Blob Storage, and he likens them to folders. He creates two buckets, uploads files to each (several at once with Ctrl-select), then downloads and deletes objects.
Handling Spring Boot Errors with ControllerAdvice [GCast 93]
  • Per-method try-catch scatters and duplicates error handling. Giard says the try-catch pattern from his previous videos works, but catching the same exception in 20 methods means copying the handler into 20 places and maintaining all 20. Controller Advice, built into the Spring Framework, moves that handling into one place.
  • A ControllerAdvice class holds one handler method per exception type. He adds a new class (his name for it is his own choice) annotated with @ControllerAdvice, and in it a method annotated with @ResponseBody and @ExceptionHandler for ArithmeticException. The method builds the endpoint's usual output model with a null quotient and the message "math error: " plus the exception message, and returns it in a ResponseEntity with HttpStatus.BAD_REQUEST (400). He says a separate status annotation is redundant because the ResponseEntity already carries the status.
  • With the advice in place, controller try-catch blocks can simply go. Posting 100 divided by 0 to the divide-numbers endpoint, with no try-catch left in it, lands in the advice method and returns a 400 with quotient null and "math error: / by zero". He adds handlers for his custom missing-arguments exception and for the generic Exception class, which he says should sit at the top or bottom as the handler of last resort, so it catches any exception the others miss.
  • Validation code shrinks to throwing the custom exception. In add-numbers he deletes the inline checks and their try-catch and just throws a new missing-arguments exception with a message; Java requires throws in the method declaration for that exception. A missing person name returns a 400 with "missing args error: missing input, person name is required", and a missing first number, thrown deep inside the math service, is caught by the same handler.
More Error Handling in a Spring Boot Application [GCast 92]
  • A divide-numbers endpoint shows Java integer division truncating. Giard adds a divide-numbers POST endpoint, called on localhost port 8080 under the app's GCast path, with input and output models copied from his add-numbers ones and a quotient in place of a sum, plus a divide method on the math service interface, its real implementation and its mock. Because an integer divided by an integer returns an integer, 10 divided by 2 returns 5 but 100 divided by 3 returns 33: the fraction is dropped, not rounded.
  • An uncaught divide-by-zero returns an unhelpful HTTP 500. Dividing by 0 throws an exception and the response has a 500 status, no useful information, and not the JSON format the endpoint normally returns.
  • Catch built-in exceptions rather than pre-checking every input. Giard says checking parameters up front, as in his last video, is not always possible: the division might be one step in a longer workflow with complex logic, perhaps one he did not write. He wraps the service call in the controller in a try-catch for ArithmeticException, plus a generic Exception catch for anything else, and how to handle each is up to the business logic.
  • The error comes back in the same format as a success. The catch returns the output model with a null quotient and a message saying an arithmetic error has occurred, with the requester's name and the exception's own message ("/ by zero") appended. His first run still returned a quotient of 0 because the output was set again after the try-catch; the assignment had to move inside the try. He says the consistent format means clients need not change their code radically, and that the next video shows a more elegant way to handle exceptions in one place.
Throwing and Catching Custom Exceptions in a Java Spring Boot Application [GCast 91]
  • Error checks move out of the controller into a custom exception. The previous episode put the validation in the controller; Giard likes to keep controllers as clean as possible. Now the service throws a custom exception carrying the error message, and the controller catches it.
  • His custom exceptions are nearly always the same boilerplate. He puts them in their own exceptions folder. MissingArgumentsException extends Exception, adds the default serial version ID the parent expects, and has two constructors, one taking a string message and one taking a Throwable, each just calling super. He says 99% of his custom exceptions look like that; the class and its constructors must be public.
  • Java requires every method that may throw it to declare that. The math service's add method checks for a null first or second number and throws the exception with a message. Its throws declaration then had to go on the interface, on a mock implementation from an earlier video, and on the GET method that also calls the add method, which otherwise failed with "unhandled exception type".
  • The controller wraps the call in try/catch and returns 400. The sum variable is declared outside the try because it is used later. The catch takes the exception's message rather than hard-coding one, and returns a null sum with that message and HTTP 400; the person-name checks stay in the controller. Inputs of 10, 20 and "David" returned 30, and a missing number still returned a 400, now raised in the service.
  • Exceptions from deep in a call chain reach one catch. Giard gives two reasons for the refactor despite near-identical output: a cleaner controller, and, more important in his view, that in real services calling other services, databases and the file system, an exception thrown anywhere bubbles up to the controller unless something swallows it on the way. He says the next video shows a simpler way using Spring itself.
Basic Error Handling in a Spring Boot Web Service [GCast 90]
  • The add-numbers service gains a POST version taking JSON. Building on the app from earlier episodes (episode 83 covers HTTP POST in Spring Boot), Giard adds an input model, addNumbersInput, with firstNumber, secondNumber and personName, and an output model, addNumbersOutput, with sum and sumMessage. The new controller method reads the input from the request body and returns a response entity of addNumbersOutput; a POST of 1, 5 and "David" returned HTTP 200 with a sum of 6 and the message "The sum is 6, David".
  • Left unhandled, a missing argument gives an unhelpful 500. Omitting the second number leaves it null, so the addition throws. Giard says Spring handles that, but the client gets back only a 500 "internal server error", which he calls not useful.
  • The controller checks for nulls and returns a 400 itself. If any of the three properties is null, it skips the add call and returns a bad request with a null sum and a message naming the required properties. Giard suggests looking for a more fitting status depending on the error, and says the check could be split into three if statements to give a specific message for each missing property.
  • A business rule can reject input the same way. As a deliberately contrived rule, a person name of "Bob" gets a 400 and "Bob is not welcome here". Giard notes that Java's string compareTo returns a number, zero when the strings match, rather than a boolean, so the test compares the result to zero.
HTTP Request and Response Headers in a Spring Boot Application [GCast 89]
  • Let the client supply the request key in a header. Building on the previous episode's mapped-diagnostic-context logging, Giard argues it may make more sense for the caller to pass the key than for the controller to generate it. He adds an HttpServletRequest parameter to the addNumber method of his Azure Spring Boot web service, alongside the two query-string numbers, which exposes the whole HTTP request including its headers.
  • Read the custom header, falling back to a random key. He calls request.getHeader("x-request-key"), using the "x-" prefix as the convention for custom headers; the name can be anything the client understands. If the header is absent and the value is null, the controller generates a random key as before. The debugger showed a null without the header and the passed value once he added it in his test client.
  • Uniqueness becomes the client's responsibility. If the client sends no key, the service creates one; if it reuses the same value every time, its logs all carry the same key and it loses the benefit of a unique number in the log. Giard says it need not be a number, but should always be unique.
  • Return the key to the caller in the response headers. His reason: a client, especially one whose key was generated server-side, can later report a problem by quoting the key so its logs can be found. He creates a Spring Framework HttpHeaders object, calls set with the same x-request-key name and the key as value, and passes it to a ResponseEntity overload that accepts headers. He reuses the request's header name because, in his view, both carry the same thing.
Spring Boot Logging with Mapped Diagnostic Context [GCast 88]
  • The service already logs to two destinations. The Spring Boot "add numbers" web service uses an org.slf4j logger configured in logback-spring.xml to write to Application Insights and the console at info level and above, which skips trace. Giard adds logger.info calls recording that the controller and the MathServiceImpl service were reached, with the two numbers passed. He warns that many classes are named Logger, and he had first imported the wrong ones (ch.qos and a Hibernate one).
  • One line in the controller tags every log in the request. In the controller he generates a request key with UUID.randomUUID().toString() and stores it with MDC.put as a key–value pair. The service's log statements then carry it too, although MDC appears nowhere in the service's code. His point is that this avoids repeating the code in every service the controller calls.
  • The key shows up as a custom dimension on every message. In Application Insights, after about five minutes of propagation, each log message, from both the controller and the service, showed a request key property under custom dimensions, alongside the timestamp and operation name. Giard's use case is filtering the logs to one request when hundreds of clients are hitting the service at once.
  • The context does not cross process boundaries. If the code calls something out of process, such as another web service, the key is not carried. Giard says you must pass it explicitly and add code in that service to log it.
Logging to Azure Application Insights from a Java Spring Boot Application [GCAST 87]
  • Application Insights can collect logs from apps hosted anywhere. Giard creates an Application Insights resource from the Azure portal (left at the default West US region, though he says it should sit near where the app is deployed) and runs the demo app locally, extending the Spring Boot MVC app's logging from the previous episode. The resource's instrumentation key is a GUID that identifies it; he says to keep it secret and shows his only because he will delete the resource.
  • Two Maven dependencies wire Spring Boot logging to Application Insights. In pom.xml he adds Application Insights Spring Boot Starter and Application Insights Logging Logback, both in the com.microsoft.azure group, the latter at a 2.0-series version. He says Maven may need a refresh before they resolve, which IntelliJ flags with red or squiggly-underlined entries.
  • A second Logback appender plus the key sends logs to Azure. In logback-spring.xml he adds an appender-ref named "AI appender" beside the existing console one, then defines that appender with the Application Insights class (the name is his choice; the class comes from the new dependency). The instrumentation key goes into application.properties.
  • Logs arrive in Azure minutes later, because shipping is asynchronous. With the level set to warning and above, a request to the greet endpoint logged a warning and an error to the console at once, but they showed up in Application Insights only a few minutes later. Giard says the asynchronous push is how it scales to millions of users without overwhelming Azure.
  • Entries land under Traces and can be filtered with a query language. Under Logs, a traces query shows each message with its severity level, timestamp, and detail on the running program and the browser. He points to IntelliSense-assisted where filters, order by timestamp ascending or descending, and null handling, and leaves the query language for a possible future video.
Logging in a Java Spring Boot Application [GCast 86]
  • Use Spring Boot's built-in logging framework unless there is reason not to. The built-in framework is Logback, and Giard says he prefers it over adding another one unless there is "some compelling reason". He enables it by adding a file named logback-spring.xml under the application's resources, with a root element whose appender-ref points to an appender named console.
  • The appender's pattern decides what each log line contains and looks like. In his file the pattern sets the colours of each part, the date format, a highlighted log level, the message text, and exception details followed by a line break. He calls the syntax "a little bit cryptic" and points to the formatting chapter of the Logback manual at logback.qos.ch/manual.
  • A single level setting turns logging on and off without code changes. There are five levels, in order: trace, debug, info, warn and error. Setting level="info" on the root logs info, warn and error; trace would log everything, and warn would log only warn and error. Code can log at every level, and the config file decides what gets output.
  • Pick the standard logger class when several are offered. Giard says to choose the Logger from org.slf4j, the one that works with LoggerFactory, which is in the same package. He declares a private Logger field built with LoggerFactory.getLogger(), passing the current class so that the class is logged automatically. The logger has trace, debug, info, warn and error methods. He says error usually belongs in a catch block because an overload also takes the exception and logs it.
  • The demo shows lower levels being ignored at the configured threshold. With the level at info, stepping through a controller method logged nothing for the trace and debug calls but logged the info, warn and error messages, alongside Spring's own info messages that he did not write. After he changed the level to warn and restarted the app, only the warn and error calls produced output.
Dependency Injection in Java Spring Apps [GCAST 85]
  • Newing up the service in the controller is a dependency worth removing. Continuing from episode 84's MathService interface and MathServiceImpl class, Giard wants room for multiple implementations: one doing the real work and one with a hard-coded answer for testing, or different ones per environment or per part of an organization. He calls the example contrived.
  • Each implementation is marked as a service and given a qualifier. Giard labels the class with the service attribute so Spring knows about it, and adds a qualifier with a name; the service label alone suffices if there is only one implementation of the interface. He names each qualifier after its class, though he says that is not required.
  • A mock implementation returns a fixed value regardless of input. He adds MathServiceMock, which implements MathService, with the same service and qualifier attributes, and makes addNumbers always return 10.
  • The controller autowires an interface-typed field and picks one by qualifier. The controller drops the new call for a private class-level field of type MathService (the interface, not the implementation), marked autowired so Spring creates the instance as soon as it is referenced, with a qualifier naming MathServiceImpl. A GET in the browser returns 30 for 10 and 20 and 35 for 10 and 25; changing the qualifier to MathServiceMock returns 10 whatever is passed in.
  • The payoff is testing without fragile, slow dependencies. Giard says you would not normally edit the live controller to switch; the live code would use the real implementation while a test uses the mock. It matters, he says, when the real implementation calls another web service or reads or writes a database: those dependencies are fragile, take time, and are not what the test is testing.
Java Services in Spring Boot [GCAST 84]
  • The starting point is a controller method that does the math itself. In the Spring Boot web service from earlier episodes, Giard adds an addNumbers method mapped to an HTTP GET at GCAST/addNumbers/{first}/{second}, each path segment mapped to a parameter; it returns the sum in a ResponseEntity with HTTP status 200. In Postman, 10 and 17 return 27.
  • Logic beyond trivial belongs in a service, not the controller. Giard says that if the method did more than add two numbers (querying a database, running a complex workflow, calling other web services) he would not leave it in the controller; the addition is only a stand-in to show how.
  • Define an interface first, then a class that implements it. He groups services in a package named services (packages show up like folders in Java) and creates an interface MathService declaring int addNumbers(int firstNumber, int secondNumber) with no code. He calls basing the class on an interface good practice, not a requirement.
  • The implementation class carries the moved logic and returns a plain value. MathServiceImpl "implements MathService", so it must have every method the interface defines; IntelliJ can generate the method stub, marked with the override attribute. He pastes in the controller's addition and returns the sum itself rather than a ResponseEntity.
  • Coding to the interface gives flexibility, but swapping still means editing code. The controller declares the variable as the MathService interface, news up MathServiceImpl, and wraps the returned sum in a ResponseEntity with status OK; Postman with 11 and 5 still works. Giard notes that changing the implementation this way requires changing the code, and says the next episode uses attributes to inject the implementation instead.
Supporting POST HTTP Requests with Spring Boot [GCast 83]
  • POST suits larger, more complex input than GET. Giard's reason is that POST carries data in the request body, not only in the URL. The episode adds a POST endpoint next to the GET methods built in the previous video's Spring Boot RESTful service.
  • Simple Java model classes define the JSON in advance. He puts them in a models package: a greeting input class with one personName property, and a greeting output class with one greeting property. Each has a getter and setter, a default constructor, and a constructor that sets the property when the object is created.
  • @RequestBody maps the posted JSON onto the parameter. The controller method returns a ResponseEntity of the greeting output type and takes the greeting input as its parameter. @PostMapping sets the path ("Hello", uppercase H) and both consumes and produces as application/json. The method reads the person's name, builds "hello" plus the name into a greeting output object, and returns it wrapped in a new ResponseEntity with HttpStatus.OK.
  • Postman sends the test POST with a JSON body. Giard describes Postman as a free, quick download for Windows, Mac or Linux. He posts to localhost:8080/gcast/hello, sets the Content-Type header to application/json, and sends a body with personName set to his own name. The demo returned HTTP 200 and a JSON greeting of "hello, David Giard". He adds that a real application will do more than join strings, but the principle is the same.
Creating a RESTful Web Service with Spring Boot [GCast 82]
  • Two annotations turn a class into a URL-mapped REST controller. Building on the project from episode 81, Giard adds a controllers package (a package maps to a folder on disk) and a controller class. He marks it @RestController and gives it @RequestMapping("gcast"), so requests to the domain followed by /gcast reach the class. IntelliJ adds the import statement when he tab-completes the annotation.
  • A method returns a ResponseEntity and gets its own mapping. A ResponseEntity<String> is an HTTP response: a status code (200 OK, 404 not found), headers and a body. His sayHello method returns new ResponseEntity<>("hello", HttpStatus.OK) and is marked @GetMapping("greet"). GET is the verb a browser uses when a URL is typed in, and @PostMapping would serve POSTs. The app runs on port 8080, and localhost:8080/gcast/greet returned "hello".
  • A path variable makes the route dynamic. A second method maps greet/{personName} and takes a parameter marked @PathVariable("personName"), returning "hello" plus the name with a 200 status. The path and parameter names need not match, but he keeps them the same so the mapping is easy to read. /gcast/greet/David returned "hello David". He calls this the gist of a REST API, which could go on to return JSON or look data up in a database or another service.
Spring Boot Quick Start [GCast 81]
  • A web page generates a complete Spring Boot project for you. Giard, who calls himself a C# developer who has almost never written Java, goes to start.spring.io (linked from Quick Start on spring.io). He keeps a Maven project, Java as the language and the default current Spring Boot version. He sets the group to com.gcast ("kind of like a namespace in C#") and the artifact to gcast. Clicking Generate downloads the project as a zip file.
  • Dependencies are chosen on the same page before generating. He searches for "web" and adds the Spring Web dependency now, because the next episode turns this project into a web service. He describes Spring Boot as built on the Spring framework, a set of tools for enterprise Java development.
  • The generated project is configuration plus a one-line bootstrap. Opened in JetBrains IntelliJ (he uses the free edition; any IDE will do), pom.xml holds the Spring Boot version, the Java version and dependencies such as Spring Boot Starter Web and Spring Boot Starter Test. The only code is a main method that calls SpringApplication.run. It compiled as generated, and after he added a print line ("gcast rocks") it ran and printed it.
Conditional Logic in Power Automate [GCast 80]
  • A Condition control step is Power Automate's if statement. Control steps include loops, if statements and switch statements; Giard adds a Condition, which evaluates to true or false and runs an "if yes" or an "if no" branch.
  • Trigger values must be initialized as variables first. Giard could not select the quantity directly in the condition, so he added Initialize variable steps, an integer "quantity" and an integer "order ID", taken from the SQL insert trigger's output; this makes them available to later steps. He renames each step to say which variable it initializes.
  • The demo branches the file name on a quantity of 50. If the quantity is greater than or equal to 50 the flow creates a second OneDrive file named "big_order_" plus the order ID plus ".txt"; otherwise "small_order_" plus the order ID plus ".txt". Both go in the same folder as the standard order file, which he also changes to end in .txt so it opens without downloading first.
  • A flow can be tested against a previous run's data. This avoids inserting another row; re-running the last run (quantity 55) took the yes branch and produced the big-order file. Triggering it live with a new order of quantity 5 took the no branch, and the run view shows whether the condition was true or false and which branch ran.
Creating a Workflow from Scratch in Power Automate [GCast 79]
  • A blank flow starts as one of four types. Under My Flows > New at flow.microsoft.com: automated fires on a trigger, instant on a button click, scheduled on a timer, and business process includes some templates. Giard picks automated and names it DG Test Flow.
  • The SQL Server insert trigger needs a table with an identity column. The "when an item is created" trigger watches a table for inserted rows; Giard says the identity column is "just a rule". He adds a new connection using SQL Server authentication to an Azure SQL database, with no gateway, which he says is for servers behind a firewall.
  • Testing lets you perform the trigger action yourself. The Test button starts the flow waiting for its trigger; inserting a row produced a successful run whose inputs and outputs, including the row data from SQL Server, can be inspected.
  • A OneDrive Create file action drops one file per order. The file goes in a folder named "flow stuff", named "ORD_" plus the order ID, with the order text as its contents. An inserted order ran within seconds and the file appeared; Giard says he should have appended .txt so it would open properly. The flow's page lists test and triggered runs and its connections, and the flow can be turned off there.
Creating a Workflow from a Microsoft Power Automate Template [GCast 78]
  • Flow is a graphical workflow engine for non-coding power users. The narration calls the product Microsoft Flow throughout, not Power Automate as the title does. Giard describes its audience as "power users or citizen developers" who understand software but don't want to write much code; it lives at flow.microsoft.com and comes with an Office 365 subscription.
  • Templates are the simplest way to build a flow. Under New, Create from Template there are hundreds of templates, each wired to connectors for Office 365, SharePoint, OneDrive and third-party applications. Giard picks "Save Office 365 Email Attachment to OneDrive for Business", which uses the Office 365 Outlook and OneDrive for Business connectors; you may need to sign in to both before clicking Create Flow.
  • The flow's page shows its connections, owners and run history. Owners can be added beyond the creator. The history lists each run with its date, time and status (running, succeeded or failed).
  • The trigger's advanced options filter which emails fire the flow. The "on new email" trigger defaults to the inbox and can point at another folder. Under Advanced it can filter by recipient, CC, sender, importance, attachment and subject; Giard sets subject equals "Flow" so only those emails trigger it.
  • The second step saves each attachment to a chosen OneDrive folder. "Apply to each attachment" creates a file using the email's attachment name and contents; Giard points it at a folder he had already made. In the demo a test email with the subject "Flow" and a photo attached triggered the flow about 60 seconds after it arrived, and the photo appeared in that OneDrive folder.
Connecting Azure Synapse to External Data [GCast 77]
  • Synapse can query external Hadoop-style data in place through external tables. Giard follows a Microsoft Docs tutorial using SQL Server Management Studio and SQL Server Authentication, against the Synapse database (formerly Azure SQL Data Warehouse) created in the previous episode. The external data is a Microsoft-hosted, comma-delimited dataset on New York City public transportation.
  • Setup needs a master key, file formats and a separate schema. The master key encrypts the database and is created once per database. He then defines uncompressed and compressed delimited file formats describing the data, and creates a schema named "ext" to hold the linked tables.
  • Creating external tables links the data rather than copying it. The dataset is split into multiple tables (dates, geography, taxi licenses, trips with pickups and drop-offs, weather and others). Creation ran quickly because nothing was copied, and the tables then appear under External Tables in Management Studio.
  • Giard copies the external data into local tables because querying it remotely is slow. A plain select from the date table took a few seconds. He creates local tables, about seven by his count, from each external table with indexes added, which took several minutes. Comparing ext against dbo versions showed matching first rows and identical row counts.
  • System request tables show the history of external work. A query in the master database joining request details on request ID lists the tables, number of files affected and gigabytes processed for external operations. The unjoined request table shows everything run on the server, which Giard says on a busy server includes work by other users and background processes.
Creating an Azure Synapse database [GCast 76]
  • Azure Synapse is the renamed Azure SQL Data Warehouse. Giard says it is not a new product but has a few new features; in the portal it is found under Create a resource as "Azure Synapse Analytics (formerly SQL DW)".
  • The database sits on a logical server, existing or new. Giard uses the logical server from the previous episode, in West Central US, which is where the data will be stored, and a resource group he describes as a logical container for managing related assets together.
  • The performance level sets scale and cost. Gen 1 is no longer available and Gen 2 is required; a slider shows performance against cost, and Giard lowers it for testing while saying an enterprise might raise it. Additional settings offer sample data and tags, and the review page shows the hourly compute cost, which he finds useful. Deployment took about a minute.
  • Connect from SQL Server Management Studio with the server name. The server name is copied from the database's overview page and used with SQL Server authentication in SSMS, which Giard notes is a free download. Sample tables appear because he chose the sample data; a blank database would be empty.
Creating an Azure SQL Server Logical Server [GCast 75]
  • A logical server can be created in advance, empty. Creating a SQL database or data warehouse otherwise prompts for a new or existing logical server; Giard creates one on its own from the portal's "SQL Server (logical server)" resource.
  • The server name must be globally unique and lowercase. The server is addressed as name.database.windows.net, so a name someone else has used is rejected; reserved words and capital letters are not allowed, and the portal flags violations immediately.
  • The admin login and password have rules. "admin" was rejected as too common, and the password must meet a length requirement and mix numbers, letters and special characters.
  • "Allow Azure services" lets other Azure resources reach the server. Giard says it is generally a good idea, for example when an app service uses the server's databases. He skips advanced security, an extra-cost option; the review page flags problems and disables Create until they are fixed, and offers a template for scripting the deployment with the CLI or PowerShell. Deployment took about two minutes.
  • The firewall blocks any IP address not explicitly allowed. In the server's firewall settings, Add Client IP allows the current machine so local tools such as SSMS or Visual Studio can connect; any server that needs access must have its IP address added too.
Continuous Deployment with Azure DevOps [GCast 74]
  • Two triggers automate commit-to-deploy in Azure DevOps. Building on the build and release pipelines from earlier episodes, which deploy an ASP.NET Core app to an Azure web app, Giard's goal is that every commit to master is compiled and tested and, if it passes, deployed.
  • Build pipelines enable continuous integration on the Triggers tab. A checkbox starts a build on every commit to master, with an option to combine commits that arrive while a build is in progress rather than build each one. He saves without queueing and adds a save comment to keep track of pipeline changes.
  • Release pipelines use the artifact's continuous deployment trigger. The lightning-bolt icon on the release pipeline's first artifact turns it on, so a successful build starts a release automatically.
  • One commit ran both pipelines without further action. Giard edited the welcome page directly on master in the browser; the build ran, release 5 was created and deployed, and the change showed in Azure. He notes committing straight to master is not the desired way; you would probably use a pull request.
  • Giard argues CI exists to catch combined changes breaking. Diligent developers who compile and test locally can still break each other's code when their changes meet, his "butterfly effect"; CI gives feedback on compile and test results as soon as a commit lands.
Creating an Azure DevOps Build Pipeline [GCast 72]
  • A build pipeline automates pull, compile, test and publish. Giard says automating these steps lets them run often and find problems early. His demo is an ASP.NET Core web app with a test project holding one unit test that always passes (asserting 1 + 2 = 3).
  • Giard prefers the classic editor to YAML. He says it is more graphical and some things are easier there, but YAML is fine to use. The ASP.NET Core template supplies the steps: get the code from the master branch, restore the .NET environment and NuGet packages, build, run tests (it assumes test projects end in "Tests"), and publish. Save and queue runs it on an agent, a computer that executes the job.
  • A failed step stops the run; the useful error is higher up. The first run failed at restore with an unhelpful "packages failed to restore"; the line above said the agent's .NET SDK did not support targeting .NET Core 3.1. Giard retargeted both projects to 3.0, rebuilt and ran locally, synced, and the rerun passed in about a minute and a half.
  • A failing test halts the pipeline before publishing. After he added a test asserting 1 + 2 = 4, the code compiled but the test step failed (2 tests, 1 passed, 1 failed) and nothing was published. Giard says that is what you want: compiled code that fails its tests should not continue through CI/CD.
Build an Azure DevOps Release Pipeline [GCast 73]
  • A release pipeline deploys what the build pipeline produced. Giard says a build verifies the app compiles and passes tests but does nothing for end users; the release pipeline takes the published binaries and pushes them to an Azure app service.
  • The Azure App Service deployment template needs its task configured. Under Pipelines > Releases > New pipeline, stage 1 uses the "Azure App Service deployment" template; a red exclamation mark shows until the task has a subscription, the app type (web app on Windows) and the app name. The app service must already exist.
  • The artifact points the release at the build pipeline's output. Giard takes the defaults, the latest version, then renames the pipeline from "New release pipeline" to something descriptive and saves with a comment, treating pipeline changes like source control.
  • Releases are numbered and still started by hand. Release 1 queued on an agent and deployed the default welcome page. After he edited the page to say "Welcome to GCast", he ran the build (1 minute 38 seconds) and then created release 2, which deployed quickly and showed the change.
Integrating a Visual Studio Solution with an Azure DevOps Repo [GCast 71]
  • Team Explorer connects Visual Studio to the Azure DevOps organization. In Visual Studio 2019 the Manage Connections icon, a plug, connects to a project in the organization. Giard uses a new ASP.NET Core app but says the process is the same for any project type.
  • "Add Solution to Source Control" creates only a local Git repository. Lock icons appear on the files, but nothing is in Azure DevOps yet.
  • The first Sync asks whether to publish to GitHub or Azure DevOps. Choosing a Git repo in Azure DevOps, with the repository name defaulting to the solution name, and clicking Publish repository creates the project in the organization with the files under Repos.
  • Git changes are committed locally, then synced to the server. Giard commits with a comment on the Changes page, then syncs; he prefers syncing both ways, though one direction is an option. To simulate a teammate he edits a file in the browser, and syncing again (or a fetch or pull) brings that change down.
Deleting an Azure DevOps Project [GCast 70]
  • Project deletion is in Organization settings, not the project. Giard says it took him a long time to find because the project's own menus have no delete; from the Azure DevOps main page, Organization settings (bottom left) > Projects lists every project with a Delete button, and deletion must be confirmed by typing a name.
  • A deleted project can still be restored. Organization settings > Overview lists recently deleted projects, and selecting one and clicking Restore brings it back. Giard's verdict: not hard to do, but placed where it is not intuitive.
Azure DevOps Branch Policies [GCast 69]
  • Without branch policies anyone can push straight to master. Giard pulls, edits the readme, and commits and pushes directly to master, which succeeds.
  • A minimum-reviewers policy blocks direct pushes to master. Policies are set from Branches > the three dots on master > Branch policies, which Giard calls a little hard to find. He requires at least one reviewer and allows requesters to approve their own changes; his next push to master was rejected.
  • Changes then reach master only through a pull request. He creates a branch with git checkout -b, pushes to it, and opens a pull request; it cannot be completed until approved, and after he approves it himself it merges into master.
  • "Check for linked work items" blocks completion until one is linked. With that policy added, an approved pull request still showed "no work items linked" marked as required but not satisfied. Linking an existing work item, searchable by name or number, let it complete.
GCast 68: Azure DevOps Work Items
  • Work items are created from the Boards section, by type. In an Azure DevOps project, Giard expands Boards in the left menu, opens the Work Items subsection, and clicks New Work Item, which offers types including task, bug, feature and epic. A title is the only required field he names.
  • Some fields depend on the type; others appear on every item. Giard says the type-specific questions differ, while fields such as priority, estimated hours, remaining hours and completed hours are common to all work items. Priority numbers have no meaning in themselves; only their order relative to each other matters.
  • Linking items as parent and child builds a hierarchy that rolls up. He creates an epic ("a grand container" for other work items), a feature under it, and a task under the feature. Each link is made under Related Work with "Add link" to an existing item, setting the other item as parent. Giard says how you structure the hierarchy is up to you.
GCast 67: Azure DevOps Repos, Branches, and Pull Requests
  • Files can be added, edited and committed entirely in the browser. Under Repos (Files is selected by default) in the project from the previous episode, Giard adds a file with New File, edits it, and commits it to the master branch with a comment. He notes a commit can also be associated with a work item, which he leaves for a later video.
  • With several users, commit to a branch rather than to master. Giard's reason is that committing straight to master may break what others are doing or collide with their commits. He creates a branch from the branch selector, commits a change there, and shows that master's copy of the file stays unchanged until the merge.
  • A pull request is what merges the branch into master. He creates one under Repos, Pull Requests. The title defaults from the commit when there is a single change and can be overridden. Policies can be set to require review before merging. Because he has none set, he completes the merge himself, and master then shows his change.
  • The same flow applies from Git commands or an IDE. Giard says most developers will do this at the command line or in Visual Studio or Visual Studio Code rather than the browser, but the process is the same: edit, commit to a branch, open a pull request, and have someone approve the merge.
GCast 66: Creating a Repo in Azure DevOps
  • A new project takes a name and a visibility setting. Giard describes Azure DevOps as an application lifecycle management suite available to anyone with an Azure or Office 365 subscription, reached at dev.azure.com. A project gets a name, an optional description, and one of three visibilities: private (invited people only), public (anyone on the internet), or available only within your enterprise.
  • A project's repo can be initialized with a README and edited in place. Under Repos, Files, he clicks "Initialize" to add a README containing boilerplate text. Giard says the repo can be cloned to the desktop and used with Git tools, or edited in the browser, as he does here, replacing the placeholder text and committing directly to the master branch.
GCast 65: Artificial Intelligence, Whale Sharks, and WildBook
  • Wildbook uses AI to identify individual animals by their markings. Giard describes the Wild Me application as treating stripes and spots like fingerprints (a zebra's stripes, a leopard's or whale shark's spots) to track individual animals' locations, movements and populations. Sightings can be reported by hand at whaleshark.org, but because that does not scale, Wildbook crawls the web and social media for photos and videos of the animals.
  • The bot turns a found video into a dated, located sighting. In what Giard says is a demo of the process rather than the bot itself, a Spanish-language whale shark video is run through language detection and translation (Azure services), keyword analysis ("whale sharks", "world's biggest fish"), keyframe extraction, detection of the shark in each frame, and text extraction from the frames. The "when" came from the upload date, September 26, 2017, and a "last month" in the text; the video gave no "where".
  • When location is missing, the bot asks the uploader. It posted a reply asking where the shark was seen; the uploader answered that it was while diving east of Isla Contoy, Mexico, which completed the record so the shark's migration can be tracked. The bot then replied that the uploader was helping a research project and linked the shark's own page.
  • Spot patterns are matched against known sharks, with confidence scores. The demo matched the video to a shark catalogued as H019 at a score of 520, with 66% of the spot triangles matching; the next candidate, BZ010, matched only about 23%. A higher score means a higher-confidence match.
  • Adopters can name a shark; the project is backed by AI for Earth. H019's page shows where it was spotted and nearby sharks, and an adopter has named it "John Cena". Giard points to microsoft.com/AI for Earth for the sponsoring program and whaleshark.org to adopt a shark.
GCast 64: Noise Reduction with Audacity
  • No room is quiet enough, so camera-mic audio needs noise reduction. Giard needs it when his handheld microphones fail and he falls back to the camera's built-in mic, which picks up fluorescent-light hum, fans and voices through walls. He uses Audacity because it is free, open source and cross-platform (download from audacityteam.org); in his words, "I'm kind of cheap".
  • Noise reduction is two passes: profile the silence, then apply. He selects about four seconds where both speakers kept quiet, runs Effect > Noise Reduction > Get Noise Profile so Audacity knows what counts as background noise, then selects everything (Ctrl+A) and runs Noise Reduction again, clicking OK with the default settings. On his interview clip the voices stayed the same and the hum was greatly reduced.
  • Check the result, because reduction also changes the voices. Giard warns the effect alters the voices it brings forward slightly; the settings can be tweaked, and leaving in a little background noise may be preferable to keep the voices as they were.
  • Export to use the audio elsewhere; save the project to keep editing. Edits live only in the Audacity project until File > Export writes a file; he exported a WAV from an imported MP3 and skipped the artist and title prompts, which he says matter more for music. File > Save Project stores an AUP file with supporting folders and files so the work can be reopened later.
GCast 63: Sentiment Analysis JavaScript Demo
  • The demo page flags text above 0.5 as positive. A web page with a text area and an Analyze button sends the text to the sentiment endpoint of a service created in West US. If the returned score is above 0.5 it shows "That is a positive thing to say" and a thumbs-up image; at 0.5 or below it shows the negative message and a scowling image. "Life is Beautiful" scored 0.99 and "Life is Terrible" about 5%. "So-so" and "medium fair" phrasings still came out fairly positive.
  • Giard likes jQuery for its elegant web service calls. The script reads the textarea with jQuery and posts to the endpoint. It sends the subscription key in the header (hidden in the video), application/json as the content type, and a documents array. That array holds one document with the language given explicitly, an ID for later reference, and the user's text.
  • The response handler checks errors before reading the first score. It displays any errors and stops. Otherwise it confirms at least one document came back and reads only the first, since the code handles one document at a time. It formats the score to two decimal places with toFixed(2) and toggles the two hidden images' display. With no documents it shows "nothing to analyze", and there is general error handling besides.
  • The code is in Giard's GitHub cognitive services demos repo. It is the sentiment analysis demo in the Cognitive Services demos repository at github.com/davidgiard. The repository includes notes on setup, including getting your own key.
GCast 62: Sentiment Analysis Cognitive Service
  • Sentiment analysis scores text from negative to positive. The service is part of the Text Analytics API, under Language at microsoft.com/cognitive. It returns a score between 0 and 1 for each document: the closer to 1, the more positive. On the documentation page's demo, a sentence about a wonderful Seattle trip scored 0.8372 (about 84%), and one about rain spoiling the trip scored 0.0364 (about 4%). The page's demo also showed the language and key phrases.
  • One POST, with a region-specific endpoint and two headers. The URL is the endpoint for the region where the resource was created (East US here), plus /text/analytics/v2.0/sentiment. An optional showStats true/false query parameter returns extra statistics. The Ocp-Apim-Subscription-Key header takes either of the resource's two keys; Giard says there are two in case one gets compromised. Content-Type is application/json or text/json, and their schemas differ slightly.
  • The status codes tell you what failed. 200 is OK. A 400 means something like a wrong key, a wrong URL or malformed JSON. A 500 means something went wrong, and the response carries information about why. The documentation also has sample code in curl, C#, Java, Ruby and Python (2.7 and 3.2 shown), with the endpoint, key and body left to fill in.
  • The browser console scores several documents in one call. Giard ran the console's default body of three documents, in English, French and Spanish. It returned 200 OK in about 0.15 seconds with no errors, scoring them 98%, 84% and 33%, with character counts included. He guesses the Spanish one says something negative.
GCast 61: Text Recognition C# Demo
  • The demo reads its endpoint and key from app config. The code is the RecognizeText demo at github.com/davidgiard/cognitivesvcsdemos. Before running it you create a Cognitive Service in Azure. Then you add an app config file with an appSettings section holding two keys, the computer vision key and the vision endpoint, filled with your own service's values. You also need a picture of text on disk; Giard uses a Robert Frost poem.
  • The first call posts the image bytes asynchronously with HttpClient. The code posts to the endpoint plus /vision/v2.0/recognizeText?mode=printed, so it assumes printed rather than handwritten text. It adds the key header and turns the file into a byte array using boilerplate Giard says he found online. It sets application/octet-stream and calls PostAsync with await, so the form stays responsive but the code after it waits for the reply. After checking for a 2xx success (he thinks 202), it reads the Operation-Location header, takes the first value because headers can be arrays, and stores the URL in a private variable.
  • The second call is a plain GET, deserialized with Newtonsoft.Json. GetAsync sends the stored URL with the key header and no body, which Giard notes GETs do not support anyway. The response string is deserialized into a strongly typed results class. It holds a status string and a recognition result containing an array of lines, each with text, an eight-integer bounding box and an array of words, each with its own box and text.
  • Large images need polling; a small one returns almost instantly. The demo poem took a fraction of a second to process. Giard says large images take longer, and you may want a loop that pings the second URL periodically. The demo just shows the status and then loops through the lines, one per line.
GCast 60: Text Recognition Cognitive Service with Binary Images
  • Postman can send an image file, which the browser tool cannot. The in-browser test tool from the previous episode only accepts a URL to an image online. Giard uses Postman, which lets you set the URL, HTTP verb, headers and body of a request, to send a picture of text from disk to the Recognize Text API without writing code.
  • The first call is a POST of the raw image bytes. The URL is the service's endpoint, copied from the portal, followed by the documented Recognize Text path with mode=printed in the query string (the documentation gives handwritten or printed as the options). Content-Type is application/octet-stream for binary data, rather than application/json. The Ocp-Apim-Subscription-Key header carries either one of the service's keys, and Giard stresses that the header name must have no spaces. In Postman the body is set to binary and a file chosen from disk.
  • The POST returns no text, only a job URL. The body comes back empty. The Operation-Location response header holds a URL of the form endpoint plus /vision/v2.0/textOperations/ plus a GUID. It matches the documented Get Recognize Text Operation Result call, and it identifies the job just started.
  • A GET to that URL returns the job's status and the text. It needs only the same subscription-key header. Giard notes the status may still show the job in process, or an error, rather than succeeded. On success the result is an array of lines, each with its text and an array of words.
  • Every line and word comes with a bounding box. A bounding box is an array of eight integers: the x, y coordinates of the four corners of the rectangle around that line or word. Giard says this lets you locate a word in the original image to highlight it, blank it out or replace it.
GCast 59: Cognitive Services Text Recognition service
  • The newer Text Recognition service beats the older OCR service. Giard says the Recognize Text operation in the Cognitive Services Computer Vision API is slightly more complicated to use than Optical Character Recognition, but its results tend to be better. It turns a picture of text into the text itself.
  • Recognizing text takes two calls: one starts a job, one collects it. A POST to the endpoint plus /vision/v2.0/recognizeText?mode= sends the image, either as a URL pointing to it online or as the binary image. mode is either printed or handwritten. The call returns a 202 and a URL carrying an operation ID. A GET to that textOperations/<operation ID> URL returns the job's status (complete, in process, or error) and, once it has succeeded, the text. Giard says the split is what lets the service handle large text-recognition jobs.
  • A Cognitive Services resource gives you an endpoint and key. He creates the resource in the Azure portal, East US and the cheapest pricing tier, which takes about 30 seconds. The Overview page gives the base endpoint for all Cognitive Services. The key has to go in the Ocp-Apim-Subscription-Key header of every call, and he says to keep it safe. The Content-Type header says whether the image goes as binary data or as JSON pointing to a URL.
  • The results give lines and words, each with a bounding box. Testing both calls in the documentation's in-browser console on a poem he found online, he got a 202 from the POST and a 200 with status "succeeded" from the GET. The response holds an array of lines, each with its text, a bounding box of eight numbers (the XY coordinates of four corners) and an array of words carrying their own boxes. Giard says the boxes let you highlight, block out or replace a line or a single word in the image.
GCast 58: Creating and Deploying Azure Resources with ARM Templates
  • The portal will write the ARM template for you. Giard says the easiest route is to walk through Create a Resource as usual, here a virtual machine (East US, no redundancy, Ubuntu Server, defaults otherwise), then click "Download a template for automation" at Review and Create instead of Create. This produces the ARM template, a parameters file and sample scripts for PowerShell, the Azure CLI and others.
  • The template describes the resources, and the parameters file supplies values. The template JSON says what to create, its properties and location, and the dependencies that make resources get created in the right order. Values such as location are passed in as parameters. The generated parameters file left the password blank, so Giard fills it in before deploying.
  • The PowerShell script needs only a subscription and a resource group. Giard runs deploy.ps1 with the subscription name and an existing empty resource group. If no template or parameters file is named, the script falls back to default file names, and either can be overridden on the command line. It prompts for sign-in. He deliberately leaves the login out of the script for security reasons.
  • One run deployed the VM and all its supporting resources. After about two minutes the resource group held the virtual machine plus its virtual network, storage account and disks. Giard says the script can become part of continuous integration and deployment, or be handed to others to run with their own parameter files.
GCast 57: Azure Data Factory GitHub Deployment
  • Data Factory assets are code, so they belong in source control. Giard says that although Azure Data Factory work is done mostly in the browser through a graphical interface, the pipelines, data sets, data flows and linked services are stored as JSON configuration. That lets them go into a repository for continuous integration and deployment, syncing with other developers, and rollback.
  • The Setup Code Repository button offers Azure DevOps or GitHub. At recording those were the only two options; Giard picks GitHub and enters his account name, which loads a drop-down of his repositories. The target repository cannot be empty, so he creates a new public one with a README file before linking it. The Data Factory itself was set up in advance, in episode 18.
  • Linking asks for a branch, a folder and whether to import. He uses the only branch, master, as the collaboration branch and the root folder. A subfolder lets several Data Factory services share one repository, each in its own folder. He chooses to import the existing resources so that a pipeline he had already built is pushed in, and leaves the other settings at their defaults.
  • Once linked, later changes are pushed to GitHub automatically. After he renames something in a pipeline and saves and publishes it, the repository shows that pipeline updated 25 seconds earlier, with the change visible in its history.
  • Use separate branches to avoid colliding with production or colleagues. Giard suggests committing to your own branch rather than straight to master, then merging later by hand or by some automatic process. This keeps a history of every change and lets you roll back.
GCast 56: Azure Web App Deployment Slots
  • Deploying straight from GitHub to production is risky. Building on the previous episode's GitHub-to-web-app integration, Giard warns that a mistake pushed to the repository reaches users at once. A deployment slot is a staging or test area that testers can see and the public cannot, running on essentially the same hardware, platform and infrastructure as production. In his view that makes it a much more accurate test.
  • A slot is added from the web app and gets its own URL. By default the app has only the production slot. He adds one called "staging", and notes you can name it "testing" or create several. The slot has its own overview page and a URL like production's with "-staging" appended to the app name.
  • The slot is wired to GitHub through its own deployment center. He repeats the previous episode's steps against the staging slot, choosing his GitHub account, the demo repository and the master branch. The deployment log shows the build steps and the commit message, and ends in success. The staging URL then serves the repository's basic ASP.NET app while production still shows the default page.
  • Swap redirects traffic rather than copying files, so it is fast. Giard explains that swap only changes which slot a gateway in front of the app points at as production, with nothing copied back and forth. After the swap the tested code is in production, and the old production code sits in staging as a backup. You can swap back if the release goes wrong.
GCast 55: Web App GitHub Deployment
  • The web app's Deployment Center links it to source control. The sources offered were Azure Repos, GitHub, Bitbucket and a local Git repository. Giard picks GitHub for a near-template ASP.NET app.
  • Choose between simple push-to-deploy and a fuller build pipeline. The simple option deploys every push straight to the web app, and that is the one Giard uses. He says Azure Pipelines is for when you need more control, such as running unit tests first. He then gives the GitHub account (organization), the repository and the branch to deploy, here master, and clicks Finish.
  • Connecting the two triggered the first deployment on its own. About a minute later, with nothing else done, the Deployment Center detected that GitHub held code the web app lacked. It went from pending to running to success, auto-refreshing as it went, and the site then served the repository's code instead of the default Azure page.
  • Every later push redeploys without touching Azure. Giard edits a heading, pushes with git push origin master, and a new deployment starts automatically. Refreshing the live site shows the changed text.
GCast 54: Azure Storage Replication
  • Locally redundant storage keeps three synchronous copies in one data center. Giard says every write to an Azure storage account produces three copies, written atomically so no success response returns until all three are on disk; only one is live, and another is promoted transparently if it fails. The copies sit in different fault zones (no shared hardware such as a power supply) and different update domains (so upgrade reboots never hit two at once). He calls it the cheapest option.
  • Geo-redundant storage adds three asynchronous copies in another region. The three atomic in-region copies stay, and three more are written asynchronously to a completely different region, so a disaster that destroys the primary data center still leaves a copy that can be promoted to live.
  • Read-access geo-redundant storage makes one secondary-region copy readable. Otherwise identical to geo-redundant, it lets users read from a copy in the secondary region as well as the primary; Giard says serving reads from two copies can make reads twice as fast.
  • Zone-redundant storage spreads the three in-region copies across data centers. Still three copies in one region, but because most regions have several data centers, a copy may go to a second one that Giard says can be hundreds of miles from the first, guarding against a natural disaster wiping out one site.
  • Replication can be changed after creation, despite looking read-only. It is set from a replication drop-down when creating the storage account; on an existing account the Overview tab shows the setting as non-editable, but the Configuration blade lets you change it.
GCast 53: Creating a Data Warehouse in Azure
  • Creating an Azure data warehouse means creating a SQL server too. Giard says the process resembles creating a SQL Azure database: in portal.azure.com, Create a resource, then Databases, then SQL data warehouse (or search for it), choosing a resource group, a warehouse name and a new SQL server with an admin login, at <name>.database.windows.net. Deployment takes a few minutes because it builds the server first and the warehouse on top of it; he accepts the defaults for the remaining settings.
  • Place the warehouse near the people and applications that consume it. Giard's reason for choosing a location is to keep latency from becoming excessive.
  • Nothing can connect until a firewall rule admits its IP address. A rule is an IP range that must include the client's address; "Add client IP" creates one covering only your current address, named with today's date. Nothing takes effect until you click Save, and other applications need their own holes opened the same way. Once connected, he says, tools such as SQL Server Management Studio or the Visual Studio data tools can be used to load data and build star schemas.
GCast 52: Using hilite.me to Format Code as HTML
  • Code pasted into Open Live Writer loses its colors and line breaks. Giard edits and publishes his blog in Open Live Writer (formerly Live Writer), which used to have a plugin for inserting formatted code; pasting C# directly now looks "awful", and even fixing line breaks in the source view fails because HTML ignores carriage returns.
  • hilite.me turns pasted code into color-coded HTML and CSS. You paste the code, pick its language, choose a style and click Highlight, and it returns a preview plus HTML to paste into the post's source. Giard always uses the "colorful" style, leaves other options at default, and omits line numbers because they make it harder for readers to copy the code.
  • Inline styles mean the blog needs no added style sheet. The generated HTML carries its styles inline, which Giard says suits his blog because adding a style sheet is hard when each page is its own thing.
  • The same approach works across many languages. He demonstrates C#, JavaScript and JSON, noting the tool recognizes each language's keywords, and says it can format HTML as well; he calls it a tool he uses quite a bit for prettier code without much effort.
GCast 51: Creating an Azure Container Instance
  • The Azure CLI creates a container instance straight from a registry image. Giard describes a container as applications, resources and data packaged and deployed together, closed to the outside world by default until holes are punched in it. Working in Cloud Shell, which is already signed in through the portal, he runs az container create into an existing, empty resource group, with --resource-group, --name, --image, --dns-label and --ports. The image is a simple web-server sample from Microsoft's documentation team at mcr.microsoft.com/azuredocs/aci-helloworld. The command took about 60 seconds and returned JSON describing what it created.
  • az container show gives the public address of the container's web app. Giard queries only the fully qualified domain name and uses --out table instead of JSON. That returned an address ending centralus.azurecontainer.io with a provisioning state of succeeded, and browsing to it showed the sample's static page with a picture.
  • az container logs shows the container's request log for troubleshooting. Each GET request to the web app appeared as a log entry, and refreshing the page a few times added more entries.
GCast 50: Angular pt 9: Incremental Search
  • Search is added as a new hero-search component on the dashboard. This is the final video of Giard's walk-through of the Tour of Heroes tutorial from angular.io (part two of lesson six). Generating the component creates an HTML template, a test file, a TypeScript file and a CSS file, all boilerplate; its selector, app-hero-search, goes into the dashboard HTML. The template loops over heroes$ and links to each hero ID; Giard explains the $ is a naming convention marking an observable rather than an array.
  • An RxJS Subject throttles keystrokes before the hero service is queried. The component's search method pushes the typed string into a searchTerms Subject, with the hero service injected through the constructor. On init it waits 300 milliseconds, checks whether the term has changed, and uses switchMap to call the service's searchHeroes.
  • searchHeroes runs a partial-match name query against the heroes API. It trims the term and returns an empty array if nothing is left; otherwise it does an HTTP GET on api/heroes filtered by name, where the single equals sign gives a partial match. Like the service's other methods it taps into logging and adds error handling. Giard notes the tutorial lists this code above the step that calls for it, and the component's CSS only at the bottom of the page.
  • The finished search narrows results with each letter typed. In the demo, adding letters cut the list down to one hero, clicking it opened that hero's detail page, and after renaming the hero the old search returned nothing.
GCast 49: Angular pt 8: HTTP
  • An in-memory web API stands in for a real REST back end. In the tutorial's last lesson, HttpClientModule is imported in app.module.ts, the in-memory web API is installed with npm, and a CLI-generated in-memory data service holds the hero array; its id generator takes the maximum existing id plus one, or 11 if the list is empty. Giard notes a real app would put a database behind it.
  • The hero service now fetches through HTTP with piped error handling. HeroService injects HttpClient and calls http.get<Hero[]> on api/heroes, or api/heroes/ plus an id for one hero, with the web API doing the filtering. RxJS tap and catchError are piped in for logging and a generic handleError that logs to the console and the on-screen messages; Giard likens this to aspect-oriented programming.
  • Fetching per view loses edits, so changes are saved with PUT. Once every view re-queries the web API, a name edited in the detail view no longer shows elsewhere. A save button calls updateHero, an http.put to api/heroes with a content-type application/json header, after which changes persist — until the app recycles, which any saved code change triggers, resetting the in-memory store.
  • Adding a hero POSTs it and pushes it onto the local list. A name box and add button call add, which trims the name, stops if empty, and calls addHero (an http.post). Giard says pushing the result onto the local array saves a round trip, at the cost of not seeing other users' changes until a full refresh.
  • Deleting removes the hero locally and on the server. An "X" button on each list item filters the hero out of the local array and calls deleteHero, which accepts a hero or just its id and sends http.delete to api/heroes/ plus the id. Giard defers the lesson's remaining search feature to a next video because this one ran long.
GCast 48: Angular pt 7: Routing
  • Angular routing picks a view from the URL, on the client. Giard compares it to routing in ASP.NET MVC or Ruby on Rails, with the difference that those frameworks route on the server. The episode, which continues his walk through the angular.io tutorial, splits the single page into three routed views: a dashboard of top heroes, the full hero list, and a hero detail.
  • A CLI-generated routing module holds the routes and initializes the router. The CLI command generates an app routing module and adds its import to app.module.ts. Giard drops the component declarations from the module, exports it, and adds a route made of a path (heroes) and the component to load there. He then initializes the router in the module's imports with those routes. A <router-outlet> tag in the app component replaces the hard-coded heroes tag and marks where views load, so /heroes now shows the heroes component.
  • A dashboard component becomes the default route. It is generated with ng generate component dashboard and uses the hero service's getHeroes, with heroes.slice(1,5), which Giard says returns the first five heroes. An empty path redirects to dashboard with a full path match, so the bare localhost address opens the dashboard. Router links to both the dashboard and the heroes list go on the main page.
  • The detail route takes the hero ID as a parameter. The path is detail/:id, where the colon marks id as a value rather than a literal word. Both the dashboard and the heroes list now use anchor tags with a router link to the detail path plus the hero's ID, which replaces the earlier click-bound onSelect with what Giard calls something more declarative. The detail component injects ActivatedRoute, the hero service and Location, reads the ID from the route, and subscribes to a new getHero(id) service method, which logs a "fetched hero id" message and finds the hero in the still hard-coded list. Edits made in the detail view carry across views because of two-way data binding.
  • A back button returns to whichever view the user came from. A goBack method in the detail component uses the browser's location, which Giard likens to hitting the browser's back button. In the demo it returns to the dashboard or the hero list, depending on where the detail was opened from.
GCast 47: Angular pt 6: Services
  • Moving data access into a service prepares it for swapping. ng generate service hero creates a HeroService whose getHeroes still returns the hard-coded mock array; Giard says a real one would call a web service backed by a database. Its @Injectable decorator supports dependency injection, which he says makes it easy to swap the mock for a web call and to supply different dependencies under test.
  • The component receives the service through its constructor. The heroes component declares heroes without initializing it, injects heroService in the constructor, and calls its own getHeroes from ngOnInit, which in turn calls the service; the app looks unchanged.
  • Returning an observable makes the call asynchronous. Giard argues web calls should be asynchronous because of latency, so getHeroes returns an RxJS Observable<Hero[]> and the component subscribes, setting this.heroes when data arrives; a delayed response then would not lock the UI.
  • A message service gives the user visible feedback. A new messages component (added to the app component) and a MessageService holding an array of strings with add and clear methods are generated; injecting it into HeroService — one service using another — logs "fetched heroes". The messages template loops the array with *ngFor, offers a clear button, and sits inside *ngIf on the array's length, since zero is falsy.
GCast 46: Angular pt 5: Master Detail Components
  • The selected-hero markup moves into its own detail component. Giard generates it with ng generate component hero-detail, which creates the folder with CSS, HTML and TS files. The selected-hero section of the heroes component's HTML template moves into the new component's template, for what he calls better separation of concerns. The heroes template now holds an <app-hero-detail> tag matching the new component's selector, with its hero set to the selected hero.
  • An @Input decorator lets a parent pass data into a component. The detail component imports Input and the hero class, then declares a hero property decorated with @Input, which marks it as settable from an outside component. Giard describes it as essentially a parameter. After the refactor the app behaves as before: highlighting, selection and data binding still work, which is the expected result.
GCast 45: Angular pt 4: Displaying a List
  • A repeat directive renders a list whose length is unknown. A hard-coded, exported array of 10 heroes in mock-heroes.ts is imported into the heroes component as a heroes property; *ngFor="let hero of heroes" on an li outputs one item per element. Giard explains the star as repeating the element itself, not just its contents, and frames it as needed because data from a database could hold ten or a hundred items.
  • Parentheses around an event name bind it to a component method. (click)="onSelect(hero)" calls a new onSelect that sets a selectedHero property, replacing the hard-coded hero in the detail section; the bindings must be renamed to selectedHero.name and selectedHero.id.
  • An uninitialized selection breaks the page until hidden with a conditional. The browser console (F12) showed "unable to get property name of undefined or null" because selectedHero starts empty. Giard notes one fix is to preselect a hero; the tutorial's is wrapping the section in *ngIf, which keeps a falsy value (false, zero, null or undefined) out of the DOM entirely rather than just hiding it.
  • A conditional class binding highlights the selected item. [class.selected]="hero === selectedHero" applies the stylesheet's selected class to whichever item was clicked; the styles, pasted into the empty generated heroes.component.css, also give a hover effect.
GCast 44: Angular pt 3: The Hero Editor
  • The CLI generates a component as four files. ng generate component heroes creates .html, .spec.ts, .ts and .css files in a heroes subfolder of src/app; the class is exported so other components can use it, and its @Component decorator maps the app-heroes selector to the template and stylesheets. It also implements OnInit, which Giard says will matter later for dependency injection.
  • Placing a component's tag nests it inside another. Adding <app-heroes> to app.component.html renders the heroes template under the title, showing a hard-coded hero name through one-way {{hero}} binding.
  • Binding a whole object prints "[object Object]", not its fields. Once hero becomes an instance of a new Hero class (an id number and a name string, in hero.ts, imported into the component), the template must bind hero.id and hero.name. A pipe such as | uppercase transforms a bound value while keeping it bound.
  • Two-way binding silently does nothing until the forms module is imported. [(ngModel)]="hero.name" — the syntax "people call a banana in a box" — on a text box updates the property and everything bound to it as the user types. On first save the page showed no output until FormsModule from @angular/forms was imported and added to the imports in app.module.ts, which Giard calls a kind of global settings file.
GCast 43: Angular pt 2: The Application Shell
  • The tutorial app starts as a fresh CLI project. Giard begins the angular.io Tour of Heroes tutorial with ng new angular-tour-of-heroes, accepting defaults, opens the folder in Visual Studio Code with code ., and serves it with ng serve --open on port 4200.
  • The shell is the generated app stripped to a title. He sets the component's title to "Tour of Heroes", replaces the template's boilerplate links, image and "Welcome to" text with just the bound title in an h1, and adds styles; he also removes router-outlet, saying it returns in a later video.
  • Each lesson ends with the final code to compare against. Giard points to the tutorial's code listing for the changed files as the fallback when an app does not look right, and notes it mislabels the stylesheet as styles.css where, he says, the file is app.component.css.
GCast 42: Angular pt 1: Getting Started
  • Angular needs Node.js, an editor and the Angular CLI. The angular.io quick start asks for Node.js version 8.x or 10.x. Giard notes that 10.15.3 is current at nodejs.org as of April 2019, and that npm comes with Node rather than as a separate install. He prefers Visual Studio Code, which is free for Windows, Mac and Linux. The CLI is installed globally with npm install -g @angular/cli. This is the first episode of a series that walks through the angular.io tutorial.
  • ng new scaffolds a working app with default options. Giard runs ng new my-app, the name the tutorial uses, and presses Enter to accept every default. The command pulls down the required Angular components and some optional ones, and creates a my-app folder with src/app inside.
  • An Angular tag in index.html is resolved by a TypeScript component. The <app-root> tag in index.html matches the selector in app.component.ts, and that component loads app.component.html as its template. Giard explains that TypeScript is strongly typed and transpiles to JavaScript, which the browser needs, but that it is easier to work with because of the typing.
  • Double curly braces give one-way data binding. The template's {{title}} displays the component's title property, which is set to "my app". ng serve --open builds the app, listens on localhost 4200 and opens a browser. When Giard changes the title to "My first Angular app" and saves, the page reloads by itself with the new heading.
  • Component styles come from an array of style sheets. The component's styleUrls is an array that holds one file, app.component.css. The CLI creates that file empty. Giard pastes in a rule that changes the H1 font, and the heading picks it up. He says the next video starts building the tutorial's app for maintaining a list of superheroes.
GCast 41: Using the PowerPoint Selection Pane
  • The selection pane lists every object on a slide. It sits on the Home ribbon under Select, and selecting an object on the slide highlights its entry in the pane. Giard says it took him years of using PowerPoint to find it because it is "really hidden".
  • The eye icon hides an object without removing it. Clicking the eye next to an entry makes that object disappear from view while it stays on the slide; clicking again brings it back. Giard's use is overlapping objects: hide the ones in front, edit the one behind, then unhide them.
  • Renaming objects replaces PowerPoint's unhelpful default names. Two triangles he draws come out as "Isosceles Triangle 9" and "Isosceles Triangle 4"; double-clicking the entry or pressing F2 lets him give each a name that means something to him while designing.
  • Names plus hiding make crowded animated slides editable. His example is a slide with dozens of objects and animations showing messages flowing from devices into an IoT hub. A naming convention (message 1A comes from device 1, then 1B, and so on) tells him which object is which, so he hides 5A and 5B to work on 5C's animation path alone, reroutes it to the top of the IoT hub, then unhides the rest.
GCast 40: IoT Hubs
  • Azure IoT Hub is the two-way gateway between devices and the cloud. Giard describes it as built to collect messages from sensor devices (temperature, location, soil moisture and the like) and to send messages back to them; it is scalable because the traffic is typically many small messages. Once data is in the hub it can be routed onward to databases, code, queues, analytics or a Power BI portal.
  • Only registered devices can talk to the hub; others are rejected. Adding a device, which Giard thinks of as registering it, tells the hub in advance which devices to expect, and a rogue device sending a message is refused. Each device also gets a secure key, and the device authenticates with its name and that key.
  • A hub is created in the portal like most Azure resources. From portal.azure.com, "Create a resource", under Internet of Things, he picks a subscription, a new resource group, a region and a unique hub name. The optional Size and Scale tab offers a free tier and a standard tier; he takes one instance of standard, which he gives as $25 a month, and notes the hub can be scaled up or out to multiple instances. Deployment took about three minutes in the 2019 recording.
  • Devices are added under IoT devices, with keys supplied or generated. The IoT devices blade sits under Explorers; Add asks for a device ID (he uses sequential numbers) and an authentication type, since the hub supports only secure authentication. With a symmetric key you can clear a checkbox and supply your own keys, or let the hub generate them and then send them down to the device; he leaves the other defaults. With many devices, the list can be filtered by a clause such as a device ID match.
GCast 39: Azure Search REST API
  • Azure Search is queried through a REST API. Giard says the .NET SDK is just a wrapper for that API, and so is the portal's Search Explorer, which sends a GET to https://<service name>.search.windows.net, then the index name and /docs, with the API version and search= plus a term in the query string. He calls this GET route not very flexible. His demo index holds 497 documents, one per row of an Azure table listing his past presentations (built in the previous episode).
  • Give users the query key, not the admin keys. Giard says the primary and secondary admin keys are for using the REST API to make changes, such as creating a service or importing data, which he did through the portal instead. Query keys are what you hand to users: less powerful, so less of a security risk.
  • A POST request is more flexible but needs a tool or code. The portal cannot send it, so Giard uses Postman (free, from getpostman.com); Fiddler or your own code also work. The URL is /indexes/<index name>/docs/search plus the api-version query string, with two headers: api-key set to the query key, and a Content-Type of application/json.
  • The JSON body selects, filters and sorts the results. In his demo, select * returns every field; a filter on state equal to 'IL' (single-quoted because it is a string) returns only Illinois rows; and ordering by presentation date descending sorts them newest first. Giard says almost any language or platform can make the same HTTP request.
GCast 38: Azure Search
  • Azure Search indexes your own private content as a search engine. Giard compares it to how Google and Bing index the public internet: it exposes searchable content from behind your firewall. For the demo he first uses Azure Storage Explorer to import a CSV of about 500 of his past presentations into an Azure Table Storage table.
  • The free pricing tier is for learning, not production. The service is created from the portal's green plus button by searching for "Azure search", then setting a name, resource group (he keeps it in the same group as the storage, though that is not required), location (West US) and pricing tier. Giard says the free tier cannot be scaled, while paid tiers scale further the more you pay, and you get only one free Azure Search per Azure account. He would not use the free tier in production.
  • Importing data can create the index for you. Rather than using Add index and entering fields by hand, Giard uses Import data. The sources offered are Azure SQL, SQL Server on a VM, Cosmos DB, blob storage and table storage. The wizard infers the fields from the table's columns, and each field can be marked retrievable (returned in results), filterable or sortable, one at a time or all at once from the top checkbox.
  • The index stays empty until its indexer runs. The indexer does the actual indexing. It can be scheduled hourly, daily or on a custom schedule ("every 17 minutes starting on a certain time"); Giard runs it once instead, so he doesn't have to wait for the first scheduled run. The index shows zero documents while the indexer runs, then 497, one per row of data.
GCast 37: Managing Blobs with the Azure Storage Explorer
  • Storage Explorer opens straight into the account from the portal. The free, lightweight Microsoft tool runs on Windows, Mac and Linux, and the storage account's overview tab carries a download link. With it installed, the open button launches the app if needed and goes directly to that storage account.
  • Blob "folders" are really just part of the blob name. Giard creates and renames containers with a right-click, then uploads a folder of pictures into a folder inside one container. He says containers hold no real folders: the slashes in the name only make it look like one, though it is still a useful way to organize. The upload asks for block or page blob; single files can be uploaded too, and the uploads show up in the portal as well.
  • Deleting can be hard or soft, and soft can be undone. Blobs open in the application associated with their file type, and can be renamed and multi-selected with Control or Shift, as in Windows Explorer. With soft delete turned on, as in his setup, a deleted blob can be undeleted within the next seven days. Selected blobs can also be downloaded to a local folder.
  • A blob URL returns 404 until you allow public access. Giard copies a blob's URL to use, for example, as an image source on a web page. It fails for security reasons until the container's public access level is set to public read for containers and blobs, or for blobs only; either allows anonymous access. He suggests this for images, videos and other assets served to a public web application.
GCast 36: Managing Tables with the Azure Storage Explorer
  • Storage Explorer handles table data the portal makes awkward. Giard says some of what the portal does is powerful, but manipulating data in a table from the browser is hard. Storage Explorer is a free Microsoft client for Windows, Mac and Linux, launched from "Open in Explorer" on the storage account's overview page. A new account shows only the tables Azure creates internally to manage itself.
  • Table rows need keys, not a shared schema. After creating a table, Giard adds a row by hand with a partition key, a row key and extra typed properties (a string and a double). Not every row has to have the same schema, but every row must have a partition key and a unique row key.
  • CSV import reads the headers and guesses each column's type. It previews the first row and lets you change things before Insert. His file of 497 past presentations plus the one hand-added row gave 498 cached items. Rows can then be edited in place.
  • Column Options reorders the display, but not by drag-and-drop. Columns can be removed or moved up and down; Giard says drag-and-drop would be nice. The table can also be exported back to a comma-separated file, and statistics generated for it.
  • Select all and delete empties a table. Rows can be multi-selected with Control or Shift, and Select All covers either the current page or every item in the table. An activity log records each operation and can be cleared.
GCast 35: Customizing Microsoft Teams
  • Teams comes with Office 365 licenses. Giard says an organization with Office 365 licenses probably has Teams deployed too, and if not, to ask the IT department "why the heck not". The Teams item on the left-hand menu lists the teams you have favorited.
  • Private teams need an owner's approval to join. "Join or create a team" offers templates, building from scratch, or creating from an Office 365 group. Anyone can add themselves to a public team. For a private team, a request emails an owner, who must approve it. Only a name is required; the description is optional. Members can be found in the Active Directory, or added by email address if they are outside it.
  • Every channel starts with Conversations, Files and Wiki tabs. A team gets one channel by default (General). More channels are added from the ellipsis next to the team, with an option to automatically favorite the channel for every member. Conversations are threaded and messages can be liked or saved. Files can hold new folders or new Word, Excel or PowerPoint files, which open online and save automatically, or uploads of any file type. The wiki holds shared notes with some formatting.
  • Extra tabs can pin a document or embed a website. The plus button offers tab templates for Office documents and third-party tools; Giard gives an important Word file its own tab so people need not hunt for it among the files. A website tab works only if Teams can reach the site and it uses HTTPS. Posting about the new tab to the channel is checked by default, and the embedded site's client-side JavaScript runs.
  • Giard sees website tabs' real value in custom team apps. His suggestion is to build a web application specific to the team, for capturing data, sharing information or showing reports, deploy it (to Azure, say) and point a tab at it.
GCast 34: Exposing Local Web Apps with ngrok
  • ngrok exposes a locally running web app to the public internet. Giard's case is an app that other applications depend on and that must be reachable publicly to be tested; ngrok lets him test his local copy instead of redeploying after every change.
  • There is nothing to install beyond a single executable. The Windows download from ngrok.com contains only the ngrok executable; Giard says to copy it anywhere you can reach it, such as your path or a tools folder, and he puts it in the app's own folder.
  • One command forwards a public URL to a local port. With a simple Node/Express app running on localhost port 3000, ngrok http 3000 returned an HTTP and an HTTPS address under ngrok.io that both redirect to it. The address changes every time the command runs, and an edit saved locally showed up on the public URL on refresh.
  • It is free but temporary, meant for development rather than hosting. Giard says the session expires after about eight hours, so it is not a permanent way to turn a local server into a public one; he describes it as designed for testing and developing without deploying every small change.
GCast 33: An Introduction to Power BI
  • Power BI is Microsoft's point-and-click tool for visualizing data. It is at powerbi.com, with a "Start Free" sign-up, and comes as a rich desktop client, a browser version, and a report server an enterprise can host itself. Giard notes that people whose organization has an Office 365 subscription may already have an account.
  • Data can come from shared content, third-party services, databases or files. The Get Data options are content your organization publishes, third-party services (three pages of them), a database, or a file in OneDrive, SharePoint or on the local machine. Giard calls a file the simplest and loads a forest-fires CSV he says came from the University of California at Irvine, to which he had added a month-number column using an Excel formula that converts the three-letter month abbreviation.
  • Values default to a sum, which usually needs changing to an average. Charting temperature by month first gave the sum; switched to the average, it showed temperatures in the 20s (Giard guesses Celsius) in months 8 and 9 against about 5 in December and January. A second visual of average wind speed showed December much windier than the rest of the year.
  • Dragging a field onto a chart again adds another series. Giard dragged temperature onto the chart twice more and changed the new series from sum to minimum and maximum, giving average, minimum and maximum lines. He reads the result as very little variance in November and a lot between minimum and maximum in summer.
  • Chart type is a single click, and Giard prefers line graphs. He switches the same data among line, bar and other types; he says he is not a fan of bar or donut graphs and likes line graphs for their high data-to-ink ratio.
GCast 32: Handwriting OCR with Cognitive Services
  • Handwriting uses the Computer Vision RecognizeText call with mode set to handwritten. It is an HTTP POST to a URL of the form https://westus.api.cognitive.microsoft.com/vision/v2.0/recognizeText?mode=Handwritten, where the region part changes with where the service is deployed; Giard suggests deploying close to your users. He says handwriting is less consistent than printed fonts, so Azure takes longer to process it.
  • Two headers set the key and how the image is sent. The Ocp-Apim-Subscription-Key header carries the key; a Content-Type of application/json means the body is {"url": ...} pointing to an image Cognitive Services can reach on the public web, while application/octet-stream means the body is the image itself, as for a photo taken in a phone app. The key and endpoint come from an Azure Computer Vision resource, created under AI + Machine Learning with a region and a free or paid tier.
  • The call is asynchronous: a 202, then a second request for the text. The POST returns 202 with no text, only an Operation-Location response header holding a text-operations URL ending in a GUID. A GET to that URL with the subscription key returns, once finished, a status of succeeded and an array of lines, each with a bounding box and an array of words that each have their own bounding box.
  • The demo read real handwriting well but not perfectly. On a handwritten love letter, tested through the region's test console and then Postman, "Sweetheart" lost its T, which Giard puts down to an odd-looking letter, while the following lines read correctly. On a 1990 letter from George H.W. Bush the salutation came out garbled.
  • A JavaScript app uses the word bounding boxes to highlight a search term. Giard's page makes the POST, finds the operation-location header by lowercasing the response headers, then GETs the result and loops through lines and words to build the text. When a word matches the one the user typed ("it", "you"), it keeps that word's bounding box and positions a rectangle image over those coordinates.
GCast 31: OCR with Cognitive Services
  • OCR returns text as nested lines and words, each boxed. The Computer Vision OCR service returns JSON holding an array of lines. Each line has its text and a bounding box (the XY coordinates of its four corners), plus an array of words, each with its own bounding box, which code can use to highlight or block out text. Giard says the better the image quality, the better the recognition.
  • Handwriting has its own endpoint but the same output format. The demo's handwritten quote was read accurately, Giard says. The response uses the same structure: lines with bounding boxes and text, then each word with its box.
  • OCR is a single POST with language and orientation options. The endpoint is https://<region>.api.cognitive.microsoft.com/vision/v2.0/ocr; the region (West US in the demo) depends on where the resource is created. The query string takes language ("unknown" auto-detects) and detectOrientation (true straightens tilted text; Giard sets it false for horizontal text because it will be "a little bit faster"). The headers are the same as for image analysis: application/octet-stream or multipart/form-data with the image in the body, or application/json with a url pointing to the image, plus a subscription key from an Azure Computer Vision resource. The free tier allows 20 calls per minute, which Giard says is probably not good enough for something like an iPhone app. In the online test console a love-letter image returned a 200; the first line came back garbled, but the rest was read well.
  • His jQuery sample posts the image URL and loops lines and words. The page sends the key in the header and url plus the image link in a JSON body. On the asynchronous callback it loops through every line and every word, appending each line as its own div with the words separated by spaces. Giard notes that the key and URL must match the resource created in Azure, and says the code is in his GitHub repository.
  • Accuracy tracks print quality, and non-Latin scripts work. On a Robert Frost poem, a word with heavy kerning was misread ("imperfect, but it's pretty good"); a Longfellow poem in larger type came out better. A Shakespeare sonnet on a dark background with text bleeding through and a large initial letter gave some trouble, but old-style S's that look like F's were read correctly. Spanish and French poems worked, and a Japanese one was returned in its own character set, which Giard says is surprisingly accurate.
GCast 30: Creating Applications with the Analyze Image Cognitive Services API
  • A C# app calls Analyze Image through HttpClient using Microsoft's sample code. Giard's WPF app builds a URL asking for categories, description, adult, color, faces and other visual features with language set to English, leaving out the celebrity and landmark details. He stresses that the region in the URL (here West US) must match the resource the key comes from, calling the mismatch a common mistake.
  • The JSON response is cast into a strongly typed C# model. The body {"url": ...} is turned into a byte array and sent with PostAsync under await, which keeps the rest of the app responsive. On a success code, Giard deserializes the string into an image-analysis-result model he built from the documentation, with categories (name and confidence score) and adult data (is-adult and is-racy booleans plus scores).
  • The app shows the caption and tags and flags racy images. It displays the first caption and loops through the tags, and if the racy score is above 0.3 it shows a warning image. A kitten picture was captioned "a bird sitting on top of a cat", and a photo of a shirtless man scored about 0.31 and tripped the warning.
  • The Node version needs no casting but punishes misspelled property names. It requests only the description feature and reads the first caption and its confidence straight from the parsed JSON. Giard says JavaScript's dynamic objects make this simpler, but a capital C in "captions" would make it fail.
  • The Node script prints the caption and also serves it as a page. Run with node analyzeimage.js against a hard-coded image, it printed "a small boat in a body of water" at 97% confidence and also served the result as a page on localhost port 8124, where Giard noticed a rounding issue he leaves as an exercise. The code is on his GitHub; users must add their own key function.
GCast 29: Introducing Cognitive Services and Computer Vision
  • Cognitive Services offers machine learning as simple web APIs. Giard describes them as APIs that let you use machine learning "without having to do the heavy lifting", with free and inexpensive versions, reached from microsoft.com/cognitive. The categories are vision, knowledge, language, speech and search; under vision alone are Computer Vision, Video Indexer, Custom Vision, Content Moderator and Face.
  • Image analysis returns tagged JSON with confidence scores for each judgement. For a train-station photo the service returned tags such as platform (99.5%) and station (97.98%) and the caption "people waiting at a train station" at 83.3%. It also returned image format and size, clip-art, line-drawing and black-and-white flags, colour information, and adult and racy scores. Giard suggests using those scores to hold user-uploaded images for human vetting. Faces come back with estimated age, gender and a bounding rectangle, and celebrity detection identified Satya Nadella at 99.9999%. A line drawing of a lion was guessed as a face at under 50% confidence, which Giard uses to show why code should act on the confidence level.
  • The analyze call is a POST shaped by query-string parameters. The endpoint is https://<region>.api.cognitive.microsoft.com/vision/v2.0/analyze. visualFeatures takes a comma-delimited list (categories, tags, description, faces, image type, color, adult); leaving a feature out returns a smaller JSON response. details takes celebrities and/or landmarks, and language was then limited to English, Japanese, Portuguese and Chinese. The headers are the content type and the Ocp-Apim-Subscription-Key. You can send a JSON body with a url to a publicly reachable image, or send the image itself as application/octet-stream or multipart/form-data; images must be under 4 MB.
  • An Azure Computer Vision resource supplies the endpoint and keys. He creates it under Create a resource → AI + Machine Learning → Computer Vision. The region sets the URL, and the key and URL must both match the resource. You get only one free-tier resource, and it is limited in calls per minute and per month, so Giard says it suits learning but not a tight loop or an app deployed to the world. There are two interchangeable keys so you can switch to one while the other is regenerated after a compromise.
  • Any HTTP-capable language works, and errors come back as status codes. Because the API is REST, HTTP and JSON, Microsoft provides sample code in cURL, C#, Java, JavaScript, Objective-C and PHP, plus a .NET SDK. A 200 is success. 400-range errors carry a code and a description, covering an unreachable URL (for example one internal to your organization), a bad format, an image over 4 MB, or a misspelled parameter. 415 means unsupported media, an authentication failure is what Giard thinks is a 401, and 500s are server-side; the service refuses to analyze child pornography. In the online test console a boat photo came back as "a small boat and a body of water" at 97%. Giard says the next video shows the call in JavaScript and C#.
GCast 28: Natural Language Processing with LUIS
  • LUIS lets an app understand phrasing nobody listed in advance. Giard presents it as Microsoft's natural language processing tool at luis.ai, part of Microsoft Cognitive Services, which he describes as machine learning without having to do the heavy lifting of machine learning.
  • An app is built from intents, utterances and entities. Intents are the statements, questions or commands the app should understand; utterances are the many ways a user might say each one; entities are the variable parameters inside them. Pre-built domains (calendar, camera, communication, entertainment, fitness and others) fill all three in at once: the fitness domain adds intents such as log weight, log activity and add note, with entities like activity type, meal type, measurement, number and stat type.
  • The app must be trained before it understands anything. Training builds a machine learning model, and queries run against that smaller model rather than the original data. Testing "record that I ran 1,000 steps" in the portal returned log activity at 90% confidence and picked up stat type = steps, but missed the number, which Giard ties to the less-than-certain score.
  • Publishing exposes the model as a simple GET web service. Publishing to production creates an endpoint whose URL ends in q=, where the utterance goes. Querying "can you record weight, please?" from a browser returned JSON with log weight as the top-scoring intent at 99%, the runner-up at only 2%, and the weight entity tagged as a measurement, for the calling application to parse.
GCast 27: QnAMaker
  • QnA Maker builds a bot that only answers questions. Giard contrasts it with building bots on the Microsoft Bot Framework. A knowledge base is started at qnamaker.ai, but step one is creating a QnA Maker service in an Azure account; in the portal it sits under Cognitive Services rather than being listed by default under AI and machine learning.
  • The service's app name becomes a public URL, so it must be unique. The hosted web service is addressed as https://<name>.azurewebsites.net. Pricing offers a free tier and a $10-a-month tier; Giard takes the free one, and West US after East US was not allowed for that resource.
  • A knowledge base can be loaded from URLs, files and chit-chat. Sources are one or more URLs containing questions and answers, tab-delimited files, and a chit-chat set of stock answers in a professional, friendly or funny personality. Giard points it at Microsoft's own QnA Maker FAQ page plus the professional chit-chat; the resulting pairs can be edited, deleted or added to, and a test of "how old are you?" returned "Age doesn't really apply to me".
  • Train, then publish, and any HTTP POST client can query it. Training builds a machine learning model from the questions; settings also allow export as a tab-separated file. Once published, the endpoint is /qnamaker/knowledgebases/<id>/generateAnswer, called with an Authorization: EndpointKey <key> header, Content-Type: application/json and the question in the body. Giard runs it from curl and from Postman, getting a 200 with the answer and a match score.
GCast 26: Creating a Chatbot in the Azure Portal
  • A Web App Bot resource creates a Bot Framework bot without an IDE. It sits under AI and Machine Learning in the Azure portal. The pricing tier sets how many messages the bot accepts (the free tier accepts fewer; Giard picks premium), the app name must be unique because it is exposed as https://<name>.azurewebsites.net, and the app service plan sets the machine's size and location.
  • Templates include an Echo Bot and a basic bot. The Echo Bot sends the user's message back; the basic bot adds a little natural language processing. Each comes in Node.js or C# and in a choice of Bot Framework version. Tested in the portal's Test in Web Chat, the Echo Bot answered "hello" with "you sent hello" plus a turn number.
  • The online code editor suits quick changes; Giard recommends Visual Studio. Open Online Code Editor on the Build tab loads the .NET project into the browser and saves edits automatically; the reply logic sits in OnTurnAsync in the echo-with-counter bot's .cs file. Giard calls this limiting but fine for "quick and dirty" changes; for real work, Download Bot Source Code produces a zip to open in Visual Studio or Visual Studio Code.
  • Downloaded code needs two keys copied from the portal. The bot file path and bot file secret, found under the App Service's Application Settings, replace placeholder strings in appsettings.json. Giard advises keeping those keys out of source control, for example via gitignore. Run locally, the bot shows only a dummy landing page, since it is just a web service.
  • The Bot Framework Emulator tests the bot against its endpoints. The open-source desktop app, installed from its GitHub releases page, opens the project's .bot file, a JSON file holding development and production endpoints. Against the locally running bot it showed Giard's edit ("you typed hello"); he says deployment then goes by right-click deploy or continuous integration.
GCast 25: Azure Durable Functions
  • Durable functions add state to normally stateless Azure Functions. Giard pitches them for long-running workflows, such as waiting on a user to click a link in an email, which could take hours, days or weeks. Since Azure Functions charge only while running, he says you don't want such a workflow running the whole time.
  • Activity functions are triggered only by an orchestration function. Their input parameter carries the ActivityTrigger attribute, unlike functions triggered by HTTP, a queue message, a database change or a timer. The orchestration function is marked by OrchestrationTrigger on its DurableOrchestrationContext parameter and calls each activity through the context's async call method.
  • A conventional starter function has to start the orchestration. The orchestration trigger does not fire on its own. In the demo, an HTTP-triggered starter accepting GET or POST on the default route (api/ plus the function name) uses a DurableOrchestrationClient to start the orchestrator asynchronously; Giard notes a timer, queue or database trigger would also work.
  • Awaiting each call makes the activities run in sequence. Although called asynchronously, await holds each line until the previous call returns, so one function's output can be the next one's input. In the demo, three activities that each wait 10 seconds ran in order from a browser GET, and the final log showed the message with "function 1 done", "function 2 done" and "function 3 done" appended.
GCast 24: Azure Function CosmosDB Binding
  • A binding attribute does the database insert with no data-access code. Giard decorates a to-do item parameter of an HTTP-triggered (POST) function with a Cosmos DB attribute giving the database name, collection name and a connection-string setting, which maps to the connection string pasted into local.settings.json; the function body only deserializes the request, logs and returns HTTP 201, yet the posted documents appear in the collection. He uses a typed class with JSON property attributes (lowercase in JSON, mixed case in .NET) because Cosmos DB is schemaless and he wants the application to validate documents before they are stored.
  • Reads come back by declaring an enumerable parameter with a query. The get-all function binds an IEnumerable of the to-do type with the SQL query select * from items and returns the list with HTTP 200. A second function changes only the query, adding a where clause on the is-complete field, and returns just the completed items.
  • A single document is fetched by binding the ID to a route variable. The binding has a special Id parameter; rather than hard-code it, Giard sets the function's route to getitem/{id}, where the curly braces mark a variable, and passes that variable to the binding. A null result returns 404, a found document returns 200 with the item.
  • Giard's case for bindings: less code means fewer bugs. He calls them "a really powerful tool" because less code generally means fewer bugs and less to test. The project was built as a Visual Studio C# Azure Functions version 2.0 project against a SQL API Cosmos DB account, and he publishes it in an "Azure Function Demos" repository on his GitHub, with a blog article, "Cosmos DB binding in Azure Functions", walking through each of the four functions.