{"@attributes":{"version":"2.0"},"channel":{"title":{},"link":"https:\/\/blog.carrio.dev","description":{},"generator":"Zola","language":"en","lastBuildDate":"Fri, 21 Nov 2025 00:00:00 +0000","item":[{"title":"TypeScript Nested Type Selection","pubDate":"Fri, 21 Nov 2025 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/typescript-nested-type-selection\/","guid":"https:\/\/blog.carrio.dev\/blog\/typescript-nested-type-selection\/","description":"<p>In TypeScript, you often need to take subsets of types. There are several utility types that can help you achieve this, such as <code>Pick<\/code>, <code>Omit<\/code>, and <code>Partial<\/code>.<\/p>\n<p>However, these utility types at scale can feel pretty clunky and can lead to verbose code. To address this, I worked on a feature called <strong>nested type selection<\/strong>.<\/p>\n<p>If you have ever worked with GraphQL, you might recognize the pattern of nested type selection. This pattern allows you to create a new type that is a subset of an existing type, while also allowing you to specify the properties that you want to include explicitly.<\/p>\n<p>In large, the need for this utility type arose from the code generation capabilities of GraphQL tools. These tools often generate types that are nested within other types, and it can be difficult to extract the necessary information from these deeply nested types.<\/p>\n<p>To that end, I showcase here the <code>Select<\/code> utility type.<\/p>\n<h2 id=\"the-current-condition\">The Current Condition<\/h2>\n<p>Let's start with the origin type definition. This will be a type that has various nested properties which we want to pick out from the type.<\/p>\n<p>The original types, which might be generated by an OpenAPI or GraphQL Codegen tool, could look something like this:<\/p>\n<pre data-lang=\"typescript\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#55859b;\">interface <\/span><span>Post {\n<\/span><span>  <\/span><span style=\"color:#b16139;\">id<\/span><span>: string;\n<\/span><span>  <\/span><span style=\"color:#b16139;\">title<\/span><span>: string;\n<\/span><span>  <\/span><span style=\"color:#b16139;\">content<\/span><span>: string;\n<\/span><span>  <\/span><span style=\"color:#b16139;\">author<\/span><span>: User;\n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">interface <\/span><span>User {\n<\/span><span>  <\/span><span style=\"color:#b16139;\">id<\/span><span>: string;\n<\/span><span>  <\/span><span style=\"color:#b16139;\">email<\/span><span>: string;\n<\/span><span>  <\/span><span style=\"color:#b16139;\">name<\/span><span>: string;\n<\/span><span>  <\/span><span style=\"color:#b16139;\">age<\/span><span>: number;\n<\/span><span>  <\/span><span style=\"color:#b16139;\">address<\/span><span>: {\n<\/span><span>    <\/span><span style=\"color:#b16139;\">street<\/span><span>: string;\n<\/span><span>    <\/span><span style=\"color:#b16139;\">city<\/span><span>: string;\n<\/span><span>    <\/span><span style=\"color:#b16139;\">state<\/span><span>: string;\n<\/span><span>    <\/span><span style=\"color:#b16139;\">zip<\/span><span>: string;\n<\/span><span>  };\n<\/span><span>  <\/span><span style=\"color:#b16139;\">friends<\/span><span>: Array&lt;User&gt;;\n<\/span><span>  <\/span><span style=\"color:#b16139;\">posts<\/span><span>: Array&lt;Post&gt;;\n<\/span><span>};\n<\/span><\/code><\/pre>\n<p>Especially with GraphQL, which allows recursive type relationships, data can often become deeply nested. Defining a type based off this normally would require a combination of <code>Pick<\/code> or <code>Omit<\/code> and intercepting the type with itself, redefining properties as references to the original type.<\/p>\n<p>Let's start with a React component. Best practices would be to define a type for this component's props that require only what is referenced.<\/p>\n<pre data-lang=\"tsx\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-tsx \"><code class=\"language-tsx\" data-lang=\"tsx\"><span style=\"color:#55859b;\">function <\/span><span style=\"color:#478c90;\">Post<\/span><span>(<\/span><span style=\"color:#b16139;\">props<\/span><span>: PostComponentProps) {\n<\/span><span>  <\/span><span style=\"color:#55859b;\">const <\/span><span>{ <\/span><span style=\"color:#b16139;\">friends <\/span><span>} = <\/span><span style=\"color:#b16139;\">props<\/span><span>.<\/span><span style=\"color:#b16139;\">post<\/span><span>.<\/span><span style=\"color:#b16139;\">author<\/span><span>;\n<\/span><span>  <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">totalFriends <\/span><span>= <\/span><span style=\"color:#b16139;\">friends<\/span><span>.length;\n<\/span><span>  <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">friendsBlock <\/span><span>= <\/span><span style=\"color:#478c90;\">useMemo<\/span><span>(() <\/span><span style=\"color:#55859b;\">=&gt;\n<\/span><span>    <\/span><span style=\"color:#b16139;\">totalFriends <\/span><span>&gt; <\/span><span style=\"color:#9f713c;\">1\n<\/span><span>      ? &lt;<\/span><span style=\"color:#b16139;\">p<\/span><span>&gt;Friends with &lt;<\/span><span style=\"color:#b16139;\">b<\/span><span>&gt;<\/span><span style=\"color:#867469;\">{<\/span><span style=\"color:#b16139;\">friends<\/span><span>[<\/span><span style=\"color:#9f713c;\">0<\/span><span>].name<\/span><span style=\"color:#867469;\">}<\/span><span>&lt;\/<\/span><span style=\"color:#b16139;\">b<\/span><span>&gt; and <\/span><span style=\"color:#867469;\">{<\/span><span style=\"color:#b16139;\">totalFriends <\/span><span>- <\/span><span style=\"color:#9f713c;\">1<\/span><span style=\"color:#867469;\">}<\/span><span> others&lt;\/<\/span><span style=\"color:#b16139;\">p<\/span><span>&gt;\n<\/span><span>      : <\/span><span style=\"color:#b16139;\">totalFriends <\/span><span>=== <\/span><span style=\"color:#9f713c;\">1\n<\/span><span>        ? &lt;<\/span><span style=\"color:#b16139;\">p<\/span><span>&gt;Friends with &lt;<\/span><span style=\"color:#b16139;\">b<\/span><span>&gt;<\/span><span style=\"color:#867469;\">{<\/span><span style=\"color:#b16139;\">friends<\/span><span>[<\/span><span style=\"color:#9f713c;\">0<\/span><span>].name<\/span><span style=\"color:#867469;\">}<\/span><span>&lt;\/<\/span><span style=\"color:#b16139;\">b<\/span><span>&gt;&lt;\/<\/span><span style=\"color:#b16139;\">p<\/span><span>&gt;\n<\/span><span>        : <\/span><span style=\"color:#9f713c;\">null<\/span><span>, [<\/span><span style=\"color:#b16139;\">totalFriends<\/span><span>]);\n<\/span><span>\n<\/span><span>  <\/span><span style=\"color:#55859b;\">return <\/span><span>&lt;<\/span><span style=\"color:#b16139;\">div<\/span><span>&gt;\n<\/span><span>    &lt;<\/span><span style=\"color:#b16139;\">h2<\/span><span>&gt;<\/span><span style=\"color:#867469;\">{<\/span><span style=\"color:#b16139;\">props<\/span><span>.<\/span><span style=\"color:#b16139;\">post<\/span><span>.title<\/span><span style=\"color:#867469;\">}<\/span><span>&lt;\/<\/span><span style=\"color:#b16139;\">h2<\/span><span>&gt;\n<\/span><span>    &lt;<\/span><span style=\"color:#b16139;\">p<\/span><span>&gt;By <\/span><span style=\"color:#867469;\">{<\/span><span style=\"color:#b16139;\">props<\/span><span>.<\/span><span style=\"color:#b16139;\">post<\/span><span>.<\/span><span style=\"color:#b16139;\">author<\/span><span>.name<\/span><span style=\"color:#867469;\">}<\/span><span>&lt;\/<\/span><span style=\"color:#b16139;\">p<\/span><span>&gt;\n<\/span><span>    <\/span><span style=\"color:#867469;\">{<\/span><span style=\"color:#b16139;\">friendsBlock<\/span><span style=\"color:#867469;\">}\n<\/span><span>  &lt;\/<\/span><span style=\"color:#b16139;\">div<\/span><span>&gt;;\n<\/span><span>}\n<\/span><\/code><\/pre>\n<p>Now let's define a type based on the API generated types. If you do this ad-hoc, you might have the, albeit redundant, simple type definition:<\/p>\n<pre data-lang=\"typescript\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#55859b;\">interface <\/span><span>PostComponentProps {\n<\/span><span>  <\/span><span style=\"color:#b16139;\">post<\/span><span>: {\n<\/span><span>    <\/span><span style=\"color:#b16139;\">id<\/span><span>: string;\n<\/span><span>    <\/span><span style=\"color:#b16139;\">title<\/span><span>: string;\n<\/span><span>    <\/span><span style=\"color:#b16139;\">author<\/span><span>: {\n<\/span><span>      <\/span><span style=\"color:#b16139;\">id<\/span><span>: string;\n<\/span><span>      <\/span><span style=\"color:#b16139;\">name<\/span><span>: string;\n<\/span><span>      <\/span><span style=\"color:#b16139;\">friends<\/span><span>: Array&lt;{\n<\/span><span>        <\/span><span style=\"color:#b16139;\">id<\/span><span>: string;\n<\/span><span>        <\/span><span style=\"color:#b16139;\">name<\/span><span>: string;\n<\/span><span>      }\n<\/span><span>    }\n<\/span><span>  }\n<\/span><span>}\n<\/span><\/code><\/pre>\n<p>This works, but it is not totally ideal. We had to redundantly define each property's type and ensure it matches the original type, otherwise the API data could not be utilized with it. This can be cumbersome and error-prone, especially during development, when dealing with deeply nested structures, or with complicated data structures which are often the case with generated types.<\/p>\n<p>If it is difficult, developers may try to escape the rules of the system by referring to types directly. However, a domain model or UI component should only specify the properties it truly needs to function, not egregious top-level type definitions that can contain circular references or other complex structures.<\/p>\n<p>This is an example where you are putting yourself in a position where not only is the type complicated, but also virtually impossible to satisfy:<\/p>\n<pre data-lang=\"typescript\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#55859b;\">interface <\/span><span>PostComponentProps {\n<\/span><span>  <\/span><span style=\"color:#b16139;\">post<\/span><span>: Post;\n<\/span><span>}\n<\/span><\/code><\/pre>\n<p>Technically, all the data we need for the component will be available, but so will a lot more data we don't need, and likely don't have available, especially with GraphQL's \"request only what you need\" philosophy.<\/p>\n<p>Now, the component must be passed a fully satisfied <code>Post<\/code> object. Unfortunately, this has circular references and much more data than is needed to function.<\/p>\n<h2 id=\"an-example-with-type-selection\">An Example with Type Selection<\/h2>\n<p>The alternative I propose here works very similarly to the functionality of <strong>selection sets<\/strong> in GraphQL[todo ref].<\/p>\n<p>You define what properties of a defined type you want to select, and the resulting type will only contain those properties. This also works for nested objects and arrays. It is a very expressive and lightweight way of defining the shape you want from some type.<\/p>\n<pre data-lang=\"typescript\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#55859b;\">import <\/span><span>{ <\/span><span style=\"color:#b16139;\">Select <\/span><span>} <\/span><span style=\"color:#55859b;\">from <\/span><span>&#39;<\/span><span style=\"color:#489963;\">.\/select<\/span><span>&#39;;\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">interface <\/span><span>PostComponentProps {\n<\/span><span>  <\/span><span style=\"color:#b16139;\">post<\/span><span>: Select&lt;Post, {\n<\/span><span>    <\/span><span style=\"color:#b16139;\">id\n<\/span><span>    <\/span><span style=\"color:#b16139;\">title\n<\/span><span>    <\/span><span style=\"color:#b16139;\">author<\/span><span>: {\n<\/span><span>      <\/span><span style=\"color:#b16139;\">id\n<\/span><span>      <\/span><span style=\"color:#b16139;\">name\n<\/span><span>      <\/span><span style=\"color:#b16139;\">friends<\/span><span>: {\n<\/span><span>        <\/span><span style=\"color:#b16139;\">id\n<\/span><span>        <\/span><span style=\"color:#b16139;\">name\n<\/span><span>      }\n<\/span><span>    }\n<\/span><span>  }&gt;\n<\/span><span>}\n<\/span><\/code><\/pre>\n<p>The resulting PostComponentProps type is equivalent to the inline definition in our first example. However, the type of <code>post<\/code> is entirely described by the shape of the data we need for the component based on some known supertype <code>Post<\/code>, ensuring compatibility with the API and practicing least privileges.<\/p>\n<p>In terms of behavior around arrays, it follows the same rules as GraphQL's selection sets. If you select a field that is an array, you can also select fields within the array elements. The resulting type infers the shape of the array elements based on the selection set and intelligently handles cases of arrays vs objects vs primitives.<\/p>\n<h2 id=\"types-and-the-lsp-server\">Types and the LSP Server<\/h2>\n<p>A major developer experience improvement offered in this approach is that the TypeScript language server will provide auto-suggestions for properties as you are filling out the <code>Select<\/code> type. This makes it easier to ensure that the shape of the data you are using matches the shape of the data you need for the component. You can also prompt the auto-suggest in your editor and what properties are available will be displayed.<\/p>\n<h2 id=\"the-magic-type\">The Magic Type<\/h2>\n<p>Here is the source for the type. There are certain limitations to this code currently, but it covers the majority of use cases. Given some interface, a selection set can be provided that describes the shape of the data you want, and the resulting type will included only the properties provided in that selection set.<\/p>\n<pre data-lang=\"typescript\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#55859b;\">type <\/span><span>Primitive = string | number | boolean | null | undefined;\n<\/span><span style=\"color:#55859b;\">type <\/span><span>IndexableObject = Record&lt;string, unknown&gt;;\n<\/span><span style=\"color:#55859b;\">type <\/span><span>AnyArray = Array&lt;unknown&gt;;\n<\/span><span style=\"color:#55859b;\">type <\/span><span>ArrayType&lt;T&gt; = T <\/span><span style=\"color:#55859b;\">extends <\/span><span>Array&lt;infer U&gt; ? U : never;\n<\/span><span style=\"color:#55859b;\">type <\/span><span>Sentinel = undefined;\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">type <\/span><span>SelectionOfObject&lt;Source <\/span><span style=\"color:#55859b;\">extends <\/span><span>IndexableObject&gt; = Partial&lt;{\n<\/span><span>    [K in keyof Source]: SelectionOfUnknown&lt;Source[K]&gt;;\n<\/span><span>}&gt;;\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">type <\/span><span>SelectionOfArray&lt;Source <\/span><span style=\"color:#55859b;\">extends <\/span><span>AnyArray&gt; = Partial&lt;{\n<\/span><span>    [K in keyof ArrayType&lt;Source&gt;]: SelectionOfUnknown&lt;ArrayType&lt;Source&gt;[K]&gt;;\n<\/span><span>}&gt;;\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">type <\/span><span>SelectionOfUnknown&lt;Source&gt; = Source <\/span><span style=\"color:#55859b;\">extends <\/span><span>AnyArray\n<\/span><span>    ? SelectionOfArray&lt;Source&gt;\n<\/span><span>    : Source <\/span><span style=\"color:#55859b;\">extends <\/span><span>IndexableObject\n<\/span><span>      ? SelectionOfObject&lt;Source&gt;\n<\/span><span>      : Sentinel;\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">export type <\/span><span>Select&lt;Source <\/span><span style=\"color:#55859b;\">extends <\/span><span>IndexableObject | AnyArray, Selection <\/span><span style=\"color:#55859b;\">extends <\/span><span>SelectionOfUnknown&lt;Source&gt;&gt; = {\n<\/span><span>    [K in keyof Selection &amp; keyof Source]: Selection[K] <\/span><span style=\"color:#55859b;\">extends <\/span><span>Sentinel\n<\/span><span>        ? Source[K]\n<\/span><span>        : Source[K] <\/span><span style=\"color:#55859b;\">extends <\/span><span>AnyArray\n<\/span><span>          ? ArrayType&lt;Source[K]&gt; <\/span><span style=\"color:#55859b;\">extends <\/span><span>IndexableObject\n<\/span><span>              ? Selection[K] <\/span><span style=\"color:#55859b;\">extends <\/span><span>SelectionOfUnknown&lt;ArrayType&lt;Source[K]&gt;&gt;\n<\/span><span>                  ? ArrayType&lt;Source[K]&gt; <\/span><span style=\"color:#55859b;\">extends <\/span><span>IndexableObject\n<\/span><span>                      ? Array&lt;Select&lt;ArrayType&lt;Source[K]&gt;, Selection[K]&gt;&gt;\n<\/span><span>                      : ArrayType&lt;Source[K]&gt; <\/span><span style=\"color:#55859b;\">extends <\/span><span>Primitive\n<\/span><span>                        ? Array&lt;SelectionOfUnknown&lt;ArrayType&lt;Source[K]&gt;&gt;&gt;\n<\/span><span>                        : never\n<\/span><span>                  : never\n<\/span><span>              : never\n<\/span><span>          : Source <\/span><span style=\"color:#55859b;\">extends <\/span><span>IndexableObject\n<\/span><span>            ? Selection[K] <\/span><span style=\"color:#55859b;\">extends <\/span><span>SelectionOfUnknown&lt;Source[K]&gt;\n<\/span><span>                ? Source[K] <\/span><span style=\"color:#55859b;\">extends <\/span><span>IndexableObject\n<\/span><span>                    ? Select&lt;Source[K], Selection[K]&gt;\n<\/span><span>                    : Source[K] <\/span><span style=\"color:#55859b;\">extends <\/span><span>Primitive\n<\/span><span>                      ? SelectionOfUnknown&lt;Source[K]&gt;\n<\/span><span>                      : never\n<\/span><span>                : never\n<\/span><span>            : never;\n<\/span><span>};\n<\/span><\/code><\/pre>\n"},{"title":"TypeScript Object Constants: A Flexible Enum Alternative","pubDate":"Mon, 10 Nov 2025 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/typescript-enum-alternative-object-constants\/","guid":"https:\/\/blog.carrio.dev\/blog\/typescript-enum-alternative-object-constants\/","description":"<p>Enums in TypeScript are very useful and have some utility behavior, but they are not without their limitations.<\/p>\n<p>The primary one you may run into, especially if you are crossing package boundaries such as generated GraphQL types, internal type definitions, or stubbing test data; is that when an enum type is expected, only a direct reference to the enum can be utilized. This is true even if the values stored in that enum are identical.<\/p>\n<h3 id=\"showcase-enum-limitations\">Showcase: Enum Limitations<\/h3>\n<p>Here is a quick showcase of this behavior for enums:<\/p>\n<pre data-lang=\"typescript\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#55859b;\">enum <\/span><span>EnumTypes {\n<\/span><span>  <\/span><span style=\"color:#b16139;\">First <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">FIRST<\/span><span>&quot;,\n<\/span><span>  <\/span><span style=\"color:#b16139;\">Second <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">SECOND<\/span><span>&quot;,\n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">function <\/span><span style=\"color:#478c90;\">withEnum<\/span><span>(<\/span><span style=\"color:#b16139;\">enumType<\/span><span>: EnumTypes) {\n<\/span><span>  <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#b16139;\">enumType<\/span><span>;\n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#478c90;\">withEnum<\/span><span>(<\/span><span style=\"color:#b16139;\">EnumTypes<\/span><span>.<\/span><span style=\"color:#b16139;\">First<\/span><span>);  <\/span><span style=\"color:#5f6d64;\">\/\/ \ud83d\udfe9 You referenced the enum value via the enum reference. Type checker has PASSED.\n<\/span><span style=\"color:#478c90;\">withEnum<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">FIRST<\/span><span>&quot;);          <\/span><span style=\"color:#5f6d64;\">\/\/ \ud83d\udca5 You referenced the equivalent enum value via a string reference. Type checker has FAILED.\n<\/span><\/code><\/pre>\n<h3 id=\"the-object-constant-pattern\">The Object Constant Pattern<\/h3>\n<p>There is another common pattern that you might have seen as an alternative to TypeScript enums: Object constants.<\/p>\n<p>If you are not familiar with <code>as const<\/code> syntax, this is called a <strong><a href=\"https:\/\/www.typescriptlang.org\/docs\/handbook\/release-notes\/typescript-3-4.html#const-assertions\">const assertion<\/a><\/strong>. A quick summary is that it makes a definition a literal, immutable value. That comes with convenient behavior in that TypeScript infers that type as exactly what is defined. In addition, just like an enum cannot be modified, an object constant cannot either.<\/p>\n<p>This pattern is utilized more commonly now than ever; libraries like <code>graphql-codegen<\/code> output object constants instead of enums because of their flexibility.<\/p>\n<h3 id=\"showcase-flexibility-in-object-constants\">Showcase: Flexibility in Object Constants<\/h3>\n<p>An example extending on the above to showcase how these differ:<\/p>\n<pre data-lang=\"typescript\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">ConstTypes <\/span><span>= {\n<\/span><span>  First: &quot;<\/span><span style=\"color:#489963;\">FIRST<\/span><span>&quot;,\n<\/span><span>  Second: &quot;<\/span><span style=\"color:#489963;\">SECOND<\/span><span>&quot;,\n<\/span><span>} <\/span><span style=\"color:#55859b;\">as const<\/span><span>;\n<\/span><span style=\"color:#55859b;\">type <\/span><span>ConstType = (typeof ConstTypes)[keyof typeof ConstTypes];\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">function <\/span><span style=\"color:#478c90;\">withConst<\/span><span>(<\/span><span style=\"color:#b16139;\">constType<\/span><span>: ConstType) {\n<\/span><span>  <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#b16139;\">constType<\/span><span>;\n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ TypeScript enum compatibility:\n<\/span><span style=\"color:#478c90;\">withEnum<\/span><span>(<\/span><span style=\"color:#b16139;\">EnumTypes<\/span><span>.<\/span><span style=\"color:#b16139;\">First<\/span><span>);   <\/span><span style=\"color:#5f6d64;\">\/\/ \ud83d\udfe9 You referenced the enum value via the enum reference. Type checker has PASSED.\n<\/span><span style=\"color:#478c90;\">withEnum<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">FIRST<\/span><span>&quot;);           <\/span><span style=\"color:#5f6d64;\">\/\/ \ud83d\udca5 You referenced the equivalent enum value via a string reference. Type checker has FAILED.\n<\/span><span style=\"color:#478c90;\">withEnum<\/span><span>(<\/span><span style=\"color:#b16139;\">ConstTypes<\/span><span>.<\/span><span style=\"color:#b16139;\">First<\/span><span>);  <\/span><span style=\"color:#5f6d64;\">\/\/ \ud83d\udca5 You referenced the equivalent enum value via the const reference. Type checker has FAILED.\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ Object constant compatibilty:\n<\/span><span style=\"color:#478c90;\">withConst<\/span><span>(<\/span><span style=\"color:#b16139;\">EnumTypes<\/span><span>.<\/span><span style=\"color:#b16139;\">First<\/span><span>);  <\/span><span style=\"color:#5f6d64;\">\/\/ \ud83d\udfe9 You referenced the const value via the enum reference. Type checker has PASSED.\n<\/span><span style=\"color:#478c90;\">withConst<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">FIRST<\/span><span>&quot;);          <\/span><span style=\"color:#5f6d64;\">\/\/ \ud83d\udfe9 You referenced the equivalent via a string reference. Type checker has PASSED.\n<\/span><span style=\"color:#478c90;\">withConst<\/span><span>(<\/span><span style=\"color:#b16139;\">ConstTypes<\/span><span>.<\/span><span style=\"color:#b16139;\">First<\/span><span>); <\/span><span style=\"color:#5f6d64;\">\/\/ \ud83d\udfe9 You referenced the equivalent via the const reference. Type checker has PASSED.\n<\/span><\/code><\/pre>\n<p>The trick is in the additional <code>type ConstType<\/code> declaration, which equates to a union of all possible values in the object constant. In this case <code>\"FIRST\" | \"SECOND\"<\/code>. This still ensures correctness of value, but doesn't complain about not having used <code>ConstTypes.First<\/code> literally when you provide values that are technically correct. You can refer to <code>ConstType<\/code> for typing parameters and variables where you would have reached for <code>EnumTypes<\/code> before.<\/p>\n<p>This makes the system much easier to integrate across package boundaries.<\/p>\n<p>And in the end, they are still <strong>100% type-safe<\/strong>.<\/p>\n<p>If you are shipping a consumable package, are generating code, or simply run into a scenario where you have an enum that is the cause of some headache because you are strictly required to reference the enum directly, consider refactoring to an object constant as a solution.<\/p>\n"},{"title":"Nix Deploy Actions","pubDate":"Sun, 04 May 2025 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/nix-deploy-actions\/","guid":"https:\/\/blog.carrio.dev\/blog\/nix-deploy-actions\/","description":"<p>This topic is not a new one for those who have been following Nix. Between the Cachix Nix Action and the Determine Nix Installer Action, there are not only options but enterprise-level backing for Nix on GitHub. This post will focus on a specific example: _How I stabilized my CI deployment processes, reduced configuration drift between CI and local development, and fixed an issue that left my blog CI broken for 10 months.<\/p>\n<h2 id=\"the-situation\">The Situation<\/h2>\n<p>I was making use of the <a href=\"https:\/\/github.com\/shalzz\/zola-deploy-action\">Zola GitHub Action<\/a> (props to <a href=\"https:\/\/github.com\/shalzz\">@shalzz<\/a>) when I initially started publishing my Zola-based blog. This was very simple to get started, and worked well for me for several months. But then one day, while the Zola binary on my system was working great, the CI action started to utilize a different version that was incompatible with my configuration as it was. This brought my deployment process to its knees, and I was reminded again of the random pain you will often suffer with systems that don't have strong consistency across environments.<\/p>\n<blockquote>\n<p><em>It works on my machine \ud83e\udd37<\/em><\/p>\n<\/blockquote>\n<h2 id=\"reproducible-development-shells\">Reproducible Development Shells<\/h2>\n<p>I am already making use of Nix for a development environment. A little bit of <code>direnv<\/code>, Nix flakes, and every time I pull my repository down, regardless if it's my Linux or macOS system, as long as I have the Nix tooling configured I will pull down the exact same development environment I had when I last worked on the project. It's truly an amazing feat. Even Docker doesn't provide <em>reproducibility<\/em>, it gets you pretty far though. But here, just the configuration definition and the use of flakes and the locked inputs, I will always resolve the same environment.<\/p>\n<h2 id=\"reproducible-packages\">Reproducible Packages<\/h2>\n<p>My Nix flake defined a development shell with the packages I need, with consistency in versioning and PATH. Now I wanted to apply the same package consistency but for a command I would run. In a Nix flake, you can define a package in many different ways. In this case, it being a shell script, I was going to make use of the <code>makeWrapper<\/code> package's <code>wrapProgram<\/code> command and the <code>writeScriptBin<\/code> package in Nix to take a Bash script, provide a consistent PATH of packages, and ensure the shebangs are patched to use the correct version of the shell as well. Everything would be controlled, exactly to what I need.<\/p>\n<h2 id=\"initial-version-fork-it\">Initial Version: Fork It!<\/h2>\n<p>My first version, largely to prove that it works, simply forked the source of the Zola action's <code>entrypoint.sh<\/code>. After all, it's not like anything was really wrong with the script, just the environment it executes in. Taking this verbatim (with license and reference to upstream tacked in the header for good measure), I can use Nix to simply setup the runtime. I provide it the necessary <code>coreutils<\/code>, <code>git<\/code>, and <code>zola<\/code> of course. I condense the logic largely to make the actual package line more digestible:<\/p>\n<pre data-lang=\"nix\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-nix \"><code class=\"language-nix\" data-lang=\"nix\"><span style=\"color:#b16139;\">bundleShellScript <\/span><span>{ <\/span><span style=\"color:#9f713c;\">name <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">deploy.sh<\/span><span>&quot;; <\/span><span style=\"color:#9f713c;\">filePath <\/span><span>= <\/span><span style=\"color:#489963;\">.\/ci\/deploy.sh<\/span><span>; <\/span><span style=\"color:#9f713c;\">buildInputs <\/span><span>= <\/span><span style=\"color:#55859b;\">with <\/span><span style=\"color:#b16139;\">pkgs<\/span><span>; [ <\/span><span style=\"color:#b16139;\">git zola coreutils <\/span><span>]; }\n<\/span><\/code><\/pre>\n<p>How does this work? Well, it orchestrates a bit of the wrapping necessary for the command. Let's see the function definition I wrote:<\/p>\n<pre data-lang=\"nix\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-nix \"><code class=\"language-nix\" data-lang=\"nix\"><span style=\"color:#478c90;\">{ <\/span><span>name, filePath, buildInputs, ... <\/span><span style=\"color:#478c90;\">}<\/span><span>:\n<\/span><span style=\"color:#55859b;\">let\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">command <\/span><span>= (<\/span><span style=\"color:#b16139;\">pkgs<\/span><span>.<\/span><span style=\"color:#b16139;\">writeScriptBin name <\/span><span>(<\/span><span style=\"color:#9f713c;\">builtins<\/span><span>.<\/span><span style=\"color:#b16139;\">readFile filePath<\/span><span>)).<\/span><span style=\"color:#b16139;\">overrideAttrs<\/span><span>(old: {\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">buildCommand <\/span><span>= &quot;<\/span><span style=\"font-style:italic;color:#867469;\">${<\/span><span style=\"font-style:italic;color:#b16139;\">old<\/span><span style=\"font-style:italic;color:#87928a;\">.<\/span><span style=\"font-style:italic;color:#b16139;\">buildCommand<\/span><span style=\"font-style:italic;color:#867469;\">}<\/span><span style=\"color:#1c9aa0;\">\\n<\/span><span style=\"color:#489963;\"> patchShebangs $out<\/span><span>&quot;;\n<\/span><span>    });\n<\/span><span style=\"color:#55859b;\">in <\/span><span style=\"color:#b16139;\">pkgs<\/span><span>.<\/span><span style=\"color:#b16139;\">symlinkJoin <\/span><span>{\n<\/span><span>    <\/span><span style=\"color:#55859b;\">inherit <\/span><span style=\"color:#9f713c;\">name<\/span><span>;\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">paths <\/span><span>= [ <\/span><span style=\"color:#b16139;\">command <\/span><span>] ++ <\/span><span style=\"color:#b16139;\">buildInputs<\/span><span>;\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">buildInputs <\/span><span>= [ <\/span><span style=\"color:#b16139;\">pkgs<\/span><span>.<\/span><span style=\"color:#b16139;\">makeWrapper <\/span><span>];\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">postBuild <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">wrapProgram $out\/bin\/<\/span><span style=\"font-style:italic;color:#867469;\">${<\/span><span style=\"font-style:italic;color:#b16139;\">name<\/span><span style=\"font-style:italic;color:#867469;\">}<\/span><span style=\"color:#489963;\"> --prefix PATH : $out\/bin<\/span><span>&quot;;\n<\/span><span>}\n<\/span><\/code><\/pre>\n<p>Now I can give it any file reference and build inputs necessary for the command and we'll get a packaged command with all the necessary environment configuration to resolve the exact <code>zola<\/code>, <code>git<\/code>, etc. that I need.<\/p>\n<h2 id=\"the-diff-github-workflow-edition\">The Diff: GitHub Workflow Edition<\/h2>\n<p>So how much change was necessary on the GitHub Action workflow I was using? Well, I needed to ensure Nix was installed, for starters. But beyond that, the environment variables I pass in remain the same, it's primarily <em>how I invoke the command<\/em> that differs.<\/p>\n<pre data-lang=\"diff\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-diff \"><code class=\"language-diff\" data-lang=\"diff\"><span>diff --git a\/.github\/workflows\/publish.yaml b\/.github\/workflows\/publish.yaml\n<\/span><span>index 24b5d99..b1c2bcb 100644\n<\/span><span>--- a\/.github\/workflows\/publish.yaml\n<\/span><span>+++ b\/.github\/workflows\/publish.yaml\n<\/span><span>@@ -11,12 +11,10 @@ <\/span><span style=\"color:#478c90;\">jobs:\n<\/span><span>     runs-on: ubuntu-latest\n<\/span><span>     if: github.ref != &#39;refs\/heads\/main&#39; &amp;&amp; github.ref != &#39;refs\/heads\/gh-pages&#39;\n<\/span><span>     steps:\n<\/span><span>       - name: Checkout main\n<\/span><span>         uses: actions\/checkout@v3.0.0\n<\/span><span>         with:\n<\/span><span>           submodules: true\n<\/span><span style=\"color:#489963;\">+      - uses: DeterminateSystems\/nix-installer-action@main\n<\/span><span>       - name: Build only\n<\/span><span style=\"color:#b16139;\">-        uses: shalzz\/zola-deploy-action@master\n<\/span><span style=\"color:#489963;\">+        run: nix run .#deployAction\n<\/span><span>         env:\n<\/span><span>           BUILD_ONLY: true\n<\/span><span>           BUILD_FLAGS: --drafts\n<\/span><span>@@ -25,12 +23,10 @@ <\/span><span style=\"color:#478c90;\">jobs:\n<\/span><span>     runs-on: ubuntu-latest\n<\/span><span>     if: github.ref == &#39;refs\/heads\/main&#39;\n<\/span><span>     steps:\n<\/span><span>       - name: Checkout main\n<\/span><span>         uses: actions\/checkout@v3.0.0\n<\/span><span>         with:\n<\/span><span>           submodules: true\n<\/span><span style=\"color:#489963;\">+      - uses: DeterminateSystems\/nix-installer-action@main\n<\/span><span>       - name: Build and deploy\n<\/span><span style=\"color:#b16139;\">-        uses: shalzz\/zola-deploy-action@master\n<\/span><span style=\"color:#489963;\">+        run: nix run .#deployAction\n<\/span><span>         env:\n<\/span><span>           PAGES_BRANCH: gh-pages\n<\/span><span>           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n<\/span><\/code><\/pre>\n<p>Yes. It is actually just 6 lines of code changed to accomplish this. My commit ended up catching some formatting fixes and updating the version for the checkout action, but this captures that actual requirements for migrating to using a Nix command instead of a GitHub Action. It is <strong>wildly simple<\/strong>.<\/p>\n<ol>\n<li>Install Nix<\/li>\n<li>Run Nix commands<\/li>\n<li>???<\/li>\n<li>Profit<\/li>\n<\/ol>\n"},{"title":"Archiving YouTube Videos","pubDate":"Thu, 23 Jan 2025 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/archiving-youtube-videos\/","guid":"https:\/\/blog.carrio.dev\/blog\/archiving-youtube-videos\/","description":"<p>It all started with a wedding. You hire a videographer, you wait patiently for several weeks, and finally your video is available. It's hosted on YouTube, quick and easy to start watching right away! However I can never know if this precious video may disappear, if their account may get deleted, or some other event may occur that causes such an important moment in our lives to disappear. This post provides a straightforward solution to archiving YouTube videos, in my case with a focus on retaining as high of quality as possible.<\/p>\n<h2 id=\"downloading-content-from-youtube\">Downloading content from YouTube<\/h2>\n<p>We are not pirating content, and have permission from our videographer to store this video. I may even have an external hard drive sitting around with it. But who wants to risk that breaking or disappearing? There is an existing open source project for downloading videos from YouTube and other sites called <a href=\"https:\/\/github.com\/yt-dlp\/yt-dlp\"><strong>yt-dlp<\/strong><\/a>. This works perfectly for our scenario.<\/p>\n<p>Without diving too deep into the specifics, there are many formats of content available from YouTube even for a single \"video\". Each video on YouTube can be accessed at various resolutions and audio qualities. We start off by listing what is available with the following command:<\/p>\n<p><code>yt-dlp --list-formats 'https:\/\/www.youtube.com\/watch?v=deadbeef'<\/code><\/p>\n<p>This will output a table with the available audio and video formats for the YouTube video. It is not uncommon for the formats to be completely separate, as in you have audio-only and video-only formats. We will cover the steps necessary for this scenario.<\/p>\n<p>Let's suppose the output from the command looked something like this:<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>ID  EXT   RESOLUTION FPS CH \u2502   FILESIZE    TBR PROTO \u2502 VCODEC           VBR ACODEC      ABR ASR MORE INFO\n<\/span><span>\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n<\/span><span>sb2 mhtml 48x27        0    \u2502                   mhtml \u2502 images                                   storyboard\n<\/span><span>sb1 mhtml 80x45        0    \u2502                   mhtml \u2502 images                                   storyboard\n<\/span><span>sb0 mhtml 160x90       0    \u2502                   mhtml \u2502 images                                   storyboard\n<\/span><span>233 mp4   audio only        \u2502                   m3u8  \u2502 audio only           unknown             [en] Default\n<\/span><span>234 mp4   audio only        \u2502                   m3u8  \u2502 audio only           unknown             [en] Default\n<\/span><span>139 m4a   audio only      2 \u2502    2.12MiB    49k https \u2502 audio only           mp4a.40.5   49k 22k [en] low, m4a_dash\n<\/span><span>249 webm  audio only      2 \u2502    2.16MiB    50k https \u2502 audio only           opus        50k 48k [en] low, webm_dash\n<\/span><span>250 webm  audio only      2 \u2502    2.87MiB    66k https \u2502 audio only           opus        66k 48k [en] low, webm_dash\n<\/span><span>140 m4a   audio only      2 \u2502    5.63MiB   129k https \u2502 audio only           mp4a.40.2  129k 44k [en] medium, m4a_dash\n<\/span><span>251 webm  audio only      2 \u2502    5.64MiB   130k https \u2502 audio only           opus       130k 48k [en] medium, webm_dash\n<\/span><span>602 mp4   256x144     12    \u2502 ~  4.00MiB    92k m3u8  \u2502 vp09.00.10.08    92k video only\n<\/span><span>269 mp4   256x144     24    \u2502 ~  7.76MiB   179k m3u8  \u2502 avc1.4D400C     179k video only\n<\/span><span>160 mp4   256x144     24    \u2502    3.53MiB    81k https \u2502 avc1.4D400C      81k video only          144p, mp4_dash\n<\/span><span>603 mp4   256x144     24    \u2502 ~  7.32MiB   169k m3u8  \u2502 vp09.00.11.08   169k video only\n<\/span><span>278 webm  256x144     24    \u2502    3.71MiB    85k https \u2502 vp09.00.11.08    85k video only          144p, webm_dash\n<\/span><span>229 mp4   426x240     24    \u2502 ~ 13.83MiB   319k m3u8  \u2502 avc1.4D4015     319k video only\n<\/span><span>133 mp4   426x240     24    \u2502    7.99MiB   184k https \u2502 avc1.4D4015     184k video only          240p, mp4_dash\n<\/span><span>604 mp4   426x240     24    \u2502 ~ 13.51MiB   311k m3u8  \u2502 vp09.00.20.08   311k video only\n<\/span><span>242 webm  426x240     24    \u2502    6.30MiB   145k https \u2502 vp09.00.20.08   145k video only          240p, webm_dash\n<\/span><span>230 mp4   640x360     24    \u2502 ~ 31.52MiB   726k m3u8  \u2502 avc1.4D401E     726k video only\n<\/span><span>134 mp4   640x360     24    \u2502   15.18MiB   349k https \u2502 avc1.4D401E     349k video only          360p, mp4_dash\n<\/span><span>605 mp4   640x360     24    \u2502 ~ 26.07MiB   601k m3u8  \u2502 vp09.00.21.08   601k video only\n<\/span><span>243 webm  640x360     24    \u2502   10.97MiB   253k https \u2502 vp09.00.21.08   253k video only          360p, webm_dash\n<\/span><span>231 mp4   854x480     24    \u2502 ~ 58.71MiB  1353k m3u8  \u2502 avc1.4D401E    1353k video only\n<\/span><span>135 mp4   854x480     24    \u2502   31.68MiB   729k https \u2502 avc1.4D401E     729k video only          480p, mp4_dash\n<\/span><span>606 mp4   854x480     24    \u2502 ~ 41.26MiB   951k m3u8  \u2502 vp09.00.30.08   951k video only\n<\/span><span>244 webm  854x480     24    \u2502   18.21MiB   419k https \u2502 vp09.00.30.08   419k video only          480p, webm_dash\n<\/span><span>232 mp4   1280x720    24    \u2502 ~106.77MiB  2460k m3u8  \u2502 avc1.64001F    2460k video only\n<\/span><span>136 mp4   1280x720    24    \u2502   61.14MiB  1408k https \u2502 avc1.64001F    1408k video only          720p, mp4_dash\n<\/span><span>609 mp4   1280x720    24    \u2502 ~ 76.00MiB  1752k m3u8  \u2502 vp09.00.31.08  1752k video only\n<\/span><span>247 webm  1280x720    24    \u2502   32.92MiB   758k https \u2502 vp09.00.31.08   758k video only          720p, webm_dash\n<\/span><span>270 mp4   1920x1080   24    \u2502 ~215.93MiB  4976k m3u8  \u2502 avc1.640028    4976k video only\n<\/span><span>137 mp4   1920x1080   24    \u2502  115.89MiB  2668k https \u2502 avc1.640028    2668k video only          1080p, mp4_dash\n<\/span><span>614 mp4   1920x1080   24    \u2502 ~124.34MiB  2865k m3u8  \u2502 vp09.00.40.08  2865k video only\n<\/span><span>248 webm  1920x1080   24    \u2502   56.64MiB  1304k https \u2502 vp09.00.40.08  1304k video only          1080p, webm_dash\n<\/span><span>620 mp4   2560x1440   24    \u2502 ~352.28MiB  8118k m3u8  \u2502 vp09.00.50.08  8118k video only\n<\/span><span>271 webm  2560x1440   24    \u2502  172.62MiB  3974k https \u2502 vp09.00.50.08  3974k video only          1440p, webm_dash\n<\/span><span>625 mp4   3840x2160   24    \u2502 ~634.40MiB 14620k m3u8  \u2502 vp09.00.50.08 14620k video only\n<\/span><span>313 webm  3840x2160   24    \u2502  332.22MiB  7649k https \u2502 vp09.00.50.08  7649k video only          2160p, webm_dash\n<\/span><\/code><\/pre>\n<p>There are video resolutions of 320p, 480p, 720p, etc. all the way to 2160p (4K). Additionally, there are multiple audio options available, with some useful information providing a high-level quality such as \"low\" and \"medium\". In my case, I will prefer the highest quality available for both audio and video, and make some preferential decisions on the formats. Let's go with the Opus audio codec in medium-quality and WebM video format in 2160p, or format IDs <strong>251<\/strong> and <strong>313<\/strong> respectively.<\/p>\n<p>I will use <strong>yt-dlp<\/strong> to download each of these formats to separate files before joining them together.<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span># download the audio file\n<\/span><span>yt-dlp -f 251 -o wedding.audio.webm &#39;https:\/\/www.youtube.com\/watch?v=deadbeef&#39;\n<\/span><span>\n<\/span><span># download the video file\n<\/span><span>yt-dlp -f 313 -o cwedding.video.webm &#39;https:\/\/www.youtube.com\/watch?v=deadbeef&#39;\n<\/span><\/code><\/pre>\n<p>These are both saved to WebM files, as it is only a container format that supports various video and audio formats within itself. With both files downloaded, our next task will be to join the two files together into a single, 4K AV file.<\/p>\n<h2 id=\"splicing-our-audio-and-video-together\">Splicing our audio and video together<\/h2>\n<p>A renowned tool for working with audio and video files is <a href=\"https:\/\/ffmpeg.org\/\"><strong>ffmpeg<\/strong><\/a>. We will use this to pull the audio from the audio-only WebM file and the video from the video-only WebM file. The command we will run for this is:<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>ffmpeg \\\n<\/span><span>  -i wedding.video.webm \\\n<\/span><span>  -i wedding.audio.webm \\\n<\/span><span>  -c:v copy \\\n<\/span><span>  -c:a copy \\\n<\/span><span>  -map 0:v:0 \\\n<\/span><span>  -map 1:a:0 \\\n<\/span><span>  carrio-wedding.webm\n<\/span><\/code><\/pre>\n<p>I split out the lines mostly for readability, as the command becomes more self-describing to me with this logical grouping.<\/p>\n<p>We take two input files, first the video and then the audio file.<\/p>\n<p>We copy over any video and audio codecs as in- no transcoding will be performed. This retains the Opus audio codec and the VP9 video codec used in these files.<\/p>\n<p>We provide two mappings which dictate explicitly video and audio. For the first video (0, in a zero-index tool like ffmpeg), which had the video, we will pull the video. For the second video (1), which had the audio, we will pull the audio. The target for both is 0, the output file.<\/p>\n<p>Lastly, we tell <strong>ffmpeg<\/strong> where to save the video: at <code>wedding.webm<\/code>.<\/p>\n<p>Since there was no transcoding necessary for this, the operation should complete <em>very<\/em> quickly. In my case: 468ms.<\/p>\n<h2 id=\"verifying-your-content\">Verifying your content<\/h2>\n<p>Load your content in a compatible video player for your content! If you decided to go with WebM &amp; Opus and targeted a high resolution like 4K, make sure you use a hardware-accelerated tool. You can also upload your content to something like Google Drive and the web player in the preview tool should automatically work for playback as well. In my case, I loaded up <a href=\"https:\/\/mpv.io\/\"><strong>mpv<\/strong><\/a> to watch this beautiful moment as it included the necessary hardware acceleration libraries for my system to playback 4K content.<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>nix-shell -p mpv --run &#39;mpv .\/wedding.webm&#39;\n<\/span><\/code><\/pre>\n<p><img src=\"https:\/\/0xc.carrio.dev\/media\/posts\/21\/wedding-screenshot-0.png\" alt=\"mpv playback of retrieving Anduril during wedding ceremony \" \/><\/p>\n"},{"title":"Datadog and StatsD for Serverless","pubDate":"Wed, 15 May 2024 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/datadog-statsd-serverless\/","guid":"https:\/\/blog.carrio.dev\/blog\/datadog-statsd-serverless\/","description":"<h2 id=\"tl-dr\">tl;dr<\/h2>\n<blockquote>\n<p><strong>Do not use StatsD metrics with Datadog for serverless applications.<\/strong> Instead, utilize the <code>distribution<\/code> metric type available in DogStatsD.<\/p>\n<\/blockquote>\n<p>You will save yourself many hours or even days of lost time trying to get it to work. The <code>distribution<\/code> metric type is specifically designed for serverless applications and is more resilient to data loss and function termination.<\/p>\n<h2 id=\"monitoring-types\">Monitoring Types<\/h2>\n<p>A quick review of available types of monitoring in applications:<\/p>\n<ul>\n<li>Logging<\/li>\n<li>Metrics<\/li>\n<li>Tracing<\/li>\n<li>Profiling<\/li>\n<\/ul>\n<p>The focus of this article is on <strong>metrics<\/strong>, specifically the use of StatsD metrics with Datadog for serverless applications.<\/p>\n<h2 id=\"serverless-architecture\">Serverless Architecture<\/h2>\n<p>Serverless architecture is a cloud computing model that allows developers to build and run applications and services without having to manage infrastructure. Serverless applications are composed of one or more functions that are triggered by events such as HTTP requests, database events, or file uploads. These functions are typically short-lived and stateless, and are executed in response to events.<\/p>\n<p>A serverless architecture is a great way to build scalable, cost-effective applications that can respond to events in real-time. However, monitoring serverless applications can be challenging due to the ephemeral nature of the functions and the lack of visibility into the underlying infrastructure.<\/p>\n<h2 id=\"statsd-architecture\">StatsD Architecture<\/h2>\n<p>StatsD is a simple, lightweight network daemon that listens for statistics, like counters and timers, sent over UDP or TCP and sends aggregates to one or more pluggable backend services (e.g., Graphite, Datadog, etc.). StatsD is commonly used to collect and aggregate metrics from applications and services and send them to a monitoring system for analysis and visualization.<\/p>\n<h2 id=\"datadog-and-statsd-dogstatsd\">Datadog and StatsD: DogStatsD<\/h2>\n<p>Datadog is a popular monitoring and observability platform that provides a wide range of features for monitoring applications and infrastructure. Datadog supports StatsD metrics natively, allowing you to send metrics from your applications to Datadog using the StatsD protocol.<\/p>\n<p>There is an embedded component in the Datadog agent called DogStatsD. This is a StatsD-compatible server that listens for metrics and forwards them to the Datadog backend. It supports a superset of the StatsD protocol, including additional metric types like <code>distribution<\/code>.<\/p>\n<p>If you are using a Datadog Agent, you can send metrics to DogStatsD using the StatsD protocol. This allows you to leverage the full power of Datadog's monitoring and alerting capabilities with your serverless applications.<\/p>\n<h2 id=\"dogstatsd-and-serverless\">DogStatsD and Serverless<\/h2>\n<p>When using DogStatsD with serverless applications, it is important to be aware of the limitations of the StatsD protocol. The StatsD protocol was designed for traditional server-based applications and may not be well-suited for serverless applications due to the ephemeral nature of the functions. StatsD servers typically aggregate metrics in memory and flush them to the backend at regular intervals. The implications of this are two-fold:<\/p>\n<ol>\n<li>This can lead to data loss if the server is restarted or if the function is terminated before the metrics are flushed.<\/li>\n<li>The in-memory aggregation of metrics is necessary for the data to be handed off successfully to Datadog, relying on properties such as <code>host<\/code> in the agent.<\/li>\n<\/ol>\n<h2 id=\"metrics-in-serverless-functions\">Metrics in Serverless Functions<\/h2>\n<p>Datadog provides capabilities for serverless monitoring including a Lambda Layer which provides an embedded Serverless Datadog Agent. This supports most of the same functionalities as the Datadog Agent in a traditional server environment.<\/p>\n<blockquote>\n<p>\u26a0\ufe0f Due to the second limitation of DogStatsD and Serverless though, it is important to use the <code>distribution<\/code> metric type instead of the <code>gauge<\/code> or <code>count<\/code> types.<\/p>\n<\/blockquote>\n<p>The <code>distribution<\/code> metric type is specifically designed for serverless applications and is more resilient to data loss and function termination. It does not rely on in-memory aggregation, and the data is sent directly to the Datadog backend.<\/p>\n<h2 id=\"why-do-statsd-metrics-not-work-well-with-serverless\">Why do StatsD metrics not work well with serverless?<\/h2>\n<p>There are many components involved in the failure mode of StatsD metrics in serverless environments. Here are a few:<\/p>\n<ul>\n<li><strong>Data Loss<\/strong>: The ephemeral nature of serverless functions can lead to data loss if the function is terminated before the metrics are flushed to the backend.<\/li>\n<li><strong>In-Memory Aggregation<\/strong>: StatsD servers typically aggregate metrics in memory and flush them to the backend at regular intervals. This can lead to data loss if the function is terminated before the metrics are flushed.<\/li>\n<li><strong>Host Dependency<\/strong>: The in-memory aggregation of metrics relies on properties such as <code>host<\/code> in the agent. This can lead to data loss if the function is terminated before the metrics are flushed.<\/li>\n<\/ul>\n<h2 id=\"how-to-use-distribution-metrics-with-dogstatsd\">How to use <code>distribution<\/code> metrics with DogStatsD<\/h2>\n<p>To use the <code>distribution<\/code> metric type with DogStatsD, you need to send the metrics using the <code>distribution<\/code> method in the DogStatsD client. Here is an example of how to send a <code>distribution<\/code> metric in NodeJS:<\/p>\n<pre data-lang=\"javascript\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-javascript \"><code class=\"language-javascript\" data-lang=\"javascript\"><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">StatsD <\/span><span>= <\/span><span style=\"color:#1c9aa0;\">require<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">hot-shots<\/span><span>&quot;);\n<\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">dogstatsd <\/span><span>= new StatsD();\n<\/span><span>\n<\/span><span style=\"color:#b16139;\">dogstatsd<\/span><span>.<\/span><span style=\"color:#478c90;\">distribution<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">metric.name<\/span><span>&quot;, <\/span><span style=\"color:#9f713c;\">42<\/span><span>);\n<\/span><\/code><\/pre>\n<p>This will send a <code>distribution<\/code> metric with the name <code>metric.name<\/code> and the value <code>42<\/code> to the DogStatsD server. Behind the scenes, the way DogStatsD passes this data along is different than the way it handles <code>gauge<\/code> or <code>count<\/code> metrics. It is raw, unaggregated data that is sent directly to the Datadog backend.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>When monitoring serverless applications with Datadog, it is important to use the <code>distribution<\/code> metric type instead of the <code>gauge<\/code> or <code>count<\/code> types. This will ensure that your metrics are resilient to data loss and function termination and will provide more accurate monitoring and alerting capabilities for your serverless applications.<\/p>\n"},{"title":"DVD Backups on Linux","pubDate":"Wed, 27 Dec 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/dvd-backups-on-linux\/","guid":"https:\/\/blog.carrio.dev\/blog\/dvd-backups-on-linux\/","description":"<h1 id=\"dvd-backup\">DVD Backup<\/h1>\n<p>I recently went through the process up backing up a series of DVDs from my partners childhood. Since this process entailed multiple systems and both CPU and GPU hardware for video rendering, I figured I would capture some the steps at a high level that I took to accomplish the task.<\/p>\n<h2 id=\"background-the-hardware\">Background: The Hardware<\/h2>\n<p>During this run-through I utilized a number of systems. Namely, I pulled out my <a href=\"https:\/\/github.com\/tcarrio\/nix-config\/blob\/main\/nixos\/workstation\/t510\/default.nix\">Lenovo T510<\/a> to make use of the DVD bay, my DiskStation 420 for intermediate network storage and backup of the DVD and MP4 files, and <a href=\"https:\/\/github.com\/tcarrio\/nix-config\/blob\/main\/nixos\/workstation\/glass\/default.nix\">my personal desktop<\/a> with both an AMD Ryzen and GTX 1080Ti- both come into play in the following.<\/p>\n<h2 id=\"backing-up-dvds-with-dvdbackup\">Backing up DVDs with dvdbackup<\/h2>\n<p>Unsurprisingly, the tool <code>dvdbackup<\/code> exists as a means to backup DVDs. It mostly retains the same filesystem structure as the mounted DVD, but it's a useful tool for inspecting and saving videos from a DVD.<\/p>\n<pre data-lang=\"sh\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-sh \"><code class=\"language-sh\" data-lang=\"sh\"><span style=\"color:#5f6d64;\"># CWD must be where you want to save your DVD backup\n<\/span><span style=\"color:#b16139;\">dvdbackup -i<\/span><span> \/dev\/sr0<\/span><span style=\"color:#b16139;\"> -o<\/span><span> .<\/span><span style=\"color:#b16139;\"> -M\n<\/span><\/code><\/pre>\n<p>What you'll end up with is the main feature of the DVD but in the typical hierarchy of the original DVD itself, had you mounted <code>\/dev\/sr0<\/code> directly.<\/p>\n<blockquote>\n<p>\u2139\ufe0f If you need to recover a damaged disk, you may need to use a tool like <code>dvdisaster<\/code> or <code>ddrescue<\/code>. There is no guarantee your data is recoverable.<\/p>\n<\/blockquote>\n<h2 id=\"splicing-the-video-files\">Splicing the video files<\/h2>\n<p>The VOB files can be directly concatenated, and is this is exactly how you would perform a splicing of the video content before conversion.<\/p>\n<pre data-lang=\"sh\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-sh \"><code class=\"language-sh\" data-lang=\"sh\"><span style=\"color:#b16139;\">cat<\/span><span> VTS_01_*.VOB &gt; video.vob\n<\/span><\/code><\/pre>\n<blockquote>\n<p>\u26a0\ufe0f If you include the <code>VIDEOTS.VOB<\/code> file you may encounter audio issues with your spliced video.<\/p>\n<\/blockquote>\n<h2 id=\"syncing-to-the-sftp-server\">Syncing to the SFTP server<\/h2>\n<pre data-lang=\"sh\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-sh \"><code class=\"language-sh\" data-lang=\"sh\"><span style=\"color:#b16139;\">scp<\/span><span> .\/video.vob 0xc@nas-ds418-00:\/path\/to\/target\/video.vob\n<\/span><\/code><\/pre>\n<p>All of your operations from here <em>can<\/em> be entirely remote, if you utilize a filesystem mount for the SFTP server, and have a stable connection. In one case I encountered a packet failure and resorted to copying to and from my local system responsible for the conversions going forward.<\/p>\n<blockquote>\n<p>\u2139\ufe0f For hostnames, I'm utilizing <strong>[tailscale]<\/strong> for both private P2P networking <strong>and<\/strong> DNS resolution of private hosts.<\/p>\n<\/blockquote>\n<h2 id=\"converting-from-vob-to-mp4\">Converting from VOB to MP4<\/h2>\n<p>This was done in two separate modes: CPU and GPU. I'm on NixOS, so for me I dropped into separate shells of non-NVENC-supported ffmpeg and an NVENC-supported ffmpeg.<\/p>\n<p>One key component is that for the DVD videos (which are 420p) the best (the most 1:1 comparatively) format to target is <code>yuv420p<\/code>.<\/p>\n<h3 id=\"on-network-conversion\">On-network conversion<\/h3>\n<p>Suppose you have an SFTP server, you can mount that locally and then perform the following conversions directly in the network share.<\/p>\n<pre data-lang=\"sh\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-sh \"><code class=\"language-sh\" data-lang=\"sh\"><span style=\"color:#1c9aa0;\">cd<\/span><span> \/run\/user\/1000\/gvfs\/sftp<\/span><span style=\"color:#1c9aa0;\">\\:<\/span><span>host<\/span><span style=\"color:#1c9aa0;\">\\=<\/span><span>nas-ds418-00<\/span><span style=\"color:#1c9aa0;\">\\,<\/span><span>user<\/span><span style=\"color:#1c9aa0;\">\\=<\/span><span>0xc\/path\/to\/target\n<\/span><\/code><\/pre>\n<p>Now, within the network share, you could run the following instructions for CPU\/GPU.<\/p>\n<blockquote>\n<p>\u2139\ufe0f Alternatively, <code>scp<\/code> the VOB files to the local machine first<\/p>\n<\/blockquote>\n<h3 id=\"cpu-acceleration\">CPU acceleration<\/h3>\n<pre data-lang=\"sh\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-sh \"><code class=\"language-sh\" data-lang=\"sh\"><span style=\"color:#5f6d64;\"># without NVENC support\n<\/span><span>\n<\/span><span style=\"color:#b16139;\">ffmpeg -i<\/span><span> video.vob<\/span><span style=\"color:#b16139;\"> -vf<\/span><span> format=yuv420p video.mp4\n<\/span><\/code><\/pre>\n<h3 id=\"gpu-acceleration\">GPU acceleration<\/h3>\n<pre data-lang=\"sh\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-sh \"><code class=\"language-sh\" data-lang=\"sh\"><span style=\"color:#b16139;\">nix-shell -p<\/span><span> ffmpeg-full\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\"># in the subshell\n<\/span><span>\n<\/span><span style=\"color:#b16139;\">ffmpeg -hwaccel_device<\/span><span> 0<\/span><span style=\"color:#b16139;\"> -hwaccel<\/span><span> cuda<\/span><span style=\"color:#b16139;\"> -i<\/span><span> video.vob<\/span><span style=\"color:#b16139;\"> -c<\/span><span>:v h264_nvenc<\/span><span style=\"color:#b16139;\"> -vf<\/span><span> format=yuv420p video.mp4\n<\/span><\/code><\/pre>\n<h2 id=\"enhancements\">Enhancements<\/h2>\n<p>A project I was considering toying around with that might have made it simpler to work is the <a href=\"https:\/\/nbd.sourceforge.io\/\">Network Block Device (NBD) project<\/a>. This exists as a user-space tool for interacting with block devices that are mounted over the network. As an example, a T510 with a DVD disk drive accessible over the network could expose the disk device via NBD, and my desktop could mount it. This would save me from having to switch back and forth between the two systems.<\/p>\n<p>With NixOS, this is directly supported as a <em>service<\/em>:<\/p>\n<pre data-lang=\"nix\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-nix \"><code class=\"language-nix\" data-lang=\"nix\"><span>_:\n<\/span><span>\n<\/span><span>{\n<\/span><span>  <\/span><span style=\"color:#9f713c;\">services<\/span><span>.<\/span><span style=\"color:#9f713c;\">nbd<\/span><span>.<\/span><span style=\"color:#9f713c;\">server <\/span><span>= {\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">enable <\/span><span>= <\/span><span style=\"color:#9f713c;\">true<\/span><span>;\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">listenAddress <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">0.0.0.0<\/span><span>&quot;;\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">exports <\/span><span>= {\n<\/span><span>      <\/span><span style=\"color:#9f713c;\">dvd-drive <\/span><span>= {\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">path <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">\/dev\/sr0<\/span><span>&quot;;\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">allowAddresses <\/span><span>= [ &quot;<\/span><span style=\"color:#489963;\">10.0.0.0\/8<\/span><span>&quot; ];\n<\/span><span>      };\n<\/span><span>    };\n<\/span><span>  };\n<\/span><span>}\n<\/span><\/code><\/pre>\n<p>Then, mount the share from another system with<\/p>\n<pre data-lang=\"sh\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-sh \"><code class=\"language-sh\" data-lang=\"sh\"><span style=\"color:#5f6d64;\"># as root\n<\/span><span style=\"color:#b16139;\">nbd-client<\/span><span> t510<\/span><span style=\"color:#b16139;\"> -N<\/span><span> dvd-drive \/dev\/sr0\n<\/span><\/code><\/pre>\n<p>Now you can open the network-attached DVD via VLC as though it were a local device:<\/p>\n<pre data-lang=\"sh\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-sh \"><code class=\"language-sh\" data-lang=\"sh\"><span style=\"color:#b16139;\">vlc<\/span><span> \/dev\/sr0\n<\/span><\/code><\/pre>\n<p>Next, enjoy T2: Judgement Day \ud83d\udc4b<\/p>\n<!-- References -->\n"},{"title":"Nvidia on NixOS","pubDate":"Wed, 15 Nov 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/nvidia-on-nixos\/","guid":"https:\/\/blog.carrio.dev\/blog\/nvidia-on-nixos\/","description":"<h1 id=\"nvidia-on-nixos\">Nvidia on NixOS<\/h1>\n<p>NixOS is a really awesome Linux distribution. The declarative, functional approach of defining a configuration that applies consistently to your system is an amazing feat. I have worked with principles akin to this in my professional work for years now, applying infrastructure-as-code methodologies with Terraform &amp; HCL, Pulumi, and Ansible. So, I have not only installed NixOS on my development workstations and home servers, but also on my personal desktop. This device has an Nvidia GTX 1080Ti, and this blog encompasses some of the journey- the highs and the lows- of setting this system up.<\/p>\n<blockquote>\n<p>\u26a0\ufe0f This blog post will continue to evolve, as I document more shortcomings and friction in the NixOS journey with Nvidia.<\/p>\n<\/blockquote>\n<h2 id=\"the-highs\">The Highs<\/h2>\n<p>Declarative configuration is often much more concise and hands-off than the more imperative tools. Offerings like Ansible get you much closer to this, but not all the way. NixOS is truly <strong>holistic<\/strong> in its approach- with everything being possible to manage with Nix expressions, with the help of <a href=\"https:\/\/nix-community.github.io\/home-manager\">home-manager<\/a> particularly. These can be broken out and shared between systems. Custom packages can be defined in one place and reused across all of your NixOS systems. You can reference my <a href=\"https:\/\/github.com\/tcarrio\/nix-config\">nix-config<\/a><\/p>\n<h2 id=\"the-lows\">The Lows<\/h2>\n<p>Declarative has its downsides. The interface provided by the system for <strong>what you want<\/strong> is extremely important as to allow the implicit <strong>how we do it<\/strong> to be configurable. Suppose you have a system with an Nvidia graphics card- with NixOS, you can enable most of the behavior you'd like to have with this fairly easily. You include several lines such as which driver you want to use (proprietary vs open-source vs nouveau) as on example. The Nix expression is executed and determines the resulting system configuration.<\/p>\n<p>This is the catch- suppose that how the system is configured does not align with your desires, then you better hope that overriding this is easy or even possible based on what NixOS offers.<\/p>\n<p>In the case of this Nvidia example- many things <em>just work<\/em>, but there are a couple cases that <em>do not<\/em>, and these are the pain points that leave you stuck for weeks without a 100% functional system.<\/p>\n<h2 id=\"the-history\">The History<\/h2>\n<p>I switched from an Arch Linux system to NixOS in the desire to more easily maintain my workstation in entirety. My Arch system was over 6 years old and had reached over 1000 packages installed. What were half of these? What had I done to configure them? What configurations could be cleaned up? The evidence is scattered across the filesystem, only clear to those most familiar with the configuration rules of each particular software I had. This was a situation I wanted to escape- so I could better understand everything I had and also to be better able to maintain that system over the long-term.<\/p>\n<p>Documentation separate from code tends to decay. Once it's committed, the drift begins. Code <strong>as<\/strong> documentation breaks from this problem at its core- you don't need to have entirely separate docs from your code- often times having a service enabled or configured in some way is <strong>clear as day<\/strong>.<\/p>\n<pre data-lang=\"nix\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-nix \"><code class=\"language-nix\" data-lang=\"nix\"><span>{\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">services<\/span><span>.<\/span><span style=\"color:#9f713c;\">deathStar <\/span><span>= {\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">enabled <\/span><span>= <\/span><span style=\"color:#9f713c;\">true<\/span><span>;\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">customConfig <\/span><span>= &#39;&#39;\n<\/span><span style=\"color:#489963;\">            something something dark side\n<\/span><span style=\"color:#489963;\">        <\/span><span>&#39;&#39;;\n<\/span><span>    };\n<\/span><span>}\n<\/span><\/code><\/pre>\n<p>Even enabling Nvidia hardware is remarkably simple!<\/p>\n<pre data-lang=\"nix\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-nix \"><code class=\"language-nix\" data-lang=\"nix\"><span>{\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">hardware<\/span><span>.<\/span><span style=\"color:#9f713c;\">nvidia <\/span><span>= <\/span><span style=\"color:#55859b;\">with <\/span><span style=\"color:#b16139;\">config<\/span><span>.<\/span><span style=\"color:#b16139;\">boot<\/span><span>.<\/span><span style=\"color:#b16139;\">kernelPackages<\/span><span>.<\/span><span style=\"color:#b16139;\">nvidiaPackages<\/span><span>; {\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">package <\/span><span>= <\/span><span style=\"color:#b16139;\">stable<\/span><span>;\n<\/span><span>    };\n<\/span><span>}\n<\/span><\/code><\/pre>\n<p>The NixOS docs provide a list of which packages are available plainly <a href=\"https:\/\/blog.carrio.dev\/blog\/nvidia-on-nixos\/nvidia-driver-versions\">here<\/a>. You can now readily switch out various Nvidia driver versions with just a few characters. Want to run the latest beta version?<\/p>\n<pre data-lang=\"nix\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-nix \"><code class=\"language-nix\" data-lang=\"nix\"><span>{\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">hardware<\/span><span>.<\/span><span style=\"color:#9f713c;\">nvidia <\/span><span>= <\/span><span style=\"color:#55859b;\">with <\/span><span style=\"color:#b16139;\">config<\/span><span>.<\/span><span style=\"color:#b16139;\">boot<\/span><span>.<\/span><span style=\"color:#b16139;\">kernelPackages<\/span><span>.<\/span><span style=\"color:#b16139;\">nvidiaPackages<\/span><span>; {\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">package <\/span><span>= <\/span><span style=\"color:#b16139;\">beta<\/span><span>;\n<\/span><span>    };\n<\/span><span>}\n<\/span><\/code><\/pre>\n<h2 id=\"software-rendering-not-how-i-would-have-done-it\">Software-rendering: Not HOW I would have done it<\/h2>\n<p>This is the moment that <strong>declarative<\/strong> configuration became a pain. As I mentioned before, I replaced my Arch Linux system. I had a few games installed: Overwatch on Lutris and Elder Scrolls IV: Oblivion. These were the only two I played much of.<\/p>\n<p>I installed Steam through my Nix config. Super easy.<\/p>\n<p>I installed Elder Scrolls IV: Oblivion. Turned on the compatibility mode for Proton. Super easy.<\/p>\n<p>Started up the game: 0.00000001 FPS.<\/p>\n<p>It would appear I'm using software rendering. But why? Why in the world would my system even want to use software rendering when it has a <strong>perfectly powerful discrete graphics card<\/strong>?<\/p>\n<p>Well, let's see what's available.<\/p>\n<pre data-lang=\"sh\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-sh \"><code class=\"language-sh\" data-lang=\"sh\"><span style=\"color:#b16139;\">vulkaninfo --summary\n<\/span><\/code><\/pre>\n<p>This pops out a massive list of info, but the final section I'm including here is the most important piece:<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>Devices:\n<\/span><span>========\n<\/span><span>GPU0:\n<\/span><span>\tapiVersion         = 1.3.260\n<\/span><span>\tdriverVersion      = 545.29.2.0\n<\/span><span>\tvendorID           = 0x10de\n<\/span><span>\tdeviceID           = 0x1b06\n<\/span><span>\tdeviceType         = PHYSICAL_DEVICE_TYPE_DISCRETE_GPU\n<\/span><span>\tdeviceName         = NVIDIA GeForce GTX 1080 Ti\n<\/span><span>\tdriverID           = DRIVER_ID_NVIDIA_PROPRIETARY\n<\/span><span>\tdriverName         = NVIDIA\n<\/span><span>\tdriverInfo         = 545.29.02\n<\/span><span>\tconformanceVersion = 1.3.6.0\n<\/span><span>\tdeviceUUID         = 61ede7bb-89b8-5ef5-50a7-899e37452c5d\n<\/span><span>\tdriverUUID         = aa471f58-d70e-5a93-a86b-06356da49d1c\n<\/span><span>GPU1:\n<\/span><span>\tapiVersion         = 1.3.246\n<\/span><span>\tdriverVersion      = 0.0.1\n<\/span><span>\tvendorID           = 0x10005\n<\/span><span>\tdeviceID           = 0x0000\n<\/span><span>\tdeviceType         = PHYSICAL_DEVICE_TYPE_CPU\n<\/span><span>\tdeviceName         = llvmpipe (LLVM 16.0.6, 256 bits)\n<\/span><span>\tdriverID           = DRIVER_ID_MESA_LLVMPIPE\n<\/span><span>\tdriverName         = llvmpipe\n<\/span><span>\tdriverInfo         = Mesa 23.1.9 (LLVM 16.0.6)\n<\/span><span>\tconformanceVersion = 1.3.1.1\n<\/span><span>\tdeviceUUID         = 6d657361-3233-2e31-2e39-000000000000\n<\/span><span>\tdriverUUID         = 6c6c766d-7069-7065-5555-494400000000\n<\/span><\/code><\/pre>\n<p>It would appear that a <code>llvmpipe<\/code> device is automatically configured here. But why? I didn't turn on Mesa myself, what is a Mesa driver doing here?<\/p>\n<p>Well- there are plenty of packages that may rely on Mesa. In fact, a large number of them will. So this is going to be a dependency in the system and automatically be installed.<\/p>\n<h3 id=\"solution\">Solution?<\/h3>\n<p>Honestly, I've tried a number of things so far: from overlays and explicit configurations to not include mesa in the stack when configuring opengl.<\/p>\n<p>I'll update the post with a <strong>Solved:<\/strong> tag once I make it there. For now, I suffer. \ud83d\ude2d<\/p>\n<!-- Reference -->\n"},{"title":"direnv magic: instant project environments","pubDate":"Thu, 21 Sep 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/direnv-magic\/","guid":"https:\/\/blog.carrio.dev\/blog\/direnv-magic\/","description":"<h1 id=\"direnv-magic\">direnv magic<\/h1>\n<p>A very popular project for managing environment variables in projects today is <code>dotenv<\/code>. There are packages for various languages, like NodeJS and PHP. They are built on a simple principle: to load environment variables from a <code>.env<\/code> file located in the project root.<\/p>\n<p>When it comes to the 12 Factor App, managing environment variables in source control is generally discouraged. Instead, environment variables should be set externally, like via the OS or a container orchestrator. However, <code>dotenv<\/code> packages rely on the application loading the <code>.env<\/code> file itself, which means that the application must process a file in order to retrieve its environment configuration.<\/p>\n<p>As a whole, this practice is convenient for local development, but does not lend well to the Config principle of the 12 Factor App.<\/p>\n<h2 id=\"an-alternative-approach\">an alternative approach<\/h2>\n<p>The <code>direnv<\/code> tool allows environment variables to be set based on the directory. With <code>direnv<\/code>, you can define environment variables in a <code>.envrc<\/code> file that will be loaded automatically when entering that directory. This avoids embedding environment configuration in the application code\/source control, and makes variables configurable on a per-directory basis. It also entirely avoids having dependencies on files and the entirety of the <code>dotenv<\/code> package itself in a production application. You don't have to conditionally load files - the environment is configured automatically by the shell in local development environments, and configured by the orchestrator in production, like Kubernetes, in the exact same manner: <strong>the environment variables<\/strong>.<\/p>\n<h2 id=\"the-behavior\">the behavior<\/h2>\n<p>Working with <code>direnv<\/code>, you gain the ability to not only define environment variables in a <code>.envrc<\/code> file per directory, but also automatically configure your shell environment based on that file in other means. For example, you can automatically execute scripts or enter a Nix flake dev shell.<\/p>\n<h2 id=\"automated-secure-shell-environments\">automated, secure shell environments<\/h2>\n<p>Due to the simple approach of a <code>.envrc<\/code> file and automatic nature of <code>direnv<\/code>, it provides a streamlined solution for automatically entering development environments in a snap. It also requires that you permit a directory before <code>direnv<\/code> will load variables or execute scripts, preventing accidental exposure in untrusted directories.<\/p>\n<h2 id=\"an-example-with-nix-flakes\">an example with Nix flakes<\/h2>\n<p>Suppose we have a Nix flake in our project repository that defines a development shell environment. With <code>direnv<\/code>, we can automatically enter this shell whenever we cd into the project directory. We'll start with this project's Nix flake, which provides a shell with the necessary tools to build and develop the blog.<\/p>\n<pre data-lang=\"nix\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-nix \"><code class=\"language-nix\" data-lang=\"nix\"><span>{\n<\/span><span>  <\/span><span style=\"color:#9f713c;\">description <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">0xc dev shell<\/span><span>&quot;;\n<\/span><span>\n<\/span><span>  <\/span><span style=\"color:#9f713c;\">inputs<\/span><span>.<\/span><span style=\"color:#9f713c;\">nixpkgs<\/span><span>.<\/span><span style=\"color:#9f713c;\">url <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">github:NixOS\/nixpkgs\/nixpkgs-unstable<\/span><span>&quot;;\n<\/span><span>\n<\/span><span>  <\/span><span style=\"color:#9f713c;\">outputs <\/span><span>= <\/span><span style=\"color:#478c90;\">{ <\/span><span>pkgs <\/span><span style=\"color:#478c90;\">}<\/span><span>: {\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">devShells<\/span><span>.&quot;<\/span><span style=\"color:#489963;\">x86_64-linux<\/span><span>&quot;.<\/span><span style=\"color:#9f713c;\">default <\/span><span>= <\/span><span style=\"color:#b16139;\">pkgs<\/span><span>.<\/span><span style=\"color:#b16139;\">mkShell <\/span><span>{\n<\/span><span>      <\/span><span style=\"color:#9f713c;\">packages <\/span><span>= <\/span><span style=\"color:#55859b;\">with <\/span><span style=\"color:#b16139;\">pkgs<\/span><span>; [\n<\/span><span>        <\/span><span style=\"color:#b16139;\">git\n<\/span><span>        <\/span><span style=\"color:#b16139;\">zola\n<\/span><span>      ];\n<\/span><span>\n<\/span><span>      <\/span><span style=\"color:#9f713c;\">PROJECT_NAME <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">0xc<\/span><span>&quot;;\n<\/span><span>\n<\/span><span>      <\/span><span style=\"color:#9f713c;\">shellHook <\/span><span>= &#39;&#39;\n<\/span><span style=\"color:#489963;\">        echo $ Started devshell for $PROJECT_NAME\n<\/span><span style=\"color:#489963;\">        echo\n<\/span><span style=\"color:#489963;\">        uname -v\n<\/span><span style=\"color:#489963;\">        echo\n<\/span><span style=\"color:#489963;\">        git --version\n<\/span><span style=\"color:#489963;\">        echo\n<\/span><span style=\"color:#489963;\">        echo &quot;zola version $(zola --version)&quot;\n<\/span><span style=\"color:#489963;\">        echo\n<\/span><span style=\"color:#489963;\">      <\/span><span>&#39;&#39;;\n<\/span><span>    };\n<\/span><span>  };\n<\/span><span>}\n<\/span><\/code><\/pre>\n<p>That ensures that I can access both <code>git<\/code> and <code>zola<\/code> in my dev shell.<\/p>\n<p>The <code>direnv<\/code> tool has native support for Nix flakes, so enabling this is a single line in our <code>.envrc<\/code>:<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>use flake\n<\/span><\/code><\/pre>\n<p>That's it! Now in the project, you'll have to permit <code>direnv<\/code> once:<\/p>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#b16139;\">direnv<\/span><span> allow\n<\/span><\/code><\/pre>\n<p>Now you are ready to automatically enter your desired shell environment.<\/p>\n<h2 id=\"graceful-departures\">graceful departures<\/h2>\n<p>Not only does <code>direnv<\/code> work well when navigating around projects, it also handles exiting an environment smoothly. Dependencies you may not have had that the Nix flake included in the dev shell, such as <code>zola<\/code>, will no longer be available after leaving the project directory.<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>[ ~\/Code\/blog ]: which zola\n<\/span><span>\/nix\/store\/qsaq50z4hln6f86ymvp5f5j01wqg21c3-zola-0.17.2\/bin\/zola\n<\/span><span>\n<\/span><span>[ ~\/Code\/blog ]: cd ..\n<\/span><span>direnv: unloading\n<\/span><span>\n<\/span><span>[ ~\/Code ]: which zola\n<\/span><span>which: no zola in (\/nix\/store\/16d7k6ljgy635fz5jn1flnvpx1gnx9cp-glib-2.76.4-bin\/bin:\/run\/wrappers\/bin:\/home\/tcarrio\/.local\/share\/flatpak\/exports\/bin:\/var\/lib\/flatpak\/exports\/bin:\/home\/tcarrio\/.nix-profile\/bin:\/etc\/profiles\/per-user\/tcarrio\/bin:\/nix\/var\/nix\/profiles\/default\/bin:\/run\/current-system\/sw\/bin:\/home\/tcarrio\/.local\/bin)\n<\/span><\/code><\/pre>\n"},{"title":"NixOS Secrets with Agenix and Systemd","pubDate":"Tue, 19 Sep 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/nixos-agenix-systemd-secrets\/","guid":"https:\/\/blog.carrio.dev\/blog\/nixos-agenix-systemd-secrets\/","description":"<h2 id=\"prologue-what-is-nixos\">Prologue: What is NixOS?<\/h2>\n<p>I will assume that you're here to learn more about managing secrets on a NixOS system. If you want to learn more about NixOS itself, check out the <a href=\"https:\/\/nixos.org\/manual\/\">NixOS manual<\/a>. There is a lot to catch up on.<\/p>\n<blockquote>\n<p>\u2139\ufe0f I may add more updates to this blog post, but I want it available in case others run into the same issue I did for utilizing <code>agenix<\/code> in Systemd service units.<\/p>\n<\/blockquote>\n<blockquote>\n<p>\ud83c\udf99\ufe0fI do make use of voice to text tooling, but I try to correct as much as possible.<\/p>\n<\/blockquote>\n<h2 id=\"managing-secrets-on-nixos\">Managing Secrets on NixOS<\/h2>\n<p>NixOS being an entirely automated system has to conquer some of the same battles fought by other tools in the same space- Terraform for example automates the provisioning of resources and systems, and needs a way to maintain secrets on those. These should be kept as safe as possible, and as such has primitives around secrets. NixOS similarly has many tools that can be used, some reused across the industry like <code>age<\/code> and <code>sops<\/code>. They provide a full comparison of these tools and their integration with the NixOS system <a href=\"https:\/\/nixos.wiki\/wiki\/Comparison_of_secret_managing_schemes\">here<\/a>.<\/p>\n<h2 id=\"age-and-agenix\">Age and Agenix<\/h2>\n<p>The tool <a href=\"https:\/\/age-encryption.org\/\">age<\/a> is a modern encryption tool designed to be simple to work with, requires no configuration out-of-the-box, and is designed to be composable. This makes it a great tool to use with Nix. The <a href=\"https:\/\/github.com\/ryantm\/agenix\">agenix<\/a> project utilizes <code>age<\/code> in order to provide a pattern for managing secrets, and is separated into the CLI and the NixOS module. The CLI is used for interacting with secrets, and a <code>secrets.nix<\/code> file is provided in order to configure the target recipients for secret files, with these files existing in paths beneath the root directory of that <code>secrets.nix<\/code> file. Within your NixOS configuration, you can import the module, and then reference existing secrets and how they should be utilized in the system.<\/p>\n<h2 id=\"where-does-systemd-come-into-play\">Where Does Systemd Come Into Play?<\/h2>\n<p>Well, I was building off an existing blog post by Tailscale on how to configure a NixOS server for Minecraft on a Tailnet. I'm mostly concerned on automatically wiring up a Tailscale service securely on my NixOS servers, so I wanted to apply the same principle while utilizing one of the secrets managing tools.<\/p>\n<p>So in this post, I'll demonstrate this in the way I implemented the NixOS configuration to utilize <code>agenix<\/code> for automatic Tailscale connection with a secret token, managed in code securely with <code>age<\/code> encryption.<\/p>\n<h2 id=\"generating-secrets-with-agenix\">Generating secrets with agenix<\/h2>\n<p>First step degenerating secrets with <code>agenix<\/code> is by setting up a <code>secrets.nix<\/code> file this file should define the public SSH keys of hosts or users who are able to decrypt the secrets.<\/p>\n<blockquote>\n<p>This is a hint for those who are not familiar, but the system has its own SSH public and private keys in the <code>\/etc<\/code> directory. If these exist then <code>agenix<\/code> will utilize those to decrypt the mounted secrets.<\/p>\n<\/blockquote>\n<h3 id=\"secrets-nix\">secrets.nix<\/h3>\n<p>The output of the Nix expression is a map set. Each of these is a path, relative to the current directory of secrets.nix, and the public keys that the secret should be encrypted for. An example of the secrets.nix file:<\/p>\n<pre data-lang=\"nix\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-nix \"><code class=\"language-nix\" data-lang=\"nix\"><span style=\"color:#55859b;\">let\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">keys <\/span><span>= [ &quot;<\/span><span style=\"color:#489963;\">ssh-rsa foobarbaz... host@system<\/span><span>&quot; ];\n<\/span><span style=\"color:#55859b;\">in\n<\/span><span>{\n<\/span><span>    &quot;<\/span><span style=\"color:#489963;\">tailscale.age<\/span><span>&quot;.<\/span><span style=\"color:#9f713c;\">publicKeys <\/span><span>= <\/span><span style=\"color:#b16139;\">keys<\/span><span>;\n<\/span><span>}\n<\/span><\/code><\/pre>\n<p>Once this file is defined, <code>agenix<\/code> now understands within the context of the directory how to encrypt secrets with <code>age<\/code>. So, you can execute the <code>agenix<\/code> command in order to open a terminal editor, determined by the configured <code>VISUAL<\/code> environment variables, in which you can then insert the content and after saving the buffer will be encrypted to the desired file location.<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>agenix -e tailscale.age\n<\/span><\/code><\/pre>\n<h3 id=\"tailscale-nix\">tailscale.nix<\/h3>\n<p>I have broken out the tailscale.nix file into its own expression that can be imported by an exos configuration. It encapsulates all of the necessary configurations, namely installing the tailscale package, enabling the tailscale service, enabling port forwarding for the tailscale service, configuring a one-off Systemd unit file which references the agents mounted secret file. By referencing the content of that file in line within the Systemd unit script, the encrypted token is now available in plain text for the tailscale auto-configuration.<\/p>\n<p>The last important piece is that you must wait for the <code>run-agenix.d.mount<\/code> unit in this unit, otherwise there is the potential for a race condition where the <code>agenix<\/code> secret has not been decrypted to the secure location you are referencing, thus resulting in no content being passed for the token.<\/p>\n<pre data-lang=\"nix\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-nix \"><code class=\"language-nix\" data-lang=\"nix\"><span style=\"color:#5f6d64;\"># tailscale.nix\n<\/span><span style=\"color:#478c90;\">{ <\/span><span>config, pkgs, ... <\/span><span style=\"color:#478c90;\">}<\/span><span>: {\n<\/span><span>    <\/span><span style=\"color:#5f6d64;\"># the nix expression containing age secret configuration, enabling tailscale packages and service, networking rules, and the systemd autoconnect unit file\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#5f6d64;\"># Here, we mount the token file\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">age<\/span><span>.<\/span><span style=\"color:#9f713c;\">secrets<\/span><span>.<\/span><span style=\"color:#9f713c;\">tailscale-token <\/span><span>= {\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">file <\/span><span>= <\/span><span style=\"color:#489963;\">.\/tailscale.age<\/span><span>;\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">owner <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">root<\/span><span>&quot;;\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">group <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">root<\/span><span>&quot;;\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">mode <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">600<\/span><span>&quot;;\n<\/span><span>    };\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#5f6d64;\"># We&#39;ll install the package to the system, enable the service, and set up some networking rules\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">environment<\/span><span>.<\/span><span style=\"color:#9f713c;\">systemPackages <\/span><span>= <\/span><span style=\"color:#55859b;\">with <\/span><span style=\"color:#b16139;\">pkgs<\/span><span>; [ <\/span><span style=\"color:#b16139;\">tailscale <\/span><span>];\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">services<\/span><span>.<\/span><span style=\"color:#9f713c;\">tailscale<\/span><span>.<\/span><span style=\"color:#9f713c;\">enable <\/span><span>= <\/span><span style=\"color:#9f713c;\">true<\/span><span>;\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">networking <\/span><span>= {\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">firewall <\/span><span>= {\n<\/span><span>            <\/span><span style=\"color:#9f713c;\">checkReversePath <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">loose<\/span><span>&quot;;\n<\/span><span>            <\/span><span style=\"color:#9f713c;\">allowedUDPPorts <\/span><span>= [ <\/span><span style=\"color:#b16139;\">config<\/span><span>.<\/span><span style=\"color:#b16139;\">services<\/span><span>.<\/span><span style=\"color:#b16139;\">tailscale<\/span><span>.<\/span><span style=\"color:#b16139;\">port <\/span><span>];\n<\/span><span>            <\/span><span style=\"color:#9f713c;\">trustedInterfaces <\/span><span>= [ &quot;<\/span><span style=\"color:#489963;\">tailscale0<\/span><span>&quot; ];\n<\/span><span>        };\n<\/span><span>    };\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#5f6d64;\"># Here is the magic, where we automatically connect with the tailscale CLI by passing our secret token, and ensure that agenix mounting was completed\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">systemd<\/span><span>.<\/span><span style=\"color:#9f713c;\">services<\/span><span>.<\/span><span style=\"color:#9f713c;\">tailscale-autoconnect <\/span><span>= {\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">description <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">Automatic connection to Tailscale<\/span><span>&quot;;\n<\/span><span>\n<\/span><span>        <\/span><span style=\"color:#5f6d64;\"># We must make sure that both the tailscale service and the agenix file mounting are running \/ complete before trying to connect to tailscale\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">after <\/span><span>= [ &quot;<\/span><span style=\"color:#489963;\">network-pre.target<\/span><span>&quot; &quot;<\/span><span style=\"color:#489963;\">tailscale.service<\/span><span>&quot; &quot;<\/span><span style=\"color:#489963;\">run-agenix.d.mount<\/span><span>&quot; ];\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">wants <\/span><span>= [ &quot;<\/span><span style=\"color:#489963;\">network-pre.target<\/span><span>&quot; &quot;<\/span><span style=\"color:#489963;\">tailscale.service<\/span><span>&quot; &quot;<\/span><span style=\"color:#489963;\">run-agenix.d.mount<\/span><span>&quot; ];\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">wantedBy <\/span><span>= [ &quot;<\/span><span style=\"color:#489963;\">multi-user.target<\/span><span>&quot; ];\n<\/span><span>\n<\/span><span>        <\/span><span style=\"color:#5f6d64;\"># Set this service as a oneshot job\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">serviceConfig<\/span><span>.<\/span><span style=\"color:#9f713c;\">Type <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">oneshot<\/span><span>&quot;;\n<\/span><span>\n<\/span><span>        <\/span><span style=\"color:#5f6d64;\"># Run the following shell script for the job, passing the mounted secret for the tailscale connection\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">script <\/span><span>= <\/span><span style=\"color:#55859b;\">with <\/span><span style=\"color:#b16139;\">pkgs<\/span><span>; &#39;&#39;\n<\/span><span style=\"color:#489963;\">            # wait for tailscaled to settle\n<\/span><span style=\"color:#489963;\">            sleep 2\n<\/span><span style=\"color:#489963;\">\n<\/span><span style=\"color:#489963;\">            # check if we are already authenticated to tailscale\n<\/span><span style=\"color:#489963;\">            status=&quot;$(<\/span><span style=\"font-style:italic;color:#867469;\">${<\/span><span style=\"font-style:italic;color:#b16139;\">tailscale<\/span><span style=\"font-style:italic;color:#867469;\">}<\/span><span style=\"color:#489963;\">\/bin\/tailscale status -json | <\/span><span style=\"font-style:italic;color:#867469;\">${<\/span><span style=\"font-style:italic;color:#b16139;\">jq<\/span><span style=\"font-style:italic;color:#867469;\">}<\/span><span style=\"color:#489963;\">\/bin\/jq -r .BackendState)&quot;\n<\/span><span style=\"color:#489963;\">            if [ $status = &quot;Running&quot; ]; then\n<\/span><span style=\"color:#489963;\">                exit 0\n<\/span><span style=\"color:#489963;\">            fi\n<\/span><span style=\"color:#489963;\">\n<\/span><span style=\"color:#489963;\">            # otherwise authenticate with tailscale\n<\/span><span style=\"color:#489963;\">            <\/span><span style=\"font-style:italic;color:#867469;\">${<\/span><span style=\"font-style:italic;color:#b16139;\">tailscale<\/span><span style=\"font-style:italic;color:#867469;\">}<\/span><span style=\"color:#489963;\">\/bin\/tailscale up -authkey &quot;$(cat &quot;<\/span><span style=\"font-style:italic;color:#867469;\">${<\/span><span style=\"font-style:italic;color:#b16139;\">config<\/span><span style=\"font-style:italic;color:#87928a;\">.<\/span><span style=\"font-style:italic;color:#b16139;\">age<\/span><span style=\"font-style:italic;color:#87928a;\">.<\/span><span style=\"font-style:italic;color:#b16139;\">secrets<\/span><span style=\"font-style:italic;color:#87928a;\">.<\/span><span style=\"font-style:italic;color:#b16139;\">tailscale-token<\/span><span style=\"font-style:italic;color:#87928a;\">.<\/span><span style=\"font-style:italic;color:#b16139;\">path<\/span><span style=\"font-style:italic;color:#867469;\">}<\/span><span style=\"color:#489963;\">&quot;)&quot;\n<\/span><span style=\"color:#489963;\">        <\/span><span>&#39;&#39;;\n<\/span><span>    };\n<\/span><span>}\n<\/span><\/code><\/pre>\n<h2 id=\"tl-dr\">tl;dr<\/h2>\n<p>Agenix itself mounts files with Systemd in the <code>run-agenix.mount<\/code> unit. As such, you can utilize the mechanism of Systemd service definitions, namely <code>after<\/code> and <code>wants<\/code>, in order to ensure that the <code>agenix<\/code> secret mounts have been completed prior to starting your service. In this way, you can be sure that the secret is available.<\/p>\n<p>If you want to read more on NixOS configuration, you can check out my <a href=\"https:\/\/github.com\/tcarrio\/nix-config\">nix-config<\/a> repository which maintains several of my systems.<\/p>\n<!-- References -->\n"},{"title":"Nix Flakes Starter","pubDate":"Fri, 11 Aug 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/nix-flakes-starter\/","guid":"https:\/\/blog.carrio.dev\/blog\/nix-flakes-starter\/","description":"<h2 id=\"what-is-nix\">What is Nix<\/h2>\n<p>Nix consists of many things, and because of the common naming of \"Nix\" throughout it all, it can be confusing beyond just the surface level.<\/p>\n<ul>\n<li><strong>Nix<\/strong>OS: An Operating System powered by Nix configurations and package manager<\/li>\n<li><strong>Nix<\/strong> language: A declarative, pure, functional, domain-specific language<\/li>\n<li><strong>Nix<\/strong> package manager: A purely functional package manager<\/li>\n<\/ul>\n<p>As it pertains to this post on Nix Flakes, we're mostly talking about the Nix <em>language<\/em>, which is used to implement a flake, and the Nix <em>package manager<\/em>, which can utilize and interact with flakes.<\/p>\n<h2 id=\"nix-flakes\">Nix Flakes<\/h2>\n<p>If you look up Nix flakes, the first article you'll find it likely <a href=\"https:\/\/nixos.wiki\/wiki\/Flakes\">the one on the NixOS Wiki<\/a>. This same article also clearly states at the top<\/p>\n<blockquote>\n<p><strong>Nix flakes<\/strong> are an <em>experimental feature<\/em> of the Nix package manager.<\/p>\n<\/blockquote>\n<p>Well that sounds dangerous, unstable, fragile, etc. etc. Yeah it does. But a lot of the Nix community believe that Nix flakes are <strong>The Future<\/strong>. And it's been considered \"experimental\" for many years now, to be clear. But this post is less focused on the political discussion of flakes' stability and future and more on what it is, how to get started, and some example use cases.<\/p>\n<h3 id=\"what-are-flakes\">What Are Flakes<\/h3>\n<p>Flakes provide a kind of specification around how to define a Nix expression, how dependencies are managed between it and others, and provide general improvements to the Nix ecosystem such as reproducibility and composability. A flake consists of a file system tree which contains a <code>flake.nix<\/code> file in its root directory. You would expect to see something like the following in a Nix flake:<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>[0xc@sys ~]$ tree .\/devshells\n<\/span><span>.\n<\/span><span>\u251c\u2500\u2500 flake.lock\n<\/span><span>\u251c\u2500\u2500 flake.nix\n<\/span><span>\u2514\u2500\u2500 README.md\n<\/span><span>\n<\/span><span>1 directory, 3 files\n<\/span><\/code><\/pre>\n<p>This <code>flake.nix<\/code> file offers a uniform <a href=\"https:\/\/nixos.org\/manual\/nix\/stable\/command-ref\/new-cli\/nix3-flake.html#flake-format\">schema<\/a> that allows other flakes to be referenced as dependencies, and the values produced by the Nix expression in the <code>flake.nix<\/code> file follow a specific structure to support certain use cases. Since a flake can reference others in a way that supports the lockfile mechanism, even composed Nix flakes support reproducibility.<\/p>\n<p>The <code>nix<\/code> CLI also supports flakes as an experimental feature.<\/p>\n<h2 id=\"creating-a-flake\">Creating a Flake<\/h2>\n<p>With the <code>nix<\/code> CLI, you can run:<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>[0xc@sys ~]$ mkdir flake-test\n<\/span><span>[0xc@sys ~]$ cd flake-test\n<\/span><span>[0xc@sys ~]$ nix flake init\n<\/span><\/code><\/pre>\n<h2 id=\"crafting-a-flake-file\">Crafting a Flake File<\/h2>\n<p>As mentioned, there is a uniform schema to Flake files. The following attributes are defined at the top-level in a Nix flake:<\/p>\n<p>Flake schema<\/p>\n<p>The flake.nix file is a Nix file but that has special restrictions (more on that later).<\/p>\n<p><strong>description<\/strong>: a string describing the flake.\n<strong>inputs<\/strong>: an attribute set of all the dependencies of the flake.\n<strong>outputs<\/strong>: a function of one argument that takes an attribute set of all the realized inputs, and outputs another attribute set whose schema is described below.\n<strong>nixConfig<\/strong>: an attribute set of values which reflect the values given to nix.conf. This can extend the normal behavior of a user's nix experience by adding flake-specific configuration, such as a binary cache.<\/p>\n<p><a href=\"https:\/\/nixos.wiki\/wiki\/Flakes\"><em>Reference<\/em><\/a><\/p>\n<p>The <code>description<\/code> is very straightforward, but let's break down the remaining attributes, particularly <code>inputs<\/code> and <code>outputs<\/code>.<\/p>\n<h3 id=\"inputs\">Inputs<\/h3>\n<!-- TODO -->\n<p>The <code>inputs<\/code> schema allows the definition of zero or more flakes as references to the <code>outputs<\/code> schema. Any external requirements for the flake will be defined here, whether it's a CLI tool, library, or service.<\/p>\n<p>The <code>inputs<\/code> allows you to define any number of flake inputs as local paths, Git repositories over SSH or HTTPS, and special shorthands for GitHub.<\/p>\n<pre data-lang=\"nix\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-nix \"><code class=\"language-nix\" data-lang=\"nix\"><span style=\"color:#b16139;\">inputs <\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">=<\/span><span> {\n<\/span><span>    <\/span><span style=\"color:#5f6d64;\"># specifying a GitHub repository by org\/repo and branch name (&quot;master&quot;)\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">nixpkgs<\/span><span>.<\/span><span style=\"color:#9f713c;\">url <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">github:NixOS\/nixpkgs\/master<\/span><span>&quot;;\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#5f6d64;\"># specifying a Git repository by URL, using HTTPS or SSH protocol\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">https-example<\/span><span>.<\/span><span style=\"color:#9f713c;\">url <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">git+https:\/\/git.example.test\/org\/repo?ref=branch&amp;rev=deadbeef<\/span><span>&quot;;\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">ssh-example<\/span><span>.<\/span><span style=\"color:#9f713c;\">url <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">git+ssh:\/\/git.example.test\/org\/repo?ref=branch&amp;rev=deadbeef<\/span><span>&quot;;\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#5f6d64;\"># specifying a shallow clone (won&#39;t clone the `.git` directory)\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">shallow-clone-example<\/span><span>.<\/span><span style=\"color:#9f713c;\">url <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">git+file:\/local\/project\/path?shallow=1<\/span><span>&quot;;\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#5f6d64;\"># specifying a local directory\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">relative-path-dir-example<\/span><span>.<\/span><span style=\"color:#9f713c;\">url <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">path:\/local\/project\/path<\/span><span>&quot;;\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">absolute-path-dir-example<\/span><span>.<\/span><span style=\"color:#9f713c;\">url <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">\/local\/project\/path<\/span><span>&quot;;\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#5f6d64;\"># specifying a non-flake input\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">not-a-flake <\/span><span>= {\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">url <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">github:0xc\/nonflake\/branch<\/span><span>&quot;;\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">flake <\/span><span>= <\/span><span style=\"color:#9f713c;\">false<\/span><span>;\n<\/span><span>    };\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#5f6d64;\"># specifying that the dependency&#39;s `inputs.nixpkgs` should inherit from this flake\n<\/span><span>    <\/span><span style=\"color:#55859b;\">inherit<\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">-<\/span><span style=\"color:#9f713c;\">nixpkgs-example <\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">=<\/span><span> <\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">{<\/span><span>\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">url <\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">=<\/span><span> <\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">&quot;<\/span><span style=\"color:#9f713c;\">github<\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">:<\/span><span style=\"color:#9f713c;\">another<\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">\/<\/span><span style=\"color:#9f713c;\">example<\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">&quot;<\/span><span>;\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">inputs<\/span><span>.<\/span><span style=\"color:#9f713c;\">nixpkgs<\/span><span>.<\/span><span style=\"color:#9f713c;\">follows <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">nixpkgs<\/span><span>&quot;;\n<\/span><span>    }<\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">;<\/span><span>\n<\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">}<\/span><span>\n<\/span><\/code><\/pre>\n<p>These inputs and their controls give flakes substantially more power over deterministic build processes and consistency across the dependencies utilized within the inputs and the flake definitions' resources.<\/p>\n<h3 id=\"outputs\">Outputs<\/h3>\n<p>The magic of a flake. This is where we actually define the resources of a flake, and the schema provides us several mechanisms for things like development shells, applications, build targets, overlays, and more.<\/p>\n<h4 id=\"applications\">Applications<\/h4>\n<p>These are predefined run targets in your flake. These are suitable for packaging your application so you can execute it consistently.<\/p>\n<p>Utilized with the <code>nix run<\/code> command. Within the outputs, you can specify these by doing:<\/p>\n<pre data-lang=\"nix\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-nix \"><code class=\"language-nix\" data-lang=\"nix\"><span style=\"color:#b16139;\">apps<\/span><span>.<\/span><span style=\"font-style:italic;color:#867469;\">${<\/span><span style=\"font-style:italic;color:#b16139;\">system<\/span><span style=\"font-style:italic;color:#867469;\">}<\/span><span>.<\/span><span style=\"color:#489963;\">&lt;target-name&gt; <\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">=<\/span><span> {\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">type <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">app<\/span><span>&quot;;\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">program <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">run-the-thing<\/span><span>&quot;;\n<\/span><span>}<\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">;<\/span><span>\n<\/span><\/code><\/pre>\n<p>This can be executed using <code>nix run .#target-name<\/code>.<\/p>\n<p>If you want to execute this with arguments you would run <code>nix run .#target-name -- ...<\/code><\/p>\n<h4 id=\"development-shells\">Development shells<\/h4>\n<p>Dev shells are an extremely useful feature of flakes. There are some differences to the legacy Nix shell and the new <code>devShells<\/code> functionality of Nix flakes.<\/p>\n<p><strong>TODO: Add more info on these differences<\/strong><\/p>\n<p>You can define <code>devShells<\/code> in the <code>outputs<\/code>, and the most convenient way is using the <code>mkShell<\/code> function exposed in the <code>nixpkgs<\/code> input argument. Suppose you have the nixpkgs repository input as <code>pkgs<\/code>, then you would be able to do<\/p>\n<pre data-lang=\"nix\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-nix \"><code class=\"language-nix\" data-lang=\"nix\"><span style=\"color:#b16139;\">outputs <\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">=<\/span><span> <\/span><span style=\"color:#478c90;\">{ <\/span><span>self, pkgs <\/span><span style=\"color:#478c90;\">}<\/span><span>: {\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">devShells <\/span><span>= {\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">default <\/span><span>= <\/span><span style=\"color:#b16139;\">pkgs<\/span><span>.<\/span><span style=\"color:#b16139;\">mkShell <\/span><span>{\n<\/span><span>            <\/span><span style=\"color:#9f713c;\">packages <\/span><span>= [<\/span><span style=\"color:#b16139;\">pkgs<\/span><span>.<\/span><span style=\"color:#b16139;\">git<\/span><span>];\n<\/span><span>        };\n<\/span><span>\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">go <\/span><span>= <\/span><span style=\"color:#b16139;\">pkgs<\/span><span>.<\/span><span style=\"color:#b16139;\">mkShell <\/span><span>{\n<\/span><span>            <\/span><span style=\"color:#9f713c;\">packages <\/span><span>= [<\/span><span style=\"color:#b16139;\">pkgs<\/span><span>.<\/span><span style=\"color:#b16139;\">go<\/span><span>];\n<\/span><span>        };\n<\/span><span>    };\n<\/span><span>}<\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">;<\/span><span>\n<\/span><\/code><\/pre>\n<p>The default target can be invoked with <code>nix develop .<\/code> and in this case will provide the <code>git<\/code> package, available in your PATH.<\/p>\n<p>To invoke the <code>go<\/code> target, you would do <code>nix develop .#go<\/code>. Then we'd have the Go toolchain loaded and available so we could run or compile some Go code with <code>go build main.go<\/code>.<\/p>\n<h4 id=\"overlays\">Overlays<\/h4>\n<p>Overlays are an interesting albeit somewhat advanced topic in Nix, but the goal of overlays is to support advanced flake customization capabilities, such as overriding packages within a flake. Overlays supercedes an old approach to this which was limited in scope to this one simple use case, called <code>packageOverride<\/code> and <code>overridePackages<\/code>.<\/p>\n<p>Overlays are defined as a nested function whose first argument is <code>final<\/code> and second argument is <code>prev<\/code>.<\/p>\n<p>The following diagram visualizes the flow of the overlay function components throughout the system.<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>+---------------------+-----------------------+------------------------------+\n<\/span><span>|                     |                       |                              |\n<\/span><span>|                     |                       |                              |\n<\/span><span>|  +-------------+    |  +-------------+      |  +--------------+            |\n<\/span><span>|  |             |    |  |             |      |  |              |            |\n<\/span><span>|  +-----+       |    |  +-----+       |      |  +-----+        |            |\n<\/span><span>+-&gt;|final|       |    +-&gt;|final|       |      +-&gt;|final|        |            |\n<\/span><span>   +-----+       |       +-----+       |         +-----+        |            |\n<\/span><span>   |             |       |             |         |              |            |\n<\/span><span>   |    main     +---+   |             +--+      |              +------+     |\n<\/span><span>   |             |   |   |             |  |      |              |      |     |\n<\/span><span>   |             |   |   +-----+       |  |      +-----+        |      |     |\n<\/span><span>   |             |   +--&gt;|prev |       |  |    +&gt;|prev |        |      |     |\n<\/span><span>   |             |   |   +-----+       |  |    | +-----+        |      |     |\n<\/span><span>   |             |   |   |             |  |    | |              |      |     |\n<\/span><span>   +-------------+   |   +-------------+  |    | +--------------+      |     |\n<\/span><span>                     |                    |    |                       |     |\n<\/span><span>                     |                    |    |                       |     |\n<\/span><span>                     |                    |    |                       |     |\n<\/span><span>                     |                  +-v--+ |                     +-v--+  |\n<\/span><span>                     |                  |    | |                     |    |  |\n<\/span><span>                     +------------------&gt; \/\/ +-+---------------------&gt; \/\/ +--+\n<\/span><span>                                        +----+                       +----+\n<\/span><\/code><\/pre>\n<p>Within your flake, you can define overlays with the following:<\/p>\n<pre data-lang=\"nix\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-nix \"><code class=\"language-nix\" data-lang=\"nix\"><span style=\"color:#5f6d64;\"># Specifying an overlay by &quot;name&quot;\n<\/span><span style=\"color:#b16139;\">overlays<\/span><span>.&quot;<\/span><span style=\"color:#489963;\">&lt;name&gt;<\/span><span>&quot; <\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">=<\/span><span> final: prev: { }<\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">;<\/span><span>\n<\/span><span style=\"color:#5f6d64;\"># Specifying the default overlay\n<\/span><span style=\"color:#b16139;\">overlays<\/span><span>.<\/span><span style=\"color:#b16139;\">default <\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">=<\/span><span> final: prev: { }<\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">;<\/span><span>\n<\/span><\/code><\/pre>\n<p>These can be utilized in interesting ways, a good example is how the NodeJS runtimes and NPM dependencies like <a href=\"https:\/\/yarnpkg.com\/\">Yarn<\/a> can be configured with overlays to ensure the correct underlying runtime is used for the package.<\/p>\n<p>My <a href=\"https:\/\/github.com\/tcarrio\/devshells\">devshells<\/a> repository showcases this. A <em>paraphrased<\/em> version of the code would be:<\/p>\n<pre data-lang=\"nix\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-nix \"><code class=\"language-nix\" data-lang=\"nix\"><span style=\"color:#55859b;\">let\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">node16Overlay <\/span><span>= self: super: {\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">nodejs <\/span><span>= <\/span><span style=\"color:#b16139;\">self<\/span><span>.<\/span><span style=\"color:#b16139;\">nodejs-16_x<\/span><span>;\n<\/span><span>    };\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">yarn16Overlay <\/span><span>= self: super: {\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">yarn <\/span><span>= <\/span><span style=\"color:#b16139;\">super<\/span><span>.<\/span><span style=\"color:#b16139;\">yarn<\/span><span>.<\/span><span style=\"color:#b16139;\">override <\/span><span>{\n<\/span><span>            <\/span><span style=\"color:#9f713c;\">nodejs <\/span><span>= <\/span><span style=\"color:#b16139;\">self<\/span><span>.<\/span><span style=\"color:#b16139;\">nodejs-16_x<\/span><span>;\n<\/span><span>        };\n<\/span><span>    };\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">pkgsNode16 <\/span><span>= <\/span><span style=\"color:#1c9aa0;\">import <\/span><span style=\"color:#b16139;\">nixpkgs <\/span><span>{\n<\/span><span>        <\/span><span style=\"color:#55859b;\">inherit <\/span><span style=\"color:#9f713c;\">system<\/span><span>;\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">overlays <\/span><span>= [<\/span><span style=\"color:#b16139;\">node16Overlay yarn16Overlay<\/span><span>];\n<\/span><span>    };\n<\/span><span style=\"color:#55859b;\">in rec <\/span><span>{\n<\/span><span>    <\/span><span style=\"color:#9f713c;\">devShells <\/span><span>= {\n<\/span><span>        <\/span><span style=\"color:#9f713c;\">default <\/span><span>= <\/span><span style=\"color:#b16139;\">pkgs<\/span><span>.<\/span><span style=\"color:#b16139;\">mkShell <\/span><span>{\n<\/span><span>            <\/span><span style=\"color:#9f713c;\">packages <\/span><span>= <\/span><span style=\"color:#55859b;\">with <\/span><span style=\"color:#b16139;\">pkgsNode16<\/span><span>; [\n<\/span><span>                <\/span><span style=\"color:#b16139;\">nodejs-16_x\n<\/span><span>                <\/span><span style=\"color:#b16139;\">yarn\n<\/span><span>            ];\n<\/span><span>        };\n<\/span><span>    };\n<\/span><span>}\n<\/span><\/code><\/pre>\n<h4 id=\"and-more\">And more<\/h4>\n<p>There are <em>even more<\/em> use cases for Nix flake outputs, that I won't dive into much here. The resources mentioned throughout this article are extremely useful though, and there is tremendous depth to Nix that you can dive into.<\/p>\n<!-- References -->\n"},{"title":"OpenFeature Introduction","pubDate":"Mon, 07 Aug 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/openfeature-introduction\/","guid":"https:\/\/blog.carrio.dev\/blog\/openfeature-introduction\/","description":"<h2 id=\"what-are-feature-flags\">What Are Feature Flags<\/h2>\n<p>Feature flags are a way to dynamically control the capabilities of software, often with granularity to specific users, regions, and more. Flagging empowers companies to experiment with new features, and companies like Spotify, Duolingo, and Google use these heavily to prove out their hypotheses around generating better user experiences and products. It can be as simple as flipping the title of a landing page to an entire suite of features in the product- the possibilities are almost endless.<\/p>\n<p>This is often times pretty simple, and in the code could be done with a basic <code>if\/else<\/code> block:<\/p>\n<pre data-lang=\"tsx\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-tsx \"><code class=\"language-tsx\" data-lang=\"tsx\"><span style=\"color:#55859b;\">const <\/span><span>{<\/span><span style=\"color:#b16139;\">flagService<\/span><span>} = <\/span><span style=\"color:#478c90;\">useContext<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">FeatureFlagging<\/span><span>&#39;);\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">isNewExperience <\/span><span>= <\/span><span style=\"color:#b16139;\">flagService<\/span><span>.<\/span><span style=\"color:#478c90;\">isOn<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">new-experience<\/span><span>&#39;);\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">if <\/span><span>(<\/span><span style=\"color:#b16139;\">isNewExperience<\/span><span>) {\n<\/span><span>  <\/span><span style=\"color:#55859b;\">return <\/span><span>&lt;<\/span><span style=\"color:#a07e3b;\">NewComponent <\/span><span style=\"color:#867469;\">{<\/span><span>...<\/span><span style=\"color:#b16139;\">props<\/span><span style=\"color:#867469;\">} <\/span><span>\/&gt;;\n<\/span><span>} <\/span><span style=\"color:#55859b;\">else <\/span><span>{\n<\/span><span>  <\/span><span style=\"color:#55859b;\">return <\/span><span>&lt;<\/span><span style=\"color:#a07e3b;\">Component <\/span><span style=\"color:#867469;\">{<\/span><span>...<\/span><span style=\"color:#b16139;\">props<\/span><span style=\"color:#867469;\">} <\/span><span>\/&gt;;\n<\/span><span>}\n<\/span><\/code><\/pre>\n<p>And there you have it. You now have a dynamically response interface based on logic determined by your feature flagging service. None of the above code related to any existing tool in particular, but what this post will do is get you acquainted with a new open source project called OpenFeature which you can get started with today.<\/p>\n<h2 id=\"what-is-openfeature\">What is OpenFeature<\/h2>\n<p>OpenFeature is a project that defines an <strong>open specification<\/strong> for feature flagging SDK behaviors to support consistent developer experiences backed by any feature flagging vendor in the ecosystem. The project provides an open source, vendor-agnostic SDK for many languages, which vendors can support with <strong>providers<\/strong> to back the flagging logic and <strong>hooks<\/strong> to enhance with various capabilities in the feature flagging lifecycle. The SDK is an implementation upon the OpenFeature specification, and can be configured against any one of the available providers for that language. If you're interested, take a look on their <a href=\"https:\/\/openfeature.dev\/ecosystem\">ecosystem<\/a> page, which lets you search across different types like server-side and client-side, technologies like Go, JavaScript, and PHP, Vendors like Split and CloudBees, and more.<\/p>\n<h3 id=\"providers\">Providers<\/h3>\n<p>OpenFeature itself is a specification with vendor-agnostic open source packages for various languages. The vendors provide the feature flag evaluation component of the architecture though- and you'll need one. These can be backed by open source projects, companies' SDKs like LaunchDarkly and Split, or an in-house flagging system. You can develop your application against the OpenFeature interfaces and swap out providers across environments- easily allowing your local dev system utilize environment variable configurations where production is backed by an enterprise solution. Find out more on providers <a href=\"https:\/\/openfeature.dev\/specification\/sections\/hooks\">here<\/a>.<\/p>\n<h3 id=\"hooks\">Hooks<\/h3>\n<p>The flag evaluation lifecyle is well documented in OpenFeature, and supports <em>hooks<\/em>, which can enhance or augment a flag evaluation. Perhaps you want to inject a logger in staging or provide tracing capabilities with OpenTelemetry. All of this is easily doable by utilizing a hooks package or writing your own hook against the interface. You can read more on the hooks lifecycle <a href=\"https:\/\/openfeature.dev\/specification\/sections\/hooks\">here<\/a>.<\/p>\n<h2 id=\"getting-started\">Getting Started<\/h2>\n<p>This will depend on your language of choice, so I'll provide a couple of examples. The first will be JavaScript, with a focus on client-side use cases. The next will be a server-side reference with PHP. You can find more samples in the <a href=\"https:\/\/openfeature.dev\/docs\/reference\/technologies\/\">technologies<\/a> page as well.<\/p>\n<h3 id=\"client-side-javascript\">Client-side JavaScript<\/h3>\n<p>These utilize a pattern in OpenFeature called <em>static context<\/em>. What this amounts to is that there is just the current user, the one interacting with the web client, so there doesn't need to be as highly dynamic of a flag evaluation system backing the OpenFeature client.<\/p>\n<p>Start off by installing the package to your project. Here I'll use <code>yarn<\/code>:<\/p>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#b16139;\">yarn<\/span><span> add @openfeature\/js-sdk\n<\/span><\/code><\/pre>\n<p>Now you can start working with the SDK by coding the following:<\/p>\n<pre data-lang=\"ts\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-ts \"><code class=\"language-ts\" data-lang=\"ts\"><span style=\"color:#55859b;\">import <\/span><span>{ <\/span><span style=\"color:#b16139;\">OpenFeature <\/span><span>} <\/span><span style=\"color:#55859b;\">from <\/span><span>&#39;<\/span><span style=\"color:#489963;\">@openfeature\/js-sdk<\/span><span>&#39;;\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">client <\/span><span>= <\/span><span style=\"color:#b16139;\">OpenFeature<\/span><span>.<\/span><span style=\"color:#478c90;\">getClient<\/span><span>();\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">isNewExperience <\/span><span>= <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">client<\/span><span>.<\/span><span style=\"color:#478c90;\">getBooleanValue<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">new-experience<\/span><span>&#39;, <\/span><span style=\"color:#9f713c;\">false<\/span><span>);\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">if <\/span><span>(<\/span><span style=\"color:#b16139;\">isNewExperience<\/span><span>) {\n<\/span><span>  <\/span><span style=\"color:#5f6d64;\">\/\/ ...\n<\/span><span>}\n<\/span><\/code><\/pre>\n<p>This is very similar to the above example, but needs the magic sauce to actually <em>provide<\/em> the logic- an OpenFeature <strong>provider<\/strong>.<\/p>\n<h4 id=\"wire-up-a-provider\">Wire Up A Provider<\/h4>\n<p>You need a provider to back the flag evaluation in the OpenFeature SDK. These are pluggable, and anything that adheres to the defined Provider interface can fulfill this contract. I'll pull in a specific provider, just as an example. In my case I'll use the [Split] provider. The Split provider has a peer dependency on the Split SDK as well, so I will install them both with:<\/p>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#b16139;\">yarn<\/span><span> add @splitsoftware\/openfeature-js-split-provider @splitsoftware\/splitio\n<\/span><\/code><\/pre>\n<p>Now that we have a provider, we can update our code example above accordingly:<\/p>\n<pre data-lang=\"ts\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-ts \"><code class=\"language-ts\" data-lang=\"ts\"><span style=\"color:#55859b;\">import <\/span><span>{ <\/span><span style=\"color:#b16139;\">OpenFeature <\/span><span>} <\/span><span style=\"color:#55859b;\">from <\/span><span>&#39;<\/span><span style=\"color:#489963;\">@openfeature\/js-sdk<\/span><span>&#39;;\n<\/span><span style=\"color:#55859b;\">import <\/span><span>{ <\/span><span style=\"color:#b16139;\">SplitFactory <\/span><span>} <\/span><span style=\"color:#55859b;\">from <\/span><span>&#39;<\/span><span style=\"color:#489963;\">@splitsoftware\/splitio<\/span><span>&#39;;\n<\/span><span style=\"color:#55859b;\">import <\/span><span>{ <\/span><span style=\"color:#b16139;\">OpenFeatureSplitProvider <\/span><span>} <\/span><span style=\"color:#55859b;\">from <\/span><span>&#39;<\/span><span style=\"color:#489963;\">@splitsoftware\/openfeature-js-split-provider<\/span><span>&#39;;\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ The key that authorizes the Split client to connect to the Split API\n<\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">SPLIT_AUTHORIZATION_KEY <\/span><span>= &#39;<\/span><span style=\"color:#489963;\">your-split-auth-key<\/span><span>&#39;;\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">client <\/span><span>= <\/span><span style=\"color:#b16139;\">OpenFeature<\/span><span>.<\/span><span style=\"color:#478c90;\">getClient<\/span><span>();\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">splitClient <\/span><span>= <\/span><span style=\"color:#478c90;\">SplitFactory<\/span><span>({core: {<\/span><span style=\"color:#b16139;\">authorizationKey<\/span><span>}}).<\/span><span style=\"color:#478c90;\">client<\/span><span>();\n<\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">provider <\/span><span>= new OpenFeatureSplitProvider({<\/span><span style=\"color:#b16139;\">splitClient<\/span><span>});\n<\/span><span>\n<\/span><span style=\"color:#b16139;\">OpenFeature<\/span><span>.<\/span><span style=\"color:#478c90;\">setProvider<\/span><span>(<\/span><span style=\"color:#b16139;\">provider<\/span><span>);\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ With the provider set, let&#39;s get to work\n<\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">isNewExperience <\/span><span>= <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">client<\/span><span>.<\/span><span style=\"color:#478c90;\">getBooleanValue<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">new-experience<\/span><span>&#39;, <\/span><span style=\"color:#9f713c;\">false<\/span><span>);\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">if <\/span><span>(<\/span><span style=\"color:#b16139;\">isNewExperience<\/span><span>) {\n<\/span><span>  <\/span><span style=\"color:#5f6d64;\">\/\/ ...\n<\/span><span>}\n<\/span><\/code><\/pre>\n<h3 id=\"server-side-php\">Server-side PHP<\/h3>\n<p>As mentioned, several languages are supported, including .NET, Go, and more. In this example we'll utilize the PHP SDK since I wrote it.<\/p>\n<p>Let's assume you're using <code>composer<\/code> like every other PHP project- then you would install the SDK by running:<\/p>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#b16139;\">composer<\/span><span> require open-feature\/sdk\n<\/span><\/code><\/pre>\n<p>This will pull in the package and update your <code>composer.json<\/code> and <code>composer.lock<\/code> accordingly.<\/p>\n<p>Now, to utilize the SDK, you will simply retrieve an instance from the SDK:<\/p>\n<pre data-lang=\"php\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-php \"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#867469;\">&lt;?php\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">use <\/span><span>Api\\<\/span><span style=\"color:#a07e3b;\">Controller<\/span><span>;\n<\/span><span style=\"color:#55859b;\">use <\/span><span>Api\\<\/span><span style=\"color:#a07e3b;\">Method<\/span><span>;\n<\/span><span style=\"color:#55859b;\">use <\/span><span>Api\\<\/span><span style=\"color:#a07e3b;\">Route<\/span><span>;\n<\/span><span style=\"color:#55859b;\">use <\/span><span>OpenFeature\\<\/span><span style=\"color:#a07e3b;\">OpenFeatureClient<\/span><span>;\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">#[Route(&quot;\/cats&quot;)]\n<\/span><span style=\"color:#55859b;\">class <\/span><span style=\"color:#a07e3b;\">CatsController <\/span><span style=\"color:#55859b;\">extends <\/span><span style=\"color:#489963;\">Controller\n<\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">public function <\/span><span style=\"color:#1c9aa0;\">__construct<\/span><span style=\"color:#ecf4ee;\">(\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#a07e3b;\">private readonly OpenFeatureClient <\/span><span>$<\/span><span style=\"color:#b16139;\">client\n<\/span><span style=\"color:#ecf4ee;\">  ) {}\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#5f6d64;\">#[Method\\Get]\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">public function <\/span><span style=\"color:#478c90;\">actionFavorite<\/span><span style=\"color:#ecf4ee;\">(): <\/span><span style=\"color:#a07e3b;\">UI\n<\/span><span style=\"color:#ecf4ee;\">  {\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span>$<\/span><span style=\"color:#b16139;\">userId <\/span><span>= $<\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">-&gt;<\/span><span style=\"color:#b16139;\">getUserIdFromRequest<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span>$<\/span><span style=\"color:#b16139;\">favoriteCat <\/span><span>= $<\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">-&gt;<\/span><span style=\"color:#b16139;\">client<\/span><span style=\"color:#ecf4ee;\">-&gt;<\/span><span style=\"color:#b16139;\">getStringValue<\/span><span style=\"color:#ecf4ee;\">(<\/span><span>&#39;<\/span><span style=\"color:#489963;\">favorite-cat<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">, <\/span><span>&#39;<\/span><span style=\"color:#489963;\">Nebelung<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">, [\n<\/span><span style=\"color:#ecf4ee;\">      <\/span><span>&#39;<\/span><span style=\"color:#489963;\">user-id<\/span><span>&#39; =&gt; $<\/span><span style=\"color:#b16139;\">userId<\/span><span style=\"color:#ecf4ee;\">,\n<\/span><span style=\"color:#ecf4ee;\">    ]);\n<\/span><span style=\"color:#ecf4ee;\">    \n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#ecf4ee;\">[\n<\/span><span style=\"color:#ecf4ee;\">      <\/span><span>&#39;<\/span><span style=\"color:#489963;\">favoriteCat<\/span><span>&#39; =&gt; $<\/span><span style=\"color:#b16139;\">favoriteCat<\/span><span style=\"color:#ecf4ee;\">,\n<\/span><span style=\"color:#ecf4ee;\">    ];\n<\/span><span style=\"color:#ecf4ee;\">  }\n<\/span><span style=\"color:#ecf4ee;\">}\n<\/span><\/code><\/pre>\n<p>Now when the OpenFeature client evaluates the flag for the request, it'll pass some evaluation context as well, which includes the user's ID if it exists. The provider will utilize this to determine what the correct value to return will be, which allows us to provide consistent experiences at the user-level. As long as the same user is accessing the API, they will receive the same behavior.<\/p>\n<blockquote>\n<p>Note: This example removed the steps of instantiating a provider and instead utilized inversion of control to allow the framework to provide the necessary OpenFeature client instead. The process is similar to that shown in the JS SDK.<\/p>\n<\/blockquote>\n<h3 id=\"hooks-manual-instrumentation\">Hooks Manual Instrumentation<\/h3>\n<p>Just like you can set a provider in your OpenFeature SDK, you can also add hooks. The hooks are executed in a particular ordered defined by the specification, such that you can expect the behavior in the JavaScript SDK to be identical to that of the PHP SDK, Go SDK, etc.<\/p>\n<p>When adding hooks, you can do so at any level of the OpenFeature SDK: API, Client, Provider, and invocation. How these are each evaluated is defined in the <a href=\"https:\/\/openfeature.dev\/specification\/sections\/hooks#requirement-441\">hook ordering<\/a> specification.<\/p>\n<p>Here we will add the <a href=\"https:\/\/packagist.org\/packages\/open-feature\/validators-hook\">validators<\/a> hook for PHP, available in the <a href=\"https:\/\/github.com\/open-feature\/php-sdk-contrib\">php-sdk-contrib<\/a> repository:<\/p>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#b16139;\">composer<\/span><span> require open-feature\/validators-hook\n<\/span><\/code><\/pre>\n<p>And we can utilize the hook at each of the mentioned levels by doing:<\/p>\n<pre data-lang=\"php\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-php \"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#867469;\">&lt;?php\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">use <\/span><span>OpenFeature\\Hooks\\Validators\\<\/span><span style=\"color:#a07e3b;\">RegexpValidatorHook<\/span><span>;\n<\/span><span style=\"color:#55859b;\">use <\/span><span>OpenFeature\\<\/span><span style=\"color:#a07e3b;\">OpenFeatureAPI<\/span><span>;\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ Custom hook\n<\/span><span>$<\/span><span style=\"color:#b16139;\">hexadecimalValidator <\/span><span>= <\/span><span style=\"color:#55859b;\">new <\/span><span style=\"color:#a07e3b;\">RegexpValidatorHook<\/span><span>(&#39;\/<\/span><span style=\"color:#55859b;\">^<\/span><span style=\"color:#9f713c;\">[0-9a-f]<\/span><span>+<\/span><span style=\"color:#55859b;\">$<\/span><span>\/&#39;);\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ API\n<\/span><span>$<\/span><span style=\"color:#b16139;\">api <\/span><span>= <\/span><span style=\"color:#a07e3b;\">OpenFeatureAPI<\/span><span>::<\/span><span style=\"color:#b16139;\">getInstance<\/span><span>();\n<\/span><span>$<\/span><span style=\"color:#b16139;\">api<\/span><span>-&gt;<\/span><span style=\"color:#b16139;\">addHooks<\/span><span>($<\/span><span style=\"color:#b16139;\">hexadecimalValidator<\/span><span>);\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ Client\n<\/span><span>$<\/span><span style=\"color:#b16139;\">client <\/span><span>= $<\/span><span style=\"color:#b16139;\">api<\/span><span>-&gt;<\/span><span style=\"color:#b16139;\">getClient<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">hooks-test<\/span><span>&#39;);\n<\/span><span>$<\/span><span style=\"color:#b16139;\">client<\/span><span>-&gt;<\/span><span style=\"color:#b16139;\">addHooks<\/span><span>($<\/span><span style=\"color:#b16139;\">hexadecimalValidator<\/span><span>);\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ Provider\n<\/span><span>$<\/span><span style=\"color:#b16139;\">provider <\/span><span>= <\/span><span style=\"color:#55859b;\">new <\/span><span style=\"color:#a07e3b;\">ExampleProvider<\/span><span>();\n<\/span><span>$<\/span><span style=\"color:#b16139;\">provider<\/span><span>-&gt;<\/span><span style=\"color:#b16139;\">addHooks<\/span><span>($<\/span><span style=\"color:#b16139;\">hexadecimalValidator<\/span><span>);\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ Invocation\n<\/span><span>$<\/span><span style=\"color:#b16139;\">client<\/span><span>-&gt;<\/span><span style=\"color:#b16139;\">resolveStringValue<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">test-flag<\/span><span>&#39;, &#39;<\/span><span style=\"color:#489963;\">deadbeef<\/span><span>&#39;, <\/span><span style=\"color:#9f713c;\">null<\/span><span>, <\/span><span style=\"color:#55859b;\">new <\/span><span style=\"color:#a07e3b;\">EvaluationOptions<\/span><span>([$<\/span><span style=\"color:#b16139;\">hexadecimalValidator<\/span><span>]));\n<\/span><\/code><\/pre>\n<p>Easy as that! The higher up you place the hook, the more <em>universal<\/em> it becomes across your application. Make sure to keep this in mind as applying something at the API-level will impact <em>every evaluation<\/em> in the entire application.<\/p>\n<h3 id=\"hooks-observability-with-opentelemetry\">Hooks: Observability with OpenTelemetry<\/h3>\n<p>Something that you might find useful as a developer pushing code to production is how the behavior of the feature flagging system may impact your users. Perhaps you want to know whether the evaluation in the provider, or what value was determined for a given request. Well, you can utilize observability tools like OpenTelemetry to accomplish that.<\/p>\n<p>There are a couple of observability hooks already provided, and both of them utilize the PSR-4 autoloader functionality for PHP. The convenience of this is that all it takes is having the package <em>installed<\/em> and you'll get the hook set at the API-level to trace <em>all<\/em> evaluations, following the standard practices defined in e.g. OpenTelemetry's Semantic Conventions.<\/p>\n<p>So, install the package:<\/p>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#b16139;\">composer<\/span><span> require open-feature\/otel-hook\n<\/span><\/code><\/pre>\n<p>And autoload as you normally would! This example follows the standard practice of autoloading at the entrypoint of your PHP application:<\/p>\n<pre data-lang=\"php\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-php \"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#867469;\">&lt;?php\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">declare<\/span><span>(<\/span><span style=\"color:#9f713c;\">strict_types<\/span><span>=<\/span><span style=\"color:#9f713c;\">1<\/span><span>);\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">use <\/span><span>OpenFeature\\<\/span><span style=\"color:#a07e3b;\">OpenFeatureAPI<\/span><span>;\n<\/span><span>\n<\/span><span style=\"color:#1c9aa0;\">putenv<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">OTEL_PHP_AUTOLOAD_ENABLED=true<\/span><span>&#39;);\n<\/span><span style=\"color:#1c9aa0;\">putenv<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">OTEL_TRACES_EXPORTER=otlp<\/span><span>&#39;);\n<\/span><span style=\"color:#1c9aa0;\">putenv<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">OTEL_EXPORTER_OTLP_PROTOCOL=grpc<\/span><span>&#39;);\n<\/span><span style=\"color:#1c9aa0;\">putenv<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">OTEL_METRICS_EXPORTER=otlp<\/span><span>&#39;);\n<\/span><span style=\"color:#1c9aa0;\">putenv<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">OTEL_EXPORTER_OTLP_METRICS_PROTOCOL=grpc<\/span><span>&#39;);\n<\/span><span style=\"color:#1c9aa0;\">putenv<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">OTEL_EXPORTER_OTLP_ENDPOINT=http:\/\/collector:4317<\/span><span>&#39;);\n<\/span><span style=\"color:#1c9aa0;\">putenv<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">OTEL_PHP_TRACES_PROCESSOR=batch<\/span><span>&#39;);\n<\/span><span style=\"color:#1c9aa0;\">putenv<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">OTEL_PROPAGATORS=b3,baggage,tracecontext<\/span><span>&#39;);\n<\/span><span>\n<\/span><span style=\"color:#1c9aa0;\">echo <\/span><span>&#39;<\/span><span style=\"color:#489963;\">autoloading SDK example starting...<\/span><span>&#39; . PHP_EOL;\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ Composer autoloader will execute SDK\/_autoload.php which will register global instrumentation from environment configuration\n<\/span><span style=\"color:#55859b;\">require <\/span><span style=\"color:#1c9aa0;\">dirname<\/span><span>(<\/span><span style=\"color:#9f713c;\">__DIR__<\/span><span>) . &#39;<\/span><span style=\"color:#489963;\">\/vendor\/autoload.php<\/span><span>&#39;;\n<\/span><span>\n<\/span><span>$<\/span><span style=\"color:#b16139;\">client <\/span><span>= <\/span><span style=\"color:#a07e3b;\">OpenFeatureAPI<\/span><span>::<\/span><span style=\"color:#b16139;\">getInstance<\/span><span>()-&gt;<\/span><span style=\"color:#b16139;\">getClient<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">dev.openfeature.contrib.php.demo<\/span><span>&#39;, &#39;<\/span><span style=\"color:#489963;\">1.0.0<\/span><span>&#39;);\n<\/span><span>\n<\/span><span>$<\/span><span style=\"color:#b16139;\">version <\/span><span>= $<\/span><span style=\"color:#b16139;\">client<\/span><span>-&gt;<\/span><span style=\"color:#b16139;\">getStringValue<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">dev.openfeature.contrib.php.version-value<\/span><span>&#39;, &#39;<\/span><span style=\"color:#489963;\">unknown<\/span><span>&#39;);\n<\/span><span>\n<\/span><span style=\"color:#1c9aa0;\">echo <\/span><span>&#39;<\/span><span style=\"color:#489963;\">Version is <\/span><span>&#39; . $<\/span><span style=\"color:#b16139;\">version<\/span><span>;\n<\/span><\/code><\/pre>\n<p>As you can see, there were no <strong>explicit actions<\/strong> necessary. However, the OpenTelemetry hook is set up at the API-level and providing tracing based on the configuration of your OTel exporter.<\/p>\n<!-- References -->\n"},{"title":"JSON Web Tokens: The Full Picture","pubDate":"Sun, 06 Aug 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/jwt-the-full-picture-jose\/","guid":"https:\/\/blog.carrio.dev\/blog\/jwt-the-full-picture-jose\/","description":"<p>JSON Web Tokens, commonly abbreviated as JWTs, are a standard that is a part of the JSON Object Signing and Encryption (JOSE) set of standards. There is a lot to break down here, depending on what you want to accomplish. JWTs are often conflated with a combination of these standards, which include:<\/p>\n<ul>\n<li><a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7518\">JSON Web Algorithms (JWA)<\/a>: Defines <strong>cryptographic algorithms<\/strong> and <strong>identifiers<\/strong> used across JOSE standards.<\/li>\n<li><a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7517\">JSON Web Keys (JWK)<\/a>: Defines the JSON representation format for <strong>cryptographic keys<\/strong>.<\/li>\n<li><a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7516\">JSON Web Encryption (JWE)<\/a>: Defines the JSON structure for representing <strong>encrypted<\/strong> content using JWA.<\/li>\n<li><a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7515\">JSON Web Signatures (JWS)<\/a>: Defines the JSON structure for representing <strong>cryptographical veriable signatures<\/strong> of content using JWA.<\/li>\n<li><a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7519\">JSON Web Tokens (JWT)<\/a>: Defines subject claims using JSON structures. These claims can be optionally protected via JWE or JWS.<\/li>\n<\/ul>\n<p>That is a mouthful to say the least, but maybe you're starting to see how there is a lot more to JWTs than you might originally think, especially when you go to a site like <a href=\"https:\/\/jwt.io\">jwt.io<\/a> that provides an opinionated example of a JSON Web Token protected with a JSON Web Signature. In fact, that page doesn't even allow you to showcase an example of an unprotected JSON Web Token (e.g. <code>\"alg\": \"none\"<\/code> in the header), so you can sort of understand just how JWTs have been adopted (and it has <em>something<\/em> to do with security).<\/p>\n<h2 id=\"a-simple-jwt-starting-with-a-basic-example\">A Simple JWT: Starting with a Basic Example<\/h2>\n<p>A JSON Web Token can optionally utilize no other JOSE protection (JWS\/JWE)- which is the simplest use case. The additional protections sort of build on top of this. To start, a JSON Web Token consists of a <strong>header<\/strong>, in the form of a JSON Web Key, and a <strong>payload<\/strong>, a JSON object which contains subject claims. The JWK header defines what algorithm is used for the JWT, and the basic case is that there is none.<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>header: { &quot;alg&quot;: &quot;none&quot; }\n<\/span><span>payload: { &quot;sub&quot;: &quot;user@example.test&quot; }\n<\/span><\/code><\/pre>\n<p>Now, while technically JSON is used to define all of the structured data in JSON Web Tokens, the delivery format is entirely URL safe. Each of the parts of a JWT are encoded in a common manner:<\/p>\n<pre data-lang=\"python\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-python \"><code class=\"language-python\" data-lang=\"python\"><span style=\"color:#b16139;\">base64UrlEncode<\/span><span>(\n<\/span><span>  <\/span><span style=\"color:#b16139;\">utf8Encode<\/span><span>(\n<\/span><span>    JSONObject\n<\/span><span>  )\n<\/span><span>)\n<\/span><\/code><\/pre>\n<blockquote>\n<p>Note: Binary data formats within the payload need to be worked around in their own way, and are not covered as part of the specification.<\/p>\n<\/blockquote>\n<p>That encoding mechanism is applied individually to the header and payload, and the end result of each concatenated with <code>.<\/code>s.<\/p>\n<pre data-lang=\"python\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-python \"><code class=\"language-python\" data-lang=\"python\"><span>header  = &#39;<\/span><span style=\"color:#489963;\">{&quot;alg&quot;:&quot;none&quot;}<\/span><span>&#39;\n<\/span><span>payload = &#39;<\/span><span style=\"color:#489963;\">{&quot;sub&quot;:&quot;user@example.test&quot;}<\/span><span>&#39;\n<\/span><span>\n<\/span><span>&#39;<\/span><span style=\"color:#489963;\">{&quot;alg&quot;:&quot;none&quot;}<\/span><span>&#39; =&gt; &#39;<\/span><span style=\"color:#489963;\">eyJhbGciOiJub25lIn0K<\/span><span>&#39;\n<\/span><span>&#39;<\/span><span style=\"color:#489963;\">{&quot;sub&quot;:&quot;user@example.test&quot;}<\/span><span>&#39; =&gt; &#39;<\/span><span style=\"color:#489963;\">eyJzdWIiOiJ1c2VyQGV4YW1wbGUudGVzdCJ9Cg<\/span><span>&#39;\n<\/span><span>\n<\/span><span>jwt = &#39;<\/span><span style=\"color:#489963;\">eyJhbGciOiJub25lIn0K.eyJzdWIiOiJ1c2VyQGV4YW1wbGUudGVzdCJ9Cg<\/span><span>&#39;\n<\/span><span>       <\/span><span style=\"color:#5f6d64;\">## encoded header ## ########### encoded payload ##########    \n<\/span><\/code><\/pre>\n<p>As you can see, there are only two parts to this. This differs from a JWT you might see used in OpenID Connect OAuth flows, which will typically construct JWTs with some type of <em>secret<\/em> to support <em>validation<\/em> of tokens, a functionality of JSON Web Signatures. Let's start to put together the building blocks to these cryptographic components of the JOSE standards.<\/p>\n<h2 id=\"json-web-algorithms-or-definitions-we-ll-need-for-everything-else-really\">JSON Web Algorithms, or \"Definitions We'll Need For Everything Else, Really\"<\/h2>\n<p>Somewhere we need to define what <code>\"alg\"<\/code> and <code>\"enc\"<\/code> and all of these header keys <em>mean<\/em>. That's what JWA, JSON Web Algorithms, defines. The header is primarily reserved for all of the cryptographic functionality, and being able to read this metadata to determine how to decrypt or validate tokens is one of the strong suits of JWTs.<\/p>\n<p>The standard is defined in <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7518\">RFC-7518<\/a>, and that will provide all of the information you need on various supported algorithms, but I'll give a few examples here to cover the use cases I'll preset across this document.<\/p>\n<p>As we'll get to later, these definitions support the functionality of JSON Web Signatures (JWS) and JSON Web Encryption (JWE). The RFC is similarly broken down to cover each of these cases:<\/p>\n<h2 id=\"json-web-signatures-jws\">JSON Web Signatures (JWS)<\/h2>\n<p>JWS is focused on providing <em>verifiable<\/em> data. The metadata in the header will dictate how to verify the payload of the JWT using the <em>signature<\/em> appended to it.<\/p>\n<p>You can provide the following (case-insensitive) values for <code>\"alg\"<\/code> keys in the header, which will apply a cryptographic signature utilizing the described algorithm:<\/p>\n<ul>\n<li><strong>HS256<\/strong>: HMAC using SHA-256<\/li>\n<li><strong>HS384<\/strong>: HMAC using SHA-384<\/li>\n<li><strong>HS512<\/strong>: HMAC using SHA-512<\/li>\n<li><strong>RS256<\/strong>: RSASSA-PKCS1-v1_5 using SHA-256<\/li>\n<li><strong>RS384<\/strong>: RSASSA-PKCS1-v1_5 using SHA-384<\/li>\n<li><strong>RS512<\/strong>: RSASSA-PKCS1-v1_5 using SHA-512<\/li>\n<li><strong>ES256<\/strong>: ECDSA using P-256 and SHA-256<\/li>\n<li><strong>ES384<\/strong>: ECDSA using P-384 and SHA-384<\/li>\n<li><strong>ES512<\/strong>: ECDSA using P-521 and SHA-512<\/li>\n<li><strong>PS256<\/strong>: RSASSA-PSS using SHA-256 and MGF1 with SHA-256<\/li>\n<li><strong>PS384<\/strong>: RSASSA-PSS using SHA-384 and MGF1 with SHA-384<\/li>\n<li><strong>PS512<\/strong>: RSASSA-PSS using SHA-512 and MGF1 with SHA-512<\/li>\n<li><strong>none<\/strong>: No digital signature or MAC performed<\/li>\n<\/ul>\n<h3 id=\"symmetric-hashing\">Symmetric Hashing<\/h3>\n<p>HMAC, Hash-based Message Authentication Codes, are useful when you can utilize <em>shared<\/em> secrets. That is, the party creating the JWT and the party consuming the JWT both know of a secret value that is used to generate the signature and later verify it. When both parties are familiar with the secret and have communicated this securely, this form of cryptography is still hardened against man-in-the-middle attacks, since any intercepting party cannot manipulate the token and pass it on- once the body has changed the signature would no longer be valid. This is true outside of the very, <em>very<\/em> small probability of a hash collision. In the example of the weakest encryption suggested, which is SHA-256, that relates to 256 bits. In terms of how big a number that equates to, well, to quote Douglas Adams:<\/p>\n<blockquote>\n<p>You just won't believe how vastly, hugely, mind-bogglingly big it is.<\/p>\n<\/blockquote>\n<p>But if you <em>must<\/em> know, <code>2^256 = 115,792,089,237,316,195,423,570,985,008,687,907,853,269,984,665,640,564,039,457,584,007,913,129,639,936<\/code>, which happens to be a <em>mind-boggingly big<\/em> number.<\/p>\n<p>A SHA-256 collision is about as likely as getting one Powerball ticket <strong>four<\/strong> weeks in a row and winning the maximum jackpot <strong>every time<\/strong>.<\/p>\n<p>I think it's landed by now, and I'm just having fun with numbers now so I digress..<\/p>\n<h3 id=\"asymmetric-hashing\">Asymmetric Hashing<\/h3>\n<p>Also known as public-private key cryptography, asymmetric hashing allows you to have a party whose familiar with a <em>generative<\/em> secret, and the consuming parties can be configured with a <em>validation<\/em> secret. This approach is hardened not just against man-in-the-middle (MITM) attacks but also disallows the consumer to manipulate the JWT in any way either.<\/p>\n<p>You will often see approaches like this utilized in OAuth systems since you can't allow an OAuth client to not just validate access tokens but <em>manipulate<\/em> them in any way they would like.<\/p>\n<p>Asymmetric algorithms power many tools we make use of today; it enables a <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc8446\">secure TLS handshake<\/a>, <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc4253\">secure shells<\/a>, extends and protects <a href=\"https:\/\/gnupg.org\/\">email communication<\/a>, and more.<\/p>\n<h3 id=\"no-hashing\">No Hashing<\/h3>\n<p>Like we had shown in our example above, you can also specify not to use any algorithm. This would amount to no signature being generated at all, so you're just passing a base64 encoded JSON object with some additional JSON metadata.<\/p>\n<p>If you're using this approach, you <em>probably<\/em> shouldn't even be using JWTs, unless that's an imposed requirement and you are <strong>absolutely certain<\/strong> there are no security requirements on the exchanged data.<\/p>\n<h2 id=\"json-web-encryption-jwe\">JSON Web Encryption (JWE)<\/h2>\n<p>In contrast to JWS, JWE is focused on protecting the data in transit. Where JWS protects your data from being manipulated in transit, JWE also protects your data from being <strong>read<\/strong> in transit. Only someone who has the necessary secrets to perform the decryption described by the metadata in the header will be able to do this.<\/p>\n<p>In the header, the <code>\"alg\"<\/code> field will be used to describe the encryption algorithm, each respective one may include additional header fields accordingly. See the full specification under the JWE section of the JWA specification <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7518#section-4\">here<\/a>.<\/p>\n<ul>\n<li><strong>RSA1_5<\/strong>: RSAES-PKCS1-v1_5<\/li>\n<li><strong>RSA-OAEP<\/strong>: RSAES OAEP using default parameters<\/li>\n<li><strong>RSA-OAEP-256<\/strong>: RSAES OAEP using SHA-256 and MGF1 with SHA-256<\/li>\n<li><strong>A128KW<\/strong>: AES Key Wrap with default initial value using 128-bit key<\/li>\n<li><strong>A192KW<\/strong>: AES Key Wrap with default initial value using 192-bit key<\/li>\n<li><strong>A256KW<\/strong>: AES Key Wrap with default initial value using 256-bit key<\/li>\n<li><strong>dir<\/strong>: Direct use of a shared symmetric key as the CEK<\/li>\n<li><strong>ECDH-ES<\/strong>: Elliptic Curve key agreement using Concat KDF<\/li>\n<li><strong>ECDH-ES+A128KW<\/strong>: ECDH-ES using Concat KDF and CEK wrapped with A128KW<\/li>\n<li><strong>ECDH-ES+A192KW<\/strong>: ECDH-ES using Concat KDF and CEK wrapped with A192KW<\/li>\n<li><strong>ECDH-ES+A256KW<\/strong>: ECDH-ES using Concat KDF and CEK wrapped with A256KW<\/li>\n<li><strong>A128GCMKW<\/strong>: Key wrapping with AES GCM using 128-bit key<\/li>\n<li><strong>A192GCMKW<\/strong>: Key wrapping with AES GCM using 192-bit key<\/li>\n<li><strong>A256GCMKW<\/strong>: Key wrapping with AES GCM using 256-bit key<\/li>\n<li><strong>PBES2-HS256+A128KW<\/strong>: PBES2 with HMAC SHA-256 and \"A128KW\" wrapping<\/li>\n<li><strong>PBES2-HS384+A192KW<\/strong>: PBES2 with HMAC SHA-384 and \"A192KW\" wrapping<\/li>\n<li><strong>PBES2-HS512+A256KW<\/strong>: PBES2 with HMAC SHA-512 and \"A256KW\" wrapping<\/li>\n<\/ul>\n<p>I won't dive much farther into these, the important note here is that the header metadata maintains the definition for how these encryptions are applied, thus how the client would understand how to decrypt them.<\/p>\n<h2 id=\"the-structure-of-a-jwt\">The Structure of a JWT<\/h2>\n<p><strong>Required<\/strong>:<\/p>\n<ul>\n<li>Header (defines whether to use JWS\/JWE\/none)<\/li>\n<li>Payload<\/li>\n<\/ul>\n<p><strong>Optional<\/strong>:<\/p>\n<ul>\n<li>Signature (JWS)<\/li>\n<\/ul>\n<h3 id=\"header\">Header<\/h3>\n<p>The header of a JSON Web Token defines the cryptographic mechanism, as discussed in JWS\/JWE. This can be based on HMAC in combination with a common hashing algorithm like SHA-512 or asymmetric key cryptography such as RSA. The former allows for an opaque secret value to be used for generating the JWT signature which is also used to verify it. The latter utilizes public\/private key cryptography that allows you to generate a secret with a private key and utilize a public key, one that can be known by anyone without risking security around the tokens, to verify the signature of the JSON Web Token.<\/p>\n<h3 id=\"payload\">Payload<\/h3>\n<p>The payload of a JSON Web Token can contain any valid json object, Jwt's do not have any further intrinsic limitations, but standards that build upon JWTs such as OpenID Connect use JWTs to provide stateless context in an access or identity token that can be utilized by resource servers or OAuth clients.<\/p>\n<h3 id=\"signature\">Signature<\/h3>\n<p>Looking back at the header, we can reference the utilized <strong>algorithm<\/strong> and <strong>token type<\/strong>.<\/p>\n<h2 id=\"json-web-keys\">JSON Web Keys<\/h2>\n<p>These specify how to define cryptographic keys, typically used in combination with JWS\/JWE. This builds upon specification for JWA, and can be utilized to provide JSON Web Key Sets, which consumers of secured JWTs can reach out to in order to retrieve metadata for verifying tokens, as an example.<\/p>\n<p>The RFC for for JSON Web Key includes <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc7517#appendix-A\">an example in Appendix A<\/a>, which offers both elliptic curve and RSA <em>public<\/em> keys for validating a JWT against its signature by the defined header metadata.<\/p>\n<p>This piece of magic provides the mechanism for verification of JWS-secured JWTs with OAuth \/ OpenID Connect.<\/p>\n<p>An example of this in the wild would be Auth0's JWKS. One is exposed for every customer, but because of the security provided by asymmetric cryptography, this <em>public<\/em> key serves no special purpose outside of verification of tokens. You cannot construct a JWT with a public key that can be verified by other consumers using that public key, so it's still secure against attack vectors such as MITM.<\/p>\n<h2 id=\"additional-reading\">Additional reading<\/h2>\n<p>You can dig more into the official specification of JSON Web Tokens in IETF's <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7519\">RFC-7519<\/a>. The JWT specification builds upon two other important standards, which are JSON Web Signatures (JWS) defined in <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7515\">RFC-7515<\/a> and JSON Web Encryption (JWE) defined in <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7516\">RFC-7516<\/a><\/p>\n<p>A great playground space for messing around with JWT's is <a href=\"https:\/\/jwt.io\">jwt.io<\/a>. They have an interactive JSON Web Token editor that shows the raw JWT and a breakdown of its parts, even so far as allowing you to verify signatures.<\/p>\n<p>There is a short\/long summary of the JOSE standards on StackOverflow <a href=\"https:\/\/stackoverflow.com\/questions\/74257560\/what-is-the-difference-between-jose-jwa-jwe-jwk-jws-and-jwt\">here<\/a> as well, which I also found helpful when RFCs got a bit too boring.<\/p>\n<!-- References -->\n"},{"title":"Automating NLP Model Development with Dialogflow","pubDate":"Sat, 05 Aug 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/automate-dialogflow-nlp\/","guid":"https:\/\/blog.carrio.dev\/blog\/automate-dialogflow-nlp\/","description":"<h2 id=\"dialogflow\">Dialogflow<\/h2>\n<p><a href=\"https:\/\/cloud.google.com\/dialogflow\/\">Dialogflow<\/a> is a natural language processing system developed by Google. It provides all the constructs necessary in order to define a natural language processing model that can intelligently infer what a user is saying, but also providing various functionality on top of this including sentiment analysis and any recognition and more<\/p>\n<p>At the time I was working on the <a href=\"https:\/\/www.Dynatrace.com\/news\/blog\/davis-assistant-is-now-smarter-than-ever\/\">Davis Assistant<\/a> project at Dynatrace. After joining the team, I assisted with our project automation, TypeScript migration, and DevOps enhancements. One of my proposed projects thereafter was to completely automate our natural language processing definitions in such a way that it would also be entirely reusable inside of our codebase. Thus, not only would we have safe deployments and consistent definitions, they would be utilized inside of our APIs and hook directly into the domain logic of our system. As an example, this means the same enums powering various event definitions in Dialogflow training phrases could also be utilized in logic in our API handlers relating to them.<\/p>\n<h2 id=\"dialogflow-s-natural-language-processing-model\">Dialogflow's Natural Language Processing Model<\/h2>\n<p>I won't do a deep dive into this subject, as it's now been several years and I definitely wouldn't call myself an expert on it at this point. However, this gives you an idea of the various elements involved in defining an NLP model with Dialogflow, which is what the later solution automates.<\/p>\n<h3 id=\"entities\">Entities<\/h3>\n<p>Referred to as Entity Types, these allow you to control how the user input data gets extracted. There are many predefined entity, which I'll refer to as <em>default entities<\/em> later on in the automation. The entity type allows us to define many entries for a single entity, or synonyms. So you could recognize multiple specific <em>types<\/em> of <strong>fruit<\/strong>, like strawberries grapes and oranges, as a <strong>fruit<\/strong> entity.<\/p>\n<h3 id=\"intents\">Intents<\/h3>\n<p>An Intent will categorize the intention of the user interaction. What these eventually break down for us, in the context of a tool like Davis Assistant, are the various user journeys of interactions with the bot. Think of the sentence \"Show me the <strong>Apdex<\/strong> for <strong>Production<\/strong> over the <strong>last week<\/strong>\". We might have broken down that user journey as \"application performance\".<\/p>\n<p>For an Intent you can specify a number of <em>training phrases<\/em> and <em>parameters<\/em>. The <em>training phrases<\/em> can reference various entity types, custom or default, parts, and more. These become particularly useful as defined variables since many of the training phrases we'll build out end up being permutations of the same input parameters, sometimes including a date or sometimes including an application name, etc. The <em>parameters<\/em> allow you to specify parts of the user input that you might want to extract, effectively acting as parameters to the intent <em>handlers<\/em> in our service.<\/p>\n<h3 id=\"events\">Events<\/h3>\n<p>While intents are typically matched when users provide some input phrase, we can also utilize events to trigger intents. This is particularly useful for directing user interactions in a similar fashion to invoking callbacks on various functionality in a system.<\/p>\n<h3 id=\"context\">Context<\/h3>\n<p>An important component of our design was to support specific user's and their data only during the lifecycle of their requests. When someone from Average Joe Gym says \"Hey Google, talk to Davis Assistant\" and asks a question about their website, we don't need data about their tenant leaking into the rest of our user's requests. We can utilize a context to fulfill metadata relevant to processing user input, such as application and service names and more that are available inside the Dynatrace tenants. We fulfill this context prior to executing the user action as best as possible so that user's can naturally interact with Davis Assistant. Otherwise, references to your applications, like \"the blog\", simply mean nothing to us.<\/p>\n<h3 id=\"webhooks\">Webhooks<\/h3>\n<p>Webhooks are very common in the industry today, and we can utilize them with Dialogflow to allow them to direct the processed user input to our services. When user input is processed and in intent and its parameters are determined, we'll receive a request to our Router which handles the validation and forwarding of the request to our internal service for handling and responding to user interactions.<\/p>\n<h2 id=\"research\">Research<\/h2>\n<p>Various tools were looked at in terms of how to support such a feature. Infrastructure-as-Code tools at the time didn't support general purpose programming languages and general platforms, it was typically one or the other. Newer projects today may not have this limitation, such as Pulumi, but because of that a custom solution was the final option for how we would implement such functionality.<\/p>\n<p>Our stack was now entirely in TypeScript as previously mentioned, and so the tool itself would need to be reusable inside of that code. Since we controlled all of the components of our system we didn't need to implement this tool and such a manner to support a polyglot environment generating JSON definitions or the like. This gave us a lot of power and simplified the overall solution more, as opposed to requiring a code generator component as part of the integration.<\/p>\n<h2 id=\"dialogflow-and-nodejs\">Dialogflow and NodeJS<\/h2>\n<p>Google provided a NodeJS SDK for Dialogflow under the NPM package <a href=\"https:\/\/github.com\/googleapis\/nodejs-dialogflow\">nodejs-dialogflow<\/a>. This <em>was<\/em> a purely JavaScript package when this work started, and in our time utilizing Dialogflow we contributed the [@types\/dialogflow] package in the <a href=\"https:\/\/github.com\/DefinitelyTyped\/DefinitelyTyped\">DefinitelyTyped<\/a> repository, helped facilitate resolution around <a href=\"https:\/\/github.com\/DefinitelyTyped\/DefinitelyTyped\/pull\/39627\">typing chaos<\/a> during a package migration, and eased others over to the new <a href=\"https:\/\/www.npmjs.com\/package\/@google-cloud\/dialogflow\">@google-cloud\/dialogflow<\/a> package after its release.<\/p>\n<h2 id=\"migration-phase\">Migration Phase<\/h2>\n<p>One component of the project was the ability to synchronize the definitions in our code to Dialogflow servers, but during the development phase of the project we also had to be able to continue utilizing the Dialogflow UI. As such, I also implemented a capability for importing Dialogflow resources and automatically generating all of the necessary entity types, contexts, events, intents, and more. You could simply export the Dialogflow project to a file and then run:<\/p>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#5f6d64;\"># given you had installed `@0xc\/dialogflow-as-code\n<\/span><span style=\"color:#b16139;\">dialogflow-as-code -i<\/span><span> .\/export-dir<\/span><span style=\"color:#b16139;\"> -o<\/span><span> .\/src\/dialogflow\n<\/span><\/code><\/pre>\n<p>And you now had an entire set of Dialogflow-as-Code source code in TypeScript that defined <strong>all<\/strong> of your project resources. This functionality made the continuous integration of UI changes into our source code possible until we flipped the responsibilities, eventually making our source code the source of truth for our Dialogflow project. We still had the ability to triage issues in the web interface when necessary, but due to the change our environment inconsistencies dropped significantly.<\/p>\n<h2 id=\"example-time\">Example Time<\/h2>\n<blockquote>\n<p>You can find out more about each of these types of resources on <a href=\"https:\/\/cloud.google.com\/dialogflow\/cx\/docs\/concept\">the Dialogflow documentation site<\/a>.<\/p>\n<\/blockquote>\n<pre data-lang=\"ts\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-ts \"><code class=\"language-ts\" data-lang=\"ts\"><span style=\"color:#5f6d64;\">\/\/ Sample Entity Type Builder\n<\/span><span style=\"color:#55859b;\">export const <\/span><span style=\"color:#b16139;\">etFruit <\/span><span>= <\/span><span style=\"color:#478c90;\">entityType<\/span><span>()\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">d<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">fruit<\/span><span>&quot;)\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">e<\/span><span>([<\/span><span style=\"color:#478c90;\">syn<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">apple<\/span><span>&quot;), <\/span><span style=\"color:#478c90;\">syn<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">strawberry<\/span><span>&quot;)])\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">k<\/span><span>(<\/span><span style=\"color:#b16139;\">ek<\/span><span>.<\/span><span style=\"color:#b16139;\">list<\/span><span>)\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">build<\/span><span>();\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ Sample Entity Type\n<\/span><span style=\"color:#55859b;\">export const <\/span><span style=\"color:#b16139;\">etSample<\/span><span>: EntityType = {\n<\/span><span>  displayName: &quot;<\/span><span style=\"color:#489963;\">sample<\/span><span>&quot;,\n<\/span><span>  entities: [{ value: &quot;<\/span><span style=\"color:#489963;\">sample<\/span><span>&quot;, synonyms: [&quot;<\/span><span style=\"color:#489963;\">piece<\/span><span>&quot;, &quot;<\/span><span style=\"color:#489963;\">swab<\/span><span>&quot;, &quot;<\/span><span style=\"color:#489963;\">choice<\/span><span>&quot;] }],\n<\/span><span>  kind: &quot;<\/span><span style=\"color:#489963;\">KIND_MAP<\/span><span>&quot;,\n<\/span><span>  autoExpansionMode: &quot;<\/span><span style=\"color:#489963;\">AUTO_EXPANSION_MODE_DEFAULT<\/span><span>&quot;,\n<\/span><span>};\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ Sample Context Builder\n<\/span><span style=\"color:#55859b;\">export const <\/span><span style=\"color:#b16139;\">cxFruit <\/span><span>= <\/span><span style=\"color:#478c90;\">cx<\/span><span>()\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">n<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">fruit-context<\/span><span>&quot;)\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">lc<\/span><span>(<\/span><span style=\"color:#9f713c;\">5<\/span><span>)\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">p<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">date-time-original<\/span><span>&quot;, &quot;<\/span><span style=\"color:#489963;\">string_value<\/span><span>&quot;)\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">build<\/span><span>();\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ Sample Events\n<\/span><span style=\"color:#55859b;\">export enum <\/span><span>Event {\n<\/span><span>  <\/span><span style=\"color:#b16139;\">FEEDBACK <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">FEEDBACK<\/span><span>&quot;,\n<\/span><span>  <\/span><span style=\"color:#b16139;\">YES <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">YES<\/span><span>&quot;,\n<\/span><span>  <\/span><span style=\"color:#b16139;\">NO <\/span><span>= &quot;<\/span><span style=\"color:#489963;\">NO<\/span><span>&quot;,\n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ Sample Intent\n<\/span><span style=\"color:#5f6d64;\">\/\/ prettier-ignore\n<\/span><span style=\"color:#55859b;\">export const <\/span><span style=\"color:#b16139;\">ntFruitInfo <\/span><span>= <\/span><span style=\"color:#478c90;\">intent<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">fruitInfo<\/span><span>&quot;)\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">priority<\/span><span>(<\/span><span style=\"color:#b16139;\">Priority<\/span><span>.<\/span><span style=\"color:#b16139;\">LOW<\/span><span>)\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">webhook<\/span><span>(<\/span><span style=\"color:#9f713c;\">true<\/span><span>)\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">trainingPhrases<\/span><span>([\n<\/span><span>    <\/span><span style=\"color:#478c90;\">tp<\/span><span>([&quot;<\/span><span style=\"color:#489963;\">describe the <\/span><span>&quot;, <\/span><span style=\"color:#478c90;\">pb<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">sample<\/span><span>&quot;), &quot;<\/span><span style=\"color:#489963;\"> of <\/span><span>&quot;, <\/span><span style=\"color:#b16139;\">etFruit<\/span><span>, &quot;<\/span><span style=\"color:#489963;\"> over <\/span><span>&quot;, <\/span><span style=\"color:#478c90;\">det<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">date-time<\/span><span>&quot;)]),\n<\/span><span>    <\/span><span style=\"color:#478c90;\">tp<\/span><span>([&quot;<\/span><span style=\"color:#489963;\">how was the <\/span><span>&quot;, <\/span><span style=\"color:#478c90;\">pb<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">sample<\/span><span>&quot;), &quot;<\/span><span style=\"color:#489963;\"> of <\/span><span>&quot;, <\/span><span style=\"color:#b16139;\">etFruit<\/span><span>]),\n<\/span><span>    <\/span><span style=\"color:#478c90;\">tp<\/span><span>([<\/span><span style=\"color:#478c90;\">pb<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">sample<\/span><span>&quot;), &quot;<\/span><span style=\"color:#489963;\"> of <\/span><span>&quot;, <\/span><span style=\"color:#b16139;\">etFruit<\/span><span>, &quot; &quot;, <\/span><span style=\"color:#478c90;\">det<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">date-time<\/span><span>&quot;)]),\n<\/span><span>    <\/span><span style=\"color:#478c90;\">tp<\/span><span>([<\/span><span style=\"color:#478c90;\">pb<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">sample<\/span><span>&quot;), &quot;<\/span><span style=\"color:#489963;\"> of <\/span><span>&quot;, <\/span><span style=\"color:#b16139;\">etFruit<\/span><span>]),\n<\/span><span>    <\/span><span style=\"color:#478c90;\">tp<\/span><span>([&quot;<\/span><span style=\"color:#489963;\">what was the <\/span><span>&quot;, <\/span><span style=\"color:#478c90;\">pb<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">sample<\/span><span>&quot;), &quot;<\/span><span style=\"color:#489963;\"> of <\/span><span>&quot;, <\/span><span style=\"color:#b16139;\">etFruit<\/span><span>, &quot; &quot;, <\/span><span style=\"color:#478c90;\">det<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">date-time<\/span><span>&quot;), &quot;<\/span><span style=\"color:#489963;\">?<\/span><span>&quot;]),\n<\/span><span>    <\/span><span style=\"color:#478c90;\">tp<\/span><span>([&quot;<\/span><span style=\"color:#489963;\">what was the <\/span><span>&quot;, <\/span><span style=\"color:#478c90;\">pb<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">sample<\/span><span>&quot;), &quot;<\/span><span style=\"color:#489963;\"> of <\/span><span>&quot;, <\/span><span style=\"color:#b16139;\">etFruit<\/span><span>]),\n<\/span><span>  ])\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">messages<\/span><span>([\n<\/span><span>    <\/span><span style=\"color:#478c90;\">msg<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">text<\/span><span>&quot;).<\/span><span style=\"color:#1c9aa0;\">set<\/span><span>([&quot;<\/span><span style=\"color:#489963;\">I&#39;m sorry Dave, I can&#39;t do that<\/span><span>&quot;]).<\/span><span style=\"color:#478c90;\">build<\/span><span>(),\n<\/span><span>    <\/span><span style=\"color:#478c90;\">msg<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">text<\/span><span>&quot;).<\/span><span style=\"color:#1c9aa0;\">set<\/span><span>([&quot;<\/span><span style=\"color:#489963;\">Second response<\/span><span>&quot;]).<\/span><span style=\"color:#478c90;\">build<\/span><span>(),\n<\/span><span>  ])\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">events<\/span><span>([<\/span><span style=\"color:#a07e3b;\">Event<\/span><span>.<\/span><span style=\"color:#b16139;\">FEEDBACK<\/span><span>])\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">outputContexts<\/span><span>([<\/span><span style=\"color:#b16139;\">cxFruit<\/span><span>])\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">followUpOf<\/span><span>(<\/span><span style=\"color:#b16139;\">ntFruitReminder<\/span><span>);\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ Sample Resource Build and Sync Script\n<\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">svcAcctKeyJson<\/span><span>: string = &quot;<\/span><span style=\"color:#489963;\">.\/service-account-key.json<\/span><span>&quot;;\n<\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">svcAcctConfig<\/span><span>: DialogflowServiceAccount = <\/span><span style=\"color:#1c9aa0;\">require<\/span><span>(`<\/span><span style=\"color:#489963;\">.${<\/span><span style=\"color:#b16139;\">svcAcctKeyJson<\/span><span style=\"color:#489963;\">}<\/span><span>`);\n<\/span><span style=\"color:#b16139;\">Container<\/span><span>.<\/span><span style=\"color:#1c9aa0;\">set<\/span><span>(<\/span><span style=\"color:#b16139;\">KEY_FILENAME<\/span><span>, <\/span><span style=\"color:#b16139;\">svcAcctKeyJson<\/span><span>);\n<\/span><span style=\"color:#b16139;\">Container<\/span><span>.<\/span><span style=\"color:#1c9aa0;\">set<\/span><span>(<\/span><span style=\"color:#b16139;\">DIALOGFLOW_CONFIG<\/span><span>, <\/span><span style=\"color:#b16139;\">svcAcctConfig<\/span><span>);\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">resources <\/span><span>= <\/span><span style=\"color:#b16139;\">Container<\/span><span>.<\/span><span style=\"color:#1c9aa0;\">get<\/span><span>(<\/span><span style=\"color:#b16139;\">DialogflowBuilder<\/span><span>)\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">entityTypes<\/span><span>([<\/span><span style=\"color:#b16139;\">etSample<\/span><span>, <\/span><span style=\"color:#b16139;\">etFruit<\/span><span>])\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">intents<\/span><span>([<\/span><span style=\"color:#b16139;\">ntFruitInfo<\/span><span>, <\/span><span style=\"color:#b16139;\">ntFruitReminder<\/span><span>])\n<\/span><span>  .<\/span><span style=\"color:#478c90;\">build<\/span><span>();\n<\/span><span>\n<\/span><span style=\"color:#b16139;\">Container<\/span><span>.<\/span><span style=\"color:#1c9aa0;\">get<\/span><span>(<\/span><span style=\"color:#b16139;\">DialogflowCreator<\/span><span>).<\/span><span style=\"color:#478c90;\">sync<\/span><span>(<\/span><span style=\"color:#b16139;\">resources<\/span><span>);\n<\/span><\/code><\/pre>\n<h2 id=\"wrap-up\">Wrap-up<\/h2>\n<p>The outcome of the project: Dialogflow-as-Code. This package was made available as <code>@0xc\/dialogflow-as-code<\/code> on the NPM registry through an open source project on <a href=\"https:\/\/github.com\/tcarrio\/dialogflow-as-code\">my GitHub<\/a>.<\/p>\n<!-- References -->\n"},{"title":"Domain-Driven Design Patterns: An Introduction","pubDate":"Sat, 29 Jul 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/ddd-patterns-intro\/","guid":"https:\/\/blog.carrio.dev\/blog\/ddd-patterns-intro\/","description":"<p>What this post is, and what is isn't.<\/p>\n<p>This post is:<\/p>\n<ul>\n<li>A brief intro to what <a href=\"https:\/\/martinfowler.com\/bliki\/DomainDrivenDesign.html\">DDD<\/a> is.<\/li>\n<li>Covering some tactical design patterns for DDD.<\/li>\n<\/ul>\n<p>This post is not:<\/p>\n<ul>\n<li>Covering strategic design patterns, e.g. <a href=\"https:\/\/en.wikipedia.org\/wiki\/Event_storming\">Event Storming<\/a><\/li>\n<\/ul>\n<p><a href=\"https:\/\/martinfowler.com\/bliki\/DomainDrivenDesign.html\">Domain-driven design (DDD)<\/a> is an approach I've taken on various projects historical and one that I'm still not sure I've entirely <em>mastered<\/em>. There is a lot of nuance to DDD, namely the matter of buy-in from stakeholders across the organization, truly working hand-in-hand with domain experts, assuming you have them- and if you don't, then also building up that expertise in your team- and finally championing that mentality across all of the silos in your workplace. <em>Everyone<\/em> needs to be on board, and that's just from a <em>strategic<\/em> design standpoint. Practicing domain-driven design is hard, much like software engineering can be in general, but it's also a foreign concept to many developers out there.<\/p>\n<p>The core of domain-driven design, in my own words:<\/p>\n<blockquote>\n<p>Domain-driven design is about designing your software in the way the business domain is structured, from the terminology used by each context of your product to the naming of your programming constructs.<\/p>\n<\/blockquote>\n<p>By most DDD practicioners' standards: your domain experts should be able to understand what's happening in your code without being a programmer. This relates only to the domain portion of your code, and in many architectural approachs to software such as [hexagonal architecture], you would have that complete separation of the core domain logic from any application or infrastructure logic. These often pair well DDD, and there are many examples of implementing tactical DDD patterns online.<\/p>\n<h2 id=\"reading-resources\">Reading Resources<\/h2>\n<p>The absolute classics are the original <a href=\"https:\/\/www.amazon.com\/gp\/product\/0321125215\">Eric Evans blue book, Domain-Driven Design: Tackling Complexity in the Heart of Software<\/a>, and the <a href=\"https:\/\/www.amazon.com\/gp\/product\/0321834577\">Vaughn Vernon red book Implementing Domain-Driven Design<\/a>.<\/p>\n<h2 id=\"identifying-the-structure-of-the-business-domain\">Identifying the structure of the business domain<\/h2>\n<p>This is the part I mentioned this post would NOT be. I'll only cover some terminology that will be useful within this post:<\/p>\n<ul>\n<li><strong>Domain<\/strong>: Outside of DDD, this is defined as \"a specified sphere of activity or knowledge\", which captures the essence well. This encapsulates both <em>what<\/em> your product does, and <em>how<\/em> it does it.<\/li>\n<li><strong>Subdomain<\/strong>: Your domain often will be split up into various subdomains, especially if it is as a whole a very broad concept. There are several types of subdomains, including <em>core<\/em>, <em>supporting<\/em>, and <em>generic<\/em> subdomains. The <em>core<\/em> subdomain would be the primary focus of your product and the value it offers that makes it great. A <em>supporting<\/em> subdomain is important for the product to succeed, but not the primary focus. A <em>generic<\/em> subdomain contains nothing <em>special<\/em> to the organization, but is necessary for the solution to work (think IAM or ERP platforms).<\/li>\n<li><strong><a href=\"https:\/\/martinfowler.com\/bliki\/UbiquitousLanguage.html\">Ubiquitous Language<\/a><\/strong>: Specific to each bounded context, the language is agreed upon and standard for how to refer to each component in the system. Domain experts and software engineers can easily discuss features because the ubiquitous language is consistent from design to implementation. As you can imagine, this takes a lot of interaction between domain experts and the programmers building the software.<\/li>\n<li><strong><a href=\"https:\/\/martinfowler.com\/bliki\/BoundedContext.html\">Bounded Context<\/a><\/strong>: This is a specific subset of the overall domain where ubiquitous language is consistent. Often times the best structure for bounded contexts is 1:1 with subdomains of your system, but like many things in SWE this is situational. Not only is this a specific context, but there is well defined boundary for the context. This separates the components of your system linguistically, so the same terminology such as \"Account\" may not mean the same thing between two contexts, such as \"Checkings\" context and \"Savings\" context for a \"Banking\" domain.<\/li>\n<li><strong>Context Map<\/strong>: These define what the boundaries of the various contexts are, how contexts will communicate, how mappings between entities and other constructs will be done between contexts (e.g. translating the ubiquoutous language), how to protect against unwanted changes in upstream contexts, or how to ensure stability for downstream contexts.<\/li>\n<\/ul>\n<p>That is a lot to gather without much <em>context<\/em>, and if you are interested in the strategic design elements you should read more on it from the <a href=\"https:\/\/www.amazon.com\/gp\/product\/0321125215\">blue book<\/a> and\/or <a href=\"https:\/\/www.amazon.com\/gp\/product\/0321834577\">red book<\/a>.<\/p>\n<h2 id=\"domain-objects\">Domain Objects<\/h2>\n<p>Even in 2003, Evans' <a href=\"https:\/\/martinfowler.com\/bliki\/EvansClassification.html\">classified<\/a> some of the still relevant types of domain objects you'll find in domain-driven design. These classifications include:<\/p>\n<ul>\n<li><strong>Entities<\/strong>: A distinctly identifiable object.<\/li>\n<li><strong>Value Objects<\/strong>: An object that matters only as an combination of its properties. There is no identifier for a value object, only what it contains.<\/li>\n<li><strong>Services<\/strong>: Typically stateless, these can provide a standalone operation within the context your domain.<\/li>\n<\/ul>\n<p>Types from other patterns such as enterprise architecture, layered architecture, design patterns and more have been mostly adopted into domain-driven design as well, and you'll commonly see many of the following:<\/p>\n<ul>\n<li><strong>Aggregates<\/strong>: An entity that defines the transactional boundary of logical operations within a context. It controls the entities beneath it, exposes functionality to domain logic that can impact those entities, but does not allow access to those nested entities. When the aggregate is persisted, the operations of root aggregate entity and all of the related entities must all successfully complete or the transaction will be rolled back. In this way, aggregates are atomic.<\/li>\n<li><strong>Domain Events<\/strong>: Events that signify specific, important happenings within a bounded context. This is a common way to communicate across bounded contexts while also reducing coupling of services.<\/li>\n<li><strong>Repositories<\/strong>: An abstraction over a collection of domain entities. This typically follows a collection-like or persistence-based approach. The repository mediates between the domain and data-mapping layers of the system.<\/li>\n<li><strong>Factory<\/strong>: A creational design pattern, which in its simplest form is an object that creates other objects. There are more specific subsets of the Factory pattern that support polymorphic return types as well.<\/li>\n<\/ul>\n<p>Depending on which DDD tactical design patterns you implement, you may also end up seeing terminology like CQRS. We'll hold off on diving any deeper for now.<\/p>\n<h3 id=\"applied-domain-patterns\">Applied Domain Patterns<\/h3>\n<p>The following adapts some of the code from a Destiny bot project a friend of mine was working on. The code was originally in JavaScript at the time and I thought I would convert it to utilize more domain-driven design patterns instead.<\/p>\n<blockquote>\n<p>By my standards, this is still a work in progress, but it's a start.<\/p>\n<\/blockquote>\n<pre data-lang=\"typescript\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-typescript \"><code class=\"language-typescript\" data-lang=\"typescript\"><span style=\"color:#5f6d64;\">\/\/\/ Domain layer: Value Objects, Entities, Aggregates\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">export abstract class <\/span><span style=\"color:#a07e3b;\">ValueObject<\/span><span style=\"color:#ecf4ee;\">&lt;T&gt; {\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">constructor<\/span><span>(<\/span><span style=\"color:#55859b;\">public readonly <\/span><span style=\"color:#b16139;\">value<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">T<\/span><span>) <\/span><span style=\"color:#ecf4ee;\">{}\n<\/span><span style=\"color:#ecf4ee;\">}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">export class <\/span><span style=\"color:#a07e3b;\">StringValueObject <\/span><span style=\"color:#55859b;\">extends <\/span><span style=\"color:#489963;\">ValueObject<\/span><span style=\"color:#ecf4ee;\">&lt;string&gt; {}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">export class <\/span><span style=\"color:#a07e3b;\">NumberValueObject <\/span><span style=\"color:#55859b;\">extends <\/span><span style=\"color:#489963;\">ValueObject<\/span><span style=\"color:#ecf4ee;\">&lt;number&gt; {}\n<\/span><span>\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">type <\/span><span>Hex = &#39;<\/span><span style=\"color:#489963;\">0<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">1<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">2<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">3<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">4<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">5<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">6<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">7<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">8<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">9<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">a<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">b<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">c<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">d<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">e<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">f<\/span><span>&#39;;\n<\/span><span style=\"color:#55859b;\">type <\/span><span>Hex2 = `<\/span><span style=\"color:#489963;\">${<\/span><span style=\"color:#b16139;\">Hex<\/span><span style=\"color:#489963;\">}${<\/span><span style=\"color:#b16139;\">Hex<\/span><span style=\"color:#489963;\">}<\/span><span>`;\n<\/span><span style=\"color:#55859b;\">type <\/span><span>Hex3 = `<\/span><span style=\"color:#489963;\">${<\/span><span style=\"color:#b16139;\">Hex2<\/span><span style=\"color:#489963;\">}${<\/span><span style=\"color:#b16139;\">Hex<\/span><span style=\"color:#489963;\">}<\/span><span>`;\n<\/span><span style=\"color:#55859b;\">type <\/span><span>Hex4 = `<\/span><span style=\"color:#489963;\">${<\/span><span style=\"color:#b16139;\">Hex2<\/span><span style=\"color:#489963;\">}${<\/span><span style=\"color:#b16139;\">Hex2<\/span><span style=\"color:#489963;\">}<\/span><span>`;\n<\/span><span style=\"color:#55859b;\">type <\/span><span>Hex8 = `<\/span><span style=\"color:#489963;\">${<\/span><span style=\"color:#b16139;\">Hex4<\/span><span style=\"color:#489963;\">}${<\/span><span style=\"color:#b16139;\">Hex4<\/span><span style=\"color:#489963;\">}<\/span><span>`;\n<\/span><span style=\"color:#55859b;\">type <\/span><span>Hex12 = `<\/span><span style=\"color:#489963;\">${<\/span><span style=\"color:#b16139;\">Hex8<\/span><span style=\"color:#489963;\">}${<\/span><span style=\"color:#b16139;\">Hex4<\/span><span style=\"color:#489963;\">}<\/span><span>`;\n<\/span><span style=\"color:#55859b;\">type <\/span><span>FOUR = &#39;<\/span><span style=\"color:#489963;\">4<\/span><span>&#39;;\n<\/span><span style=\"color:#55859b;\">type <\/span><span>AB89 = &#39;<\/span><span style=\"color:#489963;\">a<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">b<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">8<\/span><span>&#39;|&#39;<\/span><span style=\"color:#489963;\">9<\/span><span>&#39;;\n<\/span><span style=\"color:#55859b;\">type <\/span><span>UuidV4 = `<\/span><span style=\"color:#489963;\">${<\/span><span style=\"color:#b16139;\">Hex8<\/span><span style=\"color:#489963;\">}-${<\/span><span style=\"color:#b16139;\">Hex4<\/span><span style=\"color:#489963;\">}-${<\/span><span style=\"color:#b16139;\">FOUR<\/span><span style=\"color:#489963;\">}${<\/span><span style=\"color:#b16139;\">Hex3<\/span><span style=\"color:#489963;\">}-${<\/span><span style=\"color:#b16139;\">AB89<\/span><span style=\"color:#489963;\">}${<\/span><span style=\"color:#b16139;\">HEX3<\/span><span style=\"color:#489963;\">}-${<\/span><span style=\"color:#b16139;\">HEX12<\/span><span style=\"color:#489963;\">}<\/span><span>`;\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">export class <\/span><span style=\"color:#a07e3b;\">Uuid <\/span><span style=\"color:#55859b;\">extends <\/span><span style=\"color:#489963;\">ValueObject<\/span><span style=\"color:#ecf4ee;\">&lt;UuidV4&gt; {\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">private static readonly <\/span><span style=\"color:#b16139;\">REGEX <\/span><span>= \/<\/span><span style=\"color:#55859b;\">^<\/span><span style=\"color:#9f713c;\">[0-9a-f]<\/span><span>{8}<\/span><span style=\"color:#1c9aa0;\">-<\/span><span style=\"color:#9f713c;\">[0-9a-f]<\/span><span>{4}<\/span><span style=\"color:#1c9aa0;\">-4<\/span><span style=\"color:#9f713c;\">[0-9a-f]<\/span><span>{3}<\/span><span style=\"color:#1c9aa0;\">-<\/span><span style=\"color:#9f713c;\">[89ab][0-9a-f]<\/span><span>{3}<\/span><span style=\"color:#1c9aa0;\">-<\/span><span style=\"color:#9f713c;\">[0-9a-f]<\/span><span>{12}<\/span><span style=\"color:#55859b;\">$<\/span><span>\/<\/span><span style=\"color:#55859b;\">i<\/span><span style=\"color:#ecf4ee;\">;\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">constructor<\/span><span>(<\/span><span style=\"color:#b16139;\">value<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">UuidV4<\/span><span>) <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#b16139;\">super<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">value<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">validate<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">  }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">private <\/span><span style=\"color:#478c90;\">validate<\/span><span>(): <\/span><span style=\"color:#ecf4ee;\">void {\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#478c90;\">assert<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">Uuid<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">REGEX<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#1c9aa0;\">test<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.value), <\/span><span>&#39;<\/span><span style=\"color:#489963;\">Value was not a valid Version 4 UUID<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">  }\n<\/span><span style=\"color:#ecf4ee;\">}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">export class <\/span><span style=\"color:#a07e3b;\">Email <\/span><span style=\"color:#55859b;\">extends <\/span><span style=\"color:#489963;\">StringValueObject <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">private static readonly <\/span><span style=\"color:#b16139;\">REGEX <\/span><span>= \/<\/span><span style=\"color:#55859b;\">^<\/span><span style=\"color:#9f713c;\">[<\/span><span>^<\/span><span style=\"color:#9f713c;\">@]<\/span><span>+<\/span><span style=\"color:#1c9aa0;\">@<\/span><span style=\"color:#9f713c;\">[<\/span><span>^<\/span><span style=\"color:#9f713c;\">@]<\/span><span>+<\/span><span style=\"color:#55859b;\">$<\/span><span>\/<\/span><span style=\"color:#55859b;\">i<\/span><span style=\"color:#ecf4ee;\">;\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">constructor<\/span><span>(<\/span><span style=\"color:#b16139;\">value<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">Email<\/span><span>) <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#b16139;\">super<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">value<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">validate<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">  }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">private <\/span><span style=\"color:#478c90;\">validate<\/span><span>(): <\/span><span style=\"color:#ecf4ee;\">void {\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#478c90;\">assert<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">Email<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">REGEX<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#1c9aa0;\">test<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.value), <\/span><span>&#39;<\/span><span style=\"color:#489963;\">Value was not a valid email address<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">  }\n<\/span><span style=\"color:#ecf4ee;\">}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">export class <\/span><span style=\"color:#a07e3b;\">Username <\/span><span style=\"color:#55859b;\">extends <\/span><span style=\"color:#489963;\">StringValueObject <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">private static readonly <\/span><span style=\"color:#b16139;\">REGEX <\/span><span>= \/<\/span><span style=\"color:#55859b;\">^<\/span><span style=\"color:#9f713c;\">[<\/span><span>^<\/span><span style=\"color:#9f713c;\">@]<\/span><span>+<\/span><span style=\"color:#1c9aa0;\">@<\/span><span style=\"color:#9f713c;\">[<\/span><span>^<\/span><span style=\"color:#9f713c;\">@]<\/span><span>+<\/span><span style=\"color:#55859b;\">$<\/span><span>\/<\/span><span style=\"color:#55859b;\">i<\/span><span style=\"color:#ecf4ee;\">;\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">constructor<\/span><span>(<\/span><span style=\"color:#b16139;\">value<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">Username<\/span><span>) <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#b16139;\">super<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">value<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">validate<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">  }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">private <\/span><span style=\"color:#478c90;\">validate<\/span><span>(): <\/span><span style=\"color:#ecf4ee;\">void {\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#478c90;\">assert<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">Username<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">REGEX<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#1c9aa0;\">test<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.value), <\/span><span>&#39;<\/span><span style=\"color:#489963;\">Value was not a valid email address<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">  }\n<\/span><span style=\"color:#ecf4ee;\">}\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ Entities &amp; Aggregates\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">export interface <\/span><span>Entity {\n<\/span><span>  <\/span><span style=\"color:#55859b;\">public <\/span><span style=\"color:#b16139;\">id<\/span><span>: Uuid;\n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">export class <\/span><span style=\"color:#a07e3b;\">Entity <\/span><span style=\"color:#55859b;\">implements <\/span><span style=\"color:#489963;\">Entity <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">constructor<\/span><span>(<\/span><span style=\"color:#55859b;\">public readonly <\/span><span style=\"color:#b16139;\">id<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">Uuid<\/span><span>) <\/span><span style=\"color:#ecf4ee;\">{}\n<\/span><span style=\"color:#ecf4ee;\">}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">export type <\/span><span>Aggregate = Entity;\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">export class <\/span><span style=\"color:#a07e3b;\">Aggregate <\/span><span style=\"color:#55859b;\">extends <\/span><span style=\"color:#489963;\">Entity <\/span><span style=\"color:#ecf4ee;\">{}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">export class <\/span><span style=\"color:#a07e3b;\">User <\/span><span style=\"color:#55859b;\">implements <\/span><span style=\"color:#489963;\">Aggregate <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">constructor<\/span><span>(\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">public readonly <\/span><span style=\"color:#b16139;\">id<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">Uuid,\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">public readonly <\/span><span style=\"color:#b16139;\">email<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">Email,\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">public readonly <\/span><span style=\"color:#b16139;\">firstName<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">StringValueObject,\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">public readonly <\/span><span style=\"color:#b16139;\">lastName<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">StringValueObject,\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">public readonly <\/span><span style=\"color:#b16139;\">username<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">Username,\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span>) <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#b16139;\">super<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">id<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">  }\n<\/span><span style=\"color:#ecf4ee;\">}\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/\/ Domain Repositories\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ A collection-oriented repository:\n<\/span><span style=\"color:#55859b;\">interface <\/span><span>Repository&lt;T <\/span><span style=\"color:#55859b;\">extends <\/span><span>Entity&gt; {\n<\/span><span>  <\/span><span style=\"color:#478c90;\">add<\/span><span>(<\/span><span style=\"color:#b16139;\">model<\/span><span>: T): Promise&lt;void&gt;;\n<\/span><span>  <\/span><span style=\"color:#478c90;\">update<\/span><span>(<\/span><span style=\"color:#b16139;\">model<\/span><span>: T): Promise&lt;void&gt;;\n<\/span><span>  <\/span><span style=\"color:#478c90;\">delete<\/span><span>(<\/span><span style=\"color:#b16139;\">model<\/span><span>: T): Promise&lt;void&gt;;\n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/ A collection-oriented repository:\n<\/span><span style=\"color:#55859b;\">interface <\/span><span>PersistenceRepository&lt;T&gt; <\/span><span style=\"color:#55859b;\">extends <\/span><span style=\"color:#489963;\">Repository<\/span><span>&lt;T&gt; {\n<\/span><span>  <\/span><span style=\"color:#478c90;\">persist<\/span><span>(): Promise&lt;void&gt;; \n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">interface <\/span><span>UserRepository <\/span><span style=\"color:#55859b;\">extends <\/span><span style=\"color:#489963;\">Repository<\/span><span>&lt;User&gt; {\n<\/span><span>  <\/span><span style=\"color:#478c90;\">existsByUsername<\/span><span>(<\/span><span style=\"color:#b16139;\">username<\/span><span>: string): Promise&lt;boolean&gt;;\n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">\/\/\/ Infrastructure layer: We implement our interfaces \n<\/span><span>\n<\/span><span style=\"color:#55859b;\">interface <\/span><span>MongooseSchema&lt;T&gt; {\n<\/span><span>  <\/span><span style=\"color:#478c90;\">exists<\/span><span>(<\/span><span style=\"color:#b16139;\">criteria<\/span><span>: Partial&lt;T&gt;): Command&lt;boolean&gt;;\n<\/span><span>  <\/span><span style=\"color:#478c90;\">updateOne<\/span><span>(\n<\/span><span>    <\/span><span style=\"color:#b16139;\">criteria<\/span><span>: Partial&lt;T&gt;,\n<\/span><span>    <\/span><span style=\"color:#b16139;\">operation<\/span><span>: MongooseOperation&lt;T&gt;,\n<\/span><span>    <\/span><span style=\"color:#478c90;\">callback<\/span><span>: (<\/span><span style=\"color:#b16139;\">error<\/span><span>?: Error) <\/span><span style=\"color:#55859b;\">=&gt; <\/span><span>any\n<\/span><span>  ): Promise&lt;void&gt;;\n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">interface <\/span><span>MongooseOperation&lt;T&gt; {\n<\/span><span>  <\/span><span style=\"color:#b16139;\">$set<\/span><span>: Partial&lt;T&gt;;\n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">interface <\/span><span>Command&lt;T&gt; {\n<\/span><span>  <\/span><span style=\"color:#478c90;\">exec<\/span><span>(): Promise&lt;T&gt;;\n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">interface <\/span><span>UserSchema {\n<\/span><span>  <\/span><span style=\"color:#b16139;\">id<\/span><span>: string,\n<\/span><span>  <\/span><span style=\"color:#b16139;\">email<\/span><span>: string,\n<\/span><span>  <\/span><span style=\"color:#b16139;\">firstName<\/span><span>: string,\n<\/span><span>  <\/span><span style=\"color:#b16139;\">lastName<\/span><span>: string,\n<\/span><span>  <\/span><span style=\"color:#b16139;\">username<\/span><span>: string,\n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">interface <\/span><span>PersistenceObject {\n<\/span><span>  <\/span><span style=\"color:#478c90;\">save<\/span><span>(): Promise&lt;void&gt;;\n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">type <\/span><span>PersistenceObjectFactory&lt;T&gt; = (<\/span><span style=\"color:#b16139;\">model<\/span><span>: T) <\/span><span style=\"color:#55859b;\">=&gt; <\/span><span>PersistenceObject;\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">export class <\/span><span style=\"color:#a07e3b;\">MongoUserRepository <\/span><span style=\"color:#55859b;\">implements <\/span><span style=\"color:#489963;\">UserRepository <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">constructor<\/span><span>(\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">private readonly <\/span><span style=\"color:#b16139;\">schema<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">MongooseSchema&lt;UserSchema&gt;,\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">private readonly <\/span><span style=\"color:#b16139;\">factory<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">PersistenceObjectFactory&lt;User&gt;,\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">private readonly <\/span><span style=\"color:#b16139;\">logger<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">Logger,\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span>) <\/span><span style=\"color:#ecf4ee;\">{ }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">async <\/span><span style=\"color:#478c90;\">add<\/span><span>(<\/span><span style=\"color:#b16139;\">user<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">User<\/span><span>): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">userModel <\/span><span>= <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">factory<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">userModel<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">save<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">  }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">async <\/span><span style=\"color:#478c90;\">update<\/span><span>(<\/span><span style=\"color:#b16139;\">user<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">User<\/span><span>): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">updateModel <\/span><span>= <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">      email: <\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">email<\/span><span style=\"color:#ecf4ee;\">,\n<\/span><span style=\"color:#ecf4ee;\">      firstName: <\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">firstName<\/span><span style=\"color:#ecf4ee;\">,\n<\/span><span style=\"color:#ecf4ee;\">      lastName: <\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">lastName<\/span><span style=\"color:#ecf4ee;\">,\n<\/span><span style=\"color:#ecf4ee;\">      username: <\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">username<\/span><span style=\"color:#ecf4ee;\">,\n<\/span><span style=\"color:#ecf4ee;\">    };\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">schema<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">updateOne<\/span><span style=\"color:#ecf4ee;\">(\n<\/span><span style=\"color:#ecf4ee;\">      { id: <\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">.id },\n<\/span><span style=\"color:#ecf4ee;\">      { $set: <\/span><span style=\"color:#b16139;\">updateModel <\/span><span style=\"color:#ecf4ee;\">},\n<\/span><span style=\"color:#ecf4ee;\">    )\n<\/span><span style=\"color:#ecf4ee;\">  }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">async <\/span><span style=\"color:#478c90;\">delete<\/span><span>(<\/span><span style=\"color:#b16139;\">user<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">User<\/span><span>) <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">schema<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">deleteOne<\/span><span style=\"color:#ecf4ee;\">({ id: <\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">.id });\n<\/span><span style=\"color:#ecf4ee;\">  }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">  <\/span><span style=\"color:#55859b;\">async <\/span><span style=\"color:#478c90;\">existsByUsername<\/span><span>(<\/span><span style=\"color:#b16139;\">username<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">string<\/span><span>): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;boolean&gt; {\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">schema<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">exists<\/span><span style=\"color:#ecf4ee;\">({ <\/span><span style=\"color:#b16139;\">username <\/span><span style=\"color:#ecf4ee;\">}).<\/span><span style=\"color:#1c9aa0;\">exec<\/span><span style=\"color:#ecf4ee;\">() <\/span><span>? <\/span><span style=\"color:#9f713c;\">true <\/span><span>: <\/span><span style=\"color:#9f713c;\">false<\/span><span style=\"color:#ecf4ee;\">;\n<\/span><span style=\"color:#ecf4ee;\">  }\n<\/span><span style=\"color:#ecf4ee;\">}\n<\/span><\/code><\/pre>\n<!-- References -->\n"},{"title":"SOLID Principles: Dependency Inversion","pubDate":"Sat, 29 Jul 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/solid-dependency-inversion\/","guid":"https:\/\/blog.carrio.dev\/blog\/solid-dependency-inversion\/","description":"<p>SOLID constitutes five design principles focused on making object-oriented designs more maintainable, understable, and flexible. The principles of SOLID are:<\/p>\n<p><strong>S<\/strong>ingle-responsibility principle<\/p>\n<p><strong>O<\/strong>pen-closed principle<\/p>\n<p><strong>L<\/strong>iskov substitution principle<\/p>\n<p><strong>I<\/strong>nterface segregation principle<\/p>\n<p><strong>D<\/strong>ependency inversion principle<\/p>\n<p>In this post, I'm going to cover the final principle, <strong>Dependency Inversion<\/strong>.<\/p>\n<h2 id=\"what-is-dependency-inversion\">What is Dependency Inversion<\/h2>\n<p>In a few words, the Dependency Inversion principle (DIP) can be described as:<\/p>\n<blockquote>\n<p>Depend on abstractions, not concretions<\/p>\n<\/blockquote>\n<p>In many object-oriented languages, abstractions would often refer to an Interface and concretions on a Class implementation of it. There are several benefits that come from this:<\/p>\n<p><em>Decoupling of components<\/em>: DIP encourages the decoupling of high-level modules from low-level modules. By introducing abstractions and interfaces, it allows components to interact with each other without needing to know specific implementation details. This reduces the tight coupling between modules, making the code more flexible and easier to maintain.<\/p>\n<p><em>Reusability<\/em>: With DIP, components depend on abstractions rather than concrete implementations. This promotes reusability as multiple implementations can be created for the same interface, allowing different components to use them interchangeably.<\/p>\n<p><em>Testability<\/em>: By programming to interfaces rather than concrete classes, unit testing becomes easier. Mocking or stubbing interfaces during testing becomes straightforward, enabling more effective and isolated testing of individual components.<\/p>\n<p><em>Encourages a stable architecture<\/em>: Following DIP leads to a more stable architecture as changes to low-level modules or concrete implementations are less likely to affect higher-level modules. This principle facilitates the \"Open\/Closed Principle\" (OCP) by allowing the system to be easily extended with new functionalities without modifying existing code.<\/p>\n<p><em>Inversion of control (IoC)<\/em>: DIP is often associated with IoC containers that manage the creation and resolution of dependencies. By using an IoC container, the responsibility for managing object instantiation and dependency resolution is shifted from the application code to the container, simplifying the overall design and promoting a more modular and maintainable structure.<\/p>\n<p>While there are many benefits to following DIP, I would still warn that there are definitely some downfalls to <em>over-applying<\/em> this pattern. However, it generally leaves your project in a significantly better state for future changes and growth.<\/p>\n<h2 id=\"example\">Example<\/h2>\n<p>This example dives into an application of the dependency inversion principle, but it inadvertently will also showcase a couple of other patterns closely related to this, including the general pattern of Inversion of Control and the Dependency Injection pattern, whose functionality is often provided by some application framework like Spring.<\/p>\n<p>Here, we'll have some application which manipulates users in the system. We will have a UserService and a UserRepository, and the UserRepository will be an <strong>interface<\/strong> as opposed to a specific implementation. The UserRepository is implemented by multiple classes, and in this case we have an implementation backed by MySQL and another by Redis. However, the UserService itself has no knowledge of which implementation it is interacting with, and never should. It only cares about the contract in which it interacts with the instance it has a reference to.<\/p>\n<p>To start off, I'll define a core model I won't include in the graph below but will be necessary regardless. The <code>User<\/code>!<\/p>\n<pre data-lang=\"ts\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-ts \"><code class=\"language-ts\" data-lang=\"ts\"><span style=\"color:#55859b;\">class <\/span><span style=\"color:#a07e3b;\">User <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">constructor<\/span><span>(\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">public readonly <\/span><span style=\"color:#b16139;\">id<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">string,\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">public readonly <\/span><span style=\"color:#b16139;\">email<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">string,\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span>)\n<\/span><span style=\"color:#ecf4ee;\">}\n<\/span><\/code><\/pre>\n<p>Now, we can define our abstraction around interacting with the Users in the system. In my case, I would be defining the UserRepository as a collection-like interface (no necessary <code>persist()<\/code> calls on the Repository to push updates to the data store).<\/p>\n<pre data-lang=\"ts\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-ts \"><code class=\"language-ts\" data-lang=\"ts\"><span style=\"color:#55859b;\">interface <\/span><span>UserRepository {\n<\/span><span>    <\/span><span style=\"color:#478c90;\">nextId<\/span><span>(): Promise&lt;string&gt;;\n<\/span><span>    <\/span><span style=\"color:#478c90;\">add<\/span><span>(<\/span><span style=\"color:#b16139;\">user<\/span><span>: User): Promise&lt;void&gt;;\n<\/span><span>    <\/span><span style=\"color:#478c90;\">delete<\/span><span>(<\/span><span style=\"color:#b16139;\">user<\/span><span>: User): Promise&lt;void&gt;;\n<\/span><span>    <\/span><span style=\"color:#478c90;\">findByEmail<\/span><span>(<\/span><span style=\"color:#b16139;\">id<\/span><span>: string): Promise&lt;User&gt;;\n<\/span><span>}\n<\/span><\/code><\/pre>\n<p>Now, I haven't actually implemented this repository yet, but I can already start to envision my domain service, where I'll provide functionality for creating a User with their email.<\/p>\n<pre data-lang=\"ts\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-ts \"><code class=\"language-ts\" data-lang=\"ts\"><span style=\"color:#55859b;\">class <\/span><span style=\"color:#a07e3b;\">UserService <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">constructor<\/span><span>(<\/span><span style=\"color:#55859b;\">private readonly <\/span><span style=\"color:#b16139;\">userRepository<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">UserRepository<\/span><span>) <\/span><span style=\"color:#ecf4ee;\">{}\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">async <\/span><span style=\"color:#478c90;\">createNewUser<\/span><span>(<\/span><span style=\"color:#b16139;\">email<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">string<\/span><span>): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">userRepository<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">findByEmail<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">email<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">user <\/span><span>= new <\/span><span style=\"color:#ecf4ee;\">User(<\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">userRepository<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">nextId<\/span><span style=\"color:#ecf4ee;\">(), <\/span><span style=\"color:#b16139;\">email<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">userRepository<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#1c9aa0;\">add<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">}\n<\/span><\/code><\/pre>\n<p>Great! We're going to be creating user's with this service, now let's really implement a UserRepository that we can get started with. I'm going to use a simple in-memory backend like Redis here. I might lose everything when I restart Redis but I just want to test the system out locally for now.<\/p>\n<pre data-lang=\"ts\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-ts \"><code class=\"language-ts\" data-lang=\"ts\"><span style=\"color:#55859b;\">class <\/span><span style=\"color:#a07e3b;\">RedisUserRepository <\/span><span style=\"color:#55859b;\">implements <\/span><span style=\"color:#489963;\">UserRepository <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">private static readonly <\/span><span style=\"color:#b16139;\">PREFIX <\/span><span>= &#39;<\/span><span style=\"color:#489963;\">user<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">;\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">constructor<\/span><span>(<\/span><span style=\"color:#55859b;\">private readonly <\/span><span style=\"color:#b16139;\">redis<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">Redis<\/span><span>) <\/span><span style=\"color:#ecf4ee;\">{}\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">async <\/span><span style=\"color:#478c90;\">nextId<\/span><span>(): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;string&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#478c90;\">uuid<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">async <\/span><span style=\"color:#478c90;\">add<\/span><span>(<\/span><span style=\"color:#b16139;\">user<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">User<\/span><span>): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">redis<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#1c9aa0;\">set<\/span><span style=\"color:#ecf4ee;\">(\n<\/span><span style=\"color:#ecf4ee;\">            <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">calculateKey<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">),\n<\/span><span style=\"color:#ecf4ee;\">            <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">serializeUser<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">),\n<\/span><span style=\"color:#ecf4ee;\">        );\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">async <\/span><span style=\"color:#478c90;\">delete<\/span><span>(<\/span><span style=\"color:#b16139;\">user<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">User<\/span><span>): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">redis<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">del<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">calculateKey<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">));\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">async <\/span><span style=\"color:#478c90;\">findByEmail<\/span><span>(<\/span><span style=\"color:#b16139;\">email<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">string<\/span><span>): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;User&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">matchingKeys <\/span><span>= <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">redis<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">scan<\/span><span style=\"color:#ecf4ee;\">(<\/span><span>`<\/span><span style=\"color:#489963;\">0 MATCH user::*::${<\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#489963;\">.<\/span><span style=\"color:#478c90;\">base64Encode<\/span><span style=\"color:#489963;\">(<\/span><span style=\"color:#b16139;\">email<\/span><span style=\"color:#489963;\">)}<\/span><span>`<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">if <\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">matchingKeys<\/span><span style=\"color:#ecf4ee;\">.length <\/span><span>=== <\/span><span style=\"color:#9f713c;\">0<\/span><span style=\"color:#ecf4ee;\">) {\n<\/span><span style=\"color:#ecf4ee;\">            <\/span><span style=\"color:#55859b;\">throw <\/span><span>new <\/span><span style=\"color:#ecf4ee;\">NotFoundException();\n<\/span><span style=\"color:#ecf4ee;\">        }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">if <\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">matchingKeys<\/span><span style=\"color:#ecf4ee;\">.length <\/span><span>&gt; <\/span><span style=\"color:#9f713c;\">1<\/span><span style=\"color:#ecf4ee;\">) {\n<\/span><span style=\"color:#ecf4ee;\">            <\/span><span style=\"color:#55859b;\">throw <\/span><span>new <\/span><span style=\"color:#ecf4ee;\">MultipleUsersForSameEmailException();\n<\/span><span style=\"color:#ecf4ee;\">        }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">user <\/span><span>= <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">redis<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#1c9aa0;\">get<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">matchingKeys<\/span><span style=\"color:#ecf4ee;\">[<\/span><span style=\"color:#9f713c;\">0<\/span><span style=\"color:#ecf4ee;\">]);\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">if <\/span><span style=\"color:#ecf4ee;\">(<\/span><span>typeof <\/span><span style=\"color:#b16139;\">user <\/span><span>=== &#39;<\/span><span style=\"color:#489963;\">string<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">) {\n<\/span><span style=\"color:#ecf4ee;\">            <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">deserializeUser<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">        }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">;\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">private <\/span><span style=\"color:#478c90;\">calculateKey<\/span><span>(<\/span><span style=\"color:#b16139;\">user<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">User<\/span><span>): <\/span><span style=\"color:#ecf4ee;\">string {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#ecf4ee;\">{ <\/span><span style=\"color:#b16139;\">id<\/span><span style=\"color:#ecf4ee;\">, <\/span><span style=\"color:#b16139;\">email <\/span><span style=\"color:#ecf4ee;\">} <\/span><span>= <\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">;\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">return <\/span><span>`<\/span><span style=\"color:#489963;\">${<\/span><span style=\"color:#b16139;\">RedisUserRepository<\/span><span style=\"color:#489963;\">.<\/span><span style=\"color:#b16139;\">PREFIX<\/span><span style=\"color:#489963;\">}::${<\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#489963;\">.<\/span><span style=\"color:#478c90;\">base64Encode<\/span><span style=\"color:#489963;\">(<\/span><span style=\"color:#b16139;\">id<\/span><span style=\"color:#489963;\">)}::${<\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#489963;\">.<\/span><span style=\"color:#478c90;\">base64Encode<\/span><span style=\"color:#489963;\">(<\/span><span style=\"color:#b16139;\">email<\/span><span style=\"color:#489963;\">)}<\/span><span>`<\/span><span style=\"color:#ecf4ee;\">;\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">private <\/span><span style=\"color:#478c90;\">base64Encode<\/span><span>(<\/span><span style=\"color:#b16139;\">text<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">string<\/span><span>): <\/span><span style=\"color:#ecf4ee;\">string {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">return <\/span><span>new <\/span><span style=\"color:#ecf4ee;\">Buffer(<\/span><span style=\"color:#b16139;\">text<\/span><span style=\"color:#ecf4ee;\">).<\/span><span style=\"color:#1c9aa0;\">toString<\/span><span style=\"color:#ecf4ee;\">(<\/span><span>&#39;<\/span><span style=\"color:#489963;\">base64<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">private <\/span><span style=\"color:#478c90;\">deserializeUser<\/span><span>(<\/span><span style=\"color:#b16139;\">user<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">string<\/span><span>): <\/span><span style=\"color:#ecf4ee;\">User {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#ecf4ee;\">JSON.<\/span><span style=\"color:#1c9aa0;\">parse<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">private <\/span><span style=\"color:#478c90;\">serializeUser<\/span><span>(<\/span><span style=\"color:#b16139;\">user<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">User<\/span><span>): <\/span><span style=\"color:#ecf4ee;\">string {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#ecf4ee;\">JSON.<\/span><span style=\"color:#478c90;\">serialize<\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">}\n<\/span><\/code><\/pre>\n<p>Now we need to actually instantiate all of these things. Here I'll build out my application container that creates instances of these components and wires it all together. Many people prefer to use a Dependency Injection framework to accomplish this, but I'm keeping this post example simple:<\/p>\n<pre data-lang=\"ts\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-ts \"><code class=\"language-ts\" data-lang=\"ts\"><span style=\"color:#55859b;\">import <\/span><span>{ <\/span><span style=\"color:#b16139;\">createClient <\/span><span>} <\/span><span style=\"color:#55859b;\">from <\/span><span>&#39;<\/span><span style=\"color:#489963;\">redis<\/span><span>&#39;;\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">class <\/span><span style=\"color:#a07e3b;\">ApplicationContainer <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">private static <\/span><span style=\"color:#b16139;\">redis<\/span><span style=\"color:#ecf4ee;\">;\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">static async <\/span><span style=\"color:#478c90;\">bootstrap<\/span><span>(): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">startUp<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">main<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">shutDown<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">static async <\/span><span style=\"color:#478c90;\">startUp<\/span><span>(): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">redis <\/span><span>= <\/span><span style=\"color:#478c90;\">createClient<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">redis<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">on<\/span><span style=\"color:#ecf4ee;\">(<\/span><span>&#39;<\/span><span style=\"color:#489963;\">error<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">, <\/span><span style=\"color:#b16139;\">err <\/span><span style=\"color:#55859b;\">=&gt; <\/span><span style=\"color:#a07e3b;\">console<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#1c9aa0;\">error<\/span><span style=\"color:#ecf4ee;\">(<\/span><span>&#39;<\/span><span style=\"color:#489963;\">Redis Client Error<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">, <\/span><span style=\"color:#b16139;\">err<\/span><span style=\"color:#ecf4ee;\">));\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">redis<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">connect<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">static async <\/span><span style=\"color:#478c90;\">main<\/span><span>(): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">userRepository <\/span><span>= new <\/span><span style=\"color:#ecf4ee;\">RedisUserRepository(<\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">redis<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">userService <\/span><span>= new <\/span><span style=\"color:#ecf4ee;\">UserService(<\/span><span style=\"color:#b16139;\">userRepository<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">userService<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">createNewUser<\/span><span style=\"color:#ecf4ee;\">(<\/span><span>&#39;<\/span><span style=\"color:#489963;\">username@example.com<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">static async <\/span><span style=\"color:#478c90;\">shutDown<\/span><span>(): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">redis<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#1c9aa0;\">disconnect<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">}\n<\/span><span>\n<\/span><span>new ApplicationContainer().<\/span><span style=\"color:#478c90;\">bootstrap<\/span><span>();\n<\/span><\/code><\/pre>\n<p>Everything is working great so far. I'd like to build out an implementation now that backs itself on the same data store the rest of our production services run on- MySQL! Let's see what that looks like:<\/p>\n<pre data-lang=\"ts\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-ts \"><code class=\"language-ts\" data-lang=\"ts\"><span style=\"color:#55859b;\">class <\/span><span style=\"color:#a07e3b;\">MySQLUserRepository <\/span><span style=\"color:#55859b;\">implements <\/span><span style=\"color:#489963;\">UserRepository <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">constructor<\/span><span>(<\/span><span style=\"color:#55859b;\">private readonly <\/span><span style=\"color:#b16139;\">connection<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">MySQL<\/span><span>) <\/span><span style=\"color:#ecf4ee;\">{}\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">async <\/span><span style=\"color:#478c90;\">nextId<\/span><span>(): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;string&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#478c90;\">uuid<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">async <\/span><span style=\"color:#478c90;\">add<\/span><span>(<\/span><span style=\"color:#b16139;\">user<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">User<\/span><span>): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#ecf4ee;\">{ <\/span><span style=\"color:#b16139;\">id<\/span><span style=\"color:#ecf4ee;\">, <\/span><span style=\"color:#b16139;\">email <\/span><span style=\"color:#ecf4ee;\">} <\/span><span>= <\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">;\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">connection<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">execute<\/span><span style=\"color:#ecf4ee;\">(<\/span><span>&#39;<\/span><span style=\"color:#489963;\">INSERT INTO `Users` VALUES (?, ?);<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">, [<\/span><span style=\"color:#b16139;\">id<\/span><span style=\"color:#ecf4ee;\">, <\/span><span style=\"color:#b16139;\">email<\/span><span style=\"color:#ecf4ee;\">]);\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">async <\/span><span style=\"color:#478c90;\">delete<\/span><span>(<\/span><span style=\"color:#b16139;\">user<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">User<\/span><span>): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">connection<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">execute<\/span><span style=\"color:#ecf4ee;\">(<\/span><span>&#39;<\/span><span style=\"color:#489963;\">DELETE FROM `Users` WHERE `id` = ?;<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">, <\/span><span style=\"color:#b16139;\">user<\/span><span style=\"color:#ecf4ee;\">.id);\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">async <\/span><span style=\"color:#478c90;\">findByEmail<\/span><span>(<\/span><span style=\"color:#b16139;\">email<\/span><span>: <\/span><span style=\"color:#ecf4ee;\">string<\/span><span>): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;User&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#ecf4ee;\">[<\/span><span style=\"color:#b16139;\">users<\/span><span style=\"color:#ecf4ee;\">] <\/span><span>= <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">connection<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">queryRows<\/span><span style=\"color:#ecf4ee;\">(\n<\/span><span style=\"color:#ecf4ee;\">            <\/span><span>&#39;<\/span><span style=\"color:#489963;\">SELECT * FROM `Users` ORDER BY `id` ASC;<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">,\n<\/span><span style=\"color:#ecf4ee;\">        );\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">if <\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">users<\/span><span style=\"color:#ecf4ee;\">.length <\/span><span>=== <\/span><span style=\"color:#9f713c;\">0<\/span><span style=\"color:#ecf4ee;\">) {\n<\/span><span style=\"color:#ecf4ee;\">            <\/span><span style=\"color:#55859b;\">throw <\/span><span>new <\/span><span style=\"color:#ecf4ee;\">NotFoundException();\n<\/span><span style=\"color:#ecf4ee;\">        }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">if <\/span><span style=\"color:#ecf4ee;\">(<\/span><span style=\"color:#b16139;\">users<\/span><span style=\"color:#ecf4ee;\">.length <\/span><span>&gt; <\/span><span style=\"color:#9f713c;\">1<\/span><span style=\"color:#ecf4ee;\">) {\n<\/span><span style=\"color:#ecf4ee;\">            <\/span><span style=\"color:#55859b;\">throw <\/span><span>new <\/span><span style=\"color:#ecf4ee;\">MultipleUsersForSameEmailException();\n<\/span><span style=\"color:#ecf4ee;\">        }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#b16139;\">users<\/span><span style=\"color:#ecf4ee;\">[<\/span><span style=\"color:#9f713c;\">0<\/span><span style=\"color:#ecf4ee;\">];\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">}\n<\/span><\/code><\/pre>\n<p>Now, we'll update the Application Container to create the MySQLUserRepository instance and pass that in instead. All set to go!<\/p>\n<pre data-lang=\"ts\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-ts \"><code class=\"language-ts\" data-lang=\"ts\"><span style=\"color:#55859b;\">import <\/span><span style=\"color:#b16139;\">mysql <\/span><span style=\"color:#55859b;\">from <\/span><span>&#39;<\/span><span style=\"color:#489963;\">mysql2\/promise<\/span><span>&#39;;\n<\/span><span>\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">class <\/span><span style=\"color:#a07e3b;\">ApplicationContainer <\/span><span style=\"color:#ecf4ee;\">{\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">private static <\/span><span style=\"color:#b16139;\">mysql<\/span><span style=\"color:#ecf4ee;\">;\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">static async <\/span><span style=\"color:#478c90;\">bootstrap<\/span><span>(): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">startUp<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">main<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">shutDown<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">protected static async <\/span><span style=\"color:#478c90;\">startUp<\/span><span>(): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">mysql <\/span><span>= <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">mysql<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">createConnection<\/span><span style=\"color:#ecf4ee;\">({host:<\/span><span>&#39;<\/span><span style=\"color:#489963;\">localhost<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">, user: <\/span><span>&#39;<\/span><span style=\"color:#489963;\">root<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">, database: <\/span><span>&#39;<\/span><span style=\"color:#489963;\">test<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">});\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">protected static async <\/span><span style=\"color:#478c90;\">main<\/span><span>(): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">userRepository <\/span><span>= new <\/span><span style=\"color:#ecf4ee;\">MySQLUserRepository(<\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">mysql<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">userService <\/span><span>= new <\/span><span style=\"color:#ecf4ee;\">UserService(<\/span><span style=\"color:#b16139;\">userRepository<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">userService<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#478c90;\">createNewUser<\/span><span style=\"color:#ecf4ee;\">(<\/span><span>&#39;<\/span><span style=\"color:#489963;\">username@example.com<\/span><span>&#39;<\/span><span style=\"color:#ecf4ee;\">);\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">\n<\/span><span style=\"color:#ecf4ee;\">    <\/span><span style=\"color:#55859b;\">protected static async <\/span><span style=\"color:#478c90;\">shutDown<\/span><span>(): <\/span><span style=\"color:#ecf4ee;\">Promise&lt;void&gt; {\n<\/span><span style=\"color:#ecf4ee;\">        <\/span><span style=\"color:#55859b;\">await <\/span><span style=\"color:#b16139;\">this<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#b16139;\">mysql<\/span><span style=\"color:#ecf4ee;\">.<\/span><span style=\"color:#1c9aa0;\">close<\/span><span style=\"color:#ecf4ee;\">();\n<\/span><span style=\"color:#ecf4ee;\">    }\n<\/span><span style=\"color:#ecf4ee;\">}\n<\/span><span>\n<\/span><span>new ApplicationContainer().<\/span><span style=\"color:#478c90;\">bootstrap<\/span><span>();\n<\/span><\/code><\/pre>\n<p>As you can see from this, none of our logic needed to change around the actual business logic layer of our code. This is also why Dependency Inversion is seen as one way to facilitate Inversion of Control, as we are passing dependencies from the top down as opposed to calling or creating external modules within our domain logic.<\/p>\n<p>Here is a graph of how these interfaces, class implementations, and the application container relate to each other:<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n<\/span><span>\u2502                      \u2502\n<\/span><span>\u2502     Application      \u2502    passes instance to\n<\/span><span>\u2502     Container        \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n<\/span><span>\u2502                      \u2502                        \u2502\n<\/span><span>\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518                        \u2502\n<\/span><span>               \u2502                                \u2502\n<\/span><span>               \u2502                                \u2502\n<\/span><span>               \u2502                     \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25bc\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n<\/span><span>               \u2502                     \u2502      UserService    \u2502\n<\/span><span>               \u2502                     \u2502                     \u2502\n<\/span><span>               \u2502                     \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/span><span>               \u2502                                \u2502\n<\/span><span>               \u2502                                \u2502\n<\/span><span>               \u2502                                \u2502\n<\/span><span>          instantiates                   interacts with\n<\/span><span>               \u2502                                \u2502\n<\/span><span>               \u2502                                \u2502\n<\/span><span>               \u2502                                \u2502\n<\/span><span>               \u2502                     \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n<\/span><span>               \u2502                     \u2502   UserRepository    \u2502\n<\/span><span>               \u2502                     \u2502                     \u2502\n<\/span><span>               \u2502                     \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/span><span>               \u2502                                \u2502\n<\/span><span>               \u2502                    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n<\/span><span>               \u2502                    \u2502      implements         \u2502\n<\/span><span>               \u2502                    \u2502                         \u2502\n<\/span><span>               \u2502                    \u2502                         \u2502\n<\/span><span>               \u2502         \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510   \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n<\/span><span>               \u2502         \u2502 MySQLUserRepository \u2502   \u2502 RedisUserRepository \u2502\n<\/span><span>               \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25ba                     \u2502   \u2502                     \u2502\n<\/span><span>                         \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518   \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/span><\/code><\/pre>\n"},{"title":"Notes on algorithms","pubDate":"Fri, 28 Jul 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/algorithms-notes\/","guid":"https:\/\/blog.carrio.dev\/blog\/algorithms-notes\/","description":"<p>This was exported from an <a href=\"https:\/\/orgmode.org\/\">org-mode<\/a> notebook documenting various\nalgorithm knowledge, with some example code snippets here and there\nimplementing and executing algorithms inline.<\/p>\n<p>Code is reused where possible, such as algorithm implementations being\ndefined in one section with multiple data tests in others, referencing\nthat function.<\/p>\n<blockquote>\n<p>This post is a bit different, as I am documenting this for myself only- and I'm sorry if I lead you astray :)<\/p>\n<\/blockquote>\n<h1 id=\"preface\">Preface<\/h1>\n<p>Since this is an <strong>org-mode<\/strong> notebook, it's actually possible to\nevaluate code using\n<a href=\"https:\/\/orgmode.org\/worg\/org-contrib\/babel\/\">Babel<\/a>. As a bit of an\norg-mode novice, I'm not familiar with all of the capabilities, but it\nis possible to share sessions between source code snippets. Due to this,\nthere are some shared functions I will define that will be reused across\nthe notebook.<\/p>\n<h2 id=\"shared-code\">Shared code<\/h2>\n<pre data-lang=\"python\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-python \"><code class=\"language-python\" data-lang=\"python\"><span style=\"color:#55859b;\">def <\/span><span style=\"color:#478c90;\">swap_indices<\/span><span>(<\/span><span style=\"color:#b16139;\">array<\/span><span>, <\/span><span style=\"color:#b16139;\">i<\/span><span>, <\/span><span style=\"color:#b16139;\">j<\/span><span>):\n<\/span><span>    array[i], array[j] = array[j], array[i]\n<\/span><span>\n<\/span><span>debug_state = {\n<\/span><span>    &#39;<\/span><span style=\"color:#489963;\">enabled<\/span><span>&#39;: <\/span><span style=\"color:#9f713c;\">False\n<\/span><span>}\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">class <\/span><span style=\"color:#a07e3b;\">Logger<\/span><span style=\"color:#ecf4ee;\">:\n<\/span><span>    <\/span><span style=\"color:#55859b;\">def <\/span><span style=\"color:#478c90;\">debug<\/span><span>(<\/span><span style=\"color:#b16139;\">self<\/span><span>, <\/span><span style=\"color:#b16139;\">content<\/span><span>):\n<\/span><span>        <\/span><span style=\"color:#55859b;\">if <\/span><span>(debug_state[&#39;<\/span><span style=\"color:#489963;\">enabled<\/span><span>&#39;]):\n<\/span><span>            <\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(content)\n<\/span><span>\n<\/span><span>logger = <\/span><span style=\"color:#b16139;\">Logger<\/span><span>()\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">def <\/span><span style=\"color:#478c90;\">set_debugging<\/span><span>(<\/span><span style=\"color:#b16139;\">enabled<\/span><span>):\n<\/span><span>    debug_state[&#39;<\/span><span style=\"color:#489963;\">enabled<\/span><span>&#39;] = enabled\n<\/span><\/code><\/pre>\n<h1 id=\"algorithms-terminology\">Algorithms Terminology<\/h1>\n<h2 id=\"cardinality\">Cardinality<\/h2>\n<p>The size of the data. For set, arrays, or tuples, it's the number of\nelements stored in it.<\/p>\n<h2 id=\"regression\">Regression<\/h2>\n<p>Predicting a response. An example of this is K-Nearest Neighbor.<\/p>\n<h2 id=\"classification\">Classification<\/h2>\n<p>Categorization into a group. See features.<\/p>\n<h2 id=\"feature\">Feature<\/h2>\n<p>Attributes or tangible data points that can be construed about the data\nset.<\/p>\n<p>Take for example, fruit, you might compare color and size.<\/p>\n<p>For another example, consider a pizza storefront. You might utilize a\nnumber of features, such as:<\/p>\n<ul>\n<li>The day of the week (Sunday is 0, Monday is 1, ... Saturday is 6)<\/li>\n<li>Weekend or holiday (Yes is 1, No is 0)<\/li>\n<li>Is it a game day (Yes is 1, No is 0)<\/li>\n<\/ul>\n<p>And so on. These could be used to guess what a likely outcome of another\ndatapoint might be.<\/p>\n<h2 id=\"heuristic\">Heuristic<\/h2>\n<p>Unlike an algorithm, which always produces a correct result, a heuristic\nusually does a godo job but does not provide any guarantees.<\/p>\n<p>To provide a more direct definition:<\/p>\n<p>&gt; proceeding to a solution by trial and error or by rules that are only\nloosely defined.<\/p>\n<h2 id=\"more-general-terms\">More general terms<\/h2>\n<h1 id=\"arrays\">Arrays<\/h1>\n<p>Arrays are continugous data structures. They provide instant access\n(O(1)) to any element of the array by index. They are limited in\nremovals and insertions though, requiring on average O(n) time to insert\nor delete elements from the array.<\/p>\n<p>For removals, this is because any element to the right of the element\nneeds to be shifted left.<\/p>\n<p>For insertions, this is because any element to the right of the element\nneeds to be shifted right.<\/p>\n<p>So on average, since (n + 0)\/2 or 1\/2 * n, this equates to O(n).<\/p>\n<h2 id=\"sorting-algorithms\">Sorting algorithms<\/h2>\n<p>There are many sorting algorithms relevant to array data structures.<\/p>\n<h3 id=\"insertion-sort\">Insertion sort<\/h3>\n<p>This is one of the more simple sorting algorithms, and has a complexity\nof O(n^2^). The logic for this is simple.<\/p>\n<ol>\n<li>Start from the beginning of the array<\/li>\n<li>Step forward<\/li>\n<li>If the current element is less than the previous element<\/li>\n<li>If the current element is less than the previous element, walk\nbackwards in the array until either you reach an element that is\nless than the current element, or the beginning of the list, and\nplace it there<\/li>\n<li>If not at the end of the array, repeat from step 2<\/li>\n<\/ol>\n<p>An example implementation for this would be:<\/p>\n<pre data-lang=\"python\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-python \"><code class=\"language-python\" data-lang=\"python\"><span style=\"color:#55859b;\">def <\/span><span style=\"color:#478c90;\">insertion_sort<\/span><span>(<\/span><span style=\"color:#b16139;\">array<\/span><span>, <\/span><span style=\"color:#b16139;\">debug<\/span><span>=<\/span><span style=\"color:#9f713c;\">False<\/span><span>):\n<\/span><span>    logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Starting from position 1<\/span><span>&quot;)\n<\/span><span>    backwards_index = <\/span><span style=\"color:#9f713c;\">1\n<\/span><span>\n<\/span><span>    logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Starting iterations...<\/span><span>&quot;)\n<\/span><span>    <\/span><span style=\"color:#55859b;\">for <\/span><span>forwards_index <\/span><span style=\"color:#55859b;\">in <\/span><span style=\"color:#1c9aa0;\">range<\/span><span>(<\/span><span style=\"color:#9f713c;\">1<\/span><span>, <\/span><span style=\"color:#1c9aa0;\">len<\/span><span>(array)):\n<\/span><span>        backwards_index = forwards_index\n<\/span><span>\n<\/span><span>        logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Finding the best place for <\/span><span style=\"color:#9f713c;\">{}<\/span><span>&quot;.<\/span><span style=\"color:#b16139;\">format<\/span><span>(array[forwards_index]))\n<\/span><span>\n<\/span><span>        <\/span><span style=\"color:#55859b;\">while <\/span><span>(backwards_index &gt; <\/span><span style=\"color:#9f713c;\">0 <\/span><span>and array[backwards_index] &lt; array[backwards_index-<\/span><span style=\"color:#9f713c;\">1<\/span><span>]):\n<\/span><span>\n<\/span><span>            logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Swapping from position <\/span><span style=\"color:#9f713c;\">{}<\/span><span style=\"color:#489963;\"> to position <\/span><span style=\"color:#9f713c;\">{}<\/span><span>&quot;.<\/span><span style=\"color:#b16139;\">format<\/span><span>(backwards_index, backwards_index-<\/span><span style=\"color:#9f713c;\">1<\/span><span>))\n<\/span><span>            <\/span><span style=\"color:#5f6d64;\"># array[backwards_index], array[backwards_index-1] = array[backwards_index-1], array[backwards_index]\n<\/span><span>            <\/span><span style=\"color:#b16139;\">swap_indices<\/span><span>(array, backwards_index, backwards_index-<\/span><span style=\"color:#9f713c;\">1<\/span><span>)\n<\/span><span>            backwards_index = backwards_index-<\/span><span style=\"color:#9f713c;\">1\n<\/span><span>\n<\/span><span>        logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Found the resting place for <\/span><span style=\"color:#9f713c;\">{}<\/span><span>&quot;.<\/span><span style=\"color:#b16139;\">format<\/span><span>(array[forwards_index]))\n<\/span><\/code><\/pre>\n<ol>\n<li>\n<p>Examples<\/p>\n<pre data-lang=\"python\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-python \"><code class=\"language-python\" data-lang=\"python\"><span style=\"color:#55859b;\">import <\/span><span>random\n<\/span><span>\n<\/span><span style=\"color:#b16139;\">set_debugging<\/span><span>(<\/span><span style=\"color:#9f713c;\">False<\/span><span>)\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\"># always generates a list from 1-13 with 4 instances of each integer\n<\/span><span>arr = [x <\/span><span style=\"color:#55859b;\">for <\/span><span>x <\/span><span style=\"color:#55859b;\">in <\/span><span style=\"color:#1c9aa0;\">range<\/span><span>(<\/span><span style=\"color:#9f713c;\">1<\/span><span>,<\/span><span style=\"color:#9f713c;\">14<\/span><span>) <\/span><span style=\"color:#55859b;\">for <\/span><span>y <\/span><span style=\"color:#55859b;\">in <\/span><span style=\"color:#1c9aa0;\">range<\/span><span>(<\/span><span style=\"color:#9f713c;\">0<\/span><span>,<\/span><span style=\"color:#9f713c;\">4<\/span><span>)]\n<\/span><span>\n<\/span><span>random.<\/span><span style=\"color:#b16139;\">shuffle<\/span><span>(arr)\n<\/span><span>\n<\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">The randomized array<\/span><span>&quot;)\n<\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(arr)\n<\/span><span>\n<\/span><span style=\"color:#b16139;\">insertion_sort<\/span><span>(arr)\n<\/span><span>\n<\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(&quot;&quot;)\n<\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">The sorted array<\/span><span>&quot;)\n<\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(arr)\n<\/span><span>\n<\/span><\/code><\/pre>\n<p>:<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>The sorted array\n<\/span><span>[1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13]\n<\/span><\/code><\/pre>\n<\/li>\n<\/ol>\n<h1 id=\"lists\">Lists<\/h1>\n<h1 id=\"graphs\">Graphs<\/h1>\n<h1 id=\"trees\">Trees<\/h1>\n<h2 id=\"summary\">Summary<\/h2>\n<p>Trees are a special type of graph. The properties of a tree are:<\/p>\n<ul>\n<li>A non-linear data structure<\/li>\n<li>Always has n-1 edges for n nodes<\/li>\n<li>Edges are always directed<\/li>\n<li>There is a root node<\/li>\n<li>A node can only have up to one parent<\/li>\n<li>There are no cycles<\/li>\n<\/ul>\n<h3 id=\"terminology\">Terminology<\/h3>\n<ol>\n<li>\n<p>Root node<\/p>\n<p>The topmost node in a tree. It has no parent, unlike the rest of the\ntree<\/p>\n<\/li>\n<li>\n<p>Parent<\/p>\n<p>In relation to a node, its parent is the node pointing to it<\/p>\n<\/li>\n<li>\n<p>Child<\/p>\n<p>In relation to a node, a child is a node it points to<\/p>\n<\/li>\n<li>\n<p>Leaf node<\/p>\n<p>A node that has no children. Also referred to as an external node<\/p>\n<\/li>\n<li>\n<p>Non-leaf node<\/p>\n<p>A node that does have children. Also referred to as an internal node<\/p>\n<\/li>\n<li>\n<p>Path<\/p>\n<p>A sequence of edges connecting a starting node to an end node.<\/p>\n<\/li>\n<li>\n<p>Edge<\/p>\n<p>A link between two node, just like in a graph. This must be\ndirected.<\/p>\n<\/li>\n<li>\n<p>Ancestor<\/p>\n<p>In relation to a node, an ancestor is any node that is its parent,\nor a parent of its parent, and so on.<\/p>\n<\/li>\n<li>\n<p>Descendant<\/p>\n<p>In relation to a node, a descendent is any node that is its child,\nor a child of its child, and so on.<\/p>\n<\/li>\n<li>\n<p>Sibling<\/p>\n<p>In relation to a node, a sibling is any node that shares the same\nparent as it.<\/p>\n<\/li>\n<li>\n<p>Degree<\/p>\n<p>The number of children a node has.<\/p>\n<\/li>\n<li>\n<p>Depth of a node<\/p>\n<p>In relation to a node, this is the number of edges between the root\nnode and it.<\/p>\n<\/li>\n<li>\n<p>Height of a node<\/p>\n<p>In relation to a node, this is the longest path that exists between\nit and any of its descendant leaf nodes.<\/p>\n<\/li>\n<li>\n<p>Level of a node<\/p>\n<p>In relation to a node, the number of edges that exist from the root\nnode to it. Typically, depth of node + 1.<\/p>\n<\/li>\n<li>\n<p>Rooted tree<\/p>\n<p>A binary tree that has a root node and every node has at most two\nchildren.<\/p>\n<\/li>\n<li>\n<p>Full tree<\/p>\n<p>A binary tree in which every node has either 0 or 2 children.<\/p>\n<\/li>\n<li>\n<p>Balanced tree<\/p>\n<p>A binary tree where the left and right subtrees of every node differ\nin height by no more than 1.<\/p>\n<\/li>\n<li>\n<p>Degenerate \/ Pathological tree<\/p>\n<p>A tree where each parent node has only one associated child node.<\/p>\n<p>This is essentially a linked list structure and provides no\nadditional benefits over it.<\/p>\n<\/li>\n<\/ol>\n<h2 id=\"b-trees\">B-trees<\/h2>\n<p>These function by optimizing the number of reads that need to be\nperformed in order to access data from a disk. These are often used in\ndatabases.<\/p>\n<p>In contrast to a binary tree which houses up to but not exceeding 2\nchild nodes per node, this structures the maximum number of children per\nnode in order to reach a full block size from the disk. In this way,\neach read maximally saturates the disk read operation and requires fewer\nreads from the disk in total to reach the desired data.<\/p>\n<h2 id=\"red-black-trees\">Red-black trees<\/h2>\n<p>Red-black trees are a specialized type of binary tree which utilize a\nset of rule to automatically balance itself during insertions and\ndeletions.<\/p>\n<h3 id=\"properties\">Properties<\/h3>\n<ol>\n<li>Red\/Black property: Every node is colored. They can either be red or\nblack.<\/li>\n<li>Root property: The root is black<\/li>\n<li>Leaf property: Every leaf (nil) is black<\/li>\n<li>Red property: If a red node has children, then the children are\nalways black.<\/li>\n<li>Depth property: Every path from a given node to any of its leaf\n(nil) nodes has the same number of black nodes.<\/li>\n<\/ol>\n<h3 id=\"rotation-rules\">Rotation rules<\/h3>\n<h2 id=\"avl-trees\">AVL trees<\/h2>\n<p>AVL trees are named after its inventors, Adelson-Velsky and Landis. It\nis another specialized type of binary tree which is self-balancing.<\/p>\n<h3 id=\"more-info\">More info<\/h3>\n<h2 id=\"heaps\">Heaps<\/h2>\n<p>A specialized tree data structure, which satisfies the <strong>heap\nproperty<\/strong>. There are two types of heaps, min heap and max heap. In a\nmax heap, for any given node N, if P is a parent node of N, then the\nvalue of P is greater than or equal to the value of N.<\/p>\n<p>A heap is an implementation of another abstract data type; the priority\nqueue. Priority queues are sometimes referred to as heaps, regardless of\ntheir implementation.<\/p>\n<p>A common implementation is the <strong>binary heap<\/strong>.<\/p>\n<h3 id=\"comparison-to-binary-search-trees\">Comparison to binary search trees<\/h3>\n<p>Binary search trees follow a different kind of rule, such that the left\nand right child nodes of any given node are less than or greater than\ntheir parent, respectively. Meanwhile, a binary heap follows no such\nordering, and has no implicit ordering for searches.<\/p>\n<h2 id=\"splay-trees\">Splay trees<\/h2>\n<p>A binary search tree that provides the additional benefit that recently\naccessed elements will be fast to access again. Operations similar\ncomplete in O(log n) time similar to self-balancing binary search trees.\nFor operations that are performed in a non-random pattern, it can\ncomplete in faster than logarithmic time, without requiring knowledge of\nthe pattern.<\/p>\n<p>All operations are combined with one basic operation called splaying.\nSplaying the tree rearranges the tree so that the element is placed at\nthe root of the tree. This requires tree rotations to move the element\nto the top. This allows all of the operations performed to move recently\naccessed elements closer to the root.<\/p>\n<p>It is possible for the structure of the tree to be pathological based on\nwhat element was most recently accessed, compared to a self-balancing\ntree which maintains an logarithmic lookup time.<\/p>\n<p>In short, a splay tree will reorganize based on most-recently used (MRU)\nelements, while a self-balancing tree will reorganize to optimize for\nrandom element searches.<\/p>\n<h1 id=\"k-nearest-neighbor\">K-Nearest Neighbor<\/h1>\n<p>Determining the most similar data point in a dataset based on determined\nfeatures.<\/p>\n<h2 id=\"examples\">Examples<\/h2>\n<h3 id=\"distance-calculation\">Distance calculation<\/h3>\n<pre data-lang=\"python\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-python \"><code class=\"language-python\" data-lang=\"python\"><span style=\"color:#55859b;\">from <\/span><span>math <\/span><span style=\"color:#55859b;\">import <\/span><span>sqrt, ceil\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">def <\/span><span style=\"color:#478c90;\">calculate_distance<\/span><span>(<\/span><span style=\"color:#b16139;\">x<\/span><span>, <\/span><span style=\"color:#b16139;\">y<\/span><span>):\n<\/span><span>    <\/span><span style=\"color:#5f6d64;\"># we can only compare the datapoints if they are equal in cardinality\n<\/span><span>    <\/span><span style=\"color:#55859b;\">if <\/span><span>(<\/span><span style=\"color:#1c9aa0;\">len<\/span><span>(x) != <\/span><span style=\"color:#1c9aa0;\">len<\/span><span>(y)):\n<\/span><span>        <\/span><span style=\"color:#55859b;\">return <\/span><span>-<\/span><span style=\"color:#9f713c;\">1\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#55859b;\">return <\/span><span>(<\/span><span style=\"color:#b16139;\">ceil<\/span><span>(<\/span><span style=\"color:#b16139;\">sqrt<\/span><span>(<\/span><span style=\"color:#1c9aa0;\">sum<\/span><span>([(x[i]-y[i]) ** <\/span><span style=\"color:#9f713c;\">2 <\/span><span style=\"color:#55859b;\">for <\/span><span>i <\/span><span style=\"color:#55859b;\">in <\/span><span style=\"color:#1c9aa0;\">range<\/span><span>(<\/span><span style=\"color:#1c9aa0;\">len<\/span><span>(x))]))))\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">def <\/span><span style=\"color:#478c90;\">map_distances<\/span><span>(<\/span><span style=\"color:#b16139;\">value<\/span><span>, <\/span><span style=\"color:#b16139;\">dataset<\/span><span>):\n<\/span><span>    <\/span><span style=\"color:#55859b;\">return <\/span><span>[<\/span><span style=\"color:#b16139;\">calculate_distance<\/span><span>(value, datapoint) <\/span><span style=\"color:#55859b;\">for <\/span><span>datapoint <\/span><span style=\"color:#55859b;\">in <\/span><span>dataset]\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">def <\/span><span style=\"color:#478c90;\">k_nearest<\/span><span>(<\/span><span style=\"color:#b16139;\">k<\/span><span>, <\/span><span style=\"color:#b16139;\">value<\/span><span>, <\/span><span style=\"color:#b16139;\">dataset<\/span><span>):\n<\/span><span>    <\/span><span style=\"color:#5f6d64;\"># todo: stuff\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#55859b;\">return <\/span><span>[]\n<\/span><\/code><\/pre>\n<h3 id=\"bread\">Bread<\/h3>\n<pre data-lang=\"python\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-python \"><code class=\"language-python\" data-lang=\"python\"><span style=\"color:#5f6d64;\">### bread data\n<\/span><span>dataset = [\n<\/span><span>    (<\/span><span style=\"color:#9f713c;\">5<\/span><span>, <\/span><span style=\"color:#9f713c;\">1<\/span><span>, <\/span><span style=\"color:#9f713c;\">0<\/span><span>),\n<\/span><span>    (<\/span><span style=\"color:#9f713c;\">3<\/span><span>, <\/span><span style=\"color:#9f713c;\">1<\/span><span>, <\/span><span style=\"color:#9f713c;\">1<\/span><span>),\n<\/span><span>    (<\/span><span style=\"color:#9f713c;\">1<\/span><span>, <\/span><span style=\"color:#9f713c;\">1<\/span><span>, <\/span><span style=\"color:#9f713c;\">0<\/span><span>),\n<\/span><span>    (<\/span><span style=\"color:#9f713c;\">4<\/span><span>, <\/span><span style=\"color:#9f713c;\">0<\/span><span>, <\/span><span style=\"color:#9f713c;\">1<\/span><span>),\n<\/span><span>    (<\/span><span style=\"color:#9f713c;\">4<\/span><span>, <\/span><span style=\"color:#9f713c;\">0<\/span><span>, <\/span><span style=\"color:#9f713c;\">0<\/span><span>),\n<\/span><span>    (<\/span><span style=\"color:#9f713c;\">2<\/span><span>, <\/span><span style=\"color:#9f713c;\">0<\/span><span>, <\/span><span style=\"color:#9f713c;\">0<\/span><span>),\n<\/span><span>]\n<\/span><span>coordinate = (<\/span><span style=\"color:#9f713c;\">4<\/span><span>,<\/span><span style=\"color:#9f713c;\">1<\/span><span>,<\/span><span style=\"color:#9f713c;\">0<\/span><span>)\n<\/span><span>\n<\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(<\/span><span style=\"color:#b16139;\">map_distances<\/span><span>(coordinate, dataset))\n<\/span><span>\n<\/span><\/code><\/pre>\n<h3 id=\"netflix-user-data\">Netflix User Data<\/h3>\n<pre data-lang=\"python\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-python \"><code class=\"language-python\" data-lang=\"python\"><span style=\"color:#5f6d64;\">### netflix user data\n<\/span><span>dataset = [\n<\/span><span>    (<\/span><span style=\"color:#9f713c;\">3<\/span><span>,<\/span><span style=\"color:#9f713c;\">4<\/span><span>,<\/span><span style=\"color:#9f713c;\">4<\/span><span>,<\/span><span style=\"color:#9f713c;\">1<\/span><span>,<\/span><span style=\"color:#9f713c;\">4<\/span><span>),\n<\/span><span>    (<\/span><span style=\"color:#9f713c;\">4<\/span><span>,<\/span><span style=\"color:#9f713c;\">3<\/span><span>,<\/span><span style=\"color:#9f713c;\">5<\/span><span>,<\/span><span style=\"color:#9f713c;\">1<\/span><span>,<\/span><span style=\"color:#9f713c;\">5<\/span><span>),\n<\/span><span>]\n<\/span><span>\n<\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(<\/span><span style=\"color:#b16139;\">calculate_distance<\/span><span>(*dataset))\n<\/span><\/code><\/pre>\n<h1 id=\"fourier-transform\">Fourier Transform<\/h1>\n<h1 id=\"mapreduce\">MapReduce<\/h1>\n<h1 id=\"scheduling-jobs\">Scheduling Jobs<\/h1>\n<p>A common algorithmic problem is optimal scheduling in regards to time\nslots. There are various approaches that can be taken here, such as\n\"shortest job first\", or \"earliest starting job first\", which do not\nalways yield the correct result. In the case of scheduling, there is a\nknown, correct algorithm for this that is optimal:<\/p>\n<h2 id=\"earliest-ending-job\">Earliest Ending Job<\/h2>\n<p>This works by finding the job that ends the earliest, as opposed to the\njob that starts the earliest. This job guarantees that less subsequent\njobs will be blocked than any others. The algorithm for this can be\ndefined as follows:<\/p>\n<pre data-lang=\"python\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-python \"><code class=\"language-python\" data-lang=\"python\"><span style=\"color:#55859b;\">class <\/span><span style=\"color:#a07e3b;\">Job<\/span><span style=\"color:#ecf4ee;\">:\n<\/span><span>    <\/span><span style=\"color:#55859b;\">def <\/span><span style=\"color:#1c9aa0;\">__init__<\/span><span>(<\/span><span style=\"color:#b16139;\">self<\/span><span>, <\/span><span style=\"color:#b16139;\">starts<\/span><span>, <\/span><span style=\"color:#b16139;\">ends<\/span><span>, <\/span><span style=\"color:#b16139;\">meta<\/span><span>={}):\n<\/span><span>        <\/span><span style=\"color:#b16139;\">self<\/span><span>.starts = starts\n<\/span><span>        <\/span><span style=\"color:#b16139;\">self<\/span><span>.ends = ends\n<\/span><span>        <\/span><span style=\"color:#b16139;\">self<\/span><span>.meta = meta\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">def <\/span><span style=\"color:#478c90;\">pop_earliest_ending_job<\/span><span>(<\/span><span style=\"color:#b16139;\">jobs<\/span><span>):\n<\/span><span>    logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Finding the earliest ending job<\/span><span>&quot;)\n<\/span><span>\n<\/span><span>    earliest_time = <\/span><span style=\"color:#b16139;\">float<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">inf<\/span><span>&#39;)\n<\/span><span>    earliest_job = <\/span><span style=\"color:#9f713c;\">None\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#55859b;\">for <\/span><span>job <\/span><span style=\"color:#55859b;\">in <\/span><span>jobs:\n<\/span><span>         logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Checking if <\/span><span style=\"color:#9f713c;\">{}<\/span><span style=\"color:#489963;\"> ends earliest...<\/span><span>&quot;.<\/span><span style=\"color:#b16139;\">format<\/span><span>(job.meta[&#39;<\/span><span style=\"color:#489963;\">title<\/span><span>&#39;]))\n<\/span><span>         <\/span><span style=\"color:#55859b;\">if <\/span><span>(job.ends &lt; earliest_time):\n<\/span><span>             logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Looks like it could be <\/span><span style=\"color:#9f713c;\">{}<\/span><span>&quot;.<\/span><span style=\"color:#b16139;\">format<\/span><span>(job.meta[&#39;<\/span><span style=\"color:#489963;\">title<\/span><span>&#39;]))\n<\/span><span>             earliest_job = job\n<\/span><span>             earliest_time = job.ends\n<\/span><span>\n<\/span><span>    logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">The earliest ending job was <\/span><span style=\"color:#9f713c;\">{}<\/span><span>&quot;.<\/span><span style=\"color:#b16139;\">format<\/span><span>(job.meta[&#39;<\/span><span style=\"color:#489963;\">title<\/span><span>&#39;]))\n<\/span><span>\n<\/span><span>    jobs.<\/span><span style=\"color:#b16139;\">remove<\/span><span>(earliest_job)\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#55859b;\">return <\/span><span>earliest_job\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">def <\/span><span style=\"color:#478c90;\">remove_overlapping_jobs<\/span><span>(<\/span><span style=\"color:#b16139;\">jobs<\/span><span>, <\/span><span style=\"color:#b16139;\">end_time<\/span><span>):\n<\/span><span>    logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Removing jobs conflicting with end time <\/span><span style=\"color:#9f713c;\">{}<\/span><span>&quot;.<\/span><span style=\"color:#b16139;\">format<\/span><span>(end_time))\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#55859b;\">for <\/span><span>job <\/span><span style=\"color:#55859b;\">in <\/span><span>[j <\/span><span style=\"color:#55859b;\">for <\/span><span>j <\/span><span style=\"color:#55859b;\">in <\/span><span>jobs]:\n<\/span><span>        logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Checking whether to remove <\/span><span style=\"color:#9f713c;\">{}<\/span><span>&quot;.<\/span><span style=\"color:#b16139;\">format<\/span><span>(job.meta[&#39;<\/span><span style=\"color:#489963;\">title<\/span><span>&#39;]))\n<\/span><span>\n<\/span><span>        <\/span><span style=\"color:#55859b;\">if <\/span><span>(job.starts &lt; end_time):\n<\/span><span>            logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Removing the job<\/span><span>&quot;)\n<\/span><span>            jobs.<\/span><span style=\"color:#b16139;\">remove<\/span><span>(job)\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">def <\/span><span style=\"color:#478c90;\">optimal_scheduling<\/span><span>(<\/span><span style=\"color:#b16139;\">jobs<\/span><span>):\n<\/span><span>    <\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Actual print statement<\/span><span>&quot;)\n<\/span><span>    logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Creating empty schedule<\/span><span>&quot;)\n<\/span><span>\n<\/span><span>    optimal_jobs = <\/span><span style=\"color:#b16139;\">set<\/span><span>()\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#55859b;\">while <\/span><span>(<\/span><span style=\"color:#1c9aa0;\">len<\/span><span>(jobs) &gt; <\/span><span style=\"color:#9f713c;\">0<\/span><span>):\n<\/span><span>        next_best_job = <\/span><span style=\"color:#b16139;\">pop_earliest_ending_job<\/span><span>(jobs)\n<\/span><span>\n<\/span><span>        logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Next best job was <\/span><span style=\"color:#9f713c;\">{}<\/span><span>&quot;.<\/span><span style=\"color:#b16139;\">format<\/span><span>(next_best_job.meta[&#39;<\/span><span style=\"color:#489963;\">title<\/span><span>&#39;]))\n<\/span><span>\n<\/span><span>        <\/span><span style=\"color:#55859b;\">if <\/span><span>(next_best_job is <\/span><span style=\"color:#9f713c;\">None<\/span><span>):\n<\/span><span>            <\/span><span style=\"color:#55859b;\">break\n<\/span><span>\n<\/span><span>        logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Adding to the optimal job schedule...<\/span><span>&quot;)\n<\/span><span>        optimal_jobs.<\/span><span style=\"color:#b16139;\">add<\/span><span>(next_best_job)\n<\/span><span>\n<\/span><span>        logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Removing overlapping jobs<\/span><span>&quot;)\n<\/span><span>        <\/span><span style=\"color:#b16139;\">remove_overlapping_jobs<\/span><span>(jobs, next_best_job.ends)\n<\/span><span>\n<\/span><span>    logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Found <\/span><span style=\"color:#9f713c;\">{}<\/span><span style=\"color:#489963;\"> job(s)!<\/span><span>&quot;.<\/span><span style=\"color:#b16139;\">format<\/span><span>(<\/span><span style=\"color:#1c9aa0;\">len<\/span><span>(optimal_jobs)))\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#55859b;\">return <\/span><span>optimal_jobs\n<\/span><span>\n<\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Defined optimal scheduler<\/span><span>&quot;)\n<\/span><\/code><\/pre>\n<h3 id=\"examples-1\">Examples<\/h3>\n<p>Take for example the following case of spending the day at the movie\ntheatre, and you want to watch as many movies as you can in a day. Here\nare the showtimes:<\/p>\n<pre data-lang=\"python\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-python \"><code class=\"language-python\" data-lang=\"python\"><span style=\"color:#b16139;\">set_debugging<\/span><span>(<\/span><span style=\"color:#9f713c;\">False<\/span><span>)\n<\/span><span>\n<\/span><span>logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Creating set of showtimes...<\/span><span>&quot;)\n<\/span><span>\n<\/span><span>showtimes = <\/span><span style=\"color:#b16139;\">set<\/span><span>([\n<\/span><span>    <\/span><span style=\"color:#b16139;\">Job<\/span><span>(<\/span><span style=\"color:#9f713c;\">9<\/span><span>, <\/span><span style=\"color:#9f713c;\">12<\/span><span>, {&#39;<\/span><span style=\"color:#489963;\">title<\/span><span>&#39;: &#39;<\/span><span style=\"color:#489963;\">Mission Impossible<\/span><span>&#39;}),\n<\/span><span>    <\/span><span style=\"color:#b16139;\">Job<\/span><span>(<\/span><span style=\"color:#9f713c;\">10<\/span><span>, <\/span><span style=\"color:#9f713c;\">14<\/span><span>, {&#39;<\/span><span style=\"color:#489963;\">title<\/span><span>&#39;: &#39;<\/span><span style=\"color:#489963;\">Fellowship of the Ring<\/span><span>&#39;}),\n<\/span><span>    <\/span><span style=\"color:#b16139;\">Job<\/span><span>(<\/span><span style=\"color:#9f713c;\">14<\/span><span>, <\/span><span style=\"color:#9f713c;\">15<\/span><span>, {&#39;<\/span><span style=\"color:#489963;\">title<\/span><span>&#39;: &#39;<\/span><span style=\"color:#489963;\">Cars<\/span><span>&#39;}),\n<\/span><span>    <\/span><span style=\"color:#b16139;\">Job<\/span><span>(<\/span><span style=\"color:#9f713c;\">13<\/span><span>, <\/span><span style=\"color:#9f713c;\">16<\/span><span>, {&#39;<\/span><span style=\"color:#489963;\">title<\/span><span>&#39;: &#39;<\/span><span style=\"color:#489963;\">Dragon Ball Z<\/span><span>&#39;}),\n<\/span><span>    <\/span><span style=\"color:#b16139;\">Job<\/span><span>(<\/span><span style=\"color:#9f713c;\">15<\/span><span>, <\/span><span style=\"color:#9f713c;\">20<\/span><span>, {&#39;<\/span><span style=\"color:#489963;\">title<\/span><span>&#39;: &#39;<\/span><span style=\"color:#489963;\">Lion King<\/span><span>&#39;}),\n<\/span><span>])\n<\/span><span>\n<\/span><span>logger.<\/span><span style=\"color:#b16139;\">debug<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Created a set...<\/span><span>&quot;)\n<\/span><span>\n<\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Generating the best showtime schedule...<\/span><span>&quot;)\n<\/span><span>\n<\/span><span>optimal_showtime_schedule = <\/span><span style=\"color:#b16139;\">optimal_scheduling<\/span><span>(showtimes)\n<\/span><span>\n<\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">I was able to schedule <\/span><span style=\"color:#9f713c;\">{}<\/span><span style=\"color:#489963;\"> show(s)!<\/span><span>&quot;.<\/span><span style=\"color:#b16139;\">format<\/span><span>(<\/span><span style=\"color:#1c9aa0;\">len<\/span><span>(optimal_showtime_schedule)))\n<\/span><span>\n<\/span><span>shows_in_order = <\/span><span style=\"color:#1c9aa0;\">sorted<\/span><span>(optimal_showtime_schedule, <\/span><span style=\"color:#b16139;\">key<\/span><span>=<\/span><span style=\"color:#55859b;\">lambda <\/span><span style=\"color:#b16139;\">s<\/span><span>: s.starts, <\/span><span style=\"color:#b16139;\">reverse<\/span><span>=<\/span><span style=\"color:#9f713c;\">False<\/span><span>)\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">for <\/span><span>show <\/span><span style=\"color:#55859b;\">in <\/span><span>shows_in_order:\n<\/span><span>    <\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(&quot;<\/span><span style=\"color:#9f713c;\">{}<\/span><span style=\"color:#489963;\">, starting at <\/span><span style=\"color:#9f713c;\">{}<\/span><span style=\"color:#489963;\"> and ending at <\/span><span style=\"color:#9f713c;\">{}<\/span><span>&quot;.<\/span><span style=\"color:#b16139;\">format<\/span><span>(show.meta[&#39;<\/span><span style=\"color:#489963;\">title<\/span><span>&#39;], show.starts, show.ends))\n<\/span><span>\n<\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">All done<\/span><span>&quot;)\n<\/span><span>\n<\/span><\/code><\/pre>\n"},{"title":"Contributing to Open Source: A Stab at Go","pubDate":"Fri, 28 Jul 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/open-source-a-go-story\/","guid":"https:\/\/blog.carrio.dev\/blog\/open-source-a-go-story\/","description":"<p>In 2018, I had been diving further into Go, a relatively new language at the time from Google. I was interested in taking it a step further and contributing to a well known and professional open source project based in Go from a reputable team that could provide me reviews and advice. As a system admin and software engineer, I was already familiar with the tool <a href=\"https:\/\/www.packer.io\/\">Packer<\/a> from HashiCorp and had used it personally on several occasions. Based on this, I decided to start there.<\/p>\n<h2 id=\"finding-work\">Finding work<\/h2>\n<p>With open source code, and especially in a well maintained project, it is very straightforward to find feature requests and bugs that need help. So, I dug through their <a href=\"https:\/\/github.com\/hashicorp\/packer\/issues\">Issue List on GitHub<\/a>. I found a <a href=\"https:\/\/github.com\/hashicorp\/packer\/issues\/6464\">feature request<\/a> I thought was interesting regarding the <a href=\"https:\/\/www.openstack.org\/\">OpenStack<\/a> integration for Packer.<\/p>\n<blockquote>\n<p>Add support to select most recent source image when name is provided<\/p>\n<\/blockquote>\n<p>This was something that felt very approachable to me, for the following reasons:<\/p>\n<ol>\n<li>An enhancement of existing code<\/li>\n<li>A feature that has been implemented for an existing integration<\/li>\n<\/ol>\n<p>I wasn't at the point of making large architectural decisions around these things with Go, so these provided me certain handrails to getting an initial change request proposal together.<\/p>\n<h2 id=\"planning\">Planning<\/h2>\n<p>I discussed with other members of the community on the issue to determine further requirements and design strategy. After digging through the existing code for OpenStack, I discovered multiple components around this integration. I'll go through each of the parts I had deemed necessary to research to scope out the required changes.<\/p>\n<h3 id=\"researching-the-openstack-api\">Researching the OpenStack API<\/h3>\n<p>The OpenStack API was going to be the primary gateway to implementing this functionality- supposing they did not offer the adequate functionality to implement this it would be a non-starter. As such, I started there, in the <a href=\"https:\/\/developer.openstack.org\/api-ref\">OpenStack API documentation<\/a>.\nThe component responsible for serving images in OpenStack is [glance], an image service where users can upload and also discover assets. The image services support discovering, registering, and retrieving virtual machine images and exposes a REST API for both querying metadata and retrieving images. The <a href=\"https:\/\/developer.openstack.org\/api-ref\/image\/v2\/\">OpenStack image v2 documentation<\/a> covered the available endpoints, including a <a href=\"https:\/\/docs.openstack.org\/api-ref\/image\/v2\/?expanded=list-images-detail#list-images\">List Images API<\/a>.\nThe image API allows for additional query parameters to be passed in to filter and sort results. The important query parameters I identified include <code>limit<\/code>, <code>sort_key<\/code>, and <code>sort_dir<\/code>.<\/p>\n<p>With the following query, it would be possible to search by an image name and return at most one result, which would be the most recent image created with that name:<\/p>\n<pre data-lang=\"text\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-text \"><code class=\"language-text\" data-lang=\"text\"><span>?sort_key=created_at&amp;sort_dir=desc&amp;limit=1\n<\/span><\/code><\/pre>\n<h3 id=\"scoping-out-packer-changes\">Scoping out Packer changes<\/h3>\n<h4 id=\"api-calls\">API calls<\/h4>\n<p>OpenStack support was already built in to Packer, so this was already a solved problem. In terms of scoping out what changes would be necessary here, I primarily wanted to determine whether there was an internal implementation to Packer for calling and handling responses from the OpenStack API or if they were utilizing some library for accomplishing this. I found the <a href=\"http:\/\/gophercloud.io\/\">gophercloud<\/a> package was being used in Packer already, so how I would interact with the API would be through that library. It also meant that if there was not support for the V2 API, I would likely be contributing a change to gophercloud in order to pull that update into Packer to support the OpenStack changes.<\/p>\n<p>My initial review of the gophercloud code led me to believe that the image v2 API was not supported, so I <a href=\"https:\/\/github.com\/gophercloud\/gophercloud\/issues\/1111\">opened an issue on their project<\/a>. In actuality it had implemented both the older compute API that allowed for listing images as well as the new glance service's API, and <code>@jtopjian<\/code> was very helpful in pointing me to the correct code in the library for API v2. This library has a <code>ListOpts<\/code> struct which is passed to marshal the query string dynamically.<\/p>\n<p>Within the Packer issue, I was also getting good feedback around the gophercloud library and its capabilities for the image V2 API. With all of that squared away, I was assured in utilizing the existing library for this new feature.<\/p>\n<h4 id=\"packer-configuration\">Packer configuration<\/h4>\n<p>Another required change to support this functionality would be in the configuration file schema and its handling in the Packer executable. The configuration file would need to provide support for a <code>source_image_filter<\/code> attribute that would allow dynamically generating the query, similar to the <code>source_ami_filter<\/code> implementation for AWS. The configuration file could be advanced and allow for a full <code>ListOpts<\/code> structure to be passed in or simplified with a <code>most_recent_image<\/code> boolean flag. I continued to vocalize my design ideas in the Packer issue, and while waiting on feedback I started off on the solution.<\/p>\n<h3 id=\"implementation\">Implementation<\/h3>\n<p>The start of my work was on the configuration file updates. This would then be passed into the new functionality. After updating the structure, I began work on the <code>image_query.go<\/code> file. This was my first time working with the Reflection API in Go, and most languages provide some mechanism around this and it all falls under the umbrella of metaprogramming. My first pass in the PR was pretty rudimentary, and not having an OpenStack environment limited my ability to run a live test of my changes. All I had to go on were docs.<\/p>\n<h4 id=\"testing\">Testing<\/h4>\n<p>Never forget to test your code. There are very few open source projects I have seen that do not have automated testing and test coverage <em>especially<\/em> those run by corporations. The software needs to work, and stay working. Refactoring should feel safe, and if a breaking change is included it should be detected without anyone manually running your code. With the advent of DevOps and automation came a bright new age for software engineering- and continuous integration in your project ensures that you detect code smells and bugs faster than ever.<\/p>\n<h3 id=\"iteration\">Iteration<\/h3>\n<p>I prefaced this post with an important note: I was not a Go expert. In fact, I'm still not- it's a tool I've used in a few scenarios over the past 5 years but it's never been my primary language in open source or professional work. So, unsurprisingly, I had feedback on my changes. This is a very good thing, because getting feedback on our work is one of the greatest ways we learn. We receive insight into design patterns, functionality we didn't know existed, or new topics we hadn't dove into before. We don't know what we don't know, and someone being there to tell you what those things are is absolutely invaluable.<\/p>\n<p>I went through several iterations of the work over the next <strong>month<\/strong>. <code>@rickard-von-essen<\/code> was immensely helpful during this code review, and I received a lot of support from other team members at Packer throughout the entire process too. It was an absolute joy to work on, and I was very happy with the result. The code hooked into the <code>packer validate<\/code> functionality, worked smoothly with the Image V2 API, applied defensive tactics on inputs, was well tested, and provided clear documentation.<\/p>\n<h3 id=\"the-final-work\">The final work<\/h3>\n<p>For reference, the full PR changes can be found <a href=\"https:\/\/github.com\/hashicorp\/packer\/pull\/6490\/files\">here<\/a>.<\/p>\n<pre data-lang=\"diff\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-diff \"><code class=\"language-diff\" data-lang=\"diff\"><span>commit 70cfafb75c09d5ea54dccffb699b3e487ea7320a\n<\/span><span>Merge: bb319fb1e e2fe5cd77\n<\/span><span>Author: Rickard von Essen &lt;rickard.von.essen@gmail.com&gt;\n<\/span><span>Date:   Thu Aug 23 12:41:06 2018 +0200\n<\/span><span>\n<\/span><span>    Merge pull request #6490 from tcarrio\/most-recent-image-openstack\n<\/span><span>    \n<\/span><span>    OpenStack source image search filter\n<\/span><span>\n<\/span><span>diff --git a\/builder\/openstack\/builder.go b\/builder\/openstack\/builder.go\n<\/span><span>index 2938d67c0..638dcc8ba 100644\n<\/span><span>--- a\/builder\/openstack\/builder.go\n<\/span><span>+++ b\/builder\/openstack\/builder.go\n<\/span><span>@@ -86,6 +86,12 @@ <\/span><span style=\"color:#478c90;\">func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe\n<\/span><span> \t\t\tPrivateKeyFile:       b.config.RunConfig.Comm.SSHPrivateKey,\n<\/span><span> \t\t\tSSHAgentAuth:         b.config.RunConfig.Comm.SSHAgentAuth,\n<\/span><span> \t\t},\n<\/span><span style=\"color:#489963;\">+\t\t&amp;StepSourceImageInfo{\n<\/span><span style=\"color:#489963;\">+\t\t\tSourceImage:      b.config.RunConfig.SourceImage,\n<\/span><span style=\"color:#489963;\">+\t\t\tSourceImageName:  b.config.RunConfig.SourceImageName,\n<\/span><span style=\"color:#489963;\">+\t\t\tSourceImageOpts:  b.config.RunConfig.sourceImageOpts,\n<\/span><span style=\"color:#489963;\">+\t\t\tSourceMostRecent: b.config.SourceImageFilters.MostRecent,\n<\/span><span style=\"color:#489963;\">+\t\t},\n<\/span><span> \t\t&amp;StepCreateVolume{\n<\/span><span> \t\t\tUseBlockStorageVolume:  b.config.UseBlockStorageVolume,\n<\/span><span> \t\t\tSourceImage:            b.config.SourceImage,\n<\/span><span>diff --git a\/builder\/openstack\/run_config.go b\/builder\/openstack\/run_config.go\n<\/span><span>index b98b65ea8..ccc87dffc 100644\n<\/span><span>--- a\/builder\/openstack\/run_config.go\n<\/span><span>+++ b\/builder\/openstack\/run_config.go\n<\/span><span>@@ -4,6 +4,7 @@ <\/span><span style=\"color:#478c90;\">import (\n<\/span><span> \t&quot;errors&quot;\n<\/span><span> \t&quot;fmt&quot;\n<\/span><span> \n<\/span><span style=\"color:#489963;\">+\t&quot;github.com\/gophercloud\/gophercloud\/openstack\/imageservice\/v2\/images&quot;\n<\/span><span> \t&quot;github.com\/hashicorp\/packer\/common\/uuid&quot;\n<\/span><span> \t&quot;github.com\/hashicorp\/packer\/helper\/communicator&quot;\n<\/span><span> \t&quot;github.com\/hashicorp\/packer\/template\/interpolate&quot;\n<\/span><span>@@ -18,21 +19,22 @@ <\/span><span style=\"color:#478c90;\">type RunConfig struct {\n<\/span><span> \tSSHInterface         string              `mapstructure:&quot;ssh_interface&quot;`\n<\/span><span> \tSSHIPVersion         string              `mapstructure:&quot;ssh_ip_version&quot;`\n<\/span><span> \n<\/span><span style=\"color:#b16139;\">-\tSourceImage       string            `mapstructure:&quot;source_image&quot;`\n<\/span><span style=\"color:#b16139;\">-\tSourceImageName   string            `mapstructure:&quot;source_image_name&quot;`\n<\/span><span style=\"color:#b16139;\">-\tFlavor            string            `mapstructure:&quot;flavor&quot;`\n<\/span><span style=\"color:#b16139;\">-\tAvailabilityZone  string            `mapstructure:&quot;availability_zone&quot;`\n<\/span><span style=\"color:#b16139;\">-\tRackconnectWait   bool              `mapstructure:&quot;rackconnect_wait&quot;`\n<\/span><span style=\"color:#b16139;\">-\tFloatingIPNetwork string            `mapstructure:&quot;floating_ip_network&quot;`\n<\/span><span style=\"color:#b16139;\">-\tFloatingIP        string            `mapstructure:&quot;floating_ip&quot;`\n<\/span><span style=\"color:#b16139;\">-\tReuseIPs          bool              `mapstructure:&quot;reuse_ips&quot;`\n<\/span><span style=\"color:#b16139;\">-\tSecurityGroups    []string          `mapstructure:&quot;security_groups&quot;`\n<\/span><span style=\"color:#b16139;\">-\tNetworks          []string          `mapstructure:&quot;networks&quot;`\n<\/span><span style=\"color:#b16139;\">-\tPorts             []string          `mapstructure:&quot;ports&quot;`\n<\/span><span style=\"color:#b16139;\">-\tUserData          string            `mapstructure:&quot;user_data&quot;`\n<\/span><span style=\"color:#b16139;\">-\tUserDataFile      string            `mapstructure:&quot;user_data_file&quot;`\n<\/span><span style=\"color:#b16139;\">-\tInstanceName      string            `mapstructure:&quot;instance_name&quot;`\n<\/span><span style=\"color:#b16139;\">-\tInstanceMetadata  map[string]string `mapstructure:&quot;instance_metadata&quot;`\n<\/span><span style=\"color:#489963;\">+\tSourceImage        string            `mapstructure:&quot;source_image&quot;`\n<\/span><span style=\"color:#489963;\">+\tSourceImageName    string            `mapstructure:&quot;source_image_name&quot;`\n<\/span><span style=\"color:#489963;\">+\tSourceImageFilters ImageFilter       `mapstructure:&quot;source_image_filter&quot;`\n<\/span><span style=\"color:#489963;\">+\tFlavor             string            `mapstructure:&quot;flavor&quot;`\n<\/span><span style=\"color:#489963;\">+\tAvailabilityZone   string            `mapstructure:&quot;availability_zone&quot;`\n<\/span><span style=\"color:#489963;\">+\tRackconnectWait    bool              `mapstructure:&quot;rackconnect_wait&quot;`\n<\/span><span style=\"color:#489963;\">+\tFloatingIPNetwork  string            `mapstructure:&quot;floating_ip_network&quot;`\n<\/span><span style=\"color:#489963;\">+\tFloatingIP         string            `mapstructure:&quot;floating_ip&quot;`\n<\/span><span style=\"color:#489963;\">+\tReuseIPs           bool              `mapstructure:&quot;reuse_ips&quot;`\n<\/span><span style=\"color:#489963;\">+\tSecurityGroups     []string          `mapstructure:&quot;security_groups&quot;`\n<\/span><span style=\"color:#489963;\">+\tNetworks           []string          `mapstructure:&quot;networks&quot;`\n<\/span><span style=\"color:#489963;\">+\tPorts              []string          `mapstructure:&quot;ports&quot;`\n<\/span><span style=\"color:#489963;\">+\tUserData           string            `mapstructure:&quot;user_data&quot;`\n<\/span><span style=\"color:#489963;\">+\tUserDataFile       string            `mapstructure:&quot;user_data_file&quot;`\n<\/span><span style=\"color:#489963;\">+\tInstanceName       string            `mapstructure:&quot;instance_name&quot;`\n<\/span><span style=\"color:#489963;\">+\tInstanceMetadata   map[string]string `mapstructure:&quot;instance_metadata&quot;`\n<\/span><span> \n<\/span><span> \tConfigDrive bool `mapstructure:&quot;config_drive&quot;`\n<\/span><span> \n<\/span><span>@@ -47,6 +49,52 @@ <\/span><span style=\"color:#478c90;\">type RunConfig struct {\n<\/span><span> \t\/\/ Not really used, but here for BC\n<\/span><span> \tOpenstackProvider string `mapstructure:&quot;openstack_provider&quot;`\n<\/span><span> \tUseFloatingIp     bool   `mapstructure:&quot;use_floating_ip&quot;`\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tsourceImageOpts images.ListOpts\n<\/span><span style=\"color:#489963;\">+}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+type ImageFilter struct {\n<\/span><span style=\"color:#489963;\">+\tFilters    ImageFilterOptions `mapstructure:&quot;filters&quot;`\n<\/span><span style=\"color:#489963;\">+\tMostRecent bool               `mapstructure:&quot;most_recent&quot;`\n<\/span><span style=\"color:#489963;\">+}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+type ImageFilterOptions struct {\n<\/span><span style=\"color:#489963;\">+\tName       string   `mapstructure:&quot;name&quot;`\n<\/span><span style=\"color:#489963;\">+\tOwner      string   `mapstructure:&quot;owner&quot;`\n<\/span><span style=\"color:#489963;\">+\tTags       []string `mapstructure:&quot;tags&quot;`\n<\/span><span style=\"color:#489963;\">+\tVisibility string   `mapstructure:&quot;visibility&quot;`\n<\/span><span style=\"color:#489963;\">+}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+func (f *ImageFilterOptions) Empty() bool {\n<\/span><span style=\"color:#489963;\">+\treturn f.Name == &quot;&quot; &amp;&amp; f.Owner == &quot;&quot; &amp;&amp; len(f.Tags) == 0 &amp;&amp; f.Visibility == &quot;&quot;\n<\/span><span style=\"color:#489963;\">+}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+func (f *ImageFilterOptions) Build() (*images.ListOpts, error) {\n<\/span><span style=\"color:#489963;\">+\topts := images.ListOpts{}\n<\/span><span style=\"color:#489963;\">+\t\/\/ Set defaults for status, member_status, and sort\n<\/span><span style=\"color:#489963;\">+\topts.Status = images.ImageStatusActive\n<\/span><span style=\"color:#489963;\">+\topts.MemberStatus = images.ImageMemberStatusAccepted\n<\/span><span style=\"color:#489963;\">+\topts.Sort = &quot;created_at:desc&quot;\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tvar err error\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tif f.Name != &quot;&quot; {\n<\/span><span style=\"color:#489963;\">+\t\topts.Name = f.Name\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\tif f.Owner != &quot;&quot; {\n<\/span><span style=\"color:#489963;\">+\t\topts.Owner = f.Owner\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\tif len(f.Tags) &gt; 0 {\n<\/span><span style=\"color:#489963;\">+\t\topts.Tags = f.Tags\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\tif f.Visibility != &quot;&quot; {\n<\/span><span style=\"color:#489963;\">+\t\tv, err := getImageVisibility(f.Visibility)\n<\/span><span style=\"color:#489963;\">+\t\tif err == nil {\n<\/span><span style=\"color:#489963;\">+\t\t\topts.Visibility = *v\n<\/span><span style=\"color:#489963;\">+\t\t}\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\treturn &amp;opts, err\n<\/span><span> }\n<\/span><span> \n<\/span><span> func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {\n<\/span><span>@@ -75,8 +123,8 @@ <\/span><span style=\"color:#478c90;\">func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {\n<\/span><span> \t\t}\n<\/span><span> \t}\n<\/span><span> \n<\/span><span style=\"color:#b16139;\">-\tif c.SourceImage == &quot;&quot; &amp;&amp; c.SourceImageName == &quot;&quot; {\n<\/span><span style=\"color:#b16139;\">-\t\terrs = append(errs, errors.New(&quot;Either a source_image or a source_image_name must be specified&quot;))\n<\/span><span style=\"color:#489963;\">+\tif c.SourceImage == &quot;&quot; &amp;&amp; c.SourceImageName == &quot;&quot; &amp;&amp; c.SourceImageFilters.Filters.Empty() {\n<\/span><span style=\"color:#489963;\">+\t\terrs = append(errs, errors.New(&quot;Either a source_image, a source_image_name, or source_image_filter must be specified&quot;))\n<\/span><span> \t} else if len(c.SourceImage) &gt; 0 &amp;&amp; len(c.SourceImageName) &gt; 0 {\n<\/span><span> \t\terrs = append(errs, errors.New(&quot;Only a source_image or a source_image_name can be specified, not both.&quot;))\n<\/span><span> \t}\n<\/span><span>@@ -111,5 +159,34 @@ <\/span><span style=\"color:#478c90;\">func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {\n<\/span><span> \t\t}\n<\/span><span> \t}\n<\/span><span> \n<\/span><span style=\"color:#489963;\">+\t\/\/ if neither ID or image name is provided outside the filter, build the filter\n<\/span><span style=\"color:#489963;\">+\tif len(c.SourceImage) == 0 &amp;&amp; len(c.SourceImageName) == 0 {\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\t\tlistOpts, filterErr := c.SourceImageFilters.Filters.Build()\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\t\tif filterErr != nil {\n<\/span><span style=\"color:#489963;\">+\t\t\terrs = append(errs, filterErr)\n<\/span><span style=\"color:#489963;\">+\t\t}\n<\/span><span style=\"color:#489963;\">+\t\tc.sourceImageOpts = *listOpts\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span> \treturn errs\n<\/span><span> }\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\/\/ Retrieve the specific ImageVisibility using the exported const from images\n<\/span><span style=\"color:#489963;\">+func getImageVisibility(visibility string) (*images.ImageVisibility, error) {\n<\/span><span style=\"color:#489963;\">+\tvisibilities := [...]images.ImageVisibility{\n<\/span><span style=\"color:#489963;\">+\t\timages.ImageVisibilityPublic,\n<\/span><span style=\"color:#489963;\">+\t\timages.ImageVisibilityPrivate,\n<\/span><span style=\"color:#489963;\">+\t\timages.ImageVisibilityCommunity,\n<\/span><span style=\"color:#489963;\">+\t\timages.ImageVisibilityShared,\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tfor _, v := range visibilities {\n<\/span><span style=\"color:#489963;\">+\t\tif string(v) == visibility {\n<\/span><span style=\"color:#489963;\">+\t\t\treturn &amp;v, nil\n<\/span><span style=\"color:#489963;\">+\t\t}\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\treturn nil, fmt.Errorf(&quot;Not a valid visibility: %s&quot;, visibility)\n<\/span><span style=\"color:#489963;\">+}\n<\/span><span>diff --git a\/builder\/openstack\/run_config_test.go b\/builder\/openstack\/run_config_test.go\n<\/span><span>index 6ce0cf602..f660a4e82 100644\n<\/span><span>--- a\/builder\/openstack\/run_config_test.go\n<\/span><span>+++ b\/builder\/openstack\/run_config_test.go\n<\/span><span>@@ -4,7 +4,9 @@ <\/span><span style=\"color:#478c90;\">import (\n<\/span><span> \t&quot;os&quot;\n<\/span><span> \t&quot;testing&quot;\n<\/span><span> \n<\/span><span style=\"color:#489963;\">+\t&quot;github.com\/gophercloud\/gophercloud\/openstack\/imageservice\/v2\/images&quot;\n<\/span><span> \t&quot;github.com\/hashicorp\/packer\/helper\/communicator&quot;\n<\/span><span style=\"color:#489963;\">+\t&quot;github.com\/mitchellh\/mapstructure&quot;\n<\/span><span> )\n<\/span><span> \n<\/span><span> func init() {\n<\/span><span>@@ -127,3 +129,84 @@ <\/span><span style=\"color:#478c90;\">func TestRunConfigPrepare_FloatingIPPoolCompat(t *testing.T) {\n<\/span><span> \t\tt.Fatalf(&quot;invalid value: %s&quot;, c.FloatingIPNetwork)\n<\/span><span> \t}\n<\/span><span> }\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\/\/ This test case confirms that only allowed fields will be set to values\n<\/span><span style=\"color:#489963;\">+\/\/ The checked values are non-nil for their target type\n<\/span><span style=\"color:#489963;\">+func TestBuildImageFilter(t *testing.T) {\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tfilters := ImageFilterOptions{\n<\/span><span style=\"color:#489963;\">+\t\tName:       &quot;Ubuntu 16.04&quot;,\n<\/span><span style=\"color:#489963;\">+\t\tVisibility: &quot;public&quot;,\n<\/span><span style=\"color:#489963;\">+\t\tOwner:      &quot;1234567890&quot;,\n<\/span><span style=\"color:#489963;\">+\t\tTags:       []string{&quot;prod&quot;, &quot;ready&quot;},\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tlistOpts, err := filters.Build()\n<\/span><span style=\"color:#489963;\">+\tif err != nil {\n<\/span><span style=\"color:#489963;\">+\t\tt.Errorf(&quot;Building filter failed with: %s&quot;, err)\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tif listOpts.Name != &quot;Ubuntu 16.04&quot; {\n<\/span><span style=\"color:#489963;\">+\t\tt.Errorf(&quot;Name did not build correctly: %s&quot;, listOpts.Name)\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tif listOpts.Visibility != images.ImageVisibilityPublic {\n<\/span><span style=\"color:#489963;\">+\t\tt.Errorf(&quot;Visibility did not build correctly: %s&quot;, listOpts.Visibility)\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tif listOpts.Owner != &quot;1234567890&quot; {\n<\/span><span style=\"color:#489963;\">+\t\tt.Errorf(&quot;Owner did not build correctly: %s&quot;, listOpts.Owner)\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+func TestBuildBadImageFilter(t *testing.T) {\n<\/span><span style=\"color:#489963;\">+\tfilterMap := map[string]interface{}{\n<\/span><span style=\"color:#489963;\">+\t\t&quot;limit&quot;:    &quot;3&quot;,\n<\/span><span style=\"color:#489963;\">+\t\t&quot;size_min&quot;: &quot;25&quot;,\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tfilters := ImageFilterOptions{}\n<\/span><span style=\"color:#489963;\">+\tmapstructure.Decode(filterMap, &amp;filters)\n<\/span><span style=\"color:#489963;\">+\tlistOpts, err := filters.Build()\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tif err != nil {\n<\/span><span style=\"color:#489963;\">+\t\tt.Errorf(&quot;Error returned processing image filter: %s&quot;, err.Error())\n<\/span><span style=\"color:#489963;\">+\t\treturn \/\/ we cannot trust listOpts to not cause unexpected behaviour\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tif listOpts.Limit == filterMap[&quot;limit&quot;] {\n<\/span><span style=\"color:#489963;\">+\t\tt.Errorf(&quot;Limit was parsed into ListOpts: %d&quot;, listOpts.Limit)\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tif listOpts.SizeMin != 0 {\n<\/span><span style=\"color:#489963;\">+\t\tt.Errorf(&quot;SizeMin was parsed into ListOpts: %d&quot;, listOpts.SizeMin)\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tif listOpts.Sort != &quot;created_at:desc&quot; {\n<\/span><span style=\"color:#489963;\">+\t\tt.Errorf(&quot;Sort was not applied: %s&quot;, listOpts.Sort)\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tif !filters.Empty() {\n<\/span><span style=\"color:#489963;\">+\t\tt.Errorf(&quot;The filters should be empty due to lack of input&quot;)\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\/\/ Tests that the Empty method on ImageFilterOptions works as expected\n<\/span><span style=\"color:#489963;\">+func TestImageFiltersEmpty(t *testing.T) {\n<\/span><span style=\"color:#489963;\">+\tfilledFilters := ImageFilterOptions{\n<\/span><span style=\"color:#489963;\">+\t\tName:       &quot;Ubuntu 16.04&quot;,\n<\/span><span style=\"color:#489963;\">+\t\tVisibility: &quot;public&quot;,\n<\/span><span style=\"color:#489963;\">+\t\tOwner:      &quot;1234567890&quot;,\n<\/span><span style=\"color:#489963;\">+\t\tTags:       []string{&quot;prod&quot;, &quot;ready&quot;},\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tif filledFilters.Empty() {\n<\/span><span style=\"color:#489963;\">+\t\tt.Errorf(&quot;Expected filled filters to be non-empty: %v&quot;, filledFilters)\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\temptyFilters := ImageFilterOptions{}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tif !emptyFilters.Empty() {\n<\/span><span style=\"color:#489963;\">+\t\tt.Errorf(&quot;Expected default filter to be empty: %v&quot;, emptyFilters)\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+}\n<\/span><span>diff --git a\/builder\/openstack\/step_run_source_server.go b\/builder\/openstack\/step_run_source_server.go\n<\/span><span>index e56218467..6bbb40eba 100644\n<\/span><span>--- a\/builder\/openstack\/step_run_source_server.go\n<\/span><span>+++ b\/builder\/openstack\/step_run_source_server.go\n<\/span><span>@@ -76,6 +76,12 @@ <\/span><span style=\"color:#478c90;\">func (s *StepRunSourceServer) Run(_ context.Context, state multistep.StateBag) m\n<\/span><span> \t\tServiceClient:    computeClient,\n<\/span><span> \t\tMetadata:         s.InstanceMetadata,\n<\/span><span> \t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\t\/\/ check if image filter returned a source image ID and replace\n<\/span><span style=\"color:#489963;\">+\tif imageID, ok := state.GetOk(&quot;source_image&quot;); ok {\n<\/span><span style=\"color:#489963;\">+\t\tserverOpts.ImageRef = imageID.(string)\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span> \tvar serverOptsExt servers.CreateOptsBuilder\n<\/span><span> \n<\/span><span> \t\/\/ Create root volume in the Block Storage service if required.\n<\/span><span>diff --git a\/builder\/openstack\/step_source_image_info.go b\/builder\/openstack\/step_source_image_info.go\n<\/span><span>new file mode 100644\n<\/span><span>index 000000000..6cf3500ae\n<\/span><span>--- \/dev\/null\n<\/span><span>+++ b\/builder\/openstack\/step_source_image_info.go\n<\/span><span>@@ -0,0 +1,76 @@\n<\/span><span style=\"color:#489963;\">+package openstack\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+import (\n<\/span><span style=\"color:#489963;\">+\t&quot;context&quot;\n<\/span><span style=\"color:#489963;\">+\t&quot;fmt&quot;\n<\/span><span style=\"color:#489963;\">+\t&quot;log&quot;\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\t&quot;github.com\/gophercloud\/gophercloud\/openstack\/imageservice\/v2\/images&quot;\n<\/span><span style=\"color:#489963;\">+\t&quot;github.com\/gophercloud\/gophercloud\/pagination&quot;\n<\/span><span style=\"color:#489963;\">+\t&quot;github.com\/hashicorp\/packer\/helper\/multistep&quot;\n<\/span><span style=\"color:#489963;\">+\t&quot;github.com\/hashicorp\/packer\/packer&quot;\n<\/span><span style=\"color:#489963;\">+)\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+type StepSourceImageInfo struct {\n<\/span><span style=\"color:#489963;\">+\tSourceImage      string\n<\/span><span style=\"color:#489963;\">+\tSourceImageName  string\n<\/span><span style=\"color:#489963;\">+\tSourceImageOpts  images.ListOpts\n<\/span><span style=\"color:#489963;\">+\tSourceMostRecent bool\n<\/span><span style=\"color:#489963;\">+}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+func (s *StepSourceImageInfo) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {\n<\/span><span style=\"color:#489963;\">+\tconfig := state.Get(&quot;config&quot;).(Config)\n<\/span><span style=\"color:#489963;\">+\tui := state.Get(&quot;ui&quot;).(packer.Ui)\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tif s.SourceImage != &quot;&quot; || s.SourceImageName != &quot;&quot; {\n<\/span><span style=\"color:#489963;\">+\t\treturn multistep.ActionContinue\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tclient, err := config.imageV2Client()\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tlog.Printf(&quot;Using Image Filters %v&quot;, s.SourceImageOpts)\n<\/span><span style=\"color:#489963;\">+\timage := &amp;images.Image{}\n<\/span><span style=\"color:#489963;\">+\terr = images.List(client, s.SourceImageOpts).EachPage(func(page pagination.Page) (bool, error) {\n<\/span><span style=\"color:#489963;\">+\t\ti, err := images.ExtractImages(page)\n<\/span><span style=\"color:#489963;\">+\t\tif err != nil {\n<\/span><span style=\"color:#489963;\">+\t\t\treturn false, err\n<\/span><span style=\"color:#489963;\">+\t\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\t\tswitch len(i) {\n<\/span><span style=\"color:#489963;\">+\t\tcase 1:\n<\/span><span style=\"color:#489963;\">+\t\t\t*image = i[0]\n<\/span><span style=\"color:#489963;\">+\t\t\treturn false, nil\n<\/span><span style=\"color:#489963;\">+\t\tdefault:\n<\/span><span style=\"color:#489963;\">+\t\t\tif s.SourceMostRecent {\n<\/span><span style=\"color:#489963;\">+\t\t\t\t*image = i[0]\n<\/span><span style=\"color:#489963;\">+\t\t\t\treturn false, nil\n<\/span><span style=\"color:#489963;\">+\t\t\t}\n<\/span><span style=\"color:#489963;\">+\t\t\treturn false, fmt.Errorf(\n<\/span><span style=\"color:#489963;\">+\t\t\t\t&quot;Your query returned more than one result. Please try a more specific search, or set most_recent to true. Search filters: %v&quot;,\n<\/span><span style=\"color:#489963;\">+\t\t\t\ts.SourceImageOpts)\n<\/span><span style=\"color:#489963;\">+\t\t}\n<\/span><span style=\"color:#489963;\">+\t})\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tif err != nil {\n<\/span><span style=\"color:#489963;\">+\t\terr := fmt.Errorf(&quot;Error querying image: %s&quot;, err)\n<\/span><span style=\"color:#489963;\">+\t\tstate.Put(&quot;error&quot;, err)\n<\/span><span style=\"color:#489963;\">+\t\tui.Error(err.Error())\n<\/span><span style=\"color:#489963;\">+\t\treturn multistep.ActionHalt\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tif image.ID == &quot;&quot; {\n<\/span><span style=\"color:#489963;\">+\t\terr := fmt.Errorf(&quot;No image was found matching filters: %v&quot;, s.SourceImageOpts)\n<\/span><span style=\"color:#489963;\">+\t\tstate.Put(&quot;error&quot;, err)\n<\/span><span style=\"color:#489963;\">+\t\tui.Error(err.Error())\n<\/span><span style=\"color:#489963;\">+\t\treturn multistep.ActionHalt\n<\/span><span style=\"color:#489963;\">+\t}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tui.Message(fmt.Sprintf(&quot;Found Image ID: %s&quot;, image.ID))\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+\tstate.Put(&quot;source_image&quot;, image.ID)\n<\/span><span style=\"color:#489963;\">+\treturn multistep.ActionContinue\n<\/span><span style=\"color:#489963;\">+}\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+func (s *StepSourceImageInfo) Cleanup(state multistep.StateBag) {\n<\/span><span style=\"color:#489963;\">+\t\/\/ No cleanup required for backout\n<\/span><span style=\"color:#489963;\">+}\n<\/span><span>diff --git a\/website\/source\/docs\/builders\/openstack.html.md b\/website\/source\/docs\/builders\/openstack.html.md\n<\/span><span>index a05ce3d67..28566bd0d 100644\n<\/span><span>--- a\/website\/source\/docs\/builders\/openstack.html.md\n<\/span><span>+++ b\/website\/source\/docs\/builders\/openstack.html.md\n<\/span><span>@@ -70,6 +70,11 @@ <\/span><span style=\"color:#478c90;\">builder.\n<\/span><span>     is an alternative way of providing `source_image` and only either of them\n<\/span><span>     can be specified.\n<\/span><span> \n<\/span><span style=\"color:#489963;\">+-   `source_image_filter` (map) - The search filters for determining the base\n<\/span><span style=\"color:#489963;\">+    image to use. This is an alternative way of providing `source_image` and\n<\/span><span style=\"color:#489963;\">+    only one of these methods can be used. `source_image` will override the\n<\/span><span style=\"color:#489963;\">+    filters.\n<\/span><span style=\"color:#489963;\">+\n<\/span><span> -   `username` or `user_id` (string) - The username or id used to connect to\n<\/span><span>     the OpenStack service. If not specified, Packer will use the environment\n<\/span><span>     variable `OS_USERNAME` or `OS_USERID`, if set. This is not required if\n<\/span><span>@@ -153,7 +158,7 @@ <\/span><span style=\"color:#478c90;\">builder.\n<\/span><span>     Defaults to false.\n<\/span><span> \n<\/span><span> -   `region` (string) - The name of the region, such as &quot;DFW&quot;, in which to\n<\/span><span style=\"color:#b16139;\">-    launch the server to create the AMI. If not specified, Packer will use the\n<\/span><span style=\"color:#489963;\">+    launch the server to create the image. If not specified, Packer will use the\n<\/span><span>     environment variable `OS_REGION_NAME`, if set.\n<\/span><span> \n<\/span><span> -   `reuse_ips` (boolean) - Whether or not to attempt to reuse existing\n<\/span><span>@@ -166,6 +171,48 @@ <\/span><span style=\"color:#478c90;\">builder.\n<\/span><span> -   `security_groups` (array of strings) - A list of security groups by name to\n<\/span><span>     add to this instance.\n<\/span><span> \n<\/span><span style=\"color:#489963;\">+-   `source_image_filter` (object) - Filters used to populate filter options.\n<\/span><span style=\"color:#489963;\">+    Example:\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+    ``` json\n<\/span><span style=\"color:#489963;\">+    {\n<\/span><span style=\"color:#489963;\">+        &quot;source_image_filter&quot;: {\n<\/span><span style=\"color:#489963;\">+            &quot;filters&quot;: {\n<\/span><span style=\"color:#489963;\">+                &quot;name&quot;: &quot;ubuntu-16.04&quot;,\n<\/span><span style=\"color:#489963;\">+                &quot;visibility&quot;: &quot;protected&quot;,\n<\/span><span style=\"color:#489963;\">+                &quot;owner&quot;: &quot;d1a588cf4b0743344508dc145649372d1&quot;,\n<\/span><span style=\"color:#489963;\">+                &quot;tags&quot;: [&quot;prod&quot;, &quot;ready&quot;]\n<\/span><span style=\"color:#489963;\">+            },\n<\/span><span style=\"color:#489963;\">+            &quot;most_recent&quot;: true\n<\/span><span style=\"color:#489963;\">+        }\n<\/span><span style=\"color:#489963;\">+    }\n<\/span><span style=\"color:#489963;\">+    ```\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+    This selects the most recent production Ubuntu 16.04 shared to you by the given owner.\n<\/span><span style=\"color:#489963;\">+    NOTE: This will fail unless *exactly* one image is returned, or `most_recent` is set to true.\n<\/span><span style=\"color:#489963;\">+    In the example of multiple returned images, `most_recent` will cause this to succeed by selecting\n<\/span><span style=\"color:#489963;\">+    the newest image of the returned images.\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+    -   `filters` (map of strings) - filters used to select a `source_image`.\n<\/span><span style=\"color:#489963;\">+        NOTE: This will fail unless *exactly* one image is returned, or `most_recent` is set to true.\n<\/span><span style=\"color:#489963;\">+        Of the filters described in [ImageService](https:\/\/developer.openstack.org\/api-ref\/image\/v2\/), the following\n<\/span><span style=\"color:#489963;\">+        are valid:\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+        - name (string)\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+        - owner (string)\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+        - tags (array of strings)\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+        - visibility (string)\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+    -   `most_recent` (boolean) - Selects the newest created image when true.\n<\/span><span style=\"color:#489963;\">+        This is most useful for selecting a daily distro build.\n<\/span><span style=\"color:#489963;\">+\n<\/span><span style=\"color:#489963;\">+    You may set use this in place of `source_image` If `source_image_filter` is provided\n<\/span><span style=\"color:#489963;\">+    alongside `source_image`, the `source_image` will override the filter. The filter\n<\/span><span style=\"color:#489963;\">+    will not be used in this case.\n<\/span><span style=\"color:#489963;\">+\n<\/span><span> -   `ssh_interface` (string) - The type of interface to connect via SSH. Values\n<\/span><span>     useful for Rackspace are &quot;public&quot; or &quot;private&quot;, and the default behavior is\n<\/span><span>     to connect via whichever is returned first from the OpenStack API.\n<\/span><\/code><\/pre>\n<!-- References -->\n"},{"title":"An Observability Starter","pubDate":"Wed, 26 Jul 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/observability-starter\/","guid":"https:\/\/blog.carrio.dev\/blog\/observability-starter\/","description":"<p>Observability refers to how well the state of a system can be understood by external outputs. When a system is more observable, you can more quickly identify root causes of performance issues, business logic bugs, and more. In the software engineering space, Application Performance Monitoring (APM) tools help in assisting in the overall observability of a software stack. The ecosystem as a whole began to evolve as distributed computing gained popularity, monoliths were broken up into microservices, and horizontal pod autoscalers were introduced to Kubernetes. New tools around tracking metrics in your applications and distributed tracing across service-to-service communication has surfaced over the years, with some larger players at the forefront of the open source space such as <a href=\"https:\/\/opentelemetry.io\/\">OpenTelemetry<\/a>, <a href=\"https:\/\/github.com\/statsd\/statsd\">StatsD<\/a>, and <a href=\"https:\/\/openmetrics.io\/\">OpenMetrics<\/a>.\nA big part of these technologies is also collecting, indexing, and presenting them to users. In this regard, there are tons of commercial private and open source solutions, including Prometheus, Datadog, Dynatrace, and more. These tools capture observability signals from across your servers, network traffic, application code, and more to provide you as much insight into your code as possible. Some have core features that give them an edge over the rest of the market, such as Dynatrace's AI-powered root cause analysis engine or Datadog's user-friendly dashboarding and extensible generated metrics tooling. Some open source options like Zipkin support OpenTelemetry and allow you to quickly aggregate traces but do not support other observability constructs like logs or metrics.<\/p>\n<h2 id=\"what-are-logs\">What are Logs<\/h2>\n<p>These are probably the most familiar of all of the observability constructs to any developer. From the most basic starter for any language, the Hello World, you are printing a string out to the console, thus generating a log. Logging in observability is a powerful too for understanding various decisions and state in a system. It can often be more expensive to track all logs compared to metrics and traces, particularly if context is injected into every log line. However, in combination with context, logs can serve as a vital tool in understanding which actions were taken within the context of a single trace. Most observability tools that support logs and traces will allow you to go from a log message with trace context to the specific trace in the system, as well as the opposite; allowing you to visualize all log messages related to a trace ID. Beyond this, they often boil down to just a string in the console, with perhaps some formal structure using JSON so you can provide not just a single message but additional context like error names and description, component names, and more.<\/p>\n<h2 id=\"what-are-metrics\">What are Metrics<\/h2>\n<p>Metrics are numeric aggregations about your application or infrastructure. They can tell you information about the number of requests to your web service over a time period or a statistical breakdown of median, minimum, and maximum of data sets for latency. These metrics can be used to measure all sorts of information about your systems, and are often a cost-effective way of doing so. You can use metrics around your application and requests to build the foundation of basic signals in your system, and all of the <a href=\"https:\/\/sre.google\/sre-book\/monitoring-distributed-systems\/#xref_monitoring_golden-signals\">four golden signals of monitoring in the Google Site Reliability Engineering book<\/a> can utilize metrics to easily capture these. Between the statistical measurement of request <em>Latency<\/em>, the total <em>Traffic<\/em> to an application with request counts, the number of <em>Errors<\/em> that are occurring, and the <em>Saturation<\/em> of an application or database, metrics either directly provide and assist in deducing these signals about your system. They can often be quite simple to implement internally as well (see StatsD).<\/p>\n<h3 id=\"statsd\">StatsD<\/h3>\n<p>StatsD is an open source project originally released by Etsy, which is a NodeJS service and accompanying specification for simple and powerful metric collection. StatsD clients are typically lightweight, requiring only some configuration for a target host and<\/p>\n<h2 id=\"what-are-traces\">What are Traces<\/h2>\n<p>Traces is a short-hand reference to distributed traces, which are graph data structures backed by some specification (OpenTelemetry, OpenTracing) that allow construct metadata about anything from database queries, HTTP requests, network calls and methods executing in your codebase. Any code execution, synchronous or asynchronous, can be visualized as a graph of spans. Spans are like the nodes of a graph, and by nature of the graph data structure there is no requirement to have a single parent node like in a tree. All spans have an implicit duration as a result of the span's start time and end time. They also support naming of the resource and the operation being performed. These constructs collectively allow you to build a graph that effectively describes various types of network, software, and hardware actions being taken, information about them, and then construct visualizations of these that elegantly portray these distributed traces across systems or calculate metrics pertaining to various types of operations. Tracing is a powerful tool in the belt of any software engineer supporting their software in a production system.<\/p>\n<h3 id=\"opentelemetry\">OpenTelemetry<\/h3>\n<p>This project defines standards around various observability constructs, including distributing tracing, metrics, and logging. In this way, OpenTelemetry provides a superset of functionality of various tools that preceded its release such as OpenCensus and OpenTracing. As a newer project, not all languages have stable releases, and certain features in the standard have progressed further than others. However, the project continues to gain traction as the open, vendor-agnostic solution for observability.<\/p>\n<h3 id=\"opentracing\">OpenTracing<\/h3>\n<p>This is an open specification which provides support for distributed tracing exclusively. It has since been superseded by OpenTelemetry, but is still supported and in use by certain vendors in the ecosystem.<\/p>\n<h3 id=\"what-metadata-do-i-include\">What Metadata Do I Include<\/h3>\n<p>There are some best practices defined, such as <a href=\"https:\/\/opentelemetry.io\/docs\/concepts\/semantic-conventions\/\">OpenTelemetry's Semantic Conventions<\/a> and <a href=\"https:\/\/www.elastic.co\/guide\/en\/ecs\/current\/index.html\">Elastic's Common Schema<\/a>, which help to ensure consistent usage of span tags and other metadata on these spans based on their domain.<\/p>\n<p>Let's take for example a span generated for an HTTP client's request:\n&gt; What HTTP method is the call?<br \/>\n&gt; What headers or how many?<br \/>\n&gt; What is the size of the payload?<\/p>\n<p>These standards help you define the metadata that answers these questions on the span in a way that's consistent across systems.<\/p>\n<h2 id=\"vendors\">Vendors<\/h2>\n<blockquote>\n<p>\u26a0\ufe0f More to come here<\/p>\n<\/blockquote>\n<h3 id=\"datadog\">Datadog<\/h3>\n<p>Datadog's dd-trace libraries are based on OpenTracing, and the clients in the package typically implement the OpenTracing client interface. OpenTracing itself is a deprecated project, but Datadog continues to base its tracing capabilities on this system. The backend for Datadog is proprietary, but their agents and dd-trace clients are all open source.<\/p>\n<h3 id=\"dynatrace\">Dynatrace<\/h3>\n<p>Dynatrace is an APM company which centers around its AI-powered root cause analysis engine. It participated in the original inception of OpenTelemetry as a new direction for observability bringing its expertise on distributed tracing. Another core product feature is the OneAgent, which is an executable you can install on any server and it will automatically handle configuring trace and log instrumentation regardless of the system or software*.<\/p>\n"},{"title":"Recovering Access to Synology DSM","pubDate":"Mon, 24 Jul 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/synology-dsm-access-recovery\/","guid":"https:\/\/blog.carrio.dev\/blog\/synology-dsm-access-recovery\/","description":"<p>I'll paint the landscape. Suppose you happened to have just applied some new permissions in your system an the effort to minimize permissions and otherwise improve the security footprint of your Synology NAS. You fly through rules on AFS, NFS, rsync, DSM, FTP- knocking off these services that you don't use (or don't think you do). Now your system has every user blocked from DSM by default. So what was DSM again?<\/p>\n<p>Oh, right. It's the web UI for your Synology NAS. You're locked out and can't administrate your server anymore. What do you do? Well, there are a couple of options depending on what access you have left. The worst case scenario is<\/p>\n<h2 id=\"update-your-dsm-permissions\">Update your DSM permissions<\/h2>\n<p>If you still have SSH permissions and sudo privileges on the user, you can take this approach.<\/p>\n<p>First, connect to your Synology NAS via SSH:<\/p>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#b16139;\">ssh<\/span><span> username@synology_hostname\n<\/span><\/code><\/pre>\n<p>Next, get the user ID of your user by username:<\/p>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#b16139;\">id<\/span><span> username\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\"># uid=1026(username) gid=100(users) groups=100(users),101(administrators)\n<\/span><\/code><\/pre>\n<p>You'll have to take note of the ID (in the command output as <code>uid=$id($username)<\/code>, e.g. 1026).<\/p>\n<p>The next step is modifying the SQLite database. Synology maintains its application-level privileges in a SQLite database at <code>\/etc\/synoappprvilege.db<\/code>. You can modify this in order to provide yourself permissions. Just to be safe, make a copy of the database. In case you decide to truncate a table.<\/p>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#5f6d64;\"># copies the file to the same name with a `.bak` extension\n<\/span><span style=\"color:#b16139;\">sudo<\/span><span> cp \/etc\/synoappprivilege.db{,.bak}\n<\/span><\/code><\/pre>\n<p>Now open the database using<\/p>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#b16139;\">sudo<\/span><span> sqlite3 \/etc\/synoappprivilege.db\n<\/span><span style=\"color:#5f6d64;\"># SQLite version 3.40.0 2022-11-16 12:10:08\n<\/span><span style=\"color:#5f6d64;\"># Enter &quot;.help&quot; for usage hints.\n<\/span><span style=\"color:#5f6d64;\"># sqlite&gt;\n<\/span><\/code><\/pre>\n<p>The table we'll need to update is <code>AppPrivRule<\/code>. I prefer to double-check all commands before I run them, especially for things like modifying a database. The SQL <code>INSERT<\/code> query itself relies on the structure of your <code>VALUES<\/code> matching that of the table schema. You can output the table schema in SQLite by calling:<\/p>\n<pre data-lang=\"sql\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-sql \"><code class=\"language-sql\" data-lang=\"sql\"><span>PRAGMA table_info(AppPrivRule);\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\">-- 0|Type|INTEGER|0||0\n<\/span><span style=\"color:#5f6d64;\">-- 1|ID|INTEGER|0||0\n<\/span><span style=\"color:#5f6d64;\">-- 2|App|varchar(50)|0||0\n<\/span><span style=\"color:#5f6d64;\">-- 3|AllowIP|TEXT|0||0\n<\/span><span style=\"color:#5f6d64;\">-- 4|AllowIPStd|TEXT|0||0\n<\/span><span style=\"color:#5f6d64;\">-- 5|DenyIP|TEXT|0||0\n<\/span><span style=\"color:#5f6d64;\">-- 6|DenyIPStd|TEXT|0||0\n<\/span><\/code><\/pre>\n<p>So the syntax in this case for inserting into the table will be <code>INSERT INTO AppPrivRule VALUES (Type: INTEGER, ID: INTEGER, App: VARCHAR(50), AllowIP: TEXT, AllowIPStd: TEXT, DenyIP: TEXT, DenyIPStd: TEXT);<\/code><\/p>\n<p>The permission to access DSM (at the time of writing) was <code>'SYNO.Desktop'<\/code>.<\/p>\n<p>As such, you'd run the following query (to allow all IPs, block none, for the $id from earlier):<\/p>\n<pre data-lang=\"sql\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-sql \"><code class=\"language-sql\" data-lang=\"sql\"><span style=\"color:#55859b;\">INSERT INTO<\/span><span> AppPrivRule\n<\/span><span style=\"color:#55859b;\">VALUES<\/span><span> (\n<\/span><span>  <\/span><span style=\"color:#9f713c;\">0<\/span><span>,                                         <\/span><span style=\"color:#5f6d64;\">-- Type\n<\/span><span>  <\/span><span style=\"color:#9f713c;\">1026<\/span><span>,                                      <\/span><span style=\"color:#5f6d64;\">-- ID\n<\/span><span>  &#39;<\/span><span style=\"color:#489963;\">SYNO.Desktop<\/span><span>&#39;,                            <\/span><span style=\"color:#5f6d64;\">-- App\n<\/span><span>  &#39;<\/span><span style=\"color:#489963;\">0.0.0.0<\/span><span>&#39;,                                 <\/span><span style=\"color:#5f6d64;\">-- AllowIP\n<\/span><span>  &#39;<\/span><span style=\"color:#489963;\">0000:0000:0000:0000:0000:FFFF:0000:0000<\/span><span>&#39;, <\/span><span style=\"color:#5f6d64;\">-- AllowIPStd\n<\/span><span>  &#39;&#39;,                                        <\/span><span style=\"color:#5f6d64;\">-- DenyIP\n<\/span><span>  &#39;&#39;                                         <\/span><span style=\"color:#5f6d64;\">-- DenyIPStd\n<\/span><span>);\n<\/span><\/code><\/pre>\n<p>At this point you should be able to go through your usual login flow into the DSM web UI.<\/p>\n<h2 id=\"reset-your-nas-a-destructive-action\">Reset your NAS (a destructive action)<\/h2>\n<p>Synology already provides a guide on this which you can find <a href=\"https:\/\/kb.synology.com\/en-us\/DSM\/tutorial\/How_do_I_log_in_if_I_forgot_the_admin_password\">here<\/a>.<\/p>\n<!-- References -->\n"},{"title":"Codility :: Sparse Integer Decomposition","pubDate":"Fri, 21 Jul 2023 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/sparse-integer-decomposition\/","guid":"https:\/\/blog.carrio.dev\/blog\/sparse-integer-decomposition\/","description":"<p>Awhile back I had worked on some Codility exercises, one of which was this case for sparse integer decomposition. This solution ended up snagging me a top 5 percentile in performance and I figured I would share the approach.<\/p>\n<h2 id=\"the-task\">The Task<\/h2>\n<p>A non-negative integer N is called sparse if its binary representation does not contain two consecutive bits set to 1. For example, 41 is sparse, because its binary representation is \"101001\" and it does not contain two consecutive 1s. On the other hand, 26 is not sparse, because its binary representation is \"11010\" and it contains two consecutive 1s.<\/p>\n<p>Two non-negative integers P and Q are called a sparse decomposition of integer N if P and Q are sparse and N = P + Q.<\/p>\n<p>For example:<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>    8 and 18 are a sparse decomposition of 26 (binary representation of 8 is &quot;1000&quot;, binary representation of 18 is &quot;10010&quot;);\n<\/span><span>    9 and 17 are a sparse decomposition of 26 (binary representation of 9 is &quot;1001&quot;, binary representation of 17 is &quot;10001&quot;);\n<\/span><span>    2 and 24 are not a sparse decomposition of 26; though 2 + 24 = 26, the binary representation of 24 is &quot;11000&quot;, which is not sparse.\n<\/span><\/code><\/pre>\n<p>Write a function:<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>def solution(N)\n<\/span><\/code><\/pre>\n<p>that, given a non-negative integer N, returns any integer that is one part of a sparse decomposition of N. The function should return \u22121 if there is no sparse decomposition of N.<\/p>\n<p>For example, given N = 26 the function may return 8, 9, 17 or 18, as explained in the example above. All other possible results for N = 26 are 5, 10, 16 and 21.<\/p>\n<p>Write an efficient algorithm for the following assumptions:<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>    N is an integer within the range [0..1,000,000,000].\n<\/span><\/code><\/pre>\n<h2 id=\"the-solution\">The Solution<\/h2>\n<pre data-lang=\"python\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-python \"><code class=\"language-python\" data-lang=\"python\"><span style=\"color:#55859b;\">from <\/span><span>math <\/span><span style=\"color:#55859b;\">import <\/span><span>ceil, log2\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">def <\/span><span style=\"color:#478c90;\">calculate_alternating_mask<\/span><span>(<\/span><span style=\"color:#b16139;\">value<\/span><span>):\n<\/span><span>    <\/span><span style=\"color:#5f6d64;\">&quot;&quot;&quot;\n<\/span><span style=\"color:#5f6d64;\">    calculate_alternating_mask determines a binary mask with a maximum\n<\/span><span style=\"color:#5f6d64;\">    range based on the provided value, then iterates through each bit in\n<\/span><span style=\"color:#5f6d64;\">    the mask, alternating the setting of that bit to 0.\n<\/span><span style=\"color:#5f6d64;\">    \n<\/span><span style=\"color:#5f6d64;\">    the end result is a mask with at most every other bit set to 1, in the\n<\/span><span style=\"color:#5f6d64;\">    worst case scenario that all bits were 1 in the binary representation\n<\/span><span style=\"color:#5f6d64;\">    of the input value.\n<\/span><span style=\"color:#5f6d64;\">    \n<\/span><span style=\"color:#5f6d64;\">    @example: calculate_alternating_mask(15) -&gt; 0b1010\n<\/span><span style=\"color:#5f6d64;\">    @example: calculate_alternating_mask(0b1111) -&gt; 0b1010\n<\/span><span style=\"color:#5f6d64;\">    @example: calculate_alternating_mask(0xf) -&gt; 0b1010\n<\/span><span style=\"color:#5f6d64;\">    \n<\/span><span style=\"color:#5f6d64;\">    @example: calculate_alternating_mask(255) -&gt; 0b10101010\n<\/span><span style=\"color:#5f6d64;\">    @example: calculate_alternating_mask(0b11111111) -&gt; 0b10101010\n<\/span><span style=\"color:#5f6d64;\">    @example: calculate_alternating_mask(0xff) -&gt; 0b10101010\n<\/span><span style=\"color:#5f6d64;\">    &quot;&quot;&quot;\n<\/span><span>    binary_index = <\/span><span style=\"color:#b16139;\">ceil<\/span><span>(<\/span><span style=\"color:#b16139;\">log2<\/span><span>(value))\n<\/span><span>    alternating_mask = <\/span><span style=\"color:#9f713c;\">2 <\/span><span>** binary_index - <\/span><span style=\"color:#9f713c;\">1\n<\/span><span>    \n<\/span><span>    zero_bit = <\/span><span style=\"color:#9f713c;\">1\n<\/span><span>    <\/span><span style=\"color:#55859b;\">while <\/span><span>binary_index &gt;= <\/span><span style=\"color:#9f713c;\">0<\/span><span>:\n<\/span><span>        <\/span><span style=\"color:#55859b;\">if <\/span><span>(zero_bit == <\/span><span style=\"color:#9f713c;\">1<\/span><span>):\n<\/span><span>            alternating_mask -= <\/span><span style=\"color:#9f713c;\">2 <\/span><span>** binary_index\n<\/span><span>        \n<\/span><span>        zero_bit ^= <\/span><span style=\"color:#9f713c;\">0b1\n<\/span><span>        binary_index -= <\/span><span style=\"color:#9f713c;\">1\n<\/span><span>    \n<\/span><span>    <\/span><span style=\"color:#55859b;\">return <\/span><span>alternating_mask\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">def <\/span><span style=\"color:#478c90;\">solution<\/span><span>(<\/span><span style=\"color:#b16139;\">N<\/span><span>):\n<\/span><span>    <\/span><span style=\"color:#5f6d64;\">&quot;&quot;&quot;\n<\/span><span style=\"color:#5f6d64;\">    solution will specially handle cases at the minimum range of valid\n<\/span><span style=\"color:#5f6d64;\">    input values to return early. otherwise, an alternating mask will\n<\/span><span style=\"color:#5f6d64;\">    be generated to calculate a sparse integer based on the value.\n<\/span><span style=\"color:#5f6d64;\">    &quot;&quot;&quot;\n<\/span><span>    <\/span><span style=\"color:#55859b;\">if <\/span><span>N &lt;= <\/span><span style=\"color:#9f713c;\">2<\/span><span>:\n<\/span><span>      <\/span><span style=\"color:#55859b;\">return <\/span><span>N\n<\/span><span>\n<\/span><span>    <\/span><span style=\"color:#55859b;\">return <\/span><span>N &amp; <\/span><span style=\"color:#b16139;\">calculate_alternating_mask<\/span><span>(N)\n<\/span><\/code><\/pre>\n<h2 id=\"the-breakdown\">The Breakdown<\/h2>\n<p>The wording of the problem would seem to indicate that it's possible to not find a sparse decomposition of a value, but generally speaking there will always be two components to derive from a single non-negative integer. The restrictions are only that the integer be part a sparse decomposition that has a corresponding but not reported sparse decomposition integer whose sums are the original value N. Since these decompositions are not limited by N &gt; 0 or N &gt; 1, this means we can break down any value M into N + P where 0 \u2264 N \u2264 M.<\/p>\n<p>With all of that in mind, the methodology here is to generate an alternating bitmask (1010101...) of equivalent binary order to the input number (4 = 0b100, mask = 0b101. 15 = 0b1111, mask = 0b1010). That is binary <code>AND<\/code>ed with the input number M to calculate a decomposition where there is never two successive 1s, as it's mathematically impossible given the mask never has two successive 1s and the nature of the binary AND operation.<\/p>\n<p>This only works because of loose requirements. For example, if the requirements were instead 0 &lt; N &lt; M, we could not return 0 or M. This solution could be extended to cover scenarios where the initial bitmask (1010) AND returns 0 or M, where we could bitshift the mask (0101) and repeat the check. At this point, if the result still returns 0 or M, there is no sparse decomposition that fulfills 0 &lt; N &lt; M.<\/p>\n"},{"title":"SSH Key Management","pubDate":"Mon, 21 Feb 2022 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/ssh-key-management\/","guid":"https:\/\/blog.carrio.dev\/blog\/ssh-key-management\/","description":"<h2 id=\"table-of-contents\">Table of Contents:<\/h2>\n<ol>\n<li><a href=\"https:\/\/blog.carrio.dev\/blog\/ssh-key-management\/#checking-for-existing-ssh-keys\">Checking for existings keys<\/a><\/li>\n<li><a href=\"https:\/\/blog.carrio.dev\/blog\/ssh-key-management\/#generating-a-new-ssh-key\">Generating a new SSH key<\/a><\/li>\n<li><a href=\"https:\/\/blog.carrio.dev\/blog\/ssh-key-management\/#adding-your-ssh-key-to-the-ssh-agent\">Adding your SSH key to the ssh-agent<\/a><\/li>\n<\/ol>\n<h2 id=\"checking-for-existing-ssh-keys\">Checking for existing SSH keys<\/h2>\n<p>Before you generate an SSH key, you can check to see if you have any existing SSH keys.<\/p>\n<ol>\n<li>\n<p>Open Git Bash.<\/p>\n<\/li>\n<li>\n<p>Enter <code>ls -al ~\/.ssh<\/code> to see if existing SSH keys are present:<\/p>\n<\/li>\n<\/ol>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#5f6d64;\"># Lists the files in your .ssh directory, if they exist\n<\/span><span style=\"color:#b16139;\">ls -al ~<\/span><span>\/.ssh\n<\/span><\/code><\/pre>\n<ol start=\"3\">\n<li>Check the directory listing to see if you already have a public SSH key.<\/li>\n<\/ol>\n<p>By default, the filenames of the public keys are one of the following, based on the SSH key type:<\/p>\n<ul>\n<li>id_dsa.pub<\/li>\n<li>id_ecdsa.pub<\/li>\n<li>id_ed25519.pub<\/li>\n<li>id_rsa.pub<\/li>\n<\/ul>\n<p>If you don't have an existing public and private key pair, or don't wish to use any that are available to connect to GitHub, then generate a new SSH key.\nIf you see an existing public and private key pair listed (for example id_rsa.pub and id_rsa) that you would like to use to connect to GitHub, you can add your SSH key to the ssh-agent.<\/p>\n<p>After you've checked that you don't have an existing SSH key or you simply want a new one, you can generate a new SSH key to use for authentication, then add it to the ssh-agent.<\/p>\n<h2 id=\"generating-a-new-ssh-key\">Generating a new SSH key<\/h2>\n<p>This will generate a key that is generally compatible with most system to date while using a more state of the art algorithm, ED25519.<\/p>\n<ol>\n<li>\n<p>Open Terminal.<\/p>\n<\/li>\n<li>\n<p>Paste the text below, substituting in your GitHub email address.<\/p>\n<\/li>\n<\/ol>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#5f6d64;\"># Creates a new ssh key, using the provided email as a label\n<\/span><span style=\"color:#b16139;\">$<\/span><span> ssh-keygen<\/span><span style=\"color:#b16139;\"> -t<\/span><span> ed25519<\/span><span style=\"color:#b16139;\"> -b<\/span><span> 4096<\/span><span style=\"color:#b16139;\"> -C <\/span><span>&quot;<\/span><span style=\"color:#489963;\">your_email@example.com<\/span><span>&quot;\n<\/span><span>&gt; Generating <\/span><span style=\"color:#b16139;\">public\/private<\/span><span> ed25519 key pair.\n<\/span><\/code><\/pre>\n<ol start=\"3\">\n<li>When you're prompted to \"Enter a file in which to save the key,\" press Enter. This accepts the default file location.<\/li>\n<\/ol>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span>&gt; Enter <\/span><span style=\"color:#b16139;\">a<\/span><span> file in which to save the key (\/Users\/you\/.ssh\/id_ed25519)<\/span><span style=\"color:#1c9aa0;\">: <\/span><span style=\"color:#55859b;\">[<\/span><span>Press enter<\/span><span style=\"color:#55859b;\">]\n<\/span><\/code><\/pre>\n<ol start=\"4\">\n<li>At the prompt, type a secure passphrase. For more information, see \"Working with SSH key passphrases\".<\/li>\n<\/ol>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span>&gt; Enter <\/span><span style=\"color:#b16139;\">passphrase<\/span><span> (empty for no passphrase)<\/span><span style=\"color:#1c9aa0;\">: <\/span><span style=\"color:#55859b;\">[<\/span><span>Type a passphrase<\/span><span style=\"color:#55859b;\">]\n<\/span><span>&gt; Enter <\/span><span style=\"color:#b16139;\">same<\/span><span> passphrase again: <\/span><span style=\"color:#55859b;\">[<\/span><span>Type passphrase again<\/span><span style=\"color:#55859b;\">]\n<\/span><\/code><\/pre>\n<h2 id=\"adding-your-ssh-key-to-the-ssh-agent\">Adding your SSH key to the ssh-agent<\/h2>\n<p>Before adding a new SSH key to the ssh-agent, you should have checked for existing SSH keys and generated a new SSH key.<\/p>\n<ol>\n<li>Ensure ssh-agent is enabled:<\/li>\n<\/ol>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#5f6d64;\"># start the ssh-agent in the background\n<\/span><span style=\"color:#b16139;\">$<\/span><span> eval &quot;$<\/span><span style=\"color:#489963;\">(<\/span><span style=\"color:#b16139;\">ssh-agent -s<\/span><span style=\"color:#489963;\">)<\/span><span>&quot;\n<\/span><span>&gt; Agent <\/span><span style=\"color:#b16139;\">pid<\/span><span> 59566\n<\/span><\/code><\/pre>\n<blockquote>\n<p>\u2139\ufe0f If you are using the <strong>Fish shell<\/strong>, recent versions support the following alternative:<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span>$ eval &quot;$(ssh-agent -c)&quot; \n<\/span><\/code><\/pre>\n<\/blockquote>\n<ol start=\"2\">\n<li>Add your SSH key to the ssh-agent. If you used an existing SSH key rather than generating a new SSH key, you'll need to replace id_ed25519 in the command with the name of your existing private key file.<\/li>\n<\/ol>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#b16139;\">$<\/span><span> ssh-add <\/span><span style=\"color:#b16139;\">~<\/span><span>\/.ssh\/id_ed25519\n<\/span><\/code><\/pre>\n<p>If you will be using this for GitHub, continue. Otherwise, enjoy your new ssh key.<\/p>\n<h2 id=\"adding-your-key-to-your-github-account\">Adding your key to your GitHub Account<\/h2>\n<p>Visit <a href=\"https:\/\/help.github.com\/articles\/adding-a-new-ssh-key-to-your-github-account\">this GitHub article<\/a>.<\/p>\n<h2 id=\"references\">References<\/h2>\n<p><a href=\"https:\/\/help.github.com\/articles\/generating-an-ssh-key\/\">Github: Generating an SSH key<\/a>\n<a href=\"https:\/\/ed25519.cr.yp.to\/\">ED25519<\/a><\/p>\n"},{"title":"MS Teams SameSite Scramble","pubDate":"Tue, 11 Feb 2020 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/teams-samesite-scramble\/","guid":"https:\/\/blog.carrio.dev\/blog\/teams-samesite-scramble\/","description":"<h2 id=\"microsoft-teams\">Microsoft Teams<\/h2>\n<p>Microsoft released their Teams chat application in 2017, but the platform didn't pick up for some time and particularly gained traction after certain <em>bundles<\/em> with other software such as Office and Windows Server setups for businesses. This is an [Electron] app, so at its core it's really an embedded Chrome instance running a web application with hooks into certain native functionalities, window management, and more. But you can consider MS Teams to effectively behave like Chrome when it comes to web standards.<\/p>\n<h2 id=\"samesite\">SameSite<\/h2>\n<p><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Set-Cookie#samesitesamesite-value\">SameSite<\/a> refers to a cookie attribute that control the security behaviors around cookies across domains. When cookies are set, they can have a SameSite property set to one of several values: <code>Strict<\/code>, <code>Lax<\/code>, and <code>None<\/code>. The<\/p>\n<p>To quote from MDN what each of these values mean:<\/p>\n<p><strong>Strict<\/strong><\/p>\n<blockquote>\n<p>Means that the browser sends the cookie only for same-site requests, that is, requests originating from the same site that set the cookie. If a request originates from a different domain or scheme (even with the same domain), no cookies with the SameSite=Strict attribute are sent.<\/p>\n<\/blockquote>\n<p><strong>Lax<\/strong><\/p>\n<blockquote>\n<p>Means that the cookie is not sent on cross-site requests, such as on requests to load images or frames, but is sent when a user is navigating to the origin site from an external site (for example, when following a link). This is the default behavior if the SameSite attribute is not specified.<\/p>\n<\/blockquote>\n<p><strong>None<\/strong><\/p>\n<blockquote>\n<p>means that the browser sends the cookie with both cross-site and same-site requests. The Secure attribute must also be set when setting this value, like so SameSite=None; Secure. If Secure is missing an error will be logged<\/p>\n<\/blockquote>\n<p>As browser's increase their security rules to protect their users, defaults around these types of configurations shift.<\/p>\n<h2 id=\"the-incompatibility\">The Incompatibility<\/h2>\n<p>The issue came up as a console log in the application that indicated what went wrong and suggestions for how to resolve it:<\/p>\n<blockquote>\n<p>Because a cookie's SameSite attribute was not set or is invalid, it defaults to SameSite=Lax, which will prevents the cookie from being set in a cross-site context in a future version of the browser. This behavior protects user data from accidentally leaking to third parties and cross-site request forgery.<\/p>\n<p>Resolve this issue by updating the attributes of the cookie:<\/p>\n<p>Specify SameSite=None and Secure if the cookie is intended to be set in cross-site contexts. Note that only cookies sent over<\/p>\n<p>HTTPS may use the Secure attribute.<\/p>\n<p>Specify SameSite=Strict or SameSite=Lax if the cookie should not be set by cross-site requests<\/p>\n<\/blockquote>\n<p>The root cause of this was a change in behavior for how Google Chrome handled the <code>SameSite=None<\/code> cookie attributes in version 67. Because a MS Teams app needs to be able a large range of browsers, just like a traditional web app. Since this change made it incompatible with the SameSite behavior of older versions of Chrome and other browsers, it had to be accounted for dynamically. Effectively, all versions of Chrome between 51.x and 67.x exhibited this issue.<\/p>\n<h2 id=\"the-solution\">The Solution<\/h2>\n<p>A JavaScript function that validates the current browser and checks whether the UserAgent matches a Microsoft Teams desktop clients with older Chromium versions incorrectly handle cookies with the SameSite=None property. We check for Teams clients with Chrome versions &gt;= <code>51.x<\/code> &lt; <code>67.x<\/code>. This method returns true if an invalid user agent is not found. Cases that are not known may fall through, this was based on testing with macOS, Windows, and Linux clients for Microsoft Teams.<\/p>\n<pre data-lang=\"ts\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-ts \"><code class=\"language-ts\" data-lang=\"ts\"><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">lowerIncompatibleVersion <\/span><span>= <\/span><span style=\"color:#9f713c;\">51<\/span><span>;\n<\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">compatibleVersion <\/span><span>= <\/span><span style=\"color:#9f713c;\">67<\/span><span>;\n<\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">teamsChromePattern <\/span><span>= \/<\/span><span style=\"color:#1c9aa0;\">(Teams<\/span><span>|<\/span><span style=\"color:#1c9aa0;\">MicrosoftTeams-Insiders)\\\/<\/span><span style=\"color:#9f713c;\">[\\d<\/span><span style=\"color:#1c9aa0;\">\\.<\/span><span style=\"color:#9f713c;\">]<\/span><span>+<\/span><span style=\"color:#1c9aa0;\"> Chrome\\\/(<\/span><span style=\"color:#9f713c;\">[\\d<\/span><span style=\"color:#1c9aa0;\">\\.<\/span><span style=\"color:#9f713c;\">]<\/span><span>+<\/span><span style=\"color:#1c9aa0;\">)<\/span><span>\/;\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">export function <\/span><span style=\"color:#478c90;\">incompatible<\/span><span>(<\/span><span style=\"color:#b16139;\">userAgent<\/span><span>?: string | null) {\n<\/span><span>  <\/span><span style=\"color:#55859b;\">if <\/span><span>(!<\/span><span style=\"color:#b16139;\">userAgent<\/span><span>) {\n<\/span><span>    <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#9f713c;\">false<\/span><span>;\n<\/span><span>  }\n<\/span><span>\n<\/span><span>  <\/span><span style=\"color:#55859b;\">const <\/span><span style=\"color:#b16139;\">match <\/span><span>= <\/span><span style=\"color:#b16139;\">userAgent<\/span><span>.<\/span><span style=\"color:#1c9aa0;\">match<\/span><span>(<\/span><span style=\"color:#b16139;\">teamsChromePattern<\/span><span>);\n<\/span><span>  <\/span><span style=\"color:#55859b;\">if <\/span><span>(!<\/span><span style=\"color:#b16139;\">match<\/span><span>) {\n<\/span><span>    <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#9f713c;\">false<\/span><span>;\n<\/span><span>  }\n<\/span><span>\n<\/span><span>  <\/span><span style=\"color:#55859b;\">let <\/span><span style=\"color:#b16139;\">teamsVersion <\/span><span>= <\/span><span style=\"color:#9f713c;\">0<\/span><span>;\n<\/span><span>  <\/span><span style=\"color:#55859b;\">try <\/span><span>{\n<\/span><span>    <\/span><span style=\"color:#55859b;\">let <\/span><span style=\"color:#b16139;\">majorVersion <\/span><span>= <\/span><span style=\"color:#b16139;\">match<\/span><span>[<\/span><span style=\"color:#9f713c;\">2<\/span><span>].<\/span><span style=\"color:#1c9aa0;\">split<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">.<\/span><span>&quot;)[<\/span><span style=\"color:#9f713c;\">0<\/span><span>];\n<\/span><span>    <\/span><span style=\"color:#b16139;\">teamsVersion <\/span><span>= <\/span><span style=\"color:#1c9aa0;\">parseInt<\/span><span>(<\/span><span style=\"color:#b16139;\">majorVersion<\/span><span>);\n<\/span><span>  } <\/span><span style=\"color:#55859b;\">catch <\/span><span>(<\/span><span style=\"color:#b16139;\">err<\/span><span>) {\n<\/span><span>    <\/span><span style=\"color:#55859b;\">return <\/span><span style=\"color:#9f713c;\">false<\/span><span>;\n<\/span><span>  }\n<\/span><span>\n<\/span><span>  <\/span><span style=\"color:#55859b;\">return <\/span><span>(\n<\/span><span>    <\/span><span style=\"color:#b16139;\">teamsVersion <\/span><span>&lt; <\/span><span style=\"color:#b16139;\">compatibleVersion <\/span><span>&amp;&amp;\n<\/span><span>    <\/span><span style=\"color:#b16139;\">teamsVersion <\/span><span>&gt;= <\/span><span style=\"color:#b16139;\">lowerIncompatibleVersion\n<\/span><span>  )\n<\/span><span>}\n<\/span><\/code><\/pre>\n<p>With this function, we can detect an unsupported scenario for our security rules and then provide an alternative method of interacting with our app. In the scenario this was built around, our account linking process for the platform to a Microsoft Teams account was breaking due to this incompatibility, and this allowed us to externalize the process to another browser to continue the flow.<\/p>\n<p>The source code for this can be found on my GitHub under the <a href=\"https:\/\/github.com\/tcarrio\/msteams-samesite-compatibility-validator\">msteams-samesite-compatibility-validator<\/a> project.<\/p>\n<!-- References -->\n"},{"title":"Python SMS scripting","pubDate":"Thu, 11 Jun 2015 00:00:00 +0000","author":"Unknown","link":"https:\/\/blog.carrio.dev\/blog\/python-sms-scripting\/","guid":"https:\/\/blog.carrio.dev\/blog\/python-sms-scripting\/","description":"<h2 id=\"foreword-from-the-future-magic-hands\">Foreword from the future :magic-hands:<\/h2>\n<p>This post is a simple summary of how you can accomplish SMS text messaging via web APIs. Services exist today like Twilio that are massively popular, but a decade ago these were not <em>as<\/em> stable or accessible. This post is based on source code from my <a href=\"https:\/\/github.com\/tcarrio\/py2sms\"><strong>py2sms<\/strong> repository<\/a>, which showcased how you could do this. This was forked from another project that was based on another SMS API service.<\/p>\n<h2 id=\"the-goal\">The goal<\/h2>\n<p>We're going to build a simple script showcasing how you can send text messages with Python. The service I'm going to utilize for this is <a href=\"https:\/\/eztexting.com\">eztexting<\/a>. This will utilize a popular HTTP library and be implemented for Python 3.<\/p>\n<h2 id=\"the-api\">The API<\/h2>\n<p>The API for sending SMS messages with <a href=\"https:\/\/eztexting.com\">eztexting<\/a> allows you to POST data to an endpoint with your authentication info (username\/password), a list of numbers, and the message content. The format is optional, and in this case we're going to prefer JSON since it's straightforward to define Python dictionaries and serialize them to JSON with the <code>json<\/code> package.<\/p>\n<h2 id=\"http-requests\">HTTP requests<\/h2>\n<p>While Python has standard libraries for working with HTTP, they can feel <em>clunky<\/em> in regards to developer experience. You have to handle a lot of defaults and calls and objects to do this yourself. Due to this, I'm going to rely on [requests], a popular HTTP request library for Python. <strong>requests<\/strong> touts itself as \"HTTP for Humans. An elegant and simple HTTP library for Python, built for human beings.\" I would request this for anyone not doing low-level networking type of work with Python, and even then consider whether you really need to subject yourself to the world of <code>urllib`` and <\/code>http`.<\/p>\n<h2 id=\"coding-it-up\">Coding it up<\/h2>\n<h3 id=\"api-credentials\">API Credentials<\/h3>\n<p>This is just a proof of concept, so in this solution I'm going to have a very basic approach to secrets management and the overall script safety. To start off, we're going to need a way to load credentials without embedding them in our version control. In my case, I define a <code>credentials<\/code> file which I configure <code>git<\/code> to ignore with a <code>.gitignore<\/code> entry:<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span># .gitignore:\n<\/span><span>\/credentials\n<\/span><\/code><\/pre>\n<p>Now I can throw my username and password for eztexting in there<\/p>\n<pre style=\"background-color:#171c19;color:#87928a;\"><code><span># \/credentials\n<\/span><span>a_username\n<\/span><span>def1n1t3lyMyP@ssword\n<\/span><\/code><\/pre>\n<h3 id=\"dependencies\">Dependencies<\/h3>\n<p>Now let's make sure we have our dependencies. In my case I'm using <code>requests<\/code>, <code>pprint<\/code>, and <code>json<\/code>. The first two need to be installed in your system. There are better ways to do this than global installs, like a virtualenv. You would run this inside there:<\/p>\n<pre data-lang=\"bash\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-bash \"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#b16139;\">pip<\/span><span> install requests pprint\n<\/span><\/code><\/pre>\n<p>And then you can import these with<\/p>\n<pre data-lang=\"py\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-py \"><code class=\"language-py\" data-lang=\"py\"><span style=\"color:#55859b;\">import <\/span><span>json\n<\/span><span style=\"color:#55859b;\">import <\/span><span>pprint\n<\/span><span style=\"color:#55859b;\">import <\/span><span>requests\n<\/span><\/code><\/pre>\n<h3 id=\"http-requests-1\">HTTP Requests<\/h3>\n<p>Next, we'll create an HTTP request with the necessary URL and body using my credentials:<\/p>\n<pre data-lang=\"py\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-py \"><code class=\"language-py\" data-lang=\"py\"><span>cred = <\/span><span style=\"color:#1c9aa0;\">open<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">credentials<\/span><span>&#39;, &#39;<\/span><span style=\"color:#489963;\">r<\/span><span>&#39;).<\/span><span style=\"color:#b16139;\">readlines<\/span><span>()\n<\/span><span>addr = &quot;<\/span><span style=\"color:#489963;\">https:\/\/app.eztexting.com\/sending\/messages?format=json<\/span><span>&quot;\n<\/span><span>number = <\/span><span style=\"color:#b16139;\">str<\/span><span>(pnumber)\n<\/span><span>message= <\/span><span style=\"color:#b16139;\">str<\/span><span>(msg)\n<\/span><span>content = {\n<\/span><span>    &#39;<\/span><span style=\"color:#489963;\">User<\/span><span>&#39;: cred[<\/span><span style=\"color:#9f713c;\">0<\/span><span>].<\/span><span style=\"color:#b16139;\">strip<\/span><span>(), \n<\/span><span>    &#39;<\/span><span style=\"color:#489963;\">Password<\/span><span>&#39;: cred[<\/span><span style=\"color:#9f713c;\">1<\/span><span>].<\/span><span style=\"color:#b16139;\">strip<\/span><span>(),\n<\/span><span>    &#39;<\/span><span style=\"color:#489963;\">PhoneNumbers[]<\/span><span>&#39;: &#39;<\/span><span style=\"color:#489963;\">555-123-4567,<\/span><span style=\"background-color:#b16139;color:#ecf4ee;\">\n<\/span><span>    &#39;<\/span><span style=\"color:#489963;\">Message<\/span><span>&#39;: &#39;<\/span><span style=\"color:#489963;\">Hello world<\/span><span>&#39;\n<\/span><span>}\n<\/span><span>\n<\/span><span>r = requests.<\/span><span style=\"color:#b16139;\">post<\/span><span>(addr,<\/span><span style=\"color:#b16139;\">data<\/span><span>=content)\n<\/span><\/code><\/pre>\n<p>This technically works as is, other than eztexting blowing up about the number. A successful response is going to be indicated with a 204, 'No Content', HTTP status code.<\/p>\n<h3 id=\"wrapping-it-all-up\">Wrapping it all up<\/h3>\n<p>Let's check the status and log errors, and finally parameterize the logic so it can be called as a function by other modules. Here's the final code afterward:<\/p>\n<pre data-lang=\"python\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-python \"><code class=\"language-python\" data-lang=\"python\"><span style=\"color:#5f6d64;\">#  _____   __     __  ___     _____   __  __    _____ \n<\/span><span style=\"color:#5f6d64;\"># |  __ \\  \\ \\   \/ \/ |__ \\   \/ ____| |  \\\/  |  \/ ____|\n<\/span><span style=\"color:#5f6d64;\"># | |__) |  \\ \\_\/ \/     ) | | (___   | \\  \/ | | (___  \n<\/span><span style=\"color:#5f6d64;\"># |  ___\/    \\   \/     \/ \/   \\___ \\  | |\\\/| |  \\___ \\ \n<\/span><span style=\"color:#5f6d64;\"># | |         | |     \/ \/_   ____) | | |  | |  ____) |\n<\/span><span style=\"color:#5f6d64;\"># |_|         |_|    |____| |_____\/  |_|  |_| |_____\/ \n<\/span><span style=\"color:#5f6d64;\">#                                                     \n<\/span><span>\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">import <\/span><span>json\n<\/span><span style=\"color:#55859b;\">import <\/span><span>pprint\n<\/span><span style=\"color:#55859b;\">import <\/span><span>requests\n<\/span><span style=\"color:#5f6d64;\"># &#39;pip install requests&#39; to install this library\n<\/span><span>\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">with <\/span><span style=\"color:#1c9aa0;\">open<\/span><span>(&#39;<\/span><span style=\"color:#489963;\">credentials<\/span><span>&#39;,&#39;<\/span><span style=\"color:#489963;\">r<\/span><span>&#39;) <\/span><span style=\"color:#55859b;\">as <\/span><span>c:\n<\/span><span>\t\tcred = c.<\/span><span style=\"color:#b16139;\">readlines<\/span><span>()\n<\/span><span>\n<\/span><span style=\"color:#55859b;\">def <\/span><span style=\"color:#478c90;\">sms<\/span><span>(<\/span><span style=\"color:#b16139;\">pnumber<\/span><span>,<\/span><span style=\"color:#b16139;\">msg<\/span><span>):\n<\/span><span>\taddr = &quot;<\/span><span style=\"color:#489963;\">https:\/\/app.eztexting.com\/sending\/messages?format=json<\/span><span>&quot;\n<\/span><span>\tnumber = <\/span><span style=\"color:#b16139;\">str<\/span><span>(pnumber)\n<\/span><span>\tmessage= <\/span><span style=\"color:#b16139;\">str<\/span><span>(msg)\n<\/span><span>\tcontent = {\n<\/span><span>\t\t&#39;<\/span><span style=\"color:#489963;\">User<\/span><span>&#39;: cred[<\/span><span style=\"color:#9f713c;\">0<\/span><span>].<\/span><span style=\"color:#b16139;\">strip<\/span><span>(), \n<\/span><span>\t\t&#39;<\/span><span style=\"color:#489963;\">Password<\/span><span>&#39;: cred[<\/span><span style=\"color:#9f713c;\">1<\/span><span>].<\/span><span style=\"color:#b16139;\">strip<\/span><span>(),\n<\/span><span>\t\t&#39;<\/span><span style=\"color:#489963;\">PhoneNumbers[]<\/span><span>&#39;: number,\n<\/span><span>\t\t&#39;<\/span><span style=\"color:#489963;\">Message<\/span><span>&#39;: message}\n<\/span><span>\tpprint.<\/span><span style=\"color:#b16139;\">pprint<\/span><span>(content)\n<\/span><span>\tr = requests.<\/span><span style=\"color:#b16139;\">post<\/span><span>(addr,<\/span><span style=\"color:#b16139;\">data<\/span><span>=content)\n<\/span><span>\t<\/span><span style=\"color:#55859b;\">if <\/span><span>r.status_code != <\/span><span style=\"color:#9f713c;\">204<\/span><span>:\n<\/span><span>\t\tpprint.<\/span><span style=\"color:#b16139;\">pprint<\/span><span>(r.<\/span><span style=\"color:#b16139;\">json<\/span><span>())\n<\/span><span>\t<\/span><span style=\"color:#55859b;\">else<\/span><span>:\n<\/span><span>\t\t<\/span><span style=\"color:#1c9aa0;\">print<\/span><span>(&quot;<\/span><span style=\"color:#489963;\">Status code 204: No content returned<\/span><span>&quot;)\n<\/span><span>\n<\/span><span style=\"color:#5f6d64;\"># To send a text message call the function with\n<\/span><span style=\"color:#5f6d64;\"># sms(phonenumber,&#39;sending this text message&#39;)\n<\/span><span style=\"color:#5f6d64;\"># sms(5551235555,&#39;Hello World!&#39;)\n<\/span><\/code><\/pre>\n<h3 id=\"documentation\">Documentation!<\/h3>\n<p>A project can always use a README. It helps people get started in your codebase and understand further context behind decisions, design, and more.<\/p>\n<pre data-lang=\"md\" style=\"background-color:#171c19;color:#87928a;\" class=\"language-md \"><code class=\"language-md\" data-lang=\"md\"><span style=\"color:#478c90;\"># About\n<\/span><span>\n<\/span><span>Fork of the py2sms project which was based off the &lt;<\/span><span style=\"color:#b16139;\">b<\/span><span>&gt;textbelt&lt;\/<\/span><span style=\"color:#b16139;\">b<\/span><span>&gt; service.\n<\/span><span>\n<\/span><span>Due to limited functionality and inconsistent service, this implementation will utilize the &lt;<\/span><span style=\"color:#b16139;\">b<\/span><span>&gt;eztexting&lt;\/<\/span><span style=\"color:#b16139;\">b<\/span><span>&gt; service instead.\n<\/span><span>\n<\/span><span style=\"color:#478c90;\"># How To Use\n<\/span><span>\n<\/span><span>You will need to create a &lt;<\/span><span style=\"color:#b16139;\">b<\/span><span>&gt;free&lt;\/<\/span><span style=\"color:#b16139;\">b<\/span><span>&gt; account at &lt;<\/span><span style=\"color:#b16139;\">a <\/span><span style=\"color:#9f713c;\">href<\/span><span>=&quot;<\/span><span style=\"color:#489963;\">https:\/\/www.eztexting.com\/start?pid=free<\/span><span>&quot;&gt;eztexting.com&lt;\/<\/span><span style=\"color:#b16139;\">a<\/span><span>&gt;, and add your username\/password to the credentials file.\n<\/span><span>\n<\/span><span>The script pulls this info to make requests to the RESTful API. \n<\/span><span>\n<\/span><span>You are alotted a total of 500 messages per month with this service. \n<\/span><span>\n<\/span><span style=\"color:#478c90;\"># API Documentation\n<\/span><span>\n<\/span><span>https:\/\/www.eztexting.com\/developers\/sms-api-documentation\/rest\n<\/span><\/code><\/pre>\n<!-- References -->\n"}]}}