<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>boro &#8211; blog.boro2g .co.uk</title>
	<atom:link href="https://blog.boro2g.co.uk/author/boro/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.boro2g.co.uk</link>
	<description>Some ideas about coding, dev and all things online.</description>
	<lastBuildDate>Tue, 30 Mar 2021 14:29:32 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.5.8</generator>
	<item>
		<title>Why is choosing a CMS so damn hard?</title>
		<link>https://blog.boro2g.co.uk/why-is-choosing-a-cms-so-damn-hard/</link>
					<comments>https://blog.boro2g.co.uk/why-is-choosing-a-cms-so-damn-hard/#respond</comments>
		
		<dc:creator><![CDATA[boro]]></dc:creator>
		<pubDate>Tue, 30 Mar 2021 13:36:58 +0000</pubDate>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[CMS]]></category>
		<category><![CDATA[Contentful]]></category>
		<category><![CDATA[Mach]]></category>
		<category><![CDATA[Sitecore]]></category>
		<guid isPermaLink="false">https://blog.boro2g.co.uk/?p=1163</guid>

					<description><![CDATA[<p>Imagine the scenario &#8211; you start on a new feature or project and there is a need for dynamic content. Sounds simple right? Just pick a CMS platform, setup an account, update a bit of content, publish and you are done. Well, if only it was that simple! * *Note &#8211; this post assumes that [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/why-is-choosing-a-cms-so-damn-hard/">Why is choosing a CMS so damn hard?</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Imagine the scenario &#8211; you start on a new feature or project and there is a need for dynamic content. Sounds simple right? Just pick a CMS platform, setup an account, update a bit of content, publish and you are done. Well, if only it was that simple! *</p>



<p><em>*Note &#8211; this post assumes that a platform like WordPress isn&#8217;t sufficient for your requirements</em> </p>



<p><strong>Where to start?</strong></p>



<p>If you look at <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/List_of_content_management_systems" target="_blank">https://en.wikipedia.org/wiki/List_of_content_management_systems</a>, it certainly won&#8217;t clear things up. There are a LOT of options! So, what sort of information should you use to feed into your decision process?</p>



<p><strong>A few core CMS concepts</strong></p>



<p>Before we go further, let&#8217;s define a few key concepts:</p>



<ul><li>Headless Content Management System (CMS) &#8211; &#8220;A headless CMS is a content management system that provides a way to author content, but instead of having your content coupled to a particular output (like web page rendering), it provides your content as data over an API.&#8221; <a rel="noreferrer noopener" href="https://www.sanity.io/blog/headless-cms-explained" target="_blank">https://www.sanity.io/blog/headless-cms-explained</a></li><li>Digital Experience Platform (DXP) &#8211; &#8220;Gartner defines a digital experience platform (DXP) as an integrated set of technologies, based on a common platform, that provides a broad range of audiences with consistent, secure and personalized access to information and applications across many digital touchpoints.&#8221; <a rel="noreferrer noopener" href="https://www.gartner.com/reviews/market/digital-experience-platforms" target="_blank">https://www.gartner.com/reviews/market/digital-experience-platforms</a></li></ul>



<p>It&#8217;s worth noting that certain vendors aim to fulfil both entries above, whereas others operate purely as headless, cloud native SAAS providers.</p>



<p><strong>How to help you make a decision?</strong></p>



<p><em>Ah, but what if the decision has already been made</em>? </p>



<p>Within your team(s) or business(es), do you have an existing CMS? If so, can it be scaled or modified to serve your new needs. It&#8217;s worth considering that &#8216;scaled&#8217; here covers many things &#8211; licensing, usability, modifiability, supportability, physical capacity and a raft more. This discussion often leads to some interesting outcomes and can easily expose issues, or the opposite, a positive view of existing tooling.</p>



<p><em>Ok, so we already successfully use CMS X</em></p>



<p>We&#8217;re getting warmer, but I&#8217;d suggest you still need to answer a few more questions:</p>



<ul><li>Is it fit for purpose?</li><li>Do it&#8217;s content delivery approaches fit the needs of your new requirements?</li><li>Will the team that use the system be the same as the existing editors?</li></ul>



<p><strong>How to select a <em>new </em>CMS?</strong></p>



<p>I&#8217;d recommend you build up your own criteria for assessing different tools, here are a few thought starters:</p>



<ul><li>Cost<ul><li>What are the license fees, and how do they scale?<ul><li>Is it a consistent cost year by year?</li><li>What if you need more editors?</li><li>What if you need more content items, or media items?</li><li>What if you need to serve more traffic?</li><li>How much would a new environment cost?</li></ul></li><li>How much does it cost to run and maintain the system?<ul><li>What hosting costs will you incur?</li><li>How much does a release cost?</li><li>What cost lies with your different DR options?</li><li>How will the infra receive security patches and software upgrades?</li><li>What does an upgrade of the tool look like? Is it handled for you, or do you need to own an upgrade?<ul><li>Note. This has stung us hard in the past with certain vendors!</li></ul></li></ul></li></ul><ul><li>How much effort/cost is required to set it up before you can focus on delivery of features to the customer?</li></ul></li><li>Features<ul><li>Does the tool support the features you require?</li><li>Or, does the tool come with features you don&#8217;t require?<ul><li>This is an interesting point &#8211; are you buying a Ferrari when all you need is a Ford?</li></ul></li><li>Are your competitors using the same tool?<ul><li>Does it suit your business model?</li></ul></li><li>What multi-lingual requirements do you have?<ul><li>And how does that map to content and presentation?</li></ul></li></ul></li><li>Technology constraints<ul><li>Are there any technology restrictions imposed by the tools<ul><li>E.g. hosting options, language choices, CI/CD patterns, tooling constraints</li><li>Who owns the hosted platform, and how do backups work?</li><li>Does the location of data matter for your business?</li></ul></li></ul></li><li>Platform vs a tool<ul><li>This ties into the concepts above, do you want a DXP or a headless CMS</li><li>Is a <a rel="noreferrer noopener" href="https://www.avenga.com/magazine/composable-architecture" target="_blank">composable architecture</a> desirable for your team(s)?</li></ul></li><li>Out the box vs bespoke<ul><li>What comes &#8216;for free&#8217;? And, do you even want the &#8216;free&#8217; features?<ul><li>If we think of enterprise platforms such as Sitecore, you get a lot OTB for free. E.g. the concept of sites, pipelines, commands and many more. </li><li>If you go down the headless route this lies in your dev teams hands.</li></ul></li></ul></li><li>Building a team<ul><li>Can you even build a team around tool X?</li><li>Do you have in-house experience in the tool or associated tools?</li></ul></li><li>Support<ul><li>What if something goes wrong, what support can you get?<ul><li>Note, I&#8217;d see support running from before you sign the contracts all the way through to post live ongoing support</li></ul></li></ul></li><li>Scalability, performance and common NFR&#8217;s<ul><li>Will the tool scale and perform to your requirements?</li></ul></li></ul>



<p>It&#8217;s worth noting, this is not meant to be an exhaustive list &#8211; every project will have different requirements and metrics that get prioritized. The goal is to provide some thought starters in areas we&#8217;ve found useful in the past.</p>



<p><strong>Finally, the fun part &#8211; rolling it out</strong></p>



<figure class="wp-block-image size-large"><a href="https://blog.boro2g.co.uk/wp-content/uploads/2021/03/a8d4e44df35f2f5f42ebe27a2bd272d674253259f5d7862ab6624effbd3d484b1.jpg"><img fetchpriority="high" decoding="async" width="400" height="400" src="https://blog.boro2g.co.uk/wp-content/uploads/2021/03/a8d4e44df35f2f5f42ebe27a2bd272d674253259f5d7862ab6624effbd3d484b1.jpg" alt="" class="wp-image-1170" srcset="https://blog.boro2g.co.uk/wp-content/uploads/2021/03/a8d4e44df35f2f5f42ebe27a2bd272d674253259f5d7862ab6624effbd3d484b1.jpg 400w, https://blog.boro2g.co.uk/wp-content/uploads/2021/03/a8d4e44df35f2f5f42ebe27a2bd272d674253259f5d7862ab6624effbd3d484b1-300x300.jpg 300w, https://blog.boro2g.co.uk/wp-content/uploads/2021/03/a8d4e44df35f2f5f42ebe27a2bd272d674253259f5d7862ab6624effbd3d484b1-150x150.jpg 150w" sizes="(max-width: 400px) 100vw, 400px" /></a></figure>



<p>Well, almost. Now the <s>fun</s> / <s>hard</s> part (omit for your preference :)). </p>



<p>You have your new tool, but how does it map to the business? How will the editors get on with it? What does multi-lingual design look like? What technology do you use to build the front ends? Where to start? What is the meaning of life?</p>



<p>Maybe that&#8217;s content for another blog post&#8230;</p>



<p><em>Happy editing!</em></p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/why-is-choosing-a-cms-so-damn-hard/">Why is choosing a CMS so damn hard?</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.boro2g.co.uk/why-is-choosing-a-cms-so-damn-hard/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Machathon 2021 &#8211; A realtime team mood board</title>
		<link>https://blog.boro2g.co.uk/machathon-2021-a-realtime-team-mood-board/</link>
					<comments>https://blog.boro2g.co.uk/machathon-2021-a-realtime-team-mood-board/#respond</comments>
		
		<dc:creator><![CDATA[boro]]></dc:creator>
		<pubDate>Thu, 28 Jan 2021 13:44:25 +0000</pubDate>
				<category><![CDATA[Contentful]]></category>
		<category><![CDATA[Mach]]></category>
		<guid isPermaLink="false">https://blog.boro2g.co.uk/?p=1146</guid>

					<description><![CDATA[<p>The MACH Alliance are running a &#8216;Machathon&#8216; at the start of 2021 &#8211; the theme is &#8216;how to help people get virtually un-stuck&#8217;. It sounded like a great opportunity to get hands on with a variety of MACH technologies! I hope you enjoy the idea and demo. Some key urls: Github repo of the code [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/machathon-2021-a-realtime-team-mood-board/">Machathon 2021 &#8211; A realtime team mood board</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>The MACH Alliance are running a &#8216;<a rel="noreferrer noopener" href="https://machalliance.org/MACHathon" target="_blank">Machathon</a>&#8216; at the start of 2021 &#8211; the theme is &#8216;how to help people get virtually un-stuck&#8217;.</p>



<p>It sounded like a great opportunity to get hands on with a variety of <a rel="noreferrer noopener" href="https://machalliance.org/" data-type="URL" data-id="https://machalliance.org/" target="_blank">MACH </a>technologies! I hope you enjoy the idea and demo.</p>



<p>Some key urls:</p>



<ul><li>Github repo of the code &#8211; <a rel="noreferrer noopener" href="https://github.com/boro2g/MachathonPublic" target="_blank">https://github.com/boro2g/MachathonPublic</a></li><li>Front end UI &#8211; <a rel="noreferrer noopener" href="https://machathon-ui.boro2g.co.uk/" target="_blank">https://machathon-ui.boro2g.co.uk/</a></li><li>Demo video &#8211; <a href="https://youtu.be/NiBDWDfN_JU" target="_blank" rel="noreferrer noopener">https://youtu.be/NiBDWDfN_JU</a></li></ul>



<p><strong>The elevator pitch</strong></p>



<figure class="wp-block-image size-large"><a href="https://blog.boro2g.co.uk/wp-content/uploads/2021/01/logo.jpg"><img decoding="async" width="1024" height="576" src="https://blog.boro2g.co.uk/wp-content/uploads/2021/01/logo-1024x576.jpg" alt="" class="wp-image-1152" srcset="https://blog.boro2g.co.uk/wp-content/uploads/2021/01/logo-1024x576.jpg 1024w, https://blog.boro2g.co.uk/wp-content/uploads/2021/01/logo-300x169.jpg 300w, https://blog.boro2g.co.uk/wp-content/uploads/2021/01/logo-768x432.jpg 768w, https://blog.boro2g.co.uk/wp-content/uploads/2021/01/logo-1536x864.jpg 1536w, https://blog.boro2g.co.uk/wp-content/uploads/2021/01/logo.jpg 1920w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>Everyone is spending a lot more time isolated from their friends, family and colleagues &#8211; so why not make it as easy as possible for people to see how you are with a shared &#8216;mood board&#8217;. </p>



<p>Each team member can be authored in a CMS and then have their status and desired outcome updated in realtime based on their mood.</p>



<p>The realtime board is a web UI. There is also an Alexa skill to provide a summary of the team mood.</p>



<p><strong>What makes a good outcome?</strong></p>



<p>It&#8217;s really down to the user on how much they want to share &#8211; but could be something as simple as &#8216;please call me for a chat&#8217;, through to &#8216;it&#8217;s a head down kinda day&#8217;, or &#8216;today is a good day, can I help you?&#8217;.</p>



<p><em>I&#8217;d recommend some form of socially acceptable ego/gloat filter is applied to messages from the team, no-one likes a smarty pants or gloat monster in their face!</em></p>



<p><strong>And now the fun bit, the tech!</strong></p>



<p>The demo video shows all of this in action, complete with alexa fails and a guided tour of how it glues together under the hood! </p>



<p>As a quick summary:</p>



<ul><li>Content<ul><li><a rel="noreferrer noopener" href="https://www.contentful.com/" target="_blank">Contentful</a></li></ul></li><li>Cloud<ul><li>AWS<ul><li>S3 and Cloudfront for WebUI</li><li>Lambda for API, Orchestration and Alexa skill</li><li>SNS for fan-out</li><li>Secrets Manager</li><li>CloudWatch</li><li>ECR for container images</li></ul></li><li>Azure<ul><li>Azure functions for SignalR trigger</li><li>SignalR service for realtime updates</li></ul></li></ul></li><li>CI / CD<ul><li>Github and <a href="https://github.com/features/actions" target="_blank" rel="noreferrer noopener">Github Actions</a></li></ul></li><li><a rel="noreferrer noopener" href="https://www.home-assistant.io/" data-type="URL" data-id="https://www.home-assistant.io/" target="_blank">Home Assistant</a> running on Raspberry PI<ul><li><a rel="noreferrer noopener" href="https://www.ikea.com/gb/en/p/tradfri-remote-control-30443124/" data-type="URL" data-id="https://www.ikea.com/gb/en/p/tradfri-remote-control-30443124/" target="_blank">IKEA tradfri remote</a> for triggering actions</li><li><a href="https://www.amazon.co.uk/dresden-elektronik-BN-600107-ConBee-II/dp/B07PZ7ZHG5/ref=sr_1_2?crid=335744GF4MO8V&amp;dchild=1&amp;keywords=conbee+ii&amp;qid=1611837503&amp;sprefix=conbee+%2Caps%2C157&amp;sr=8-2" target="_blank" rel="noreferrer noopener">Conbee2</a> with ZHA integration for Zigbee network</li><li><a href="https://www.home-assistant.io/integrations/aws/" data-type="URL" data-id="https://www.home-assistant.io/integrations/aws/" target="_blank" rel="noreferrer noopener">AWS notify</a> integration</li></ul></li></ul>



<figure class="wp-block-image size-large"><a href="https://blog.boro2g.co.uk/wp-content/uploads/2021/01/Machathon-scaled.jpg"><img decoding="async" width="1024" height="372" src="https://blog.boro2g.co.uk/wp-content/uploads/2021/01/Machathon-1024x372.jpg" alt="" class="wp-image-1148" srcset="https://blog.boro2g.co.uk/wp-content/uploads/2021/01/Machathon-1024x372.jpg 1024w, https://blog.boro2g.co.uk/wp-content/uploads/2021/01/Machathon-300x109.jpg 300w, https://blog.boro2g.co.uk/wp-content/uploads/2021/01/Machathon-768x279.jpg 768w, https://blog.boro2g.co.uk/wp-content/uploads/2021/01/Machathon-1536x558.jpg 1536w, https://blog.boro2g.co.uk/wp-content/uploads/2021/01/Machathon-2048x744.jpg 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>The underlying code makes use of a mixture of technologies:</p>



<ul><li>Data access<ul><li>Contentful Management API</li><li>GraphQL</li></ul></li><li>UI<ul><li>Nuxt and SSG generated version of site</li></ul></li><li>API<ul><li>Node JS and containers running in Lambda</li></ul></li><li>Orchestrator functions<ul><li>A mixture of dotnetcore and .net5</li></ul></li></ul>



<p><strong>Triggering behaviour</strong></p>



<p>If you&#8217;ve not seen Home Assistant, I&#8217;d highly recommend it for anyone interested in tinkering with smart home / iot appliances! It allows you to setup scripts, automations and a raft more all linked to IOT devices. </p>



<p>In our setup the trigger is:</p>



<figure class="wp-block-image"><img decoding="async" src="https://www.ikea.com/gb/en/images/products/tradfri-remote-control__0609380_PE684491_S5.JPG?f=s" alt="TRÅDFRI, Remote control - IKEA"/></figure>



<p>The different buttons are mapped to different actions:</p>



<ul><li>Top button = send &#8216;happy&#8217;</li><li>Middle = send &#8216;ok&#8217;</li><li>Bottom = send &#8216;sad&#8217; </li><li>Left button = change user</li><li>Right button = change user</li></ul>



<p>Behind the scenes, a custom lovelace UI is updated based on button presses and finally it notifies a Lambda function to initiate the process.</p>



<figure class="wp-block-image size-large"><a href="https://blog.boro2g.co.uk/wp-content/uploads/2021/01/image.png"><img loading="lazy" decoding="async" width="1016" height="566" src="https://blog.boro2g.co.uk/wp-content/uploads/2021/01/image.png" alt="" class="wp-image-1149" srcset="https://blog.boro2g.co.uk/wp-content/uploads/2021/01/image.png 1016w, https://blog.boro2g.co.uk/wp-content/uploads/2021/01/image-300x167.png 300w, https://blog.boro2g.co.uk/wp-content/uploads/2021/01/image-768x428.png 768w" sizes="(max-width: 1016px) 100vw, 1016px" /></a></figure>



<p><strong>What are we actually orchestrating?</strong></p>



<p>When you press a button this does a few things:</p>



<ul><li>Sets the user mood dropdown in the UI</li><li>Pings a message to the orchestration lambda which:<ul><li>Updates contentful with mood and outcome</li><li>Raises an SNS event &#8211; the subscribers then:<ul><li>Ping SignalR to update the UI in realtime</li><li>Ping github actions to trigger a SSG build</li></ul></li></ul></li></ul>



<p><strong>What content to model</strong></p>



<p>Contentful is used for several types of data in this setup, including basic data on the user, their mood, any custom outcomes they want and finally all the content for the Alexa skill.</p>



<p><strong>Summary</strong></p>



<p>I really hope you&#8217;ve found this a good use case of MACH technologies. What is refreshing about the approaches above are how quickly you can adapt and change. Making use of OTB cloud functionality provides a very rich toolset for multi channel, multi device applications. Oh, and it&#8217;s a lot of fun to play with <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>The downsides? Well, it&#8217;s a pretty complicated way to let people know you are having a bad day!</p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/machathon-2021-a-realtime-team-mood-board/">Machathon 2021 &#8211; A realtime team mood board</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.boro2g.co.uk/machathon-2021-a-realtime-team-mood-board/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Monolith, microservice or something in-between</title>
		<link>https://blog.boro2g.co.uk/monolith-microservice-or-something-in-between/</link>
					<comments>https://blog.boro2g.co.uk/monolith-microservice-or-something-in-between/#respond</comments>
		
		<dc:creator><![CDATA[boro]]></dc:creator>
		<pubDate>Fri, 18 Sep 2020 15:05:49 +0000</pubDate>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Webapi]]></category>
		<guid isPermaLink="false">https://blog.boro2g.co.uk/?p=1129</guid>

					<description><![CDATA[<p>In a recent project we had an interesting challenge as to how we structured and architected our dotnetcore web api&#8217;s. We wanted development and deployment agility, but to maintain the flexibility that comes from micro(macro*)services. * Arguably the term you use here depends on how you choose to cut your system and services What to [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/monolith-microservice-or-something-in-between/">Monolith, microservice or something in-between</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In a recent project we had an interesting challenge as to how we structured and architected our dotnetcore web api&#8217;s. We wanted development and deployment agility, but to maintain the flexibility that comes from micro(macro*)services. </p>



<p><em>* Arguably the term you use here depends on how you choose to cut your system and services</em></p>



<p><strong>What to expect</strong></p>



<p>Well, maybe lets start with the opposite &#8211; what not to expect?</p>



<ul><li>This isn&#8217;t aimed to be a starter kit, it&#8217;s goal is to provide examples</li><li>How you choose to cut up your apis, well that&#8217;s one for you too &#8211; sorry</li><li>And finally, how you choose to name things (models vs data, services vs domain, core vs base, shared vs common) &#8211; yep, that&#8217;s also up to you</li></ul>



<p>Now the good bits, what to expect?</p>



<p>The goal of the example project (<a rel="noreferrer noopener" href="https://github.com/boro2g/MonolithToMicroserviceApi" target="_blank">https://github.com/boro2g/MonolithToMicroserviceApi</a>) is to show working examples of the following scenarios:</p>



<ul><li>Each WebApi project can run in isolation, without knowledge of others<ul><li>The same mindset applies to deployments &#8211; each WebApi can be deployed in isolation</li></ul></li><li>All the WebApi&#8217;s can be run and deployed as a single application</li></ul>



<p><strong>Why add this extra complexity?</strong></p>



<p>Good question. The key thing for me is flexibility. If it&#8217;s done right you give yourself options &#8211; you want to deploy as a monolith, no problem. You want to deploy each bit in isolation, well that&#8217;s fine too.</p>



<p><strong>How does it look?</strong></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="387" height="231" src="https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image.png" alt="" class="wp-image-1131" srcset="https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image.png 387w, https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image-300x179.png 300w" sizes="(max-width: 387px) 100vw, 387px" /></figure>



<p>Some key highlights of the image above:</p>



<ol><li><em>MonolithToMicroserviceApi.WebApi</em><ol><li>This is the shared singular WebApi project that brings everything together</li><li>You can run this via IISExpress, or IIS etc and all the Api&#8217;s from the other projects will work within it</li></ol></li><li><em>MonolithToMicroserviceApi.Search.WebApi</em><ol><li>This is the search micro(macro) service</li><li>You can run this in isolation, much like you can the common one</li></ol></li><li><em>MonolithToMicroserviceApi.Weather.WebApi</em><ol><li>The same concept as Search, but with other example controllers and code</li></ol></li><li><em>MonolithToMicroserviceApi.Shared.*</em><ol><li>These libraries contain common functionality that&#8217;s shared between each WebApi</li></ol></li></ol>



<p><strong>Adding a new WebApi</strong></p>



<p>The search project has a good example of this. If you look in <em>MonolithToMicroserviceApi.Search.WebApi.Startup</em> </p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="623" height="395" src="https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image-1.png" alt="" class="wp-image-1134" srcset="https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image-1.png 623w, https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image-1-300x190.png 300w" sizes="(max-width: 623px) 100vw, 623px" /></figure>



<p>You need to add the ApiConfiguration class itself (see the project for examples), the ApiConfigurations code above and then register them all.</p>



<p>Similarly in the common project startup (<em>MonolithToMicroserviceApi.WebApi.Startup</em>). Simply add each ApiConfiguration and register them.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="323" height="108" src="https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image-2.png" alt="" class="wp-image-1135" srcset="https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image-2.png 323w, https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image-2-300x100.png 300w" sizes="(max-width: 323px) 100vw, 323px" /></figure>



<p><strong>The Api glue</strong></p>



<p>So how does it all glue together? The key underlying code that allows you to pool controllers from one project into another is:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="485" height="22" src="https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image-3.png" alt="" class="wp-image-1136" srcset="https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image-3.png 485w, https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image-3-300x14.png 300w" sizes="(max-width: 485px) 100vw, 485px" /></figure>



<p><strong>What issues you might run into?</strong></p>



<ul><li>Routing<ul><li>There is a commented out example of this &#8211; in the core project and weather project we have a &#8216;WeatherForecastController&#8217; &#8211; if both of these have the same [Route] attribute you will get an exception. </li><li>A simple fix is to ensure each controller has isolated routes. I&#8217;m sure a more clever approach could be used if you have LOTS of WebApi projects, but I&#8217;ll leave that for you to work out</li></ul></li></ul>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="465" height="121" src="https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image-4.png" alt="" class="wp-image-1137" srcset="https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image-4.png 465w, https://blog.boro2g.co.uk/wp-content/uploads/2020/09/image-4-300x78.png 300w" sizes="(max-width: 465px) 100vw, 465px" /></figure>



<ul><li>Dependency bleeding<ul><li>I don&#8217;t feel like this approach introduces any more risk of either cyclic dependencies or &#8216;<a href="https://en.wikipedia.org/wiki/Big_ball_of_mud" target="_blank" rel="noreferrer noopener">balls of mud</a>&#8216; &#8211; IMO that comes down the discipline of the team building your solutions.</li></ul></li></ul>



<p><strong>Summary</strong></p>



<p>What I like about this approach is flexibility. On day 1 you can deploy your common project to a single box and all your api&#8217;s are working in one place. Over time, as complexity grows, or your dev teams evolve, different parts can be cut apart but without any fundamental changes needed.</p>



<p>You need to scale your search api, well no problem &#8211; deploy it as a single api and scale as you need. </p>



<p>You need to push the weather api to multiple data centres for geo reasons, cut it out and deploy as you want.</p>



<p>Another team needs to own search, again thats fine &#8211; you could even pull out to another solution, remove the ApiConfiguration and everyone is happy!? <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>I hope it provides some good inspiration. It really doesn&#8217;t take much code, or configuration to build what I&#8217;d consider to be a very flexible approach to structuring your dotnetcore WebApi projects.</p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/monolith-microservice-or-something-in-between/">Monolith, microservice or something in-between</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.boro2g.co.uk/monolith-microservice-or-something-in-between/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Sitecore forms &#8211; custom form element save issue</title>
		<link>https://blog.boro2g.co.uk/sitecore-forms-custom-form-element-save-issue/</link>
					<comments>https://blog.boro2g.co.uk/sitecore-forms-custom-form-element-save-issue/#respond</comments>
		
		<dc:creator><![CDATA[boro]]></dc:creator>
		<pubDate>Tue, 04 Aug 2020 09:17:00 +0000</pubDate>
				<category><![CDATA[Sitecore]]></category>
		<guid isPermaLink="false">https://blog.boro2g.co.uk/?p=1126</guid>

					<description><![CDATA[<p>In a recent project we needed to add some richer functionality to a form, so decided to wrap it up in a custom Vue.js component which we could then integrate into Sitecore forms. Sounds simple right? Building the component Sitecore provides some good documentation on how to build different flavours of form rows &#8211; have [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/sitecore-forms-custom-form-element-save-issue/">Sitecore forms &#8211; custom form element save issue</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In a recent project we needed to add some richer functionality to a form, so decided to wrap it up in a custom Vue.js component which we could then integrate into Sitecore forms. Sounds simple right?</p>



<p><strong>Building the component</strong></p>



<p>Sitecore provides some good documentation on how to build different flavours of form rows &#8211; have a look at the walkthrough&#8217;s in <a rel="noreferrer noopener" href="https://doc.sitecore.com/developers/93/sitecore-experience-manager/en/sitecore-forms.html" target="_blank">https://doc.sitecore.com/developers/93/sitecore-experience-manager/en/sitecore-forms.html</a> if you are interested.</p>



<p><strong>Saving your data</strong></p>



<p>Assuming you want to build something a bit richer than the demo video component, chances are you want to actually store data that a user provides. In our use case, we used Vue.js to update a hidden input &#8211; under the hood we then save that data into the DB and also ping off to other save actions. </p>



<p>Simples? Well, not quite &#8211; unless you know where to set things up.</p>



<p><strong>Configuring the form row</strong></p>



<p>In Sitecore forms, a custom form row needs a few things. A template in <strong>master </strong>to represent the configuration of the form row, and a set of items in <strong>core </strong>to represent the UI for configuring the form row.</p>



<p><a rel="noreferrer noopener" href="https://doc.sitecore.com/developers/93/sitecore-experience-manager/en/walkthrough--creating-a-custom-form-element.html" target="_blank">https://doc.sitecore.com/developers/93/sitecore-experience-manager/en/walkthrough&#8211;creating-a-custom-form-element.html</a></p>



<p><strong>The importance of AllowSave</strong></p>



<p>This is the key bit, and took a fair amount of digging to find. I could see my custom data was being posted back to Sitecore, with the right data. But, it was never getting saved in the database <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f641.png" alt="🙁" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>To fix I needed to make sure that both the configuration in core and my custom template had <strong>AllowSave</strong> available.</p>



<ul><li>In core, under &#8216;/sitecore/client/Applications/FormsBuilder/Components/Layouts/PropertyGridForm/PageSettings/Settings&#8217; you create your custom configuration including sub-items based off the template &#8216;FormSection&#8217; (see &#8216;/sitecore/client/Applications/FormsBuilder/Components/Layouts/PropertyGridForm/PageSettings/Settings/SingleLineText/Advanced&#8217; for reference&#8217;<ul><li>Here is where you need to ensure you include &#8216;<strong>AllowSave</strong>&#8216; in the &#8216;<strong>ControlDefinitions</strong>&#8216; field for your custom item</li><li>This is enough to get the checkbox showing in the form builder ui, but not enough to get everything working</li></ul></li><li>In master, under &#8216;/sitecore/templates/System/Forms/Fields&#8217; you create the template to represent the configuration data being saved for your form element<ul><li>Here is where you need to make sure the base templates contains &#8216;<strong>Save Settings</strong>&#8216;</li></ul></li></ul>



<p><strong>Summary</strong></p>



<p>Setting up a custom form row / element is generally pretty simple. However, the documentation doesn&#8217;t cover quite a key step &#8211; saving the data. It doesn&#8217;t take much additional configuration as long as you know the right place to make changes!</p>



<p>Happy saving.</p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/sitecore-forms-custom-form-element-save-issue/">Sitecore forms &#8211; custom form element save issue</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.boro2g.co.uk/sitecore-forms-custom-form-element-save-issue/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Livestreaming with Serato, OBS, multiple webcams and a set of novation dicers</title>
		<link>https://blog.boro2g.co.uk/livestreaming-with-serato-obs-multiple-webcams-and-a-set-of-novation-dicers/</link>
					<comments>https://blog.boro2g.co.uk/livestreaming-with-serato-obs-multiple-webcams-and-a-set-of-novation-dicers/#respond</comments>
		
		<dc:creator><![CDATA[boro]]></dc:creator>
		<pubDate>Wed, 06 May 2020 13:01:29 +0000</pubDate>
				<category><![CDATA[Allsorts]]></category>
		<category><![CDATA[dicer]]></category>
		<category><![CDATA[livestream]]></category>
		<category><![CDATA[midi]]></category>
		<category><![CDATA[obs]]></category>
		<category><![CDATA[serato]]></category>
		<guid isPermaLink="false">https://blog.boro2g.co.uk/?p=1113</guid>

					<description><![CDATA[<p>In the immortal words of Monty Python&#8230; And now for something completely different! I wanted to have a play with some livestreaming so we could host a virtual birthday party for my wife. In the current climate this is all the rage so figured it should be pretty straight forwards to setup. But, how could [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/livestreaming-with-serato-obs-multiple-webcams-and-a-set-of-novation-dicers/">Livestreaming with Serato, OBS, multiple webcams and a set of novation dicers</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In the immortal words of Monty Python&#8230; <em>And now for something completely different!</em></p>



<p>I wanted to have a play with some livestreaming so we could host a virtual birthday party for my wife. In the current climate this is all the rage so figured it should be pretty straight forwards to setup. But, how could we make it a bit more interactive?</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="592" src="https://blog.boro2g.co.uk/wp-content/uploads/2020/05/action-shot-1024x592.jpg" alt="action shot" class="wp-image-1120" srcset="https://blog.boro2g.co.uk/wp-content/uploads/2020/05/action-shot-1024x592.jpg 1024w, https://blog.boro2g.co.uk/wp-content/uploads/2020/05/action-shot-300x173.jpg 300w, https://blog.boro2g.co.uk/wp-content/uploads/2020/05/action-shot-768x444.jpg 768w, https://blog.boro2g.co.uk/wp-content/uploads/2020/05/action-shot.jpg 1447w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><strong>Getting started</strong></p>



<p>It&#8217;s really quick and easy to start streaming as long as you have a few things setup:</p>



<ul><li>Good internet &#8211; you don&#8217;t want things cutting out because your bandwidth can&#8217;t cut it</li><li>Some streaming software &#8211; I&#8217;d recommend <a rel="noreferrer noopener" href="https://obsproject.com/" target="_blank">OBS</a></li><li>Some form of webcam. A lot of online retailers seem sold out of webcams atm so you could always use your phone<ul><li>I found #LiveDroid worked really well on my android phone</li></ul></li><li>A platform, or multiple platforms to stream onto e.g. facebook, twitch, mixcloud live etc.</li></ul>



<p>With all this setup you should be able to test and get your stream on. Nice!</p>



<p><strong>Stepping things up a bit</strong></p>



<p>Good start &#8211; you can now get streaming! But, lets have a bit more fun. What about if you want to show multiple cameras, or add fancy graphics, split up the stream UI or stream to multiple platforms all at once?</p>



<p>First up, I&#8217;d really recommend getting to know OBS &#8211; in particular how the different input sources can be used and layered. This allows you to mix and match multiple cameras, graphics and chunks of screen into one scene.</p>



<p>Next, look at scenes &#8211; here you can show different combinations of cameras/overlays/sources. You name it. Plus the fun part, you can use your keyboard, or even better a midi controller to toggle between scenes.</p>



<p><strong>High class framing!</strong></p>



<p>Here are a couple rather <em><strong>stylish </strong></em>frame options</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="576" src="https://blog.boro2g.co.uk/wp-content/uploads/2020/05/frame-1024x576.png" alt="easter frame" class="wp-image-1122" srcset="https://blog.boro2g.co.uk/wp-content/uploads/2020/05/frame-1024x576.png 1024w, https://blog.boro2g.co.uk/wp-content/uploads/2020/05/frame-300x169.png 300w, https://blog.boro2g.co.uk/wp-content/uploads/2020/05/frame-768x432.png 768w, https://blog.boro2g.co.uk/wp-content/uploads/2020/05/frame.png 1366w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="576" src="https://blog.boro2g.co.uk/wp-content/uploads/2020/05/urban-frame-1024x576.png" alt="urban frame" class="wp-image-1121" srcset="https://blog.boro2g.co.uk/wp-content/uploads/2020/05/urban-frame-1024x576.png 1024w, https://blog.boro2g.co.uk/wp-content/uploads/2020/05/urban-frame-300x169.png 300w, https://blog.boro2g.co.uk/wp-content/uploads/2020/05/urban-frame-768x432.png 768w, https://blog.boro2g.co.uk/wp-content/uploads/2020/05/urban-frame.png 1366w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><strong>Get your midi on</strong></p>



<p>I had an old set of <a rel="noreferrer noopener" href="https://serato.com/dj/hardware/novation-dicers" target="_blank">Novation Dicers</a> at home so wondered about whether these could come in handy. I plugged them into my streaming computer and needed to wire up a few things.</p>



<ol><li>In OBS setup <a rel="noreferrer noopener" href="https://ryanharris.dev/2019-12-11-obs-scenes/" target="_blank">multiple scenes</a></li><li>In the OBS settings, assign a key to each scene (Hotkeys)<ol><li>You can test switching scene off the keyboard. I simply mapped 1,2,3,4,5 to each scene respectively</li><li>This might be enough for you &#8211; but if you want the midi control read on&#8230;</li></ol></li><li>Plug in your midi controller</li><li>Install <a rel="noreferrer noopener" href="https://75r.de/midikey2key/" target="_blank">midikey2key</a>  &#8211; this took a bit of getting used to &#8211; I&#8217;d recommend:<ol><li>Pressing &#8216;start&#8217; in Start/Stop listening</li><li>Turn on &#8216;Log to window&#8217;</li><li>Turning on all &#8216;Channel listeners&#8217;</li><li>Press the buttons on your midi controller</li><li>When the signal shows in the log window, double click and assign the output you want</li><li>For my dicers, this was also mapped to keyboard buttons 1,2,3,4,5</li><li>Test it all <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li></ol></li></ol>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="896" height="698" src="https://blog.boro2g.co.uk/wp-content/uploads/2020/05/tempsnip.png" alt="midi software" class="wp-image-1119" srcset="https://blog.boro2g.co.uk/wp-content/uploads/2020/05/tempsnip.png 896w, https://blog.boro2g.co.uk/wp-content/uploads/2020/05/tempsnip-300x234.png 300w, https://blog.boro2g.co.uk/wp-content/uploads/2020/05/tempsnip-768x598.png 768w" sizes="(max-width: 896px) 100vw, 896px" /></figure>



<p>Here is the mapping config I used: <a href="https://blog.boro2g.co.uk/wp-content/uploads/2020/05/dicer.txt">dicer.txt</a> (rename to ini)</p>



<p><strong>Just use your phone</strong></p>



<p>#LiveDroid allows you setup your android phone as a webcam. When you fire up the app and share the screen, it will stream the output via your network. Add this as a Url source in OBS.</p>



<p>If for some reason this doesn&#8217;t show, test the url in a browser. If OBS then doesn&#8217;t show it, double click the Url Source and hit the reset button.</p>



<p><strong>Multiple streams = more viewers?!?!</strong></p>



<p>Well not quite, but using something like <a rel="noreferrer noopener" href="https://restream.io/" target="_blank">restream</a> you can submit one output from OBS but then broadcast to multiple platforms.</p>



<p><strong>Gotchas</strong></p>



<p>All too easy huh! Well there are a few things to be careful of with all this:</p>



<ol><li>Midi presses have down and up events logged into midikey2key. I&#8217;d recommend just using one or the other, not both events to map key presses to otherwise you get some funky flashing behaviours.</li><li>Test it all, including the sound!<ol><li>The first stream we did, the audio was sourced through a webcam so you could hear everything we said, cross fader clicks etc.</li><li>The second stream we did, the audio doubled up from the mixer=&gt;line in, and from the PC audio. Make sure that you just have one audio source enabled</li></ol></li><li>Some streaming platforms have rather zealous moderation/copyright restrictions. Chunks of some of our streams have been muted because of this.<ol><li>You could try pitching things up a semitone in e.g. pitch and time</li><li>Or changing the pitch</li><li>Or whacking the sampler / air horn until everyone is sick to death of it <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li></ol></li></ol>



<p><strong>Taking it to the next level</strong></p>



<p>This is only scratching the surface of what you can do on livestreams. Custom overlays are easy to add, multiple feeds/scenes allow more interesting content. But, OBS also supports things like green screens.</p>



<p>DJ Yoda does a weekly instagram show where he is green screened on top of the video output from Serato.</p>



<p>A good friend, DJ Cheeba is doing some very cool green screen demos with the video overlayed on the decks! Check out <a href="http://djcheeba.com/lockdown-live-stream/">http://djcheeba.com/lockdown-live-stream/</a> for some proper nerding out. </p>



<p><strong>Summary</strong></p>



<p>Its really easy to get live streams up and running, all off open source software. Do give it a go, and rememeber &#8211; have fun! Some of the best streams I&#8217;ve watched have been because they are fun and don&#8217;t take themselves too seriously <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/livestreaming-with-serato-obs-multiple-webcams-and-a-set-of-novation-dicers/">Livestreaming with Serato, OBS, multiple webcams and a set of novation dicers</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.boro2g.co.uk/livestreaming-with-serato-obs-multiple-webcams-and-a-set-of-novation-dicers/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>JSS &#8211; Your data, your way. Sugcon talk</title>
		<link>https://blog.boro2g.co.uk/jss-your-data-your-way-sugcon-talk/</link>
					<comments>https://blog.boro2g.co.uk/jss-your-data-your-way-sugcon-talk/#respond</comments>
		
		<dc:creator><![CDATA[boro]]></dc:creator>
		<pubDate>Fri, 01 May 2020 08:23:29 +0000</pubDate>
				<category><![CDATA[Sitecore]]></category>
		<guid isPermaLink="false">https://blog.boro2g.co.uk/?p=1109</guid>

					<description><![CDATA[<p>Thanks to anyone that managed to tune into the Sugcon Virtual event yesterday. As promised, a copy of the slides can be found at https://blog.boro2g.co.uk/wp-content/uploads/2020/05/JSS_Your_Data_Your_Way-Virtual.pdf And the repo of content https://github.com/boro2g/JssYourDataYourWay Stay safe everyone!</p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/jss-your-data-your-way-sugcon-talk/">JSS &#8211; Your data, your way. Sugcon talk</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Thanks to anyone that managed to tune into the Sugcon Virtual event yesterday. </p>



<p>As promised, a copy of the slides can be found at <a href="https://blog.boro2g.co.uk/wp-content/uploads/2020/05/JSS_Your_Data_Your_Way-Virtual.pdf" target="_blank" rel="noreferrer noopener">https://blog.boro2g.co.uk/wp-content/uploads/2020/05/JSS_Your_Data_Your_Way-Virtual.pdf</a></p>



<p>And the repo of content <a href="https://github.com/boro2g/JssYourDataYourWay" target="_blank" rel="noreferrer noopener">https://github.com/boro2g/JssYourDataYourWay</a></p>



<p>Stay safe everyone!</p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/jss-your-data-your-way-sugcon-talk/">JSS &#8211; Your data, your way. Sugcon talk</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.boro2g.co.uk/jss-your-data-your-way-sugcon-talk/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Customizing logging in a C# dotnetcore AWS Lambda function</title>
		<link>https://blog.boro2g.co.uk/customizing-logging-in-a-c-dotnetcore-aws-lambda-function/</link>
					<comments>https://blog.boro2g.co.uk/customizing-logging-in-a-c-dotnetcore-aws-lambda-function/#comments</comments>
		
		<dc:creator><![CDATA[boro]]></dc:creator>
		<pubDate>Wed, 27 Nov 2019 14:47:16 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<guid isPermaLink="false">https://blog.boro2g.co.uk/?p=1102</guid>

					<description><![CDATA[<p>One challenge we hit recently was how to build our dotnetcore lambda functions in a consistent way &#8211; in particular how would we approach logging. A pattern we&#8217;ve adopted is to write the core functionality for our functions so that it&#8217;s as easy to run from a console app as it is from a lambda. [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/customizing-logging-in-a-c-dotnetcore-aws-lambda-function/">Customizing logging in a C# dotnetcore AWS Lambda function</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>One challenge we hit recently was how to build our dotnetcore lambda functions in a consistent way &#8211; in particular how would we approach logging. </p>



<p>A pattern we&#8217;ve adopted is to write the core functionality for our functions so that it&#8217;s as easy to run from a console app as it is from a lambda. The lambda can then be considered only as the entry point to the functionality.</p>



<p><strong>Serverless Dependency injection</strong></p>



<p>I am sure there are different schools of thought here, should you use a container within a serverless function or not? For this post the design assumes you do make use of the Microsoft DependencyInjection libraries.</p>



<p><strong>Setting up your projects</strong></p>



<p>Based on the design mentioned above, ie you can run from functionality as easily from a Console App as you can a lambda, I often setup the following projects:</p>



<ul><li>Project.ActualFunctionality (e.g. SnsDemo.Publisher)</li><li>Project.ActualFunctionality.ConsoleApp (e.g. SnsDemo.Publisher.ConsoleApp)</li><li>Project.ActualFunctionality.Lambda (e.g. SnsDemo.Publisher.Lambda)</li></ul>



<p>The actual functionality lives in the top project and is shared with both other projects. Dependency injection, and AWS profiles are used to run the functionality locally.</p>



<p><strong>The actual functionality</strong></p>



<p>Let&#8217;s assume the functionality for your function does something simple like pushing messages into an SQS queue</p>



<pre class="crayon-plain-tag">public class SqsSender
{
    private readonly IAmazonSQS _amazonSQS;
    private readonly ILogger&lt;SqsSender&gt; _logger;

    public SqsSender(IAmazonSQS amazonSQS, ILogger&lt;SqsSender&gt; logger)
    {
        _amazonSQS = amazonSQS;
        _logger = logger;
    }

    public void SendMessage()
    {
        var message = new SendMessageRequest
        {
            QueueUrl = &quot;...&quot;,                 
        };
        
        message.MessageBody = $&quot;Message {Guid.NewGuid()}&quot;;
        
        _amazonSQS.SendMessageAsync(message).Wait();  

        _logger.LogInformation(&quot;_logger Messages sent&quot;);

        Console.WriteLine(&quot;Console Message(s) sent&quot;);
    }
}</pre>



<p><strong>The console app version</strong></p>



<p>It&#8217;s pretty simple to get DI working in a dotnetcore console app</p>



<pre class="crayon-plain-tag">static void Main(string[] args)
{
    IConfiguration config = new ConfigurationBuilder()
        .AddJsonFile(&quot;appsettings.json&quot;, true, true)
        .Build();

    var serviceProvider = new ServiceCollection()
        .AddSingleton(config)
        .AddSingleton&lt;SqsSender&gt;()
        .AddLogging(a =&gt;
        {
            a.AddConsole();
        })
        .AddAWSService&lt;IAmazonSQS&gt;()
        .BuildServiceProvider();

    serviceProvider.GetService&lt;SqsSender&gt;().SendMessage();
}</pre>



<p><strong>The lambda version</strong></p>



<p>This looks very similar to the console version</p>



<pre class="crayon-plain-tag">public string FunctionHandler(object input, ILambdaContext context)
{
    IConfiguration config = new ConfigurationBuilder()
            .AddJsonFile(&quot;lambdasettings.json&quot;, true, true)
            .Build();

    var serviceProvider = new ServiceCollection()
        .AddSingleton(config)
        .AddSingleton&lt;SqsSender&gt;()             
        .AddLogging(a =&gt; a.AddProvider(new CustomLambdaLogProvider(context.Logger)))
        .AddAWSService&lt;IAmazonSQS&gt;()
        .BuildServiceProvider();
   
    serviceProvider.GetService&lt;SqsSender&gt;().SendMessage();

    return &quot;...&quot;;
}</pre>



<p>The really interesting bit to take note of is: <strong>.AddLogging(a =&gt; a.AddProvider(new CustomLambdaLogProvider(context.Logger)))</strong></p>



<p>In the actual functionality we can log in many ways:</p>



<pre class="crayon-plain-tag">_logger.LogInformation(&quot;_logger Messages sent&quot;);

Console.WriteLine(&quot;Console Message(s) sent&quot;);</pre>



<p>To make things lambda agnostic I&#8217;d argue injecting <strong>ILogger&lt;Type&gt;</strong> and then <strong>_logger.LogInformation(&#8220;_logger Messages sent&#8221;);</strong> is the preferred option.</p>



<p><strong>Customizing the logger</strong></p>



<p>It&#8217;s simple to customize the dotnetcore logging framework &#8211; for this demo I setup 2 things. The CustomLambdaLogProvider and the CustomLambdaLogger.</p>



<pre class="crayon-plain-tag">internal class CustomLambdaLogProvider : ILoggerProvider
{
    private readonly ILambdaLogger _logger;

    private readonly ConcurrentDictionary&lt;string, CustomLambdaLogger&gt; _loggers = new ConcurrentDictionary&lt;string, CustomLambdaLogger&gt;();

    public CustomLambdaLogProvider(ILambdaLogger logger)
    {
        _logger = logger;
    }

    public ILogger CreateLogger(string categoryName)
    {
        return _loggers.GetOrAdd(categoryName, a =&gt; new CustomLambdaLogger(a, _logger));
    }

    public void Dispose()
    {
        _loggers.Clear();
    }
}</pre>



<p>And finally a basic version of the actual logger:</p>



<pre class="crayon-plain-tag">internal class CustomLambdaLogger : ILogger
{
    private string _categoryName;
    private ILambdaLogger _lambdaLogger;

    public CustomLambdaLogger(string categoryName, ILambdaLogger lambdaLogger)
    {
        _categoryName = categoryName;
        _lambdaLogger = lambdaLogger;
    }

    public IDisposable BeginScope&lt;TState&gt;(TState state)
    {
        return null;
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        //todo - add logic around filtering log messages if desired
        return true;
    }

    public void Log&lt;TState&gt;(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func&lt;TState, Exception, string&gt; formatter)
    {
        if (!IsEnabled(logLevel))
        {
            return;
        }

        _lambdaLogger.LogLine($&quot;{logLevel.ToString()} - {_categoryName} - {formatter(state, exception)}&quot;);
    }
}</pre>



<p><strong>Summary</strong></p>



<p>The aim here is to keep your application code agnostic to where it runs. Using dependency injection we can share core logic between any &#8216;runner&#8217; e.g. Lambda functions, Azure functions, Console App&#8217;s &#8211; you name it.</p>



<p>With some small tweaks to the lambda logging calls you can ensure the OTB lambda logger is still used under the hood, but your implementation code can make use of injecting things like <strong>ILogger&lt;T&gt;</strong> wherever needed <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/customizing-logging-in-a-c-dotnetcore-aws-lambda-function/">Customizing logging in a C# dotnetcore AWS Lambda function</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.boro2g.co.uk/customizing-logging-in-a-c-dotnetcore-aws-lambda-function/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Automating a multi region deployment with Azure Devops</title>
		<link>https://blog.boro2g.co.uk/automating-a-multi-region-deployment-with-azure-devops/</link>
					<comments>https://blog.boro2g.co.uk/automating-a-multi-region-deployment-with-azure-devops/#comments</comments>
		
		<dc:creator><![CDATA[boro]]></dc:creator>
		<pubDate>Fri, 18 Oct 2019 11:12:24 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Sitecore]]></category>
		<guid isPermaLink="false">https://blog.boro2g.co.uk/?p=1085</guid>

					<description><![CDATA[<p>For a recent project we&#8217;ve invested a lot of time into Azure Devops, and in the most part found it a very useful toolset for deploying our code to both Azure and AWS. When we started on this process, YAML pipelines weren&#8217;t available for our source code provider &#8211; this meant everything had to be [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/automating-a-multi-region-deployment-with-azure-devops/">Automating a multi region deployment with Azure Devops</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>For a recent project we&#8217;ve invested a lot of time into Azure Devops, and in the most part found it a very useful toolset for deploying our code to both Azure and AWS.</p>



<p>When we started on this process, YAML pipelines weren&#8217;t available for our source code provider &#8211; this meant everything had to be setup manually <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f641.png" alt="🙁" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>However, recently this has changed <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /> This post will run through a few ways you can optimize your release process and automate the whole thing. </p>



<p>First a bit of background and then some actual code examples.</p>



<p><strong>Why YAML</strong>?</p>



<p>Setting up your pipelines via the UI is a really good way to quickly prototype things, however what if you need to change these pipelines to mimic deployment features alongside code features. Yaml allows you to keep the pipeline definition in the same codebase as the actual features. You deploy branch XXX and that can be configured differently to branch YYY.</p>



<p>Another benefit, the changes are then visible in your pull requests so validating changes is a lot easier.</p>



<p><strong>Async Jobs</strong></p>



<p>A big optimization we gained was to release to different regions in parallel. Yaml makes this very easy by using Jobs &#8211; each job can run on an agent and hence push to multiple regions in parallel.</p>



<p><a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&amp;tabs=yaml">https://docs.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&amp;tabs=yaml</a></p>



<p><strong>Yaml file templates</strong></p>



<p>If you have common functionality you want to duplicate, e.g. &#8216;Deploy to Eu-West-1&#8217;, templates are a good way to split your functionality. They allow you to group logical functionality you want to run multiple times.</p>



<p><a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/process/templates?view=azure-devops">https://docs.microsoft.com/en-us/azure/devops/pipelines/process/templates?view=azure-devops</a></p>



<p><strong>Azure Devops rest API</strong></p>



<p>All of your build/releases can be triggered via the UI portal, however if you want to automate that process I&#8217;d suggest looking into the rest API. Via this you can trigger, monitor and administer builds, releases and a whole load more. </p>



<p>We use powershell to orchestrate the process.</p>



<p><a href="https://docs.microsoft.com/en-us/rest/api/azure/devops/build/builds/queue?view=azure-devops-rest-5.1">https://docs.microsoft.com/en-us/rest/api/azure/devops/build/builds/queue?view=azure-devops-rest-5.1</a></p>



<p><strong>Variables, and variable groups</strong></p>



<p>I have to confess, this syntax feels slightly cumbersome, but it&#8217;s very possible to reference variables passed into a specific pipeline along with global variables from groups you setup in the <strong>Library </strong>section of the portal.</p>



<p><strong>Now, some examples</strong></p>



<p>The root YAML file:</p>



<pre class="crayon-plain-tag">pr: none
trigger: none

variables:
- group: 'DataDog' # reference Variable groups if needed
- name : 'system.debug'
  value: true
- name : 'DynamicParameter' # these can be calculated off other variable values
  value: &quot;name-$(EnvironmentName)-$(ColourName)&quot;
- name: 'WebsiteFolder'
  value: 'Website/FolderName'

#- name: &quot;EnvironmentName&quot; # see the rest api example below for how to pass in variables
#  value: &quot;Set externally&quot;
#- name: &quot;ColourName&quot;
#  value: &quot;Set externally&quot;
#- name: &quot;AwsCredentials&quot;
#  value: &quot;Set externally&quot;

jobs:
- job: Build
  pool:
    vmImage: 'windows-2019' # vmImages: https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops#use-a-microsoft-hosted-agent
  steps:  
 
  - task: NuGetToolInstaller@0
    displayName: 'Use NuGet 4.4.1'
    inputs:
      versionSpec: 4.4.1    

  - task: NuGetCommand@2 # if using secure artifacts, you can download them into a dotnetcore project this way
    displayName: 'NuGet restore'
    inputs:
      restoreSolution: 'Website/###.sln'
      feedsToUse: config
      nugetConfigPath: Website/nuget.config

  - task: Npm@1
    displayName: 'NPM install'
    inputs:
      workingDir: '$(WebsiteFolder)'     
      verbose: false

  - task: Npm@1
    displayName: 'NPM build scss'
    inputs:
      workingDir: '$(WebsiteFolder)'     
      command: custom
      verbose: false
      customCommand: 'run scss-build'

  - task: DotNetCoreCLI@2
    displayName: 'dotnet publish'
    inputs:
      command: publish
      publishWebProjects: false
      projects: '$(WebsiteFolder)/Website.csproj'
      arguments: '--configuration Release --output $(Build.ArtifactStagingDirectory)\Website'
      zipAfterPublish: false  

  - task: PublishPipelineArtifact@0 # in order to share the common build with multiple releases you need to publish the artifact
    inputs:
      artifactName: &quot;Website&quot;
      targetPath: '$(Build.ArtifactStagingDirectory)'

- job: ReleaseEU
  pool:
    vmImage: 'windows-2019'
  dependsOn: Build # these will only start when the 'Build' task above starts
  steps:
  - template: TaskGroups/DeployToRegion.yaml # this
    parameters:
      AwsCredentials: '$(AwsCredentials)'
      RegionName: 'eu-west-1'      
      EnvironmentName: '$(EnvironmentName)'
      ColourName: '$(ColourName)'
      DatadogApiKey: '$(DatadogApiKey)' # referenced from a variable group      

- job: ReleaseRegionN # Will run in parallel with ReleaseEU if you have enough build agents
  pool:
    vmImage: 'windows-2019'
  dependsOn: Build
  steps:
  - template: TaskGroups/DeployToRegion.yaml # this template file is shown below
    parameters:
      AwsCredentials: '$(AwsCredentials)'
      RegionName: 'ANother region'      
      EnvironmentName: '$(EnvironmentName)'
      ColourName: '$(ColourName)'
      DatadogApiKey: '$(DatadogApiKey)' # referenced from a variable group</pre>



<p>The &#8216;DeployToRegion&#8217; template:</p>



<pre class="crayon-plain-tag">parameters:
  AwsCredentials: ''
  RegionName: ''  
  EnvironmentName: ''
  ColourName: ''
  DatadogApiKey: ''  

steps:
- task: DownloadPipelineArtifact@1 # you can download artifacts from other builds if needed
  inputs:
      buildType: 'specific'
      project: 'Project Name'
      pipeline: '##'
      buildVersionToDownload: 'latest'
      artifactName: 'Devops'
      targetPath: '$(System.ArtifactsDirectory)/Devops'

- task: DownloadPipelineArtifact@1 # or download from the current one
  inputs:
      buildType: 'current'
      artifactName: 'Website'
      targetPath: '$(System.ArtifactsDirectory)'

- template: DeployToElasticBeanstalk.yaml # and can chain templates if needed
  parameters:
      AwsCredentials: '${{ parameters.AwsCredentials }}'
      RegionName: '${{ parameters.RegionName }}'      
      EnvironmentName: '${{ parameters.EnvironmentName }}'
      ColourName: '${{ parameters.ColourName }}'
      DatadogApiKey: '${{ parameters.DatadogApiKey }}'</pre>



<p>And finally some powershell to fire it all off:</p>



<pre class="crayon-plain-tag">### Example usage: .\TriggerBuild.ps1 -branch &quot;release/release-006&quot; -isReleaseCandidate $false -additionalReleaseParameters @{ &quot;EnvironmentName&quot; = &quot;qa&quot;; &quot;ColourName&quot; = &quot;blue&quot;; }

param (
    [Parameter(Mandatory = $true)][string]$branch,   
    [boolean]$isReleaseCandidate = $false,
    [HashTable]$additionalReleaseParameters = @{ }
)

$ErrorActionPreference = &quot;Stop&quot;

$authToken = Get-DevOpsAuthToken # see https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops for how to get a token
$accountName = &quot;AzureDevopsAccountName&quot; 
$projectName = &quot;AzureDevopsProjectName&quot;

$buildDefinitionIds = @(27) # the build pipeline id

Write-Host &quot;Building with settings:&quot;
Write-Host &quot;Branch: '$branch'&quot;
Write-Host &quot;Tag as 'release-candidate' and retain build: $isReleaseCandidate&quot;
Write-Host &quot;Build definition IDs: $buildDefinitionIds&quot;
Write-Host &quot;Additional parameters: $($additionalReleaseParameters | ConvertTo-Json) &quot;
Write-Host &quot;&quot;

$releaseIds = @()

$result = @{
    Success = $false;    
}

foreach ($definitionId in $buildDefinitionIds)
{
    $deploymentParams = @{
        &quot;definition&quot; = @{
            &quot;id&quot; = $definitionId;
        }
        &quot;sourceBranch&quot; = $branch;
    }

    if ($additionalReleaseParameters.GetEnumerator().length -gt 0)
    {
        $deploymentParams.parameters = $additionalReleaseParameters | ConvertTo-Json
    }

    $content = (Invoke-WebRequest -uri &quot;https://dev.azure.com/$accountName/$projectName/_apis/build/builds?api-version=4.1&quot; `
        -ContentType &quot;application/json&quot; -Headers (Get-DevOpsHeaders -AuthToken $authToken) -Method POST -Body ($deploymentParams | ConvertTo-Json)).Content | ConvertFrom-Json

    $releaseIds += $content.id  

    Write-Host &quot;Build $($content.id) queued: https://dev.azure.com/$accountName/$projectName/_build/results?buildId=$($content.id)&quot; -ForegroundColor Yellow
}

$aBuildFailed = $false

foreach ($releaseId in $releaseIds)
{
    $status = &quot;&quot;

    while ($status -ne &quot;completed&quot;)
    {
        try
        {
            $content = (Invoke-WebRequest -uri &quot;https://dev.azure.com/$accountName/$projectName/_apis/build/builds/$releaseId&quot; -Headers (Get-DevOpsHeaders -AuthToken $authToken)).Content | ConvertFrom-Json
        }
        catch
        {
            Write-Host &quot;  Error calling DevopsAPI. If this happens several times check the url: https://dev.azure.com/$accountName/$projectName/_apis/build/builds/$releaseId&quot; -ForegroundColor red
        }

        $status = $content.status

        Write-Host &quot; Build id $releaseId has status: $status&quot;

        if ($content.result -eq &quot;failed&quot; -or $content.result -eq &quot;canceled&quot;)
        {
            $aBuildFailed = $true

            Write-Host &quot;Build $releaseId failed - check https://dev.azure.com/$accountName/$projectName/_build/results?buildId=$releaseId for details&quot; -ForegroundColor Red
        }
        elseif ($content.result -eq &quot;completed&quot;)
        {
            Write-Host &quot;Build $releaseId completed successfully&quot; -ForegroundColor Green
        }

        Start-Sleep -s 5
    }

    if ($isReleaseCandidate -eq $true)
    {
        Write-Host &quot; Adding RC tags: release-candidate&quot;
        $tags = (Invoke-WebRequest -uri &quot;https://dev.azure.com/$accountName/$projectName/_apis/build/builds/$releaseId/tags/release-candidate?api-version=4.1&quot; -Headers (Get-DevOpsHeaders -AuthToken $authToken) -Method PUT).Content | ConvertFrom-Json

        Write-Host &quot; Adding retain build to $releaseId&quot;
        $updates = (Invoke-WebRequest -uri &quot;https://dev.azure.com/$accountName/$projectName/_apis/build/builds/$($releaseId)?api-version=4.1&quot; -ContentType &quot;application/json&quot; -Headers (Get-DevOpsHeaders -AuthToken $authToken) -Method PATCH -Body (@{&quot;retainedByRelease&quot; = $true } | ConvertTo-Json)).Content | ConvertFrom-Json
    }
}

$result.Success = !$aBuildFailed

return $result</pre>



<p>Happy deploying <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/automating-a-multi-region-deployment-with-azure-devops/">Automating a multi region deployment with Azure Devops</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.boro2g.co.uk/automating-a-multi-region-deployment-with-azure-devops/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>JSS Blog post series</title>
		<link>https://blog.boro2g.co.uk/jss-blog-post-series/</link>
					<comments>https://blog.boro2g.co.uk/jss-blog-post-series/#respond</comments>
		
		<dc:creator><![CDATA[boro]]></dc:creator>
		<pubDate>Mon, 24 Jun 2019 09:34:25 +0000</pubDate>
				<category><![CDATA[Sitecore]]></category>
		<category><![CDATA[sitecore]]></category>
		<guid isPermaLink="false">https://blog.boro2g.co.uk/?p=1081</guid>

					<description><![CDATA[<p>I&#8217;ve recently been working with the Marketing team within Valtech to get a series of JSS Blog posts published onto the Valtech site. If anyone is interested you can access them via https://www.valtech.com/en-gb/insights/going-live-with-jss/ The topics cover things like what it’s like to move from being a traditional Sitecore dev to a JSS dev, how to [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/jss-blog-post-series/">JSS Blog post series</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>I&#8217;ve recently been working with the Marketing team within Valtech to get a series of JSS Blog posts published onto the Valtech site.</p>



<p>If anyone is interested you can access them via  <a href="https://www.valtech.com/en-gb/insights/going-live-with-jss/">https://www.valtech.com/en-gb/insights/going-live-with-jss/</a> </p>



<p><em>The topics cover things like what it’s like to move from being a traditional Sitecore dev to a JSS dev, how to get everything deployed, any gotchas we didn&#8217;t estimate for when we started and some key design decisions we made along the way. </em></p>



<p>I hope you find them useful <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/jss-blog-post-series/">JSS Blog post series</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.boro2g.co.uk/jss-blog-post-series/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Setting a row colour in powershell &#124; Format-Table</title>
		<link>https://blog.boro2g.co.uk/setting-a-row-colour-in-powershell-format-table/</link>
					<comments>https://blog.boro2g.co.uk/setting-a-row-colour-in-powershell-format-table/#respond</comments>
		
		<dc:creator><![CDATA[boro]]></dc:creator>
		<pubDate>Wed, 17 Apr 2019 15:33:30 +0000</pubDate>
				<category><![CDATA[Sitecore]]></category>
		<guid isPermaLink="false">https://blog.boro2g.co.uk/?p=1021</guid>

					<description><![CDATA[<p>This is quite a quick post, but a useful tip. If you are setting up some data in powershell which you then fire at the console via &#124; Format-Table it can be useful to highlight specific rows. Imagine you have a hashtable with the key as a string, and the value as a number. When [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/setting-a-row-colour-in-powershell-format-table/">Setting a row colour in powershell | Format-Table</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>This is quite a quick post, but a useful tip. If you are setting up some data in powershell which you then fire at the console via <strong>| Format-Table</strong> it can be useful to highlight specific rows.</p>



<p>Imagine you have a hashtable with the key as a string, and the value as a number. When you send to the console you will see the names and values set in a table. </p>



<pre class="crayon-plain-tag">@{&quot;Bob&quot;=1;&quot;John&quot;=3;} | Format-Table</pre>



<p>Now if you want to set <strong>John</strong> to be a certain colour then you can use the code below.  <br><em>Note for static values this doesn&#8217;t add much value, we use it for a table that is getting printed dynamically e.g. based on a timer tick and dynamic version of the Name </em></p>



<pre class="crayon-plain-tag">@{&quot;Bob&quot;=1;&quot;John&quot;=3;} | Format-Table @{
            Label = &quot;Name&quot;
            Expression =
            {
                if (&quot;John&quot; -eq $_.Name)
                {
                    $color = &quot;32&quot; #green
                }
                else
                {
                    $color = &quot;0&quot; #white
                }
                $e = [char]27
                &quot;$e[${color}m$($_.Name)${e}[0m&quot;
            }
        }, Value</pre>



<p>This requires PowerShell 5.1 or later (check with <code>$PSVersionTable.PSVersion</code>)  and doesn&#8217;t seem to play fair with the PowerShell ISE, however from a normal PowerShell window or VSCode it works a charm.</p>



<p>Happy colouring <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The post <a rel="nofollow" href="https://blog.boro2g.co.uk/setting-a-row-colour-in-powershell-format-table/">Setting a row colour in powershell | Format-Table</a> appeared first on <a rel="nofollow" href="https://blog.boro2g.co.uk">blog.boro2g .co.uk</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.boro2g.co.uk/setting-a-row-colour-in-powershell-format-table/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
