Golo Roden: Einführung in React, Folge 5: Der unidirektionale Datenfluss

Mit React entwickelte Anwendungen verwenden einen unidirektionalen Datenfluss. Das bedeutet, dass Daten stets nur in einer Richtung weitergegeben und verarbeitet werden. Das wirft einige Fragen auf, beispielsweise wie mit Zustand umzugehen ist. Worauf gilt es zu achten?

Manfred Steyer: Angular Frontends for Micro Services using iframes

Even though the word iframe causes bad feelings for most web devs, it turns out that using them for loading SPAs in a micro service based environment -- aka micro frontends -- is a good choice. For instance, they allow for a perfect isolation between clients and for a separate deployment. Because of the isolation they also allow using different SPA frameworks. Besides iframes, there are other approaches to use SPAs in micro service architectures -- of course, each of them has their own pros and cons. A good overview can be found here.

As Asim Hussain shows in this blog article, using iframes can also be a nice solution for migrating an existing AngularJS application to Angular.

The approach described here uses a "meta router" to load different spa clients for micro services in iframes. It takes care about the iframe's creation and about synchronizing their routes with the shell's url. It also resizes the iframe dynamically to prevent a scrolling bar within it. I've written this router in VanillaJS for two reasons: 1) It allows very slim shell applications that don't base upon any framework and 2) it allows using different frameworks for different micro frontends.

The router and an example that shows how to use it can be found in my GitHub repo.

This is what a shell that uses the meta router looks like:

<!-- index.html in shell -->
<a href="javascript:router.go('a')">Route to A</a> |
<a href="javascript:router.go('b')">Route to B</a> |
<a href="javascript:router.go('a', 'a')">Jump to A within A</a>
<a href="javascript:router.go('a', 'b')">Jump to B within A</a>

<div id="outlet"></div>

<script src="polyfills.js"></script>
<script src="router.js"></script>
<script>
  var config = [
      {
          path: 'a',
          app: '/app-a/dist'
      },
      {
          path: 'b',
          app: '/app-b/dist'
      }
  ];

  router.config(config);
  router.init();
  router.preload();
</script>

The router creates the iframes as children of the element with the id outlet and allows switching between them using the method go. As you see in the example, it also allows to jump to a subroute within an application.

The routed applications use the child-app.js script and define an appId which is the same as the used path above:

<!-- index.html in routed app -->
<script src="../../child-app.js"></script>
<script>
    childApp.config({ appId: 'a' });
    childApp.init();
</script>

To synchronize the shell's url with the routed application's urls, we need to use Angular's Router service:

// app.component.ts in routed app

import { Router, NavigationEnd } from '@angular/router';
import { Component } from '@angular/core';
import { filter } from 'rxjs/operators';

declare let childApp: any;

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';

  constructor(private router: Router) {
    this.initChildRouter();
  }

  // Sync Subroutes
  initChildRouter() {
    // send route to shell  
    this.router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe((e: NavigationEnd) => {
      childApp.sendRoute(e.url);
    });

    // get route from shell
    childApp.registerForRouteChange(url => this.router.navigateByUrl(url));
  }

}

The source code can be found in my GitHub repo.

Jürgen Gutsch: Trying BitBucket Pipelines with ASP.NET Core

BitBucket provides a continuous integration tool called Pipelines. This is based on Docker containers which are running on a Linux based Docker machine. Within this post I wanna try to use BitBucket Pipelines with an ASP.NET Core application.

In the past I preferred BitBucket over GitHub, because I used Mercurial more than Git. But that changed five years ago. Since than I use GitHub for almost every new personal project that doesn't need to be a private project. But at the YooApps we use the entire Atlassian ALM Stack including Jira, Confluence and BitBucket. (We don't use Bamboo yet, because we also use Azure a lot and we didn't get Bamboo running on Azure). BitBucket is a good choice, if you anyway use the other Atlassian tools, because the integration to Jira and Confluence is awesome.

Since a while, Atlassian provides Pipelines as a simple continuous integration tool directly on BitBucket. You don't need to setup Bamboo to build and test just a simple application. At the YooApps we actually use Pipelines in various projects which are not using .NET. For .NET Projects we are currently using CAKE or FAKE on Jenkins, hosted on an Azure VM.

Pipelines can also used to build and test branches and pull request, which is awesome. So why shouldn't we use Pipelines for .NET Core based projects? BitBucket actually provides an already prepared Pipelines configuration for .NET Core related projects, using the microsoft/dotnet Docker image. So let's try pipelines.

The project to build

As usual, I just setup a simple ASP.NET Core project and add a XUnit test project to it. In this case I use the same project as shown in the Unit testing ASP.NET Core post. I imported that project from GitHub to BitBucket. if you also wanna try Pipelines, feel free to use the same way or just download my solution and commit it into your repository on BitBucket. Once the sources are in the repository, you can start to setup Pipelines.

Setup Pipelines

Setting up Pipelines actually is pretty easy. In your repository on BitBucket.com is a menu item called Pipelines. After pressing it you'll see the setup page, where you are able to select a technology specific configuration. .NET Core is not the first choice for BitBucket, because the .NET Core configuration is placed under "More". It is available anyway, which is really nice. After selecting the configuration type, you'll see the configuration in an editor inside the browser. It is actually a YAML configuration, called bitbucket-pipelines.yml, which is pretty easy to read. This configuration is prepared to use the microsoft/dotnet:onbuild Docker image and it already has the most common .NET CLI commands prepared, that will be used with that ASP.NET Core projects. You just need to configure the projects names for the build and test commands.

The completed configuration for my current project looks like this:

# This is a sample build configuration for .NET Core.
# Check our guides at https://confluence.atlassian.com/x/5Q4SMw for more examples.
# Only use spaces to indent your .yml configuration.
# -----
# You can specify a custom docker image from Docker Hub as your build environment.
image: microsoft/dotnet:onbuild

pipelines:
  default:
    - step:
        caches:
          - dotnetcore
        script: # Modify the commands below to build your repository.
          - export PROJECT_NAME=WebApiDemo
          - export TEST_NAME=WebApiDemo.Tests
          - dotnet restore
          - dotnet build $PROJECT_NAME
          - dotnet test $TEST_NAME

If you don't have tests yet, comment the last line out by adding a #-sign in front of that line.

After pressing "Commit file", this configuration file gets stored in the root of your repository, which makes it available for all the developers of that project.

Let's try it

After that config was saved, the build started immediately... and failed!

Why? Because that Docker image was pretty much outdated. It contains an older version with an SDK that still uses the the project.json for .NET Core projects.

Changing the name of the Docker image from microsoft/dotnet:onbuild to microsoft/dotnet:sdk helps. You now need to change the bitbucket-pipelines.yml in your local Git workspace or using the editor on BitBucket directly. After committing the changes, again the build starts immediately and is green now

Even the tests are passed. As expected, I got a pretty detailed output about every step configured in the "script" node of the bitbucket-pipelines.yml

You don't need to know how to configure Docker using the pipelines. This is awesome.

Let's try the PR build

To create a PR, I need to create a feature branch first. I created it locally using the name "feature/build-test" and pushed that branch to the origin. You now can see that this branch got built by Pipelines:

Now let's create the PR using the BitBucket web UI. It automatically assigns my latest feature branch and the main branch, which is develop in my case:

Here we see that both branches are successfully built and tested previously. After pressing save we see the build state in the PRs overview:

This is actually not a specific built for that PR, but the build of the feature branch. So in this case, it doesn't really build the PR. (Maybe it does, if the PR comes from a fork and the branch wasn't tested previously. I didn't test it yet.)

After merging that PR back to the develop (in that case), we will see that this merge commit was successfully built too:

We have four builds done here: The failing one, the one 11 hours ago and two builds 52 minutes ago in two different branches.

The Continuous Deployment pipeline

With this, I would be save to trigger a direct deployment on every successful build of the main branches. As you maybe know, it is super simple to deploy a web application to an Azure web app, by connecting it directly to any Git repository. Usually this is pretty dangerous, if you don't have any builds and tests before you deploy the code. But in this case, we are sure the PRs and the branches are building and testing successfully.

We just need to ensure that the deployment is only be triggered, if the build is successfully done. Does this work with Pipelines? I'm pretty curious. Let's try it.

To do that, I created a new Web App on Azure and connect this app to the Git repository on BitBucket. I'll now add a failing test and commit it to the Git repository. What now should happen is, that the build starts before the code gets pushed to Azure and the failing build should disable the push to Azure.

I'm skeptical whether this is working or not. We will see.

The Azure Web App is created and running on http://build-with-bitbucket-pipelines.azurewebsites.net/. The deployment is configured to listen on the develop branch. That means, every time we push changes to that branch, the deployment to Azure will start.

I'll now create a new feature branch called "feature/failing-test" and push it to the BitBucket. I don't follow the same steps as described in the previous section about the PRs, to keep the test simple. I merge the feature branch directly and without an PR to develop and push all the changes to BitBucket. Yes, I'm a rebel... ;-)

The build starts immediately and fails as expected:

But what about the deployment? Let's have a look at the deployments on Azure. We should only see the initial successful deployment. Unfortunately there is another successful deployment with the same commit message as the failing build on BitBucket:

This is bad. We now have an unstable application running on azure. Unfortunately there is no option on BitBucket to trigger the WebHook on a successful build. We are able trigger the Hook on a build state change, but it is not possible to define on what state we want to trigger the build.

Too bad, this doesn't seem to be the right way to configure the continuous deployment pipeline in the same easy way than the continuous integration process. Sure there are many other, but more complex ways to do that.

Update 12/8/2017

There is anyway a simple option to setup an deployment after successful build. This could be done by triggering the Azure webhook inside the Pipelines. An sample bash script to do that can be found here: https://bitbucket.org/mojall/bitbucket-pipelines-deploy-to-azure/ Without the comments it looks like this:

curl -X POST "https://\$$SITE_NAME:$FTP_PASSWORD@$SITE_NAME.scm.azurewebsites.net/deploy" \
  --header "Content-Type: application/json" \
  --header "Accept: application/json" \
  --header "X-SITE-DEPLOYMENT-ID: $SITE_NAME" \
  --header "Transfer-encoding: chunked" \
  --data "{\"format\":\"basic\", \"url\":\"https://$BITBUCKET_USERNAME:$BITBUCKET_PASSWORD@bitbucket.org/$BITBUCKET_USERNAME/$REPOSITORY_NAME.git\"}"

echo Finished uploading files to site $SITE_NAME.

I now need to set the environment variables in the Pipelines configuration:

Be sure to check the "Secured" checkbox for every password variable, to hide the password in this UI and in the log output of Pipelines.

And we need to add two script commands to the bitbucket-pipelines.yml:

- chmod +x ./deploy-to-azure.bash
- ./deploy-to-azure.bash

The last step is to remove the Azure web hook from the web hook configuration in BitBucket and to remove the failing test. After pushing the changes to BitBucket the build and the first successfull deployment starts immediately.

I now add the failing test again to test the failing deployment again and it worked as expected. The test fails and the next commands don't get executed. The web hook will never triggered and the unstable app will not be deployed.

Now there is a failing build on Pipelines:

(See the commit messages)

And that failing commit is not deployed to azure:

The Continuous Deployment is successfully done.

Conclusion

Isn't it super easy to setup a continuous integration? ~~Unfortunately we are not able to complete the deployment using this.~~ But anyway, we now have a build on any branch and on any pull-request. That helps a lot.

Pros:

  • (+++) super easy to setup
  • (++) almost fully integrated
  • (+++) flexibility based on Docker

Cons:

  • (--) runs only on Linux. I would love to see windows containers working
  • (---) not fully integrated into web hooks. "trigger on successful build state" is missing for the hooks

I would like to have something like this on GitHub too. The usage is almost similar to AppVeyor, but pretty much simpler to configure, less complex and it just works. The reason is Docker, I think. For sure, AppVeyor can do a lot more stuff and couldn't really compared to Pipelines. Anyway, I will do compare it to AppVeyor and will do the same with it in one of the next posts.

Currently there is a big downside with BitBucket Pipelines: Currently this is only working with Docker images that are running on Linux. It is not yet possible to use it for full .NET Framework projects. This is the reason why we never used it at the YooApps for .NET Projects. I'm sure we need to think about doing more projects using .NET Core ;-)

David Tielke: DDC 2017 - Inhalte meiner Keynote, DevSession und Workshops


Wie in jedem Jahr so fand auch in diesem wieder die Dotnet Developer Conference im Pullman Hotel in Köln statt. Zum ersten Mal an vier Tagen, genauer gesagt vom 27.11.2017 bis 30.11.2017, wurden neben DevSessions und dem eigentlichen Koferenztag auch zwei Workshoptage angeboten. 

An dieser Stelle möchte ich nun allen Teilnehmern die Materialien meiner einzelnen Beiträge zur Verfügung stellen.

Keynote "C# - vNow & vNext"

Eröffnet wurde die Konferenz am Mittwoch mit meiner Keynote zum aktuellen und zukünftigen Stand von Microsofts Entwicklungssprache C#. In 55 Minuten zeigte ich den Zuhörern zunächst die Geschichte und den Werdegang der Sprache, die aktuell hinzugekommenen Features (C# 6.0 & C#7.0), die kürzlichen Updates 7.1 und 7.2 sowie die anstehende Sprachversion 8.0. Anschließend wurde argumentiert, warum meiner Meinung nach C# eine der besten und sichersten Plattformen für die Zukunft ist.

Folien

DevSession "Mehr Softwarequalität durch Tests"

Einen Tag vor der eigentlichen Konferenz, konnten die Teilnehmer zwischen vier parallelen DevSessions wählen, welche eine Dauer von vier Stunden hatten. Mein Beitrag zum Thema "Mehr Softwarequalität durch Tests" stellte das Thema der Tests auf eine etwas andere Art und Weise vor: Nach einer grundlegenden Betrachtung des Themas "Softwarequalität" zeigt ich den Teilnehmern, was in der Softwareentwicklung so alles getestet werden kann. Neben dem Testen von Architekturen, Teams, Funktionalitäten, Code wurde besonders dem Thema "Prozesse" viel Aufmerksamkeit gewidmet.

Notizen

Workshop "Scrum mit dem Team Foundation Server 2018"

Am Montag wurde die Konferenz mit dem ersten Workshoptag eröffnet. Dabei stellte ich das agile Projektmanagementframework "Scrum" in Verbindung mit dem Team Foundation Server 2018 vor. Dazu wurde zu Beginn der Fokus zunächst auf Prozesse im allgemeinen und später dann auf die praktische Implementierung eines solchen anhand von Scrum, gelegt. Neben den Grundlagen wurde besonders die Einführung von Scrum anhand von zahlreichen Praxisbeispielen gezeigt und dabei auf Risiken und mögliche Probleme hingewiesen. Anschließend wurde am Beispiel des Team Foundation Servers gezeigt, wie eine toolgestützte Umsetzung einer agilen Planung aussehen kann.

Folien & Notizen

Workshop "Composite Component Architecture 2.0"

Am letzten Konferenztag stellte ich den Teilnehmern die neuste Version meiner "Composite Component Architecture" vor. Nach einem kurzen Überflug über die Grundlagen wurden die neuen Aspekte im Bereich Logging, Konfiguration, EventBroker, Bootstrapping sowie das verfügbare Tooling behandelt. Final dann durften die Teilnehmer einen exklusiven Blick auf eine frühe Alpha von "CoCo.Core" werfen, dem lange geplanten Framework für eine einfachere und flexiblere Implementierung der CoCo-Architektur.

Da das Framework im Workshop alles andere als stabil lief, werde ich das Release noch einmal separat in diesem Blog ankündigen, sobald eine stabile erste Alpha verfügbar ist.

Notizen

Ich möchte an dieser Stelle noch einmal allen Teilnehmern für ihr Feedback danken, egal ob persönlich oder per Twitter, Email oder auf sonst irgend einem Wege. Die Konferenz hat mal wieder unheimlich viel Spaß gemacht! Weiter geht ein riesig großer Dank an das Team von Developer Media, die mal wieder eine grandiose Konferenz auf die Beine gestellt haben.

Bis zur DDC 2018 :) 

Code-Inside Blog: Signing with SignTool.exe - don't forget the timestamp!

If you currently not touching signtool.exe at all or have nothing to do with “signing” you can just pass this blogpost, because this is more or less a stupid “Today I learned I did a mistake”-blogpost.

Signing?

We use authenticode code signing for our software just to prove that the installer is from us and “safe to use”, otherwise you might see a big warning from Windows that the application is from an “unknown publisher”:

x

To avoid this, you need a code signing certificate and need to sign your program (e.g. the installer and the .exe)

The problem…

We are doing this code signing since the first version of our application. Last year we needed to buy a new certificate because the first code signing certificate was getting stale. Sadly, after the first certificate was expired we got a call from a customer who recently tried to install our software and the installer was signed with the “old” certificate. The result was the big “Warning”-screen from above.

I checked the file and compared it to other installers (with expired certificates) and noticed that our signature didn’t had a timestamp:

x

The solution

I stumbled upon this great blogpost about authenticode code signing and the timestamp was indeed important:

When signing your code, you have the opportunity to timestamp your code; you should definitely do this. Time-stamping adds a cryptographically-verifiable timestamp to your signature, proving when the code was signed. If you do not timestamp your code, the signature will be treated as invalid upon the expiration of your digital certificate. Since it would probably be cumbersome to re-sign every package you’ve shipped when your certificate expires, you should take advantage of time-stamping. A signed, time-stamped package remains valid indefinitely, so long as the timestamp marks the package as having been signed during the validity period of the certificate.

Time-stamping itself is pretty easy and only one parameter was missing all the time… now we invoke Signtool.exe like this and we have a digitial signature with a timestamp:

signtool.exe sign /tr http://timestamp.digicert.com /sm /n "Subject..." /d "Description..." file.msi

Remarks:

  • Our code signing cert is from Digicert and they provide the timestamp URL.
  • SignTool.exe is part of the Windows SDK and currently is in the ClickOnce folder (e.g. C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool)

Hope this helps.

Manfred Steyer: Automatically Updating Angular Modules with Schematics and the CLI

Thanks to Hans Larsen from the Angular CLI Team for providing valuable feedback

In my last blog article, I've shown how to leverage Schematics, the Angular CLI's code generator, to scaffold custom components. This article goes one step further and shows how to register generated building blocks like Components, Directives, Pipes, or Services with an existing NgModule. For this I'll extend the example from the last article that generates a SideMenuComponent. The source code shown here can also be found in my GitHub repository.

Schematics is currently experimental and can change in future.
Angular Labs

Goal

To register the generated SideMenuComponent we need to perform several tasks. For instance, we have to lookup the file with respective NgModule. After this, we have to insert several lines into this file:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

// Add this line to reference component
import { SideMenuComponent } from './side-menu/side-menu.component';

@NgModule({
  imports: [
    CommonModule
  ],
  
  // Add this Line
  declarations: [SideMenuComponent],

  // Add this Line if we want to export the component too
  exports: [SideMenuComponent]
})
export class CoreModule { }

As you've seen in the last listing, we have to create an import statement at the beginning of the file. And then we have to add the imported component to the declarations array and - if the caller requests it - to the exports array too. If those arrays don't exist, we have to create them too.

The good message is, that the Angular CLI contains existing code for such tasks. Hence, we don't have to build everything from scratch. The next section shows some of those existing utility functions.

Utility Functions provided by the Angular CLI

The Schematics Collection @schematics/angular used by the Angular CLI for generating stuff like components or services turns out to be a real gold mine for modifying existing NgModules. For instance, you find some function to look up modules within @schematics/angular/utility/find-module. The following table shows two of them which I will use in the course of this article:

Function Description
findModuleFromOptions Looks up the current module file. For this, it starts in a given folder and looks for a file with the suffix .module.ts while the suffix .routing.module.ts is not accepted. If nothing has been found in the current folder, its parent folders are searched.
buildRelativePath Builds a relative path that points from one file to another one. This function comes in handy for generating the import statement pointing from the module file to the file with the component to register.

Another file containing useful utility functions is @schematics/angular/utility/ast-utils. It helps with modifying existing TypeScript files by leveraging services provided by the TypeScript compiler. The next table shows some of its functions used here:

Function Description
addDeclarationToModule Adds a component, directive or pipe to the declarations array of an NgModule. If necessary, this array is created
addExportToModule Adds an export to the NgModule

There are also other methods that add entries to the other sections of an NgModule (addImportToModule, addProviderToModule, addBootstrapToModule).

Please note, that those files are currently not part of the package's public API. Therefore, they can change in future. To be on the safe side, Hans Larsen from the Angular CLI Team suggested to fork it. My fork of the DevKit Repository containing those functions can be found here.

After forking, I've copied the contents of the folder packages\schematics\angular\utility containing the functions in question to the folder schematics-angular-utils in my project and adjusted some import statements. For the time being, you can also copy my folder with this adjustments for your own projects. I think that sooner or later the API will stabilize and be published as a public one so that we don't need this workaround.

Creating a Rule for adding a declaration to an NgModule

After we've seen that there are handy utility functions, let's use them to build a Rule for our endeavor. For this, we use a folder utils with the following two files:

Utils for custom Rule

The file add-to-module-context.ts gets a context class holding data for the planned modifications:

import * as ts from 'typescript';

export class AddToModuleContext {
    // source of the module file
    source: ts.SourceFile;

    // the relative path that points from  
    // the module file to the component file
    relativePath: string;

    // name of the component class
    classifiedName: string;
}

In the other file, ng-module-utils.ts, a factory function for the needed rule is created:

import { Rule, Tree, SchematicsException } from '@angular-devkit/schematics';
import { AddToModuleContext } from './add-to-module-context';
import * as ts from 'typescript';
import { dasherize, classify } from '@angular-devkit/core';

import { ModuleOptions, buildRelativePath } from '../schematics-angular-utils/find-module';
import { addDeclarationToModule, addExportToModule } from '../schematics-angular-utils/ast-utils';
import { InsertChange } from '../schematics-angular-utils/change';


const stringUtils = { dasherize, classify };

export function addDeclarationToNgModule(options: ModuleOptions, exports: boolean): Rule {
  return (host: Tree) => {
   [...]
  };
}

This function takes an ModuleOptions instance that describes the NgModule in question. It can be deduced by the options object containing the command line arguments the caller passes to the CLI.

It also takes a flag exports that indicates whether the declared component should be exported too. The returned Rule is just a function that gets a Tree object representing the part of the file system it modifies. For implementing this Rule I've looked up the implementation of similar rules within the CLI's Schematics in @schematics/angular and "borrowed" the patterns found there. Especially the Rule triggered by ng generated component was very helpful for this.

Before we discuss how this function is implemented, let's have a look at some helper functions I've put in the same file. The first one collects the context information we've talked about before:

function createAddToModuleContext(host: Tree, options: ModuleOptions): AddToModuleContext {

  const result = new AddToModuleContext();

  if (!options.module) {
    throw new SchematicsException(`Module not found.`);
  }

  // Reading the module file
  const text = host.read(options.module);

  if (text === null) {
    throw new SchematicsException(`File ${options.module} does not exist.`);
  }

  const sourceText = text.toString('utf-8');
  result.source = ts.createSourceFile(options.module, sourceText, ts.ScriptTarget.Latest, true);

  const componentPath = `/${options.sourceDir}/${options.path}/`
		      + stringUtils.dasherize(options.name) + '/'
		      + stringUtils.dasherize(options.name)
		      + '.component';

  result.relativePath = buildRelativePath(options.module, componentPath);

  result.classifiedName = stringUtils.classify(`${options.name}Component`);

  return result;

}

The second helper function is addDeclaration. It delegates to addDeclarationToModule located within the package @schematics/angular to add the component to the module's declarations array:

function addDeclaration(host: Tree, options: ModuleOptions) {

  const context = createAddToModuleContext(host, options);
  const modulePath = options.module || '';

  const declarationChanges = addDeclarationToModule(
			      context.source,
			      modulePath,
			      context.classifiedName,
			      context.relativePath);

  const declarationRecorder = host.beginUpdate(modulePath);
  for (const change of declarationChanges) {
    if (change instanceof InsertChange) {
      declarationRecorder.insertLeft(change.pos, change.toAdd);
    }
  }
  host.commitUpdate(declarationRecorder);
};

The addDeclarationToModule function takes the retrieved context information and the modulePath from the passed ModuleOptions. Instead of directly updating the module file it returns an array with necessary modifications. These are iterated and applied to the module file within a transaction, started with beginUpdate and completed with commitUpdate.

The second helper function is addExport. It adds the component to the module's exports array and works exactly like the addDeclaration:

function addExport(host: Tree, options: ModuleOptions) {
  const context = createAddToModuleContext(host, options);
  const modulePath = options.module || '';

  const exportChanges = addExportToModule(
				context.source,
				modulePath,
				context.classifiedName,
				context.relativePath);

  const exportRecorder = host.beginUpdate(modulePath);

  for (const change of exportChanges) {
    if (change instanceof InsertChange) {
      exportRecorder.insertLeft(change.pos, change.toAdd);
    }
  }
  host.commitUpdate(exportRecorder);
};

Now, as we've looked at these helper function, let's finish the implementation of our Rule:

export function addDeclarationToNgModule(options: ModuleOptions, exports: boolean): Rule {
  return (host: Tree) => {
    addDeclaration(host, options);
    if (exports) {
      addExport(host, options);
    }
    return host;
  };
}

As you've seen, it just delegates to addDeclaration and addExport. After this, it returns the modified file tree represented by the variable host.

Extending the used Options Class and its JSON schema

Before we put our new Rule in place, we have to extend the class MenuOptions which describes the passed (command line) arguments. As usual in Schematics, it's defined in the file schema.ts. For our purpose, it gets two new properties:

export interface MenuOptions {
    name: string;
    appRoot: string;
    path: string;
    sourceDir: string;
    menuService: boolean;

	// New Properties:
    module: string;
    export: boolean;
}

The property module holds the path for the module file to modify and export defines whether the generated component should be exported too.

After this, we have to declare these additional property in the file schema.json:

{
    "$schema": "http://json-schema.org/schema",
    "id": "SchemanticsForMenu",
    "title": "Menu Schema",
    "type": "object",
    "properties": {
      [...]
      "module":  {
        "type": "string",
        "description": "The declaring module.",
        "alias": "m"
      },
      "export": {
        "type": "boolean",
        "default": false,
        "description": "Export component from module?"
      }
    }
  }
  

As mentioned in the last blog article, we also could generate the file schema.ts with the information provided by schema.json.

Calling the Rule

Now, as we've created our rule, let's put it in place. For this, we have to call it within the Rule function in index.ts:

export default function (options: MenuOptions): Rule {

    return (host: Tree, context: SchematicContext) => {

      options.path = options.path ? normalize(options.path) : options.path;

	  // Infer module path, if not passed:
      options.module = options.module || findModuleFromOptions(host, options) || '';

      [...]

      const rule = chain([
        branchAndMerge(chain([

          [...]

		  // Call new rule
          addDeclarationToNgModule(options, options.export)

        ])),
      ]);

      return rule(host, context);

    }
}

As the passed MenuOptions object is structurally compatible to the needed ModuleOptions we can directly pass it to addDeclarationToNgModule. This is the way, the CLI currently deals with option objects.

In addition to that, we infer the module path at the beginning using findModuleFromOptions.

Testing the extended Schematic

To try the modified Schematic out, compile it and copy everything to the node_modules folder of an example application. As in the former blog article, I've decided to copy it to node_modules/nav. Please make sure to exclude the collection's node_modules folder, so that there is no folder node_modules/nav/node_modules.

After this, switch to the example application's root, generate a module core and navigate to its folder:

ng g module core
cd src\app\core

Now call the custom Schematic:

Calling Schematic which generated component and registers it with the module

This not only generates the SideMenuComponent but also registers it with the CoreModule:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SideMenuComponent } from './side-menu/side-menu.component';

@NgModule({
  imports: [
    CommonModule
  ],
  declarations: [SideMenuComponent],
  exports: [SideMenuComponent]
})
export class CoreModule { }

Norbert Eder: .NET Core Anwendung in einem Docker Container laufen lassen

Um eine .NET Core Anwendung in Docker zu hosten, bedarf es natürlich eines installierten .NET Core, einer App (im Falle dieses Beitrags existiert eine Web API auf Basis .NET Core) und natürlich Docker.

Für Tests am Desktop empfiehlt sich, die Docker Community Edition zu installieren.

Im nächsten Schritt muss dem .NET Core Projekt ein Dockerfile hinzugefügt werden. Dabei handelt es sich eine Datei mit Instruktionen, welches Basis-Image herangezogen werden soll, welche Dateien auf das neu zu erstellende Image gepackt werden sollen und einigen Informationen mehr.

Hier ein Beispiel für ein Dockerfile:

# Stage 1
FROM microsoft/aspnetcore-build AS builder
WORKDIR /source

# Copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore

# Copy everything else and build
COPY . .
RUN dotnet publish --output /app/ --configuration Release

# Build runtime image
FROM microsoft/aspnetcore
WORKDIR /app
COPY --from=builder /app .
EXPOSE 5000
ENTRYPOINT ["dotnet", "MeinDotnetCoreProjekt.dll"]

Im Docker-Hub stehen relevante Images von Microsoft bereit:

Von den Images gibt es unterschiedliche Ausprägungen mit unterschiedlichem Tooling. Darauf ist zu achten.

Program.cs anpassen

Standardmäßig läuft ASP.NET Core unter http://localhost:5000. Das ist für Tests am eigenn Rechner in Ordnung. In einem Docker-Container würde dies jedoch bedeuten, dass nicht am öffentlichen Port gelauscht wird, Anfragen also nicht angenommen werden. Deswegen ist sicherzustellen, dass auf allen Interfaces gelauscht wird:

public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseUrls("http://*:5000")
        .Build();

Das wäre nun alles.

Docker Container erstellen

Zuerst müssen wir ein eigenes Image erstellen:

docker build -t UnsereDemoApp .

Der Aufruf sollte aus dem Verzeichnis vorgenommen werden, in dem auch das Dockerfile liegt. Dieses wird eingelesen, validiert und bei Erfolg das neue Image restellt.

Dieses muss mit unserem definierten Tag UnsereDemoApp in der Liste der Images aufgeführt werden. Diese können mittels docker images abgerufen werden.

Aus diesem Image kann nun ein neuer Container erstellt werden. Ein Container besitzt einen eindeutigen Namen. Bei keiner Angabe erstellt Docker einen Namen für uns.

docker run -d -p 5000:5000 UsereDemoApp

Zu beachten ist, dass mit der Option -d der Container in einem Detached Modus läuft, d.h. wir erhalten keine Meldungen/Ausgabe. Durch -p 5000:5000 wird der lokale Port 5000 auf den Port 5000 des Containers gemappt. Deswegen kann am Host-Computer per http://localhost:5000 auf das Service im Container zugegriffen werden.

Ein entsprechender Zugriff auf einen vorhandenen Endpunkt sollte das gewünschte Ergebnis bringen.

Hilfreiche Befehle

Ausgabe der laufenden Container:

docker container

Einen Container stoppen

docker container stop [name]

EInen Container entfernen

docker container rm [name]

An einen Container attachen (und die Ausgabe konsumieren):

docker attach [name]

Die letzten angefallenen Meldungen ausgeben:

docker logs [name]

Viel Spaß bei der Verwendung von Docker und .NET Core.

The post .NET Core Anwendung in einem Docker Container laufen lassen appeared first on Norbert Eder.

Jürgen Gutsch: NuGet, Cache and some more problems

Recently I had some problems using NuGet, two of them were huge, which took me a while to solve them. But all of them are easy to fix, if you know how to do it.

NuGet Cache

The fist and more critical problem was related to the NuGet Cache in .NET Core projects. It seems the underlying problem was a broken package in the cache. I didn't find out the real reason. Anyway, every time I tried to restore or add packages, I got an error message, that told me about an error at the first character in the project.assets.json. Yes, there is still a kind of a project.json even in .NET Core 2.0 projects. This file is in the "obj" folder of a .NET Core project and stores all information about the NuGet packages.

This error looked like a typical encoding error. This happens often if you try to read a ANSI encoded file, from a UTF-8 encoded file, or vice versa. But the project.assets.json was absolutely fine. It seemed to be a problem with one of the packages. It worked with the predefined .NET Core or ASP.NET Core packages, but it doesn't with any other. I wasn't able anymore to work on any .NET Core projects that targets .NET Core, but it worked with projects that are targeting the full .NET Framework.

I couldn't solve the real problem and I didn't really want to go threw all of the packages to find the broken one. The .NET CLI provides a nice tool to manage the NuGet Cache. It provides a more detailed CLI to NuGet.

dotnet nuget --help

This shows you tree different commands to work with NuGet. delete and push are working on the remote server to delete a package from a server or to push a new package to the server using the NuGet API. The third one is a command to work with local resources:

dotnet nuget locals --help

This command shows you the help about the locals command. Try the next one to get a list of local NuGet resources:

dotnet nuget locals all --list

You can now use the clear option to clear all caches:

dotnet nuget locals all --clear

Or a specific one by naming it:

dotnet nuget locals http-cache --clear

This is much more easier than searching for all the different cache locations and to delete them manually.

This solved my problem. The broken package was gone from all the caches and I was able to load the new, clean and healthy ones from NuGet.

Versions numbers in packages folders

The second huge problem is not related to .NET Core, but to classic .NET Framework projects using NuGet. If you also use Git-Flow to manage your source code, you'll have at least to different main branches: Master and Develop. Both branches contain different versions. Master contains the current version code and Develop contains the next version code. It is also possible that both versions use different versions of dependent NuGet packages. And here is the Problem:

Master used e. g. AwesomePackage 1.2.0 and Develop uses AwesomePackage 1.3.0-beta-build54321

Both versions of the code are referencing to the AwesomeLib.dll but in different locations:

  • Master: /packages/awesomepackage 1.2.0/lib/net4.6/AwesomeLib.dll
  • Develop: /packages/awesomepackage 1.3.0-beta-build54321/lib/net4.6/AwesomeLib.dll

If you now release the Develop to Master, you'll definitely forget to go to all the projects to change the reference paths, don't you? The build of master will fail, because this specific beta folder wont exist on the server, or even more bad: The build will not fail because the folder of the old package still exists on the build server, because you didn't clear the build work space. This will result in runtime errors. This problem will probably happen more likely, if you provide your own packages using your own NuGet Server.

I solved this by using a different NuGet client than NuGet. I use Paket, because it doesn't store the binaries in version specific folder and the reference path will be the same as long as the package name doesn't change. Using Paket I don't need to take care about reference paths and every branch loads the dependencies from the same location.

Paket officially supports the NuGet APIs and is mentioned on NuGet org, in the package details.

To learn more about Paket visit the official documentation: https://fsprojects.github.io/Paket/

Conclusion

Being an agile developer, doesn't only mean to follow an iterative process. It also means to use the best tools you can buy. But you don't always need to buy the best tools. Many of them are open source and free to use. Just help them by donating some bugs, spread the word, file some issues or contribute in a way to improve the tool. Paket is one of such tools, lightweight, fast, easy to use and it solves many problems. It is also well supported in CAKE , which is the build DSL I use to build, test and deploy applications.

Norbert Eder: Bootstrap 4: Ersatz für die Glyphicons

Mit Version 4 wurde in Bootstrap ordentlich aufgeräumt. Unter anderem werden die Glyphicons nicht mehr mitgeliefert. Diese müssen nun ab sofort manuell eingebunden werden. Als Alternative bieten sich auch andere Icon Fonts an.

Weit verbreitet und auch von mir immer wieder gerne benutzt ist Font Awesome. Sehr cool an Font Awesome ist:

Font Awesome is fully open source and is GPL friendly. You can use it for commercial projects, open source projects, or really just about whatever you want.

Auch eine Nennung ist nicht notwendig – ist aber nur fair.

Ich verwende Bootstrap nun zusammen mit Angular. Damit Font Awesome genutzt werden kann, sind diese zwei Abhängigkeiten im package.json einzutragen:

"font-awesome": "^4.7.0",
"angular-font-awesome": "^3.0.3"

Nach der Installation der der Pakete via npm install muss das Angular-Modul bekannt gemacht werden. Für die nachfolgende Variante muss Angular-CLI verwendet werden.

import { AngularFontAwesomeModule } from 'angular-font-awesome';

@NgModule({
  ...
  imports: [
    ...
    AngularFontAwesomeModule,
    ...
  ]
})

In der Datei angular-cli.json ist nun noch das Style-Sheet einzutragen:

"styles": [
  ...
  "../node_modules/font-awesome/css/font-awesome.min.css"
]

Nun noch ng serve neu starten und schon kann Font-Awesome verwendet werden.

Wer Bootstrap auf Version 4 migrieren möchte, bekommt hier ausführliche Informationen. Für ausführliche Informationen zu angular-font-awesome empfehle ich einen Blick ins README.

Happy Coding!

The post Bootstrap 4: Ersatz für die Glyphicons appeared first on Norbert Eder.

AIT: Letzter Beitrag im alten Gewand

Liebe Leserschaft,

im Rahmen der Erneuerung unserer Firmenwebseite www.aitgmbh.de wurde unser Blog www.tfsblog.de in die neue Webseite integriert:

Dies hat zur Folge, dass der bestehende RSS Feed nicht mehr weitergepflegt wird.

Im neuen Blog gibt es jedoch wieder die Möglichkeit verschiedenen Themenschwerpunkte zu abonnieren:

Weiter haben wir die bestehenden Userdaten (z.B. zum Download unserer kostenfreien TFS Tools oder unserer Fachartikel) aufgrund der geänderten Datenschutzbestimmungen nicht einfach für die neue Webseite übernommen. Wir bitten daher jeden Leser sich bei Bedarf einen neuen Account einmalig anzulegen.

Abschließend möchten wir uns für die langjährige Treue bei Ihnen bedanken und freuen uns auf viele weitere spannende Jahre.

Herzliche Grüße aus Stuttgart,

Das Team der AIT

Golo Roden: Einführung in React, Folge 4: Der Komponenten-Lebenszyklus

Komponenten in React durchlaufen einen komplexen Lebenszyklus, der es ermöglicht, das Verhalten einer Komponente zu verschiedenen Zeitpunkten detailliert zu steuern. Wie funktioniert das, und worauf gilt es dabei zu achten?

MSDN Team Blog AT [MS]: News von der österreichischen PowerShell Community

Auch wenn der Experts Live Country Event mit über 100 Besuchern den Großteil der Zeit in Anspruch nahm, so war doch auch am Powershell Blog einiges los:

Euer Team der PS Usergroup Austria

MSDN Team Blog AT [MS]: Docker Roadshow in Frankfurt & München

Einladung zur Docker MTA Roadshow Warum Sie traditionelle Applikationen modernisieren sollten.

IT-Organisationen geben weiterhin 80% ihres Budgets aus um ihre bestehenden Applikationen Aufrecht zu erhalten und nur 20% des Budgets für neue Innovationen. Gemeinsam zeigen Docker und HPE wie sie mit dem Programm „Modernisierung von traditionellen Applikationen“ dazu beitragen können, diese 80% zu eliminieren und Ihnen somit ermöglicht wird mehr auf Innovationen setzen zu können. Dieses schlüsselfertige Programm garantiert mehr als 50% Einsparungen bei den gesamten IT-Kosten und bietet gleichzeitig moderne Sicherheits-, Portabilitätseigenschaften und einen Weg zu Hybrid IT & DevOps.

Was können Sie erwarten?
·         Informative Sessions, Demos, praktische Tips und nützliche Tools - von den vertrauenswürdigsten Namen im Geschäft.

·         Gewinnen Sie Einblick in das MTA-Programm und wie Sie loslegen können.

·         Eauchen Sie in die ROI-Analyse ein und wie man einen Business Case macht.

Wer sollte teilnehmen?

IT-Operations - und IT-Infrastruktur-Manager, IT-Direktoren, CIOs und CTOs, die daran interessiert sind, zu verstehen, wie Docker dazu beitragen kann, ältere Anwendungen zu verwandeln und bestehende Anwendungen zu modernisieren.

Agenda

Vormittag

·         08:00 - 09:00 Breakfast & Networking

·         09:00 - 09:05 Welcome & Opening

·         09:05 - 09:35 What is Docker & Why MTA

·         09:35 - 09:50 Q&A

·         09:50 - 10:00 Break

·         10:00 - 12:00 Hands-On Demo - Specific MTA Use Case

·         12:00 - 13:00 Lunch & Networking

Nachmittag

·         13:00 - 13:30 Partner presentation

·         13:30 - 13:45 Partner presentation Q&A

·         13:45 - 14:15 How to measure and track progress during MTA

·         14:15 - 14:30 Break

·         14:30 - 16:00 Hands-On Demo - Expand & Growth

·         16:00 - 16:15 Day review

·         16:15 - 17:00 Q&A & Mingle

Docker Roadshow Frankfurt

Hier registrieren »

Datum: 28. November 2017 08:00 - 17:00
Tagungsort: Radisson Blu Hotel Franklinstraße 65, 60486 Frankfurt am Main

 

Docker Roadshow Munich

Hier registrieren »

Datum: 30. November 2017 08:00 - 17:00
Tagungsort: Le Méridien Bayerstraße 41, 80335 München

Stefan Henneken: IEC 61131-3: Unit-Tests

Unit-Tests sind ein unentbehrliches Hilfsmittel für jeden Programmierer, um die Funktionsfähigkeit seiner Software sicherzustellen. Programmfehler kosten Zeit und Geld, daher benötigt man eine automatisierte Lösung, um diesen Fehlern auf die Spur zu kommen – und zwar möglichst bevor die Software zum Einsatz kommt. Unit-Tests sollten überall dort eingesetzt werden, wo professionell Software entwickelt wird. Dieser Artikel soll einen schnellen Einstieg und ein Verständnis für den Nutzen der Unit-Tests ermöglichen.

Motivation

Häufig werden für das Testen von Funktionsblöcken separate Testprogramme geschrieben. In solch einem Testprogramm wird eine Instanz des gewünschten Funktionsblock angelegt und aufgerufen. Dabei werden die Ausgangsvariablen beobachtet und manuell auf Korrektheit geprüft. Stimmen diese nicht mit den erwarteten Werte überein, so wird der Funktionsblock solange angepasst, bis der Funktionsblock das gewünschte Verhalten aufweist.

Doch mit dem einmaligen Testen von Software ist es nicht getan. So führen Änderungen oder Erweiterungen an einem Programm immer wieder dazu, dass Funktionen oder Funktionsblöcke, die zuvor ausgetestet wurden und fehlerfrei funktionierten, plötzlich nicht mehr korrekt arbeiten. Auch kommt es vor, dass sich die Behebung von Programmfehlern auch auf andere Programmteile auswirkt und somit an anderen Stellen im Code zu Fehlfunktionen führen kann. Die zuvor ausgeführten und abgeschlossenen Tests müssen somit manuell wiederholt werden.

Ein mögliche Herangehensweise für eine Verbesserung dieser Arbeitsweise besteht darin, die Tests zu automatisieren. Dazu wird ein Test-Programm entwickelt, welches die Funktionalität des zu testenden Programms aufruft und die Rückgabewerte überprüft. Ein einmal geschriebenes Testprogramm bietet eine Reihe von Vorteilen:

– Die Tests sind automatisiert und mit gleichen Rahmenbedingung (Timings, ..)  somit jederzeit wiederholbar.

– Einmal geschriebene Tests bleiben auch für andere Mitglieder des Teams erhalten.

Unit-Tests

Ein Unit-Test prüft einen sehr kleinen und autarken Teil (Unit) einer Software. In der IEC 61131-3 ist dieses ein einzelner Funktionsblock oder eine Funktion. Bei jedem Test wird die zu testende Einheit (Funktionsblock, Methode oder Funktion) mit Testdaten (Parametern) aufgerufen und deren Reaktion auf diese Testdaten geprüft. Stimmt das gelieferte Ergebnis mit dem erwarteten Ergebnis überein, so gilt der Test als bestanden. Ein Test besteht im Allgemeinen aus einer ganzen Reihe von Testfällen, die nicht nur ein Soll-Ist-Paar prüft, sondern gleich mehrere.

Welche Test-Szenarien der Entwickler implementiert, bleibt ihm überlassen. Sinnvoll ist es aber mit Werten zu testen, die typischerweise auch bei deren Aufruf in der Praxis auftreten. Auch die Betrachtung von Grenzwerten (extrem große oder kleine Werte) oder besonderen Werten (Null-Zeiger, Leerstring), ist sinnvoll. Liefern all diese Testszenarien erwartungsgemäß die korrekten Werte, so kann der Entwickler davon ausgehen, dass seine Implementierung korrekt ist.

Ein positiver Nebeneffekt ist der, dass es dem Entwickler weniger Kopfschmerzen bereitet komplexe Änderungen an seinem Code vorzunehmen. Schließlich kann er nach derartigen Änderungen das System jederzeit überprüfen. Treten also nach einer solchen Änderung keine Fehler auf, so ist sie höchstwahrscheinlich geglückt.

Man darf dabei allerdings die Gefahr einer schlechten Implementierung der Tests nicht außer Acht lassen. Sind diese unzureichend oder gar falsch, liefern aber ein positives Ergebnis, so führt diese trügerische Sicherheit früher oder später zu großen Problemen.

Das Unit-Test Framework TcUnit

Unit-Test Frameworks bieten die notwendigen Funktionalitäten an, um Unit-Tests schnell und effektiv zu erstellen. Durch ergeben sich weitere Vorteile:

– Jeder aus dem Team kann die Tests schnell und einfach erweitern.

– Jeder ist in der Lage die Tests zu starten und das Ergebnis der Tests auf Korrektheit zu überprüfen.

Im Rahmen eines Projektes ist das Unit-Test Framework TcUnit entstanden. Genaugenommen handelt es sich um eine SPS-Bibliothek, welche Methoden zur Verifizierung von Variablen bereit hält (Assert-Methoden). War eine Überprüfung nicht erfolgreich, so wird eine Statusmeldung in das Ausgabefenster ausgegeben. Enthalten sind die Assert-Methoden in dem Funktionsblock FB_Assert.

Je Datentyp gibt es eine Methode, wobei der Aufbau immer ähnlich ist. Es gibt immer einen Parameter der den Istwert enthält und einen Parameter für den Sollwert. Stimmen beide überein, gibt die Methode TRUE zurück, ansonsten FALSE. Der Parameter sMessage gibt den Ausgabetext vor, der im Falle eines Fehlers ausgegeben wird. Dadurch lassen sich die Meldungen den einzelnen Testfällen zuordnen. Die Namen der Assert-Methoden beginnen immer mit AreEqual.

Hier als Beispiel die Methode um eine Variable vom Typ Integer auf Gültigkeit zu überprüfen.

Pic01

Manche Methode enthalten noch zusätzliche Parameter.

Pic02

Für alle Standarddatentypen (BOOL, BYTE, INT, WORD, STRING, TIME, …) sind entsprechende Assert-Methoden vorhanden. Aber auch einige spezielle Datentypen, wie z.B. AreEqualMEM zur Prüfung eines Speicherbereichs oder AreEqualGIUD, werden unterstützt.

Ein erstes Beispiel

Unit-Tests werden dazu verwendet einzelne Funktionsblöcke unabhängig von anderen Komponenten zu überprüfen. Diese Funktionsblöcke können sich in einer SPS-Bibliothek oder in einem SPS-Projekt befinden.

Für das erste Beispiel soll sich der zu testende FB in einem SPS-Projekt befinden. Es handelt sich hierbei um den Funktionsblock FB_Foo.

Pic03
Definiert die Zeit, die der Ausgang bOut gesetzt bleibt, falls keine weiteren positiven Flanken an bSwitch angelegt werden.

bSwitch Durch eine positive Flanke wird der Ausgang bOut auf TRUE gesetzt. Dieser bleibt für die Zeit tDuration aktiv. Ist der Ausgang schon gesetzt, so wird die Zeit tDuration neu gestartet.
bOff Der Ausgang bOut wird durch eine positive Flanke unmittelbar zurückgesetzt.
tDuration Definiert die Zeit, die der Ausgang bOut gesetzt bleibt, falls keine weiteren positiven Flanken an bSwitch angelegt werden.

Durch Unit-Tests soll bewiesen werden, dass sich der Funktionsblock FB_Foo wie erwartet verhält. Den Code zum Testen wird hierbei direkt in dem TwinCAT Projekt implementieren.

Projektaufbau

Um den Test-Code von der Applikation zu trennen, wird der Ordner TcUnit_Tests angelegt. In diesem Ordner wird der POU P_Unit_Tests abgelegt von dem aus die jeweiligen Testfälle aufgerufen werden.

Für jeden FB wird ein entsprechender Test-FB angelegt. Dieser hat den gleichen Namen plus den Postfix _Tests. Für unser Beispiel ergibt sich der Name FB_Foo_Tests.

Pic04

In P_Unit_Tests wird eine Instanz von FB_Foo_Tests angelegt und aufgerufen.

PROGRAM P_Unit_Tests
VAR
  fbFoo_Tests : FB_Foo_Tests;
END_VAR

fbFoo_Tests();

In FB_Foo_Tests befindet sich der gesamte Test-Code zur Überprüfung von FB_Foo. Hierzu werden in FB_Foo_Tests jeweils pro Testfall eine Instanz von FB_Foo angelegt. Diese werden mit unterschiedlichen Parametern aufgerufen und die Rückgabewerte werden mit Hilfe der Assert-Methoden validiert.

Die Abarbeitung der einzelnen Testfälle geschieht in einer Statemachine, die auch von der SPS-Bibliothek TcUnit verwaltet wird. Dadurch wird z.B. der Test automatisch beendet, sobald ein Fehler erkannt wurde.

Definition der Testfälle

Zuvor müssen die einzelnen Testfälle definiert werden. Jeder Testfall belegt in der Statemachine einen bestimmten Bereich.

Für die Benennung der einzelnen Testfälle haben sich einige Benennungsregeln bewährt, die helfen, den Test-Aufbau übersichtlicher zu gestalten.

Bei den Testfällen, die einen Eingang von FB_Foo prüfen sollen, setzt sich der Name zusammen aus: [Name des Eingangs]_[Testbedingung]_[erwartetes Verhalten]. Analog dazu werden Testfälle benannt, die Methoden von FB_Foo testen, also [Name der Methode]_[Testbedingung]_[erwartetes Verhalten].

Nach diesem Schema werden folgende Testfälle festgelegt:

Switch_RisingEdgeAndDuration1s_OutIsTrueFor1s

Testet, ob durch eine positive Flanke an bSwitch der Ausgang bOut für 1 s gesetzt wird, wenn tDuration auf t#1s gesetzt wurde.

Switch_RisingEdgeAndDuration1s_OutIsFalseAfter1100ms

Testet, ob durch eine positive Flanke an bSwitch der Ausgang bOut nach 1100 ms wieder FALSE wird, wenn tDuration auf t#1s gesetzt wurde.

Switch_RetriggerSwitch_OutKeepsTrue

Testet, ob durch eine erneute positive Flanke an bSwitch die Zeit tDuration neu gestartet wird.

Off_RisingEdgeAndOutIsTrue_OutIsFalse

Testet, ob durch eine positive Flanke an bOff der gesetzt Ausgang bOut auf FALSE geht.

Implementierung der Testfälle

Jeder Testfall belegt in der Statemachine mindestens einen Schritt. In diesem Beispiel wurde als Schrittweite zwischen den einzelnen Testfällen 16#0100 gewählt. Der 1. Testfall beginnt bei 16#0100, der zweite bei 16#0200, usw. In Schritt 16#0000 werden Initialisierungen durchgeführt werden, während der Schritt 16#FFFF vorhanden sein muss, da dieser von der Statemachine angesprungen wird, sobald eine Assert-Methode einen Fehler festgestellt hat. Läuft der Test fehlerfrei durch, so wird in 16#FF00 eine Meldung ausgegeben und der Unit-Test für FB_Foo ist beendet.

Das Pragma region ist hierbei sehr hilfreich, um die Navigation im Quellcode zu vereinfachen.

FUNCTION_BLOCK FB_Foo_Tests
VAR_INPUT
END_VAR
VAR_OUTPUT
  bError : BOOL;
  bDone : BOOL;
END_VAR
VAR
  Assert : FB_ASSERT('FB_Foo');
  fbFoo_0100 : FB_Foo;
  fbFoo_0200 : FB_Foo;
  fbFoo_0300 : FB_Foo;
  fbFoo_0400 : FB_Foo;
END_VAR

CASE Assert.State OF
{region 'start'}
16#0000:
  bError := FALSE;
  bDone := FALSE;
  Assert.State := 16#0100;
{endregion}

{region 'Switch_RisingEdgeAndDuration1s_OutIsTrueFor1s'}
16#0100:
  fbFoo_0100(...
  ...
  Assert.State := 16#0200;
{endregion}

{region 'Switch_RisingEdgeAndDuration1s_OutIsFalseAfter1100ms'}
16#0200:
  fbFoo_0200(...
  ...
  Assert.State := 16#0300;
{endregion}

{region 'Switch_RetriggerSwitch_OutKeepsTrue'}
16#0300:
  fbFoo_0300(...
  ...
  Assert.State := 16#0400;
{endregion}

{region 'Off_RisingEdgeAndOutIsTrue_OutIsFalse'}
16#0400:
  fbFoo_0400(...
  ...
  Assert.State := 16#FF00;
{endregion}

{region 'done'}
16#FF00:
  Assert.PrintPassed('Done');
  Assert.State := 16#FF10;

16#FF10:
  bDone := TRUE;

{endregion}

{region 'error'}
16#FFFF:
  bError := TRUE;
{endregion}

ELSE
  Assert.StateMachineError();
END_CASE

Für jeden Testfall gibt es eine separate Instanz von FB_Foo. Dadurch wird sichergestellt, dass jeder Testfall mit einer neu initialisierten Instanz von FB_Foo arbeitet. Eine gegenseitig Beeinflussung der Testfälle wird dadurch vermieden.

Im einfachsten Fall, besteht ein Testfall aus nur einen Schritt:

16#0100:
  fbFoo_0100(bSwitch := TRUE, tDuration := T#1S);
  Assert.AreEqualBOOL(TRUE, fbFoo_0100.bOut, 'Switch_RisingEdgeAndDuration1s_OutIsTrueFor1s');
  tonDelay(IN := TRUE, PT := T#900MS);
  IF (tonDelay.Q) THEN
    tonDelay(IN := FALSE);
    Assert.State := 16#0200;
  END_IF

Der zu testende Baustein wird für 900 ms aufgerufen. Während dieser Zeit muss bOut TRUE sein, da bSwitch auf TRUE gesetzt wurde und tDuration 1 s beträgt. Die Assert-Methode AreEqualBOOL prüft den Ausgang bOut. Hat dieser nicht den erwarteten Zustand, so wird eine Fehlermeldung ausgegeben. Nach 900 ms wird durch Setzen der Eigenschaft State von FB_Assert in den nächsten Testfall gewechselt

Ein Testfall kann auch aus mehreren Schritten bestehen:

16#0300:
  fbFoo_0300(bSwitch := TRUE, tDuration := T#500MS);
  Assert.AreEqualBOOL(TRUE, fbFoo_0300.bOut, 'Switch_RetriggerSwitch_OutKeepsTrue');
  tonDelay(IN := TRUE, PT := T#400MS);
  IF (tonDelay.Q) THEN
    tonDelay(IN := FALSE);
    fbFoo_0300(bSwitch := FALSE);
    Assert.State := 16#0310;
  END_IF

16#0310:
  fbFoo_0300(bSwitch := TRUE, tDuration := T#500MS);
  Assert.AreEqualBOOL(TRUE, fbFoo_0300.bOut, 'Switch_RetriggerSwitch_OutKeepsTrue');
  tonDelay(IN := TRUE, PT := T#400MS);
  IF (tonDelay.Q) THEN
    tonDelay(IN := FALSE);
    Assert.State := 16#0400;
  END_IF

Das Triggern von bSwitch wird in Zeile 7 und in Zeile 12 durchgeführt. In den Zeile 3 und 13 wird geprüft ob der Ausgang gesetzt bleibt.

Ausgabe der Meldungen

Nach Ausführung aller Testfälle für FB_Foo wird eine Meldung ausgegeben (Schritt 16#FF00).

Pic05

Sollte der Fall eintreten, dass eine Assert-Methode einen Fehler erkennt, so wird dieses ebenfalls als Meldung ausgegeben.

Pic06

Ist die Eigenschaft AbortAfterFail von FB_Assert auf TRUE gesetzt, so wird bei einem Fehler der Schritt 16#FFFF angesprungen und somit der Test beendet.

Die Assert-Methoden verhindert, dass in einem Schritt die gleiche Meldung mehrfach hintereinander ausgegeben wird. Die mehrfache Ausgabe der gleichen Meldung, z.B. in einer Schleife, wird somit unterdrückt. Durch Setzen der Eigenschaft MultipleLog auf TRUE wird dieser Filter deaktiviert und jede Meldung kommt zur Ausgabe.

Durch den oben gezeigten Aufbau sind die Unit-Tests klar von der eigentlichen Applikation getrennt. FB_Foo bleibt vollständig unverändert.

Diese TwinCAT-Solution wird gemeinsam mit der TwinCAT Solution für die SPS-Bibliothek in die Quellcodeverwaltung (wie z.B. TFS oder Git) abgelegt. Somit steht allen Team-Mitgliedern eines Projektes die Tests zur Verfügung. Durch das Unit-Test Framework können Tests auch von jedem erweitert und vorhandene Tests gestartet und einfach ausgewertet werden.

Auch wenn der Begriff Unit-Test Framework für die SPS-Bibliothek TcUnit etwas hochgegriffen ist, so zeigt sich doch das mit wenigen Hilfsmitteln automatisierte Tests auch mit der IEC 61131-3 möglich sind. Kommerzielle Unit-Test Framework gehen deutlich über das hinaus, was eine SPS-Bibliothek leisten kann. So enthalten diese entsprechende Dialoge um die Tests zu starten und das Ergebnis anzuzeigen. Auch werden häufig die Bereiche im Quellcode markiert, die von den einzelnen Testfällen durchlaufen wurden.

Bibliothek TcUnit (TwinCAT 3.1.4022) auf GitHub

Beispiel (TwinCAT 3.1.4022) auf GitHub

Tips

Die größte Hürde bei Unit-Tests ist häufig der innere Schweinehund. Ist dieser erst überwunden, schreiben sich die Unit-Tests fast von alleine. Die zweite Hürde stellt sich durch die Frage nach den zu testenden Teilen der Software. Es ist wenig sinnvoll, alles testen zu wollen. Vielmehr sollte man sich auf wesentliche Bereiche der Software konzentrieren und die Funktionsblöcke, welche die Basis der Anwendung ausmachen, gut testen.

Im Grunde gilt ein Unit-Test als einigermaßen qualitativ, wenn bei der Ausführung möglichst viele Zweige durchlaufen werden. Beim Schreiben der Unit-Tests sollten die Testfälle so gewählt werden, dass möglichst alle Zweige des Funktionsblocks durchlaufen werden.

Treten dann doch noch Fehler in der Praxis auf, so kann es von Vorteil sein, wenn für diesen Fehlerfall Tests geschrieben werden. Damit wird sichergestellt, dass ein Fehler, der einmal aufgetreten ist, nicht ein weiteres Mal auftritt.

Allein das zwei oder mehrere Funktionsblöcke korrekt arbeiten und dieses durch Unit-Tests bewiesen wird, bedeutet noch nicht, dass eine Anwendung diese Funktionsblöcke auch korrekt anwendet. Unit-Tests ersetzen somit in keinster Weise Integrations- und Akzeptanztests. Derartige Testmethoden validieren das Gesamtsystem und bewerten somit das große Ganze. Es ist auch unter Verwendung von Unit-Tests nötig, weiterhin das Gesamtwerk zu testen. Allerdings wird ein nicht unwesentlicher Teil potentieller Fehler schon im Vorfeld durch Unit-Tests ausgeschaltet, was im Endeffekt Aufwand für das Testen und somit Zeit und Geld spart.

Weitere Informationen

Während der Vorbereitung zu diesem Post, hat Jakob Sagatowski in seinem Blog AllTwinCAT den ersten Teil einer Artikel-Serie über Test driven development in TwinCAT veröffentlicht. Für alle die tiefer in das Thema einsteigen wollen, kann ich den Blog sehr empfehlen. Es ist erfreulich, dass auch andere SPS-Programmierer sich mit dem Testen ihrer Software auseinander setzen. Auch das Buch The Art of Unit Testing von Roy Osherove ist ein guter Einstieg in das Thema. Auch wenn das Buch nicht für die IEC 61131-3 geschrieben wurde, so enthält es doch einige interessante Ansätze, die sich ohne Probleme auch in der SPS umsetzen lassen.

Abschließend will ich mich noch bei meinen Kollegen Birger Evenburg und Nils Johannsen bedanken. Als Grundlage für diesen Post diente eine SPS-Bibliothek, die mir freundlicherweise von beiden zur Verfügung gestellt wurde.


Holger Schwichtenberg: .NET 4.7.1 erkennen

Mit dem Erscheinen von .NET Framework 4.7.1 am 19. Oktober 2017 ist eine neue .NET-Version hinzugekommen.

MSDN Team Blog AT [MS]: Einladung: Mobile Industry Solutions

AIT: Neu in TFS 2018: Neues für Build und Release Extensions

Extensions bieten die Möglichkeiten den TFS um weitere Funktionalität an vordefinierten Stellen, sogenannten Extension Points, zu erweitern. Im Build- und Releasemanagement gibt es hierfür einige Anwendungsfälle: zusätzliche Menüeinträge an Build- und Release Definitionen, eigene Tasks, zusätzliche Informationen in Build- und Release Zusammenfassungen (Build- und Release Summary), u.v.m.

In vorhergehenden Beiträgen unserer Blogserie “Neu in TFS 2018” wurden bereits einige spannende Neuerungen für das Build- und Releasemanagement für die neueste Major Version des TFS, wie z.B. Task Groups, beschrieben. Mit diesem Blogbeitrag möchten wir Ihnen nun die Neuigkeiten für Extension-Entwickler(innen) in diesem Bereich näherbringen sowie zwei Neuerungen für server-seitige Tasks im Release Management.

Build

Bereits seit Einführung des JSON-Buildsystems existieren Build Templates, welche häufig verwendete Tasks für bestimmte Szenarien wie z.B. das Bauen einer .NET Desktop Anwendung zusammenfassen. Diese können als Ausgangspunkt für Buildprozesse verwendet werden und z.B. durch Hinzufügen und Entfernen von Tasks für eigene Zwecke weiter angepasst werden.

Vor allem in größeren Firmen existieren oftmals standardisierte Abläufe, welche sich in mehreren Buildprozessen wiederspiegeln jedoch durch die vorgegebenen Build Templates nicht abgedeckt werden. Hierfür ist es hilfreich diese Abläufe mittels eigener Build Templates abbilden und zur Verfügung stellen zu können.  TFS 2018 bietet nun erstmals die Möglichkeit eigene Templates auch über VSTS Marketplace Erweiterungen (VSIX Dateien) zu verteilen. In den Erweiterungs-Metadaten ist hierzu ein neuer Extension Point ms.vss-build.template (vgl. Snippet 1)  eingeführt worden. Ein vollständiges Beispiel für solch eine Extension ist in GitHub zu finden.

{  "id": "AIT Build Template", 
   "type": "ms.vss-build.template", 
   "targets": [ "ms.vss-build.templates" ], 
   "properties": { "name": "AIT Build Template" } 
}

            Snippet 1: Auszug aus der Definition eines Custom Build Templates

Eine weitere Neuerung im Bereich der Build Erweiterungen ist die Möglichkeit eigens definierte Abschnitte in Build Zusammenfassungen ein- und auszublenden. Bisher wurden solche Abschnitte immer angezeigt, unabhängig davon ob die Tasks, welche die Informationen dafür zur Verfügung stellen, ausgeführt wurden oder nicht. Im letzteren Fall wurde also ggf. ein leerer Abschnitt in der Build Zusammenfassung angezeigt.

TFS 2018 stellt nun die neue Methode setSectionVisibility (vgl. Snippet 2) zur Verfügung. Diese kann im Quellcode einer Extension, welche den Abschnitt der Build Zusammenfassung definiert, verwendet werden um situationsabhängig die gewünschten Informationen und Abschnitte ein- und auszublenden. Für ein vollständiges Beispiel sei an dieser Stelle ebenfalls auf GitHub verwiesen.

VSS.getConfiguration().setSectionVisibility("$(publisherId).$(extensionId).$(sectionId)", true);

            Snippet 2: Einblenden von Custom Build Summary Sections

Release

Tasks in Release Definitionen können in verschiedenartigen Phasen ausgeführt werden. Eine Ausprägung davon ist die sogenannte Agentless Phase. Diese benötigt weder einen Agenten noch sonstige Zielmaschinen. Für Tasks, welche in solch einer Agentless Phase ausgeführt werden, sogenannte server-seitige Tasks, bringt TFS 2018 zwei Neuerungen:

Der neue Task Invoke REST API erlaubt es einen HTTP REST API-Aufruf aus dem Release heraus auszuführen (vgl. Abbildung 1). Hierzu ist es notwendig, im Vorhinein einen Generic Endpoint zu definieren, gegen welchen der Aufruf dann ausgeführt wird.

Der neue Task findet beispielsweise in folgendem Szenario Verwendung: Ein externer Dienst generiert anhand der ID eines Releases die zugehörigen Release Notes auf Basis der enthaltenen Work Items. Dieser Dienst kann mittels des Invoke Rest API-Tasks aus dem Release heraus ausgerufen werden und bis zur Fertigstellung der Release Notes gewartet werden.

Abbildung 1: Neuer server-seitiger Task Invoke REST API

Die zweite Neuerung bringt den Konfigurationsabschnitt Control Options nun auch für server-seitige Tasks. Die bereits bekannten Optionen Enabled, Continue on error, Always run sowie die Definition eines Timeouts steht damit nun auch für Tasks in Agentless Phasen zur Verfügung (vgl. Abbildung 2) und ermöglichen somit auch hier eine genauere Steuerung der Ausführung.

image

Abbildung 2: Control options

Build und Release

Eine Neuerung, welche sich sowohl im Build- als auch Releasemanagement wiederfindet, ist die Möglichkeit eigene Build Tasks als veraltet (Deprecated) zu kennzeichnen. Um Nutzern den Umstieg auf neuere Task-Versionen zu erleichtern, ist es empfehlenswert einen entsprechenden Hinweis in der Beschreibung der Tasks zu vermerken.

Um einen eigenen Task als veraltet zu markieren, muss das entsprechende Attribut in der Task Definition gesetzt werden:

"deprecated": true

Alle Deprecated Tasks werden im Task Katalog am Ende der Liste in einem eigenen Abschnitt Deprecated tasks zusammengefasst. Sowohl im Task Katalog als auch in Build- und Releasedefinition werden die Tasks zusätzlich als Deprecated gekennzeichnet (vgl. Abbildung 3).

image

Abbildung 3: Deprecated tasks im Task Katalog

Fazit

Auch im Bereich der Erweiterungen zeigt sich wieder einmal: das „neue alte“ Build- und Releasesystem wird erwachsen! Mit der Verteilung von eigenen Build Templates über VSTS- / TFS-Erweiterungen, der Möglichkeit eigene Tasks als veraltet zu markieren etc. geht Microsoft weitere Schritte um den Nutzern und Entwicklern von Erweiterungen das Leben leichter zu machen.

Bleiben Sie dran und begleiten Sie uns auch weiter bei der Entdeckung spannender Neuigkeiten in TFS 2018 in den Folgebeiträgen unserer Blogserie.

Holger Schwichtenberg: Neues Buch zu Windows PowerShell 5.1 und PowerShell Core 6.0

Die Neuauflage von Holger Schwichtenbergs Fachbuch zur PowerShell behandelt nun neben der Windows PowerShell auch die plattformunabhängige PowerShell Core.

Golo Roden: Einführung in React, Folge 3: Eingaben verarbeiten

Mit React Eingaben zu verarbeiten ist auf den ersten Blick gar nicht so einfach, denn React kennt zwei Arten von Komponenten, deren Zustandsverwaltung voneinander abweicht. Außerdem gilt es zu steuern, wie Events verteilt werden. Wie funktioniert das?

MSDN Team Blog AT [MS]: Herbst Update der österreichischen PowerShell Community

Hallo PowerShell Gemeinde!

Auch im letzten Monat war einiges los bei uns

Künftige Veranstaltungen

Vergangene Veranstaltungen

Wir hatten 2 sehr spannende und gut besuchte Veranstaltungen. Eine in Wien bei ETC und eine in Linz/Leondig bei Cubido.

Newsletter - die "Schnipseljagd"

Unsere wöchentlichen PowerShell Newsletter kamen regelmäßig raus und beinhalteten folgende Themen:

Hoffe es  war für dich etwas dabei!
Die PowerShell UserGroup Austria
www.powershell.co.at

Code-Inside Blog: Introducing Electron.NET - building Electron Desktop Apps with ASP.NET Core

x

The last couple of weeks I worked with my buddy Gregor Biswanger on a new project called Electron.NET.

As you might already guess: It is some sort of bridge between the well known Electron and .NET.

If you don’t know what Electron is: It helps to build desktop apps written in HTML/CSS/Javascript

The idea

Gregor asked me a while ago if it is possible to build desktop apps with ASP.NET Core (or .NET Core in general) and - indeed - there are some ideas how to make it, but unfortunatly there is no “official” UI stack available for .NET Core. After a little chat we agreed that the best bet would be to use Electron as is and somehow “embed” ASP.NET Core in it.

I went to bed, but Gregor was keen on to build a prototyp and he did it: He was able to launch the ASP.NET Core application inside the electron app and invoke some Electron APIs from the .NET World.

First steps done, yeah! In the following weeks Gregor was able to “bridge” most Electron APIs and I could help him with the tooling via our dotnet-extension.

Overview

The basic functionality is not too complex:

  • We ship a “standard” (more or less blank) Electron app
  • Inside the Electron part two free ports are searched:
    • The first free port is used inside the Electron app itself
    • The second free port is used for the ASP.NET Core process
  • The app launches the .NET Core process with ASP.NET Core port (e.g. localhost:8002) and injects the first port as parameter
  • Now we have a Socket.IO based linked between the launched ASP.NET Core app and the Electron app itself - this is our communication bridge!

At this point you can write your Standard ASP.NET Core Code and can communicate via our Electron.API wrapper to the Electron app.

Gregor did a fabulous blogpost with a great example.

Interested? This way!

If you are interested, maybe take a look at the ElectronNET-Org on GitHub. The complete code is OSS and there are two demo repositories.

No way - this is a stupid idea!

The last days were quite intersting. We got some nice comments about the project and (of course) there were some critics.

As far as I know the current “this is bad, because… “-list is like this:

  • We still need node.js and Electron.NET is just a wrapper around Electron: Yes, it is.
  • Perf will suck: Well… to be honest - the current startup time does really suck, because we not only launch the Electron stuff, but we also need to start the .NET Core based WebHost - maybe we will find a solution
  • Starting a web server inside the app is bad on multiple levels because of security and perf: I agree, there are some ideas how to fix it, but this might take some time.

There are lots of issues open and the project is pretty young, maybe we will find a solution for the above problems, maybe not.

Final thoughts

The interesting point for me is, that we seem to hit a nerf with this project: There is demand to write X-Plat desktop applications.

We are looking for feedback - please share your opinion on the ElectronNET-GitHub-Repo or try it out :)

Desktop is dead, long live the desktop!

Holger Schwichtenberg: Zeichen in Microsoft SQL Server ersetzen

Während man für mehrere Zeichenersetzungen bisher mehrere verschachtelte Aufrufe der Replace()-Funktion brauchte, geht es seit Microsoft SQL Server 2017 effizienter mit der neuen Translate()-Funktion.

Manfred Steyer: Generating Custom Code with the Angular CLI and Schematics

Since some versions, the Angular CLI uses a library called Schematics to scaffold building blocks like components or services. One of the best things about this is that Schematics allows to create own code generators too. Using this extension mechanism, we can modify the way the CLI generates code. But we can also provide custom collections with code generators and publish them as npm packages. A good example for this is Nrwl's Nx which allows to generated boilerplate code for Ngrx or upgrading an existing application from AngularJS 1.x to Angular.

These code generators are called Schematics and can not only create new files but also modify existing ones. For instance, the CLI uses the latter to register generated components with existing modules.

In this post, I'm showing how to create a collection with a custom Schematic from scratch and how to use it with an Angular project. The sources can be found here.

In addition to this, you'll find a nice video with Mike Brocchi from the CLI-Team explaining the basics and ideas behind Schematics here.

The public API of Schematics is currently experimental and can change in future.
Angular Labs

Goal

To demonstrate how to write a simple Schematic from scratch, I will build a code generator for a Bootstrap based side menu. With an respective template like the free ones at Creative Tim the result could look like this:

Solution

Before creating a generator it is a good idea to have an existing solution that contains the code you want to generate in all variations.

In our case, the component is quite simple:

import { Component, OnInit } from '@angular/core';

@Component({
    selector: 'menu',
    templateUrl: 'menu.component.html'
})
export class MenuComponent {
}

In addition to that, the template for this component is just a bunch of html tags with the right Bootstrap based classes -- something I cannot learn by heart what's the reason a generator seems to be a good idea:

<div class="sidebar-wrapper">
    <div class="logo">
        <a class="simple-text">
            AppTitle
        </a>
    </div>
    <ul class="nav">

        <li>
            <a>
                <i class="ti-home"></i>
                <p>Home</p>
            </a>
        </li>

        <!-- add here some other items as shown before -->
    </ul>
</div>

In addition to the code shown before, I want also have the possibility to create a more dynamic version of this side menu. This version uses an interface MenuItem to represent the items to display:

export interface MenuItem {
    title: string;
    iconClass: string;
}

A MenuService is providing instances of MenuItem:

import { MenuItem } from './menu-item';

export class MenuService {

    public items: MenuItem[] = [
        { title: 'Home', iconClass: 'ti-home' },
        { title: 'Other Menu Item', iconClass: 'ti-arrow-top-right' },
        { title: 'Further Menu Item', iconClass: 'ti-shopping-cart'},
        { title: 'Yet another one', iconClass: 'ti-close'}
    ];

}

The component gets an instance of the service by the means of dependency injection:

import { Component, OnInit } from '@angular/core';
import { menuItem } from './menu-item';
import { menuService } from './menu.service';

@Component({
    selector: 'menu',
    templateUrl: './menu.component.html',
    providers:[MenuService]
})
export class MenuComponent {

    items: MenuItem[];

    constructor(service: MenuService) {
        this.items = service.items;
    }
}

After fetching the MenuItems from the service the component iterates over them using *ngFor and creates the needed markup:

<div class="sidebar-wrapper">
    <div class="logo">
        <a class="simple-text">
            AppTitle
        </a>
    </div>
    <ul class="nav">
        <li *ngFor="let item of items">
            <a href="#">
                <i class="{{item.iconClass}}"></i>
                <p>{{item.title}}</p>
            </a>
        </li>
    </ul>
</div>

Even though this example is quite easy it provides enough stuff to demonstrate the basics of Schematics.

Scaffolding a Collection for Schematics ... with Schematics

To provide a project structure for an npm package with a Schematics Collection, we can leverage Schematics itself. The reason is that the product team provides a "meta schematic" for this. To get everything up and running we need to install the following npm packages:

  • @angular-devkit/schematics for executing Schematics
  • @schematics/schematics for scaffolding a Collection
  • ```rxjs`` which is a needed transitive dependency

For the sake of simplicity, I've installed them globally:

npm i -g @angular-devkit/schematics
npm i -g @schematics/schematics
npm i -g rxjs

In order to get our collection scaffolded we just need to type in the following command:

schematics @schematics/schematics:schematic --name nav

The parameter @schematics/schematics:schematic consists of two parts. The first part -- @schematics/schematics -- is the name of the collection, or to be more precise, the npm package with the collection. The second part -- schematic -- is the name of the Schematic we want to use for generating code.

After executing this command we get an npm package with a collection that holds three demo schematics:

npm package with collection

The file collection.json contains metadata about the collection and points to the schematics in the three sub folders. Each schematic has meta data of its own describing the command line arguments it supports as well as generator code. Usually, they also contain template files with placeholders used for generating code. But more about this in the following sections.

Before we can start, we need to npm install the dependencies the generated package.json points to. In addition to that, it is a good idea to rename its section dependencies to devDependencies because we don't want to install them when we load the npm package into a project:

{
  "name": "nav",
  "version": "0.0.0",
  "description": "A schematics",
  "scripts": {
    "build": "tsc -p tsconfig.json",
    "test": "npm run build && jasmine **/*_spec.js"
  },
  "keywords": [
    "schematics"
  ],
  "author": "",
  "license": "MIT",
  "schematics": "./src/collection.json",
  "devDependencies": {
    "@angular-devkit/core": "^0.0.15",
    "@angular-devkit/schematics": "^0.0.25",
    "@types/jasmine": "^2.6.0",
    "@types/node": "^8.0.31",
    "jasmine": "^2.8.0",
    "typescript": "^2.5.2"
  }
}

As you saw in the last listing, the packages.json contains a field schematics which is pointing to the file collection.json to inform about the metadata.

Adding an custom Schematic

The three generated schematics contain comments that describe quite well how Schematics works. It is a good idea to have a look at them. For this tutorial, I've deleted them to concentrate on my own schematic. For this, I'm using the following structure:

Structure for custom Schematics

The new folder menu contains the custom schematic. It's command line arguments are described by the file schema.json using a json schema. The described data structure can also be found as an interface within the file schema.ts. Normally it would be a good idea to generate this interface out of the schema but for this easy case I've just handwritten it.

The index.ts contains the so called factory for the schematic. This is a function that generates a rule (containing other rules) which describes how the code can be scaffolded. The templates used for this are located in the files folder. We will have a look at them later.

First of all, let's update the collection.json to make it point to our menu schematic:

{
    "schematics": {
      "menu": {
        "aliases": [ "mnu" ],
        "factory": "./menu",
        "description": "Generates a menu component",
        "schema": "./menu/schema.json"
      }
    }
}

Here we have an property menu for the menu schematic. This is also the name we reference when calling it. The array aliases contains other possible names to use and factory points to the file with the schematic's factory. Here, it points to ./menu which is just a folder. That's why the factory is looked up in the file ./menu/index.js.

In addition to that, the collection.json also points to the schema with the command line arguments. This file describes a property for each possible argument:

{
    "$schema": "http://json-schema.org/schema",
    "id": "SchemanticsForMenu",
    "title": "Menu Schema",
    "type": "object",
    "properties": {
      "name": {
        "type": "string",
        "default": "name"
      },
      "path": {
        "type": "string",
        "default": "app"
      },
      "appRoot": {
        "type": "string"
      },
      "sourceDir": {
        "type": "string",
        "default": "src"
      },
      "menuService": {
        "type": "boolean",
        "default": false,
        "description": "Flag to indicate whether an menu service should be generated.",
        "alias": "ms"
      }
    }
  }

The argument name holds the name of the menu component, its path as well as the path of the app (appRoot) and the src folder (sourceDir). These parameters are usually used by all schematics the CLI provides. In addition to that, I've defined a property menuService to indicate, whether the above mentioned service class should be generated too.

The interface for the schema within schema.ts is called MenuOptions:

export interface MenuOptions {
    name: string;
    appRoot: string;
    path: string;
    sourceDir: string;
    menuService: boolean;
}

Schematic Factory

To tell Schematics how to generated the requested code files, we need to provide a factory. This function describes the necessary steps with a rule which normally makes use of further rules:

import { MenuOptions } from './schema';
import { Rule, [...] } from '@angular-devkit/schematics';
[...]
export default function (options: MenuOptions): Rule {
    [...]
}

For this factory, I've defined two helper constructs at the top of the file:

import { dasherize, classify } from '@angular-devkit/core';
import { MenuOptions } from './schema';
import { filter, Rule, [...] } from '@angular-devkit/schematics';

[...]

const stringUtils = { dasherize, classify };

function filterTemplates(options: MenuOptions): Rule {
  if (!options.menuService) {
    return filter(path => !path.match(/\.service\.ts$/) && !path.match(/-item\.ts$/) && !path.match(/\.bak$/));
  }
  return filter(path => !path.match(/\.bak$/));
}

[...]

The first one is the object stringUtils which just groups some functions we will need later within the templates: The function dasherize transforms a name into its kebab case equivalent which can be used as a file name (e. g. SideMenu to side-menu) and classify transforms into Pascal case for class names (e. g. side-menu to SideMenu).

The function filterTemplates creates a Rule that filters the templates within the folder files. For this, it delegates to the existing filter rule. Depending on whether the user requested a menu service, more or less template files are used. To make testing and debugging easier, I'm excluding .bak in each case.

Now let's have a look at the factory function:

import { chain, mergeWith } from '@angular-devkit/schematics';
import { dasherize, classify } from '@angular-devkit/core';
import { MenuOptions } from './schema';
import { apply, filter, move, Rule, template, url, branchAndMerge } from '@angular-devkit/schematics';
import { normalize } from '@angular-devkit/core';

[...]

export default function (options: MenuOptions): Rule {

    options.path = options.path ? normalize(options.path) : options.path;
    
    const templateSource = apply(url('./files'), [
        filterTemplates(options),
        template({
          ...stringUtils,
          ...options
        }),
        move(options.sourceDir)
      ]);
      
      return chain([
          mergeWith(templateSource)
      ]);

}

At the beginning, the factory normalizes the path the caller passed in. This means that it deals with the conventions of different operating systems, e. g. using different path separators (e. g. / vs. \).

Then, it uses apply to apply all templates within the files folder to the passed rules. After filtering the available templates they are executed with the rule returned by template. The passed properties are used within the templates. This creates a virtual folder structure with generated files that is moved to the sourceDir.

The resulting templateSource is a Source instance. It's responsibility is creating a Tree object that represents a file tree which can be either virtual or physical. Schematics uses virtual file trees as a staging area. Only when everything worked, it is merged with the physical file tree on your disk. You can also think about this as committing a transaction.

At the end, the factory returns a rule created with the chain function (which is a rule too). It creates a new rule by chaining the passed ones. In this example we are just using the rule mergeWith but the enclosing chain makes it extendable.

As the name implies, mergeWith merges the Tree represented by templateSource with the tree which represents the current Angular project.

Templates

Now it's time to look at our templates within the files folder:

Folder with Templates

The nice thing about this is that the file names are templates too. For instance __x__ would be replaced with the contents of the variable x which is passed to the template rule. You can even call functions to transform these variables. In our case, we are using __name@dasherize__ which passes the variable name to the function dasherize which in turn is passed to template too.

The easiest one is the template for the item class which represents a menu item:

export interface <%= classify(name) %>Item {
    title: string;
    iconClass: string;
}

Like in other known template languages (e. g. PHP), we can execute code for the generation within the delimiters <% and %>. Here, we are using the short form <%=value%> to write a value to the generated file. This value is just the name the caller passed transformed with classify to be used as a class name.

The template for the menu service is build in a similar way:

import { <%= classify(name) %>Item } from './<%=dasherize(name)%>-item';

export class <%= classify(name) %>Service {

    public items: <%= classify(name) %>Item[] = [
        { title: 'Home', iconClass: 'ti-home' },
        { title: 'Other Menu Item', iconClass: 'ti-arrow-top-right' },
        { title: 'Further Menu Item', iconClass: 'ti-shopping-cart'},
        { title: 'Yet another one', iconClass: 'ti-close'}
    ];
}

In addition to that, the component template contains some if statements that check whether a menu service should be used:

import { Component, OnInit } from '@angular/core';
<% if (menuService) { %>
import { <%= classify(name) %>Item } from './<%=dasherize(name)%>-item';
import { <%= classify(name) %>Service } from './<%=dasherize(name)%>.service';
<% } %>

@Component({
    selector: '<%=dasherize(name)%>',
    templateUrl: '<%=dasherize(name)%>.component.html',
    <% if (menuService) { %>
        providers: [<%= classify(name) %>Service]
    <% } %>
})
export class <%= classify(name) %>Component {

<% if (menuService) { %>
    items: <%= classify(name) %>Item[];

    constructor(service: <%= classify(name) %>Service) {
        this.items = service.items;
    }
<% } %>

}

The same is the case for the component's template. When the caller requested a menu service, it's using it; otherwise it just gets hardcoded sample items:

<div class="sidebar-wrapper">
    <div class="logo">
        <a class="simple-text">
            AppTitle
        </a>
    </div>
    <ul class="nav">

<% if (menuService) { %>
    <li *ngFor="let item of items">
        <a>
            <i class="{{item.iconClass}}"></i>
            <p>{{item.title}}</p>
        </a>
    </li>

<% } else { %>
        <li>
            <a>
                <i class="ti-home"></i>
                <p>Home</p>
            </a>
        </li>

        <li>
            <a>
                <i class="ti-arrow-top-right"></i>
                <p>Other Menu Item</p>
            </a>
        </li>

		<li>
			<a>
				<i class="ti-shopping-cart"></i>
				<p>Further Menu Item</p>
			</a>
		</li>

		<li>
			<a>
				<i class="ti-close"></i>
				<p>Yet another one</p>
			</a>
		</li>
        <% } %>
    </ul>
</div>

Building and Testing with an Sample Application

To build the npm package, we just need to call npm run build which is just triggering the TypeScript compiler.

For testing it, we need a sample application that can be created with the CLI. Please make sure to use Angular CLI version 1.5 RC.4 or higher.

For me, the easiest way to test the collection was to copy the whole package into the sample application's node_module folder so that everything ended up within node_modules/nav. Please make sure to exclude the collection's node_modules folder, so that there is no folder node_modules/nav/node_modules.

Instead of this, pointing to a relative folder with the collection should work too. In my experiments, I did with a release candidate, this wasn't the case (at least not in any case).

After this, we can use the CLI to scaffold our side menu:

ng g menu side-menu --menuService --collection nav

Here, menu is the name of the schematic, side-menu the file name we are passing and nav the name of the npm package.

Using the Schematic

After this, we need to register the generated component with the AppModule:

import { SideMenuComponent } from './side-menu/side-menu.component';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent,
    SideMenuComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

In an other post, I will show how to even automate this task with Schematics.

After this, we can call the component in our AppModule. The following sample also contains some boiler blade for the Bootstrap Theme used in the initial screen shot.

<div class="wrapper">
  <div class="sidebar" data-background-color="white" data-active-color="danger">

      <side-menu></side-menu>
  
  </div>
  <div class="main-panel">
      <div class="content">
        <div class="card">
          <div class="header">
            <h1 class="title">Hello World</h1>
          </div>
          <div class="content">
            <div style="padding:7px">
             Lorem ipsum ...
            </div>
          </div>
        </div>
      </div>
  </div>
</div>

To get Bootstrap and the Bootstrap Theme, you can download the free version of the paper theme and copy it to your assets folder. Also reference the necessary files within the file .angular-cli.json to make sure they are copied to the output folder:

[...]
"styles": [
  "styles.css",
  "assets/css/bootstrap.min.css",
  "assets/css/paper-dashboard.css",
  "assets/css/demo.css",
  "assets/css/themify-icons.css"
],
[...]

After this, we can finally run our application: ng serve.

AIT: Neu in TFS 2018: Erweiterungen der Pull Requests

Damit der Code einer Software eine hohe Qualität aufweist sind regelmäßige Reviews Pflicht. Pull Requests (PR) bieten für Reviews eine tolle Plattform. Sie stellen eine Art Benachrichtigung dar, in der ein Verantwortlicher Anton eine andere Entwicklerin Berta darum bittet den neuen Code in den bestehenden zu integrieren. Entwicklerin Berta kann nun Feedback liefern und eine Diskussion beginnen, woraufhin Anton den Code wieder ändert, um schlussendlich wiederum von Berta abgesegnet zu werden. Feedback zu Code wird dabei in Form von Kommentaren hinterlassen.  Auch bei den bereits vorgestellten Git Forks, welche nun neu in TFS 2018 dazu gekommen sind, kommen Pull Requests zum Einsatz um bspw. Erweiterungen von außen in das Projekt zu integrieren.

Mit TFS 2018 kommen nun einige Neuerungen in den Bereich der Pull Requests, welche die Benutzbarkeit dieser deutlich erhöhen. Wir stellen Ihnen die Wichtigsten vor.

Zuordnung von Kommentaren

Ein großes Problem mit Pull Requests im TFS war bisher, dass Kommentare nach einem Update des Codes oftmals nun an einer falschen Stelle sichtbar waren, da Kommentare lediglich einer Zeile zugewiesen sind. Dadurch konnte es durchaus vorkommen, dass der Kommentar in Kombination mit dem neuen Code keinerlei Sinn mehr ergab oder im schlimmsten Fall sogar zu Feedback welches im Zusammenhang mit dem neuen Code falsch verstanden werden kann. Hier wurde im TFS 2018 deutlich nachgebessert.
In der Übersicht eines Pull Requests gibt es nun die Möglichkeit bei Kommentaren das originale Diff des Codes zu betrachten. Hierzu wurde ein Button zur Verfügung gestellt, welcher es ermöglicht zwischen dem aktuellen und dem originalen Diff hin- und herzuwechseln.

Filterung

Auch im Bereich der Übersichtlichkeit wurde nachgelegt. Es besteht nun die Möglichkeit Kommentare sowie Dateien zu filtern. Kommentare lassen sich im Reiter Overview praktisch nach ihrem Zustand (Active, Resolved) oder ihrer Aktualität filtern.

Im Bereich Files lassen sich diese sogar gänzlich ausblenden um mehr Übersicht über den Code zu erhalten. Dabei bleibt jedoch neben der kommentierten Zeile das Profilbild des Reviewers erhalten und über eine Quickinfo erhält man einen Ausschnitt des Kommentars. Nach einem Klick auf das Profilbild lässt sich außerdem der gesamte Kommentar öffnen.

Bei einer Vielzahl an Änderungen konnte es im Files Bereich schnell unübersichtlich werden wodurch das Auffinden einer Datei unnötig Zeit kosten konnte. Hier wurde dem TFS 2018 eine Sucheingabe spendiert, welche nicht nur nach Dateinamen, sondern auch nach Ordnernamen, partiellen Pfaden sowie Erweiterungen filtern lässt. Was wir leider vermissen ist das Filtern mit Pattern wie bspw. „/folder/*.test“.

Aufgabenlisten

Häufig kann es vorkommen, dass innerhalb eines PRs bestimmte Arbeiten abgearbeitet werden sollen. So muss beispielsweise sichergestellt werden, dass die Versionsnummer aktualisiert wird, ein Unit Test bei gefixtem Bug hinzugefügt wird oder einfach etwas getestet werden soll.

Mit dem TFS 2018 gibt es nun die Möglichkeit Aufgabenlisten in die Beschreibung eines Pull Requests oder in einen Kommentar zu integrieren. Die über Markdown eingefügten Kontrollkästchen können nun ohne extra die Beschreibung ändern zu müssen komfortabel abgehakt werden.

Zurücksetzen von Votings

Bei einem Push innerhalb eines Pull Requests ist es wahrscheinlich, dass sich nicht nur Kleinigkeiten ändern. Daher könnte es fatal sein, wenn der Reviewer bereits für ein Approved gestimmt hat, sich nun aber nun grundlegendes verändert hat. Bisher wurde das Approved gespeichert, mit dem TFS 2018 kann es nach einem neuen Push oder mit einer neuen Iteration automatisch zurückgesetzt werden, sodass die Annahme erneut manuell gesetzt werden muss. So können fälschlich abgeschlossene Pull Requests reduziert werden.

Diese Option muss über die Branch Policies im Reiter Versionskontrolle des eingestellt werden.

Abschließen von Work Items mit Abschließen des Pull Requests

Auch im Bereich des Abschließens eines Pull Requests hat Microsoft nachgebessert und Vorgänge automatisiert. Im Kontext eines Projektes beinhaltet ein Pull Request im TFS meist ein Feature, einen Bugfix oder einzelne Tasks. Zwar lassen sich die Work Items mit dem PR verknüpfen, müssen aber nach Abschluss dessen zusätzlich manuell abgeschlossen werden. Auch hier bietet der TFS 2018 eine komfortable Lösung, indem er bei Abschluss des Pull Requests neben bekannten Optionen, wie das Löschen des Branches, die Möglichkeit bietet alle verknüpften Work Items automatisch zu schließen.

Die Standardeinstellung hierzu kann, ähnlich wie beim Zurücksetzen der Votings, über die Optionen eines Repositorys gesetzt werden.

Fazit

Der TFS 2018 bessert im Bereich der Pull Requests an den richtigen Stellen nach. Kommentare sind nun endlich korrekt zuordenbar und kleine, aber feine Erweiterungen vereinfachen den Workflow mit diesem Instrument deutlich.

 

AIT: Neu in TFS 2018: Jetzt auch mit Git-Forks

Mit dem neuen Update halten Git-Forks im TFS 2018 Einzug. Egal ob es der Student oder der Kollege aus einer anderen Abteilung ist, welcher eine neue Funktion implementieren möchte und deshalb Zugriff auf mein Repository benötigt. Mögliche Anwendungsfälle für Git-Forks innerhalb von der TFS-Umgebung gibt es reichlich.

Ich, als Besitzer eines Repository möchte nicht, dass Personen, welche nur einen temporären Zugriff auf mein Repository benötigen, schreibend auf mein Repository einwirken können. Ein Grund dafür ist die erhöhte Administration dieser Benutzer. Zugriff geben und dann wieder nehmen, wenn die Zeit abgelaufen ist, erhöht den Administrationsaufwand.
Eine Lösung des Problems sind Git-Forks. Damit kann ich Personen, welche nur Leserechte auf mein Repository besitzen, an meinem Projekt mitwirken lassen. Jede Person, welche einen Fork erstellen möchte, muss das Recht besitzen Repositories im TFS anzulegen und benötigt Leserecht auf das gewünschte Projekt. Über einen Pull Request kann der Besitzer des Repositories die Änderungen aus dem Git-Fork übernommen werden.
Dadurch bleibt mein Repository übersichtlich und ich genieße trotzdem den Vorzug einer offenen Community innerhalb ihrer TFS Umgebung. Außerdem wird die Administration erleichtert.

Beispiel:

Im Folgenden zeige ich, wie ein Fork angelegt wird und wie Änderungen über einen Pull Request an das ursprüngliche Repository zurückgegeben werden.

Voraussetzung:
Der Benutzer muss mindestens das Recht besitzen sich eigene Repository in seinem Team Projekt (Studenten) anzulegen (Abbildung 1) und lesend auf das gewünschte Repository (CMMI) zugreifen zu können (Abbildung 2).


Abbildung 1: Rechte, auf das „Ziel“ Repository im eigenem Team Projekt

Abbildung 2: Ausschließlich Leserecht auf das „orginal“ Repository.

 

Über die Schaltfläche „Fork“ im Hub „Code“ wird ein neuer Fork erstellt.


Abbildung 3: Schaltfläche „Fork“ unter Code

 

Im folgenden Popup muss jetzt ein Name für das neue Repository (HelloWorldNewFork) und das Projekt (Studenten) ausgewählt werden.


Abbildung 4+5: Anlegen eines neuen Fork.

 

Wird das Team Projekt nicht geändert, so bekommt der Benutzer den Fehler, dass er keine ausreichenden Rechte im ausgewählten Projekt hat.

Nachdem ein neuer Name für das Repository festgelegt und ein Projekt zugewiesen werden konnte, kann damit wie gewohnt gearbeitet werden. Jetzt befindet sich eine Kopie des „orginal“ Repository im Projekt des Benutzers.


Abbildung 6: Neues Repository

 

Über einen Pull Request werden die Änderung wieder in das Team Projekt CMMI übernommen.



Abbildung 7 + 8: Pull Request nachdem Commit erstellen.

 

Der Pull Request verhält sich, wie es der Benutzer vom TFS bereits gewohnt ist.

Fazit

Mit Git-Forks genießt man den Vorzug einer offenen Community innerhalb einer geschlossenen TFS Umgebung.

 

 

Weitere Schritte auf diesem Weg stellen wir Ihnen auch in nachfolgenden Beiträgen unserer Blogserie vor. Bleiben Sie also dran und freuen Sie sich aufspannende Neuerungen mit TFS 2018.

Jürgen Gutsch: GraphiQL for ASP.​NET Core

One nice thing about blogging is the feedback from the readers. I got some nice kudos, but also great new ideas. One idea was born out of a question about a "graphi" UI for the GraphQL Middleware I wrote some months ago. I never heard about "graphi", which actually is "GraphiQL", a generic HTML UI over a GraphQL endpoint. It seemed to be something like a Swagger UI, but just for GraphQL. That sounds nice and I did some research about that.

What is GraphiQL?

Actually it is absolutely not the same as Swagger and not as detailed as Swagger, but it provides a simple and easy to use UI to play around with your GraphQL end-point. So you cannot really compare it.

GraphiQL is a React component provided by the GraphQL creators, that can be used in your project. It basically provides an input area to write some GraphQL queries and a button to sent that query to your GrapgQL end-point. You'll than see the result or the error on the right side of the UI.

Additionally it provides some more nice features:

  • A history of sent queries, which appears on the left side, if you press the history-button. To reuse previously used queries.
  • It rewrites the URL to support linking to a specific query. It stores the query and the variables in the URL, to sent it to someone else, or to bookmark the query to test.
  • It actually creates a documentation out of the GraphQL end-point. By clicking at the "Docks" link it opens a documentation about the types used in this API. This is really magic, because it shows the documentation of a type I never requested:

Implementing GraphiQL

The first idea was to write something like this by my own. But it should be the same as the existing GraphiQL UI. Why not using the existing implementation? Thanks to Steve Sanderson, we have the Node Services for ASP.NET Core. Why not running the existing GraphiQL implementation in a Middleware using the NodeServices?

I tried it with the "apollo-server-module-graphiql" package. I called this small JavaScript to render the graphiql UI and return it back to C# via the NodeSerices:

var graphiql = require('apollo-server-module-graphiql');

module.exports = function (callback, options) {
    var data = {
        endpointURL: options.graphQlEndpoint
    };

    var result = graphiql.renderGraphiQL(data);
    callback(null, result);
};

The usage of that script inside the Middleware looks like this:

var file = _env.WebRootFileProvider.GetFileInfo("graphiql.js");
var result = await _nodeServices.InvokeAsync<string>(file.PhysicalPath, _options);
await httpCont

That works great, but has one problem. It wraps the GraphQL query in a JSON-Object that was posted to the GraphQL end-point. I would need to change the GraphQlMiddleware implementation, because of that. The current implementation expects the plain GraphQL query in the POST body.

What is the most useful approach? Wrapping the GraphQL query in a JSON object or sending the plain query? Any Ideas? What do you use? Please tell me by dropping a comment.

With this approach I'm pretty much dependent to the Apollo developers and need to change my implementation, whenever they change their implementations.

This is why I decided to use the same concept of generating the UI as the "apollo-server-module-graphiql" package but implemented in C#. This unfortunately doesn't need the NodeServices anymore.

I use exact the same generated code as this Node module, but changed the way the query is send to the server. Now the plain query will be sent to the server.

I started playing around with this and added it to the existing project, mentioned here: GraphQL end-point Middleware for ASP.NET Core.

Using the GraphiqlMiddleware

The result is as easy to use as the GraphQlMiddleware. Let's see how it looks to add the Middlewares:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
	// adding the GraphiQL UI
    app.UseGraphiql(options =>
    {
        options.GraphiqlPath = "/graphiql"; // default
        options.GraphQlEndpoint = "/graph"; // default
    });
}
// adding the GraphQL end point
app.UseGraphQl(options =>
{
    options.GraphApiUrl = "/graph"; // default
    options.RootGraphType = new BooksQuery(bookRepository);
    options.FormatOutput = true; // default: false
});

As you can see the second Middleware is bound to the first one by using the same path "/graph". I didn't create any hidden dependency between the both Middlewares, to make it ease to use it in various combinations. Maybe you want to use the GraphiQL UI only in the Development or Staging environment as shown in this example.

Now start the web using Visual Studio (press [F5]). The web starts with the default view or API. Add "graphiql" to the URL in the browsers address bar and see what happens. You should see a generated UI for your GraphQL endpoint, where you can now start playing around with our API, testing and debugging it with your current data. (See the screenshots on top.)

I'll create a separate NuGet package for the GraphiqlMiddleware. This will not have the GraphQlMiddleware as a dependency and could be used completely separate.

Conclusion

This was a lot easier to implement than expected. Currently there is still some refactoring needed:

  • I don't like to have the HMTL and JavaScript code in the C#. I'd like to load that from an embedded resource file, which actually is a HTML file.
  • I should add some more configuration options. E.g. to change the theme, as equal to the original Node implementation, to preload queries and results, etc.
  • Find a way to use it offline as well. Currently there's a connection to the internet needed to load the CSS and JavaScripts from the CDNs.

You wanna try it? Download, clone or fork the sources on GitHub.

What do you think about that? Could this be useful to you? Please leave a comment and tell me about your opinion.

Update [10/26/2017 21:03]

GraphiQL is much more powerful than expected. I was wondering how the GraphQL create IntelliSense support in the editor and how it creates the documentation. I had a deeper look into the traffic and found two more cool things about it:

First: GraphiQL sends a special query to the GraphQL to request for the GraphQL specific documentation. IN this case it looks like this:

  query IntrospectionQuery {
    __schema {
      queryType { name }
      mutationType { name }
      subscriptionType { name }
      types {
        ...FullType
      }
      directives {
        name
        description
        locations
        args {
          ...InputValue
        }
      }
    }
  }

  fragment FullType on __Type {
    kind
    name
    description
    fields(includeDeprecated: true) {
      name
      description
      args {
        ...InputValue
      }
      type {
        ...TypeRef
      }
      isDeprecated
      deprecationReason
    }
    inputFields {
      ...InputValue
    }
    interfaces {
      ...TypeRef
    }
    enumValues(includeDeprecated: true) {
      name
      description
      isDeprecated
      deprecationReason
    }
    possibleTypes {
      ...TypeRef
    }
  }

  fragment InputValue on __InputValue {
    name
    description
    type { ...TypeRef }
    defaultValue
  }

  fragment TypeRef on __Type {
    kind
    name
    ofType {
      kind
      name
      ofType {
        kind
        name
        ofType {
          kind
          name
          ofType {
            kind
            name
            ofType {
              kind
              name
              ofType {
                kind
                name
                ofType {
                  kind
                  name
                }
              }
            }
          }
        }
      }
    }
  }

Try this query and sent it to your GraphQL API using Postman or a similar tool and see what happens :)

Second: GraphQL for .NET knows how to answer that query and sent the full documentation about my data structure to the client like this:

{
    "data": {
        "__schema": {
            "queryType": {
                "name": "BooksQuery"
            },
            "mutationType": null,
            "subscriptionType": null,
            "types": [
                {
                    "kind": "SCALAR",
                    "name": "String",
                    "description": null,
                    "fields": null,
                    "inputFields": null,
                    "interfaces": null,
                    "enumValues": null,
                    "possibleTypes": null
                },
                {
                    "kind": "SCALAR",
                    "name": "Boolean",
                    "description": null,
                    "fields": null,
                    "inputFields": null,
                    "interfaces": null,
                    "enumValues": null,
                    "possibleTypes": null
                },
                {
                    "kind": "SCALAR",
                    "name": "Float",
                    "description": null,
                    "fields": null,
                    "inputFields": null,
                    "interfaces": null,
                    "enumValues": null,
                    "possibleTypes": null
                },
                {
                    "kind": "SCALAR",
                    "name": "Int",
                    "description": null,
                    "fields": null,
                    "inputFields": null,
                    "interfaces": null,
                    "enumValues": null,
                    "possibleTypes": null
                },
                {
                    "kind": "SCALAR",
                    "name": "ID",
                    "description": null,
                    "fields": null,
                    "inputFields": null,
                    "interfaces": null,
                    "enumValues": null,
                    "possibleTypes": null
                },
                {
                    "kind": "SCALAR",
                    "name": "Date",
                    "description": "The `Date` scalar type represents a timestamp provided in UTC. `Date` expects timestamps to be formatted in accordance with the [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601) standard.",
                    "fields": null,
                    "inputFields": null,
                    "interfaces": null,
                    "enumValues": null,
                    "possibleTypes": null
                },
                {
                    "kind": "SCALAR",
                    "name": "Decimal",
                    "description": null,
                    "fields": null,
                    "inputFields": null,
                    "interfaces": null,
                    "enumValues": null,
                    "possibleTypes": null
                },
              	[ . . . ]
                // many more documentation from the server
        }
    }
}

This is really awesome. With GraphiQL I got a lot more stuff than expected. And it didn't take more than 5 hours to implement this middleware.

Christian Dennig [MS]: Deploy a hybrid Kubernetes Cluster to Azure Container Service

Lately, I have been working a lot with Kubernetes as one (of many) solutions to run Docker containers in the cloud. Microsoft therefore offers Azure Container Service (ACS), a service to create and (partly) manage a Kubernetes cluster on Azure.

You normally would deploy such a cluster via the Azure Portal or e.g. via the Azure Command Line Interface. Here is a sample command:

az acs create --orchestrator-type kubernetes 
  --resource-group k8s-rg --name myk8scluster --generate-ssh-keys

Unfortunately, you cannot customize all the properties of the Kubernetes deployment with this approach, e.g. if you want to place the cluster in an existing Azure Virtual Network (VNET) or if you want to run multiple node types within the cluster to be able to run Linux and Windows based images/pods in parallel.

To achieve this, you must use the ACS engine which is a kind of “translator” between cluster configurations (which are provided in JSON format) and Azure Resource Manager templates.

ACS Engine

The ACS engine provides a convenient way to generate an ARM template that creates a Kubernetes cluster for you in Azure. The nice thing about it is, that you can influence a lot more properties of the cluster than you can do via the portal or CLI. But more on this later…

If you execute the ACS engine, the resulting template created consists of all the resources, you need to run a cluster in Azure, e.g.:

  • Availability Sets for Master and Agent nodes
  • VMs / VM extensions
  • NICs / VNET configurations
  • Load Balancer
  • etc.

You can deploy the ARM template as you would deploy any other template to Azure by running a Powershell or CLI command, even via the portal.

But let’s get to our sample, creating a hybrid Windows/Linux cluster…

Hybrid Cluster With ACS Engine

We will start by creating a cluster definition file…

Some details on that:

  • First of all, we create the cluster configuration, setting the Kubernetes version to “1.8” (starting line 4)
  • the profile of the master node is set, giving it a name and a VM type  “Standard_D2_v2” (beginning line 8).
  • agent profiles are defined, setting up two profiles. One for Linux nodes, the other one for Windows nodes…also setting the VM size to “Standard_D2_v2” (lines 13 to 27)
  • next, the two profiles are configured, each with the corresponding access information (user name or ssh key), lines 28 to 41
  • the last step is to set the service principal (application id and password, lines 42 to 45), which is needed by Kubernetes to interact with Azure. E.g. when you define a service of type “LoadBalancer”, K8s reaches out to Azure to create an external IP at the Azure Load Balancer
  • the .env file has to be placed in the same folder as the JSON file

In case a SSH key has to be created, you can do this on Windows via PuttyGen or on Linux/Mac via ssh directly. The value of the public key must be specified in the definition file (keyData, line 37).

If no service principal already exists, you can create it using the following command:

az ad sp create-for-rbac 
  --role Contributor --scopes="/subscriptions/[YOUR_SUBSCRIPTION_ID]"

The values from “appId” and “password” must be stored in the corresponding properties of the cluster configuration (lines 43 and 44).

Generate The ARM Template

In order to create the ARM template from the cluster configuration, we first need the ACS-engine binary. You can download it from here. Unzip the contents and place the destination folder into the PATH environment variable, to be able to run it from anywhere on your machine.

Next, run the ACS engine with your configuration…

acs-engine generate .\k8s-hybrid.json

The ACS engine takes the JSON file and generates an _output directory with all the necessary artifacts (among other things, a rather large ARM template + parameters-file) to deploy the K8s cluster to Azure. Here’s a visual representation of the ARM template:

hybrid_cluster

ARMVIZ representation of the resulting template

Next, you simply deploy the template, as you would do it with other ARM templates. First, create a Azure Resource Group:

az group create -n k8scluster-rg -l westeurope

Afterwards, switch to the _output/[CLUSTERNAME] folder and deploy the template:

az group deployment create -g k8scluster-rg 
  --template-file .\azuredeploy.json 
  --parameters .\azuredeploy.parameters.json

After some time, the command returns, telling you that the cluster has been created successfully.

Connect To The Cluster

After creating the cluster, we want to connect to it via kubectl. Therefore we need the configuration of the cluster, which we have to copy by scp or pscp (from Putty) from the master node. To do this, execute this command (I’m on Windows, so I use pscp):

pscp -i [PATH_TO_PRIVATE_SSHKEY_FILE] 
  azureuser@[FQDN_OF_MASTER_NODE]:.kube/config .

If you want to, set the config file you just downloaded as the default file (environment variable KUBECONFIG), otherwise use the config by passing

--kubeconfig=".\config"

to each kubectl command.

So, let’s try to get the nodes of our cluster:

kubectl --kubeconfig=".\config" get nodes

Result:

get_nodes

That looks pretty good! Now let’s connect to the dashboard via…

kubectl --kubeconfig=".\config" proxy

…then open up a browser window and point to:

http://localhost:8001/api/v1/proxy/namespaces/kube-system/services/kubernetes-dashboard/

Now head over to the “Nodes” view and click on one of the “*acs90*” nodes:

dashboard

Nodes

windows_node

A Window node

As you can see, this node runs Windows (one of the node labels is “beta.kubernetes.io/os: windows“)…a Windows version of course, that can run Windows based containers!

Deploy Some Windows And Linux Containers

To test our cluster, we deploy the following template to Kubernetes:

What it will create:

The important part of the deployment file is the node selection for the pods/containers. In lines 25/26, the template defines to select Linux-based nodes to deploy the nginx containers:

nodeSelector:
        beta.kubernetes.io/os: linux

The same is done for the IIS deployment (lines 52/53):

nodeSelector:
        beta.kubernetes.io/os: windows

These properties tell the Kubernetes cluster to create the nginx pods only on Linux and the IIS pods on Windows nodes, magic!

After some time (give it a few “seconds”, the image of the IIS pods is about 5 GB!), you will be able to query the running pods and the services:

result

Et voilà, we are able to connect to the services via a browser…

svc_linux

Linux based container

svc_win

Windows based container

That’s what we wanted to achieve…Linux and Windows containers managed by the same cluster! 🙂

Wrap Up

I hope you could see, how easy it is to deploy a Kubernetes cluster to Azure. In this example, I only showed how to create a hybrid cluster, to be able to run Linux and Windows based containers/pods in parallel. Nevertheless, on the ACS Engine Github repository (where this sample is basically from) you can find many other examples, e.g. to place the cluster in an existing VNET, attach disks to nodes, create a Swarm cluster etc. Check it out, play with ACS…and have fun! 🙂


Holger Schwichtenberg: Buch "Moderne Datenzugriffslösungen mit Entity Framework Core 2.0: Datenbankprogrammierung mit .NET/.NET Core und C#"

Das Buch behandelt auf rund 460 Seiten alle wichtigen Szenarien des Datenbankzugriffs mit Entity Framework Core 2.0 sowie zahlreiche Praxislösungen und Tipps.

Johannes Renatus: Laden von 64Bit DLLs in 32Bit Anwendung mit Reflection

Da könnte man sich fragen wozu man sowas braucht. Aber es gibt immer einen guten Grund und in meinem Fall ging es darum, das ich in einem T4 Template 64Bit DLLs laden musste und diese nach Benutzerdefinierten Attributen zu durchsuchen um dann eine passende Ausgabe zu erstellen. Leider gibt es die Konsolenanwendung mit der VisualStudio […]

Jürgen Gutsch: .NET Core 2.0 and ASP.NET 2.0 Core are here and ready to use

Recently I did a overview talk about .NET Core, .NET Standard and ASP.NET Core at the Azure Meetup Freiburg. I told them about .NET Core 2.0, showed the dotnet CLI and the integration in Visual Studio. Explained the sense of .NET Standard and why developers should take care about it. I also showed them ASP.NET Core, how it works, how to host and explained the main differences to the ASP.NET 4.x versions.

BTW: This Meetup was really great. Well organized on a pretty nice and modern location. It was really fun to talk there. Thanks to Christian, Patrick and Nadine to organize this event :-)

After that talk they asked me some pretty interesting and important questions:

Question 1: "Should we start using ASP.NET Core and .NET Core?"

My answer is a pretty clear YES.

  • Use .NET Standard for your libraries, if you don't have dependencies to platform specific APIs (eg. Registry, drivers, etc.) even if you don't need to be x-plat. Why? Because it just works and you'll keep a door open to share your library to other platforms later on. Since .NET Standard 2.0 you are not really limited, you are able to do almost all with C# you can do with the full .NET Framework
  • Use ASP.NET Core for new web projects, if you don't need to do Web Forms. Because it is fast, lightweight and x-plat. Thanks to .NET standard you are able to reuse your older .NET Framework libraries, if you need to.
  • Use ASP.NET Core to use the new modern MVC framework with the tag helpers or the new lightweight razor pages
  • Use ASP.NET Core to host your application on various cloud providers. Not only on Azure, but also on Amazon and Google:
    • http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/dotnet-core-tutorial.html
    • https://aws.amazon.com/blogs/developer/running-serverless-asp-net-core-web-apis-with-amazon-lambda/
    • https://codelabs.developers.google.com/codelabs/cloud-app-engine-aspnetcore/#0
    • https://codelabs.developers.google.com/codelabs/cloud-aspnetcore-cloudshell/#0
  • Use ASP.NET Core to write lightweight and fast Web API services running either self hosted, in Docker or on Linux, Mac or Windows
  • Use ASP.NET Core to create lightweight back-ends for Angular or React based SPA applications.
  • Use .NET Core to write tools for different platforms

As an library developer, there is almost no reason to not use the .NET Standard. Since .NET Standard 2.0 the full API of the .NET Framework is available and can be used to write libraries for .NET Core, Xamarin, UWP and the full .NET Framework. It also supports to reference full .NET Framework assemblies.

The .NET Standard is an API specification that need to be implemented by the platform specific Frameworks. The .NET Framework 4.6.2, .NET Core 2.0 and Xamarin are implementing the .NET Standard 2.0, which means they all uses the same API (namespaces names, class names, method names). Writing libraries against the .NET Standard 2.0 API will run on .NET Framework 2.0, on .NET Core 2.0, as well as on Xamarin and on every other platform specific Framework that supports that API.

Question 2: Do we need to migrate our existing web applications to ASP.NET Core?

My answer is: NO. You don't need to and I would propose to not do it if there's no good reason to do it.

There are a lot blog posts out there about migrating web applications to ASP.NET Core, but you don't need to, if you don't face any problems with your existing one. There are just a few reasons to migrate:

  • You want to go x-plat to host on Linux
  • You want to host on small devices
  • You want to host in linux based Docker containers
  • You want to use a faster framework
    • A faster framework is useless, if your code or your dependencies are slow ;-)
  • You want to use a modern framework
    • Note: ASP.NET 4.x is not outdated, still supported and still gets new features
  • You want to run your web on a Microsoft Nano Server

Depending on the level of customizing you did in your existing application, the migration could be a lot of effort. Someone needs to pay for the effort, that's why I would propose not to migrate to ASP.NET Core, if you don't have any problems or a real need to do it.

Conclusion

I would use ASP.NET Core for every new web project and .NET Standard for every library I need to write. Because it is almost mature and really usable since the versions 2.0. You can do almost all the stuff, you can do with the full .NET framework.

BTW: Rick Strahl also just wrote an article about that. Please read it. It's great, as almost all of his posts: https://weblog.west-wind.com/posts/2017/Oct/22/NET-Core-20-and-ASPNET-20-Core-are-finally-here

BTW: The slides off that talk are on SlideShare. If you want me to do that talk in your meetup or in your user group, just ping me on twitter or drop me an email

André Krämer: Was tun, wenn die Xamarin App während der iOS Store Prüfung wegen einer Exception abgelehnt wird?

Vermutlich jeder, der bereits eine App geschrieben hat, kennt dieses Gefühl der Ungeduld wenn man die letzten Features der App entwickelt. Man möchte einfach nur noch so schnell wie möglich fertig sein, um sein Werk endlich in den App-Store schieben zu können und es somit der Welt zu präsentieren. Während der letzte Schritt, nämlich das Deployment in den Store, unter Android relativ einfach ist, stellt es unter iOS eine echte Herausforderung dar.

André Krämer: Hilfe! Xamarin iOS Simulator startet nicht und stürzt mit Fehler: 'A fatal error occured when trying to start the server' ab

Ein schönes Feature der Enterprise Edition von Visual Studio ist die Darstellung des iOS Simulators unter Windows. Während man zum Debugging einer Xamarin iOS App den Simulator normalerweise auf dem Mac bedienen muss, auch wenn man das Debugging unter Windows gestartet hat, erlaubt die Enterprise Edition von Visual Studio die Bedienung des Simulators direkt unter Windows. Hat man sich einmal an diese Arbeitsweise gewöhnt, möchte man sie eigentlich nicht mehr missen.

Holger Schwichtenberg: Überflüssige Leerzeichen in Microsoft SQL Server abschneiden

Mit SQL Server 2017 führt Microsoft endlich die Trim()-Funktion ein, mit der man Leerzeichen am Beginn und am Ende einer Zeichenkette entfernen kann.

Christina Hirth : 10 Jahre Open Space – meine Retrospektive

Workshop-Tag:

Seit ein paar Jahren gibt es die Möglichkeit, den Open Space um ein Tag Workshop zu erweitern – wenn einem die zwei Tage Nerdtalk nicht reichen  😉

Ich habe mich diesmal für Tensorflow: Programming Neural Networks mit Sören Stelzer entschieden – und es war großartig. Obwohl ein sehr schwieriges Thema (das Wort Voodoo ist öfter gefallen), ich weiß jetzt genug über Machine Learning und Neuronale Netze, um mit dem Thema gut starten zu können. Ich formuliere es mal so: ich weiß jetzt, was ich weiß und vor allem, was ich nicht weiß und wie wir weiter machen müssen. Und mehr kann man von einem Workshop nicht erwarten. Zusätzlich finde ich, dass Sören eine sehr große Bereicherung für unsere Community ist, die sich genauso weiterentwickeln muss, wie die IT-Welt da draußen. Vielen Dank für dein Engagement!

Eigentlich ein fetten Dank an alle Trainer, die sich bei Community-Events engagieren!!

Erkenntnisse der nächsten 48 Stunden – geclustert:

Agile datengetriebene Entwicklung – war meine eigene Session (das heißt, ich habe das Thema vorgeschlagen, war Themen-Owner aber das war’s dann auch mit den Pflichten).

Ich wollte Tipps und Ideen dazu hören, wie man seine Arbeit nach scrum organisieren kann wenn man Themen beackert, wie Reporting, wo die Features auf große Menge Daten basieren. Es ist eine Sache, ein Testsetup für 2 möglichen Situationen zu schreiben und es ist eine ganz andere, die vielfalt der Situationen in Reporting zu beschreiben.

Take-aways:

  • wir werden damit leben müssen, dass unsere Features, Tests, Erwartungen eventual consistent sind  😀 Wichtig ist, dass wir Annahmen treffen, die wir für den Anfang als “die Wahrheit” betrachten.
  • User labs beauftragen.
  • Measurements weit vor ihre Auswertung einzubauen ist ok, bricht nicht mit dem Konzept “Jedes Feature muss Business Value haben” – auch wenn der echte Business Value erst in 2 Jahren auswertbar ist.
  • Aha-Effekt: In der Welt von Business Teams gibt es keine Fachabteilung. Ich bin in dem Reporting-Team ergo ich bin die Fachabteilung. (finde ich gut, häßliches Wort  😎 )

Stolperfallen mit React

  • unser Internationalisierungskonzept ist richtig (Texte aufteilen nach Modulen/Bereiche/o.ä., ein common Bereich, alles via API in den State laden)
  • Package-Empfehlung: react-intl
  • das Thema so früh, wie möglich berücksichtigen, später kann es richtig weh tun.
  • DevTool-Empfehlung: https://github.com/crysislinux/chrome-react-perf um die Performance der einzelnen React-Componenten zu sehen.
  • (es)Linting Empfehlung um zirkuläre Referenzen zu vermeiden:  “import/no-internal-modules” (Danke @kjiellski)

Wann kann Scrum funktionieren

  • wenn die Möglichkeit besteht, auf Feedback zu reagieren, sprich die Entwickler sind keine Resourcen sondern kreative Menschen.
  • das Team, in dem ich die Ehre habe, unser Produkt mitzugestallten, und @cleverbridge ist führend was agiles Arbeiten betrifft.

Menschen

  • man kann bei Trinkspielen mitmachen, ohne zu trinken
  • nachts träumen, dass der Partner einen enttäuscht hat und danach den ganzen Tag sauer auf ihn sein, ist eine Frauen-Sache (bestätigt von @AHirschmueller und @timur_zanagar) 😀

Nachtrag: fast vergessen, dass

  • wir dank @agross eine super wertvolle Session über dotfiles hatten
  • DDD wird gerade durch Zertifizierung kaputt gemacht, Serverless durch Hype
  • mit der Session von @a_mirmohammadi über/zu den Anonymen Abnehmer ist der @devopenspace eindeutig in die Kategorie “es gibt nichts, was nicht geht” angekommen

AIT: WiX Heat XSL Transformation

Mit Hilfe von Heat lässt sich die Fragmenterzeugung, welche ein Hauptbestandteil eines WiX Installers ist, automatisieren. Dies wurde bereits im Beitrag Automatische Fragmenterzeugung mit Heat gezeigt. Was aber, wenn die ein oder andere Datei im Installationsvorgang nicht kopiert werden soll oder der Component bzw. dem File noch Attribute hinzugefügt werden müssen? Das erzeugte Fragment manuell zu verändern ist nicht sehr ratsam, da dies bei jedem Durchlauf von Heat überschrieben wird.
Hier kommen XSL Transformationen zum Einsatz, welche dem Heat Vorgang angehängt werden können. Mit Hilfe einer solchen Transformation lässt sich die automatisierte Fragment Datei abschließend bearbeiten.

Im oben genannten Beitrag wurde bereits die Möglichkeit der Filterung kurz angesprochen. Mit Hilfe von XSL Transformationen können jedoch nicht nur Filter realisiert werden, sondern, wie der Name bereits sagt, auch ganze Transformationen vollzogen werden. Um jedoch Filterdateien selbst erzeugen zu können, bedarf es weiterer Erklärung.

XSLT, Extensible Stylesheet Language Transformation, ist um XML-Dokumente zu transformieren. Zwar besitzen die von Heat erzeugten Dateien die Endung .wxs sind aber gewöhnliche XML-Dokumente und können daher mit Hilfe von XSLT transformiert werden. Aber Vorsicht! WiX unterstützt leider nur XSL Version 1.0.

Im Folgenden werden Transformationen anhand von zwei Beispielen erläutert, um noch tiefer in die Thematik einzusteigen empfiehlt es sich die offizielle Dokumentation von XSLT 1.0 anzusehen. Das ganze Beispiel kann unter https://github.com/JohBa/WiXSamples/tree/master/HeatTransformation eingesehen werden.

Um eine Transformation anzuwenden erzeugen wir zunächst eine einfache XML Datei, die wir transform nennen. Um nicht nur eine leere XML Datei zu erzeugen, wird als erstes ein sogenannter identity transform angewendet, um die Sourcedaten in unsere Zieldaten zu übertragen. Dazu wird folgender Code benutzt.

Identity Transform

Zunächst wenden wir template match an und übergeben @*|* um jedes Attribut (@*) und jedes Element (*) zu matchen. Innerhalb dieses templates wird copy auf die Attribute und Elemente angewendet, wodurch zunächst der komplette Inhalt kopiert wird. Um Einrückungen für die Lesbarkeit zu erhalten wird das output Element mit Attributen indent und method verwendet.

Dateien herausfiltern

Nun können wir beginnen einzelne Dateien herauszufiltern. Um ein File mit seiner Component aus dem Fragment zu entfernen sind im Grunde drei Zeilen notwendig.

Datei filtern

In der ersten Zeile wird ein Key erzeugt, der einen match auf unser File durchführt. Dazu suchen wir in unseren Components, gehen tiefer in das File Element und suchen letztendlich im Source Attribut nach einem Teilstring (contains). Dieser ist im vorliegenden Beispiel unnecessary.xml. Mit use=“@Id“ speichern wir die Id des Files zwischen. Nun wollen wir die Component entfernen, dazu wird ein einfaches template angewendet. Im match Attribut suchen wir nun in den Komponenten nach dem zuvor gesicherten Key mit der entsprechenden Id. Da, im durch Heat erzeugten Fragment, jeweils ein Component sowie ein ComponentRef erzeugt werden, muss das selbe Template auf die ComponentRef Elemente angewendet werden.

Sollen mehrere Dateien ignoriert werden können wahlweise alle drei Zeilen kopiert und angepasst werden oder lediglich das key Element. Hierbei muss das match Attribut angepasst werden, das name Attribut des Keys muss jedoch identisch bleiben.

Im Beispiel wird contains verwendet, da es für viele Fälle ausreicht. Jedoch reicht diese Funktion in manchen Fällen nicht mehr, bspw. bei Dateien wie myApp.exe.config. Soll nun mit Hilfe von contains myApp.exe gefiltert werden, wird gleichzeitig auch myApp.exe.config gefiltert, da hier auch der Teilstring vorhanden ist. Hier wäre die Funktion ends-with hilfreich, leider gibt es diese in XSL 1.0 jedoch noch nicht. Wir können aber die Funktion substring verwenden um dies zu realisieren.

Ends with in XSL 1.0

Mit Substring extrahieren wir einen Teilstring aus dem Source Attribut. Dazu subtrahieren wir von der Gesamtlänge unseres Source Strings unseren gesuchten String und addieren 1.

Attribute hinzufügen

Gelegentlich kann es vorkommen, dass ein Attribut einem File Element hinzugefügt werden muss. Soll bspw. eine bestimmte Datei nur als Read-Only Datei installiert werden, kann dies ebenfalls realisiert werden.

Read Only Attribut hinzufügen

Zunächst verwenden wir wieder ein template mit match auf alle File Elemente und filtern hier mit Hilfe von contains das Source Attribut auf die Files welche im Source den Wert readonly.txt besitzen.

Um das Element zu erhalten, wird, ähnlich wie beim identity transform, copy in Verbindung mit apply-templates verwendet. Nun haben wir das betreffende Elemente zunächst lediglich kopiert. Um dem Knoten noch ein Attribut hinzuzufügen verwenden wir das XSL attribute Element. Über das name Attribut geben wir den Namen des hinzuzufügenden Attributs hinzu und können anschließend den Wert bestimmen.

Das beispielhafte Resultat ist im Folgenden zu sehen.

Read Only Resultat

 

Fazit

Manchmal reichen einfache Heat Vorgänge nicht mehr aus, um Fragmente für WiX Installationsroutinen zu erzeugen. Mit XSL Transformationen besitzt man jedoch die Möglichkeit die Automatisierung beizubehalten und gleichzeitig individuelle Modifikationen auf das erzeugte Fragment auszuführen.

Uli Armbruster: Workshop: Conquer your Codebase – Bewährter Clean Code aus der Praxis (Materialien)

Einen herzlichen Dank an alle Teilnehmer des Workshops am vergangenen Freitag beim Developer Open Space 2017. Trotz des völlig unterschiedlichen Backgrounds – von Ruby, über PHP, zu Java oder gar nicht objektorientierte Sprachen wie JavaScript – war ebenso alles vertreten wie vom Azubi bis hin zum Entwickler mit 20-jähriger Berufserfahrung.

IMG_20171013_132904

Einer von vielen mutigen Freiwilligen

Den Code zur Super Mario Kata gibt es hier. Beachtet die neuen Anforderungen (9-12), die ich auf dem Heimweg im Zug noch hinzugefügt habe. Damit einhergehend habe ich noch ein Refactoring des Konzepts ‚Leben‘ durchgeführt, sodass keine Actions bzw. Funcs mehr an die Methoden übergeben werden müssen. Gleichfalls fällt das Branching-Statement raus und es ergibt sich eine nahezu völlig flexible Möglichkeit zur Erstellung von Spielmodi. Die Tests sind nun ebenfalls alle umgesetzt, was wir gegen Ende hin aufgrund von Zeitmangel ausgelassen haben. Eine Testmethode hat in der Regel eine Zeile und wie schon im Workshop erwähnt: Was gut zu testen ist, ist in der Regel auch eine saubere Lösung.

IMG_20171013_132938

20 Teilnehmer mit unterschiedlichen Backgrounds

Derjenige, der noch eine Lösung mit dem Command-Pattern umsetzt und dadurch das Anpassen bestehender Klassen völlständig vermeidet, erhält von mir einen Gutschein für unsere Trainings. Damit kann er kostenlos an jedem beliebigen 3-tägigen Workshop in unseren Karlsruher Büros teilnehmen (Fahrt- und Übernachtungskosten sind ausgeschlossen).

Alle, die über den Workshop bloggen oder twittern, erhalten darüber hinaus noch einen 30% Rabattcode. Einfach Link schicken, dann kriegt er diesen per Mail.

tweet-workshop

Ein Teilnehmer versucht sich in einer Lösung mit Java statt C#

Weiterführende Links

  • Das erwähnte Video zu Enumeration mit Verhalten könnt ihr hier anschauen.
  • Danke an Tim für den Link zum Case Converter für Visual Studio.
  • Außerdem lege ich euch die Reactive Extensions ans Herz, die es für unterschiedliche, gängige Programmiersprachen gibt.
  • Git Snippet
  • Wer noch ein wenig üben möchte, kann dies mit der erweiterten FizzBuzz Kata tun.
  • Blog Post über die Migration von NHibernate zu EntityFramework in 3 Tagen.

 

Demnächst geht ein Video mit dem theoretischen Teil (Why-How-What) online, in dem ich nochmal die Zusammenhänge zw. „schlechtem Code“ und den Gründen dafür erläutere. Wer automatisch informiert werden will, wenn es veröffentlich wird, abonniert einfach meinen Blog oder schickt mir eine Nachricht.

 

 


Einsortiert unter:Architecture, Development, Events, German Tagged: Clean Code, Open Space, Workshop

Golo Roden: DDD und wolkenkit - live!

In dieser Woche finden verschiedene Veranstaltungen wie Meetups und Konferenzen statt, auf denen es um Domain-Driven Design und das wolkenkit geht. Im Folgenden ein kurzer Überblick für alle Interessierten.

AIT: Neu in TFS 2018: Variable Groups für das Release Management

Wer für große Umgebungen im Release Management bereits Variablen verwalten musste, weiß wie umfangreich diese werden können. Auch kann es vorkommen, dass Variablen über verschiedene Release Definitionen wiederverwendet werden. Dafür gibt es jetzt mit TFS 2018 Abhilfe in Form von Variable Groups. Eine Variable Group enthält dabei verschiedene Variablen. Diese können entweder im Klartext oder verschlüsselt abgelegt werden, wie man es aus einer Release Definition gewohnt ist. Somit erhält man eine Sammlung von Variablen, die später in einer Release Definition verwendet werden können.

Der Vorteil ist, dass man bei Änderung der Werte nur die Variable Group an einer zentralen Stellen anpassen muss (siehe Abbildung 1). Auch das Thema Sicherheit spielt hier eine wichtige Rolle. Denn wer eine Release Definition anlegt, hat noch lange nicht Zugriff auf die Passwörter und andere verschlüsselte Daten. In dem Fall können ausgewählte Personen mit Administrators-Berechtigung die Variable Group anpassen, Passwörter hinterlegen und Personen mit Users-Berechtigung können die Variable Group in ihren Release Definitionen verwenden. Somit kann ein neuer Releaseprozess einfach aufgesetzt werden, ohne dass der Ersteller direkt Zugriff auf alle Schlüssel und Passwörter haben muss.

image

Abbildung 1: Variable Group Web Dev

Verknüpfen einer Release Definition mit einer Variable Group

Um einer Release Definition eine oder mehrere Variable Groups hinzuzufügen, können ab TFS 2018 im Reiter Variables, zusätzlich zu den eigenen Variablen, jetzt auch Variable Groups verknüpft werden (siehe Abbildung 2). Durch die Verknüpfung werden automatisch alle Änderungen in der Variable Group auch mit in die Variablen der Release Definition übertragen und alle Variablen der Variable Group können innerhalb der Release Definition verwendet werden. Die Variablen sind dann für alle Environments in der Release Definition verfügbar.

Durch die Möglichkeit beliebig viele Variable Groups zu verknüpfen, kann man hier auch die Trennung der Variablen sehr feingranular definieren. Am Ende muss jedoch jeder für sich selbst entscheiden, welche Granularität für die Variable Groups angewendet wird, um möglichst eine große Wiederverwendbarkeit zu erreichen.

Wichtig ist, eine Konvention für die Namensgebung der Variablen festzulegen. Die Variablen gleich zu benennen in unterschiedlichen Variable Groups ergibt Sinn, wenn man sich für eine dieser Gruppen in der Release Definition entscheiden muss. Wenn Variable mit dem gleichen Namen in unterschiedlichen Variable Groups eingebunden werden, so entscheidet, die Reihenfolge, in welcher die Variable Groups verknüpft wurden.

Variable Groups mit einzelnen Environments zu verknüpfen, ist aktuell nicht möglich. Variable, die nur in einer bestimmten Umgebung Verwendung finden, können über ein Prefix oder Suffix erkenntlich gemacht werden. So kann man beispielsweise den Application Insights Key für alle Development-Umgebungen als AppInsightsKeyDev ablegen, um direkt zu erkennen, für welche Umgebung dieser gedacht ist.

image

Abbildung 2: Verknüpfen einer Variable Group mit einer Release Definition

Verknüpfen eines Azure Key Vault mit einer Variable Group

Ein kleiner Vorgeschmack was die Zukunft bringt, bietet das VSTS-Feature Link secrets from Azure key vault (siehe Abbildung 3). Mit dieser Option können in einer Variable Group Variable aus einem Azure Key Vault verknüpft werden. Dadurch liegen Passwörter nicht mehr im TFS oder in VSTS sondern werden in einem Azure Key Vault verwaltet. Die Vorteile hiervon sind die Sicherheit der abgelegten Passwörter und die Möglichkeit, die Verwendung der Passwörter zu überwachen.

Variable group with Azure key vault integration

Abbildung 3: Verknüpfen eines Azure Key Vault mit einer Variable Group

Holger Schwichtenberg: Video zur PowerShell Core 6.0 auf Windows, Linux und Mac

Die Aufzeichnung eines Dotnet-Doktor-Vortrags von der CIM Lingen 2017 gibt es bei YouTube zu sehen.

MSDN Team Blog AT [MS]: Microsoft Teams Envisioning Workshop mit dem Produktteam

Gemeinsam mit dem Produktteam aus den USA bieten wir Kunden und Partnern die Möglichkeit die Produktgruppe von Microsoft Teams persönlich in Wien kennenzulernen. Am Montag den 30. Oktober findet bei uns im Büro ein Microsoft Teams Envisioning Workshop statt wo sich interessierte Kunden und Partner über die Roadmap von Microsoft Teams erkundigen können.

Diese Ganztagsveranstaltung ist eine tolle Gelegenheit sich mit technischen Experten aus der Microsoft Teams Produktgruppe persönlich zu vernetzen, die Erweiterbarkeit von Microsoft Teams kennenzulernen und sich über Ressourcen zu bzw. die Roadmap von Microsoft Teams zu informieren.

Anmeldeseite: Anmeldung zum Microsoft Teams Envision Workshop

Wer sollte teilnehmen?

  • Technische Entscheidungsträger die in Ihrer Firma die technologische Roadmap definieren und die Richtung der Produktentwicklung bestimmen
  • Solution Architects welche die Möglichkeiten der Integration von Teams näher beleuchten wollen
  • Entwicklungsleiter die Teams kennenlernen und Ihre Entwicklerressourcen entsprechen planen wollen

Was Kunden und Partner aus der Veranstaltung mitnehmen können

  • Ein Verständnis für Microsoft Teams, wie es in die Office 365 Platform paßt und welchen Wert es für sie selber bzw. ihre Kunden bietet
  • Wissen über die Microsoft Teams Erweiterungsmöglichkeiten, und wie sie Ihre eigenen Anwendungen dafür entwickeln können.
  • Details zur Microsoft Teams Roadmap, insbesondere im Zusammenhang mit der “Skype for Business Ankündigung“
  • Einen persönlichen Kontakt zur Microsoft Teams Produktgruppe und den Ingenieuren / Entwicklern

Was Kunden und Partner von uns erwarten können:

  • Tiefgreifende Informationen: Wir helfen Ihnen das Potential von Microsoft Teams zu erkennen, welche Ressourcen Sie zum Entwickeln eigener Anwendungen benötigen und die Unterstützung, die wir Ihnen dabei geben können
  • In dieser Veranstaltung wird (noch) nicht geocoded. Es handelt sich hier um einen ersten Schritt in Richtung ihrer eigenen Anwendung.
    In weiterer Folge können wir mit Ihnen eine Lösung im Detail planen und danach auch gemeinsam mit Microsoft Software Ingenieuren ein Hackfest durchführen.

Was Kunden und Partner mitbringen sollten

  • Ihre Fragen und Interessen – Wie kommen Sie mit der Microsoft Teams Erweiterungs-Plattform zurecht?
  • Ihre Erfahrung – lassen Sie uns Ihre Einstellung zu Microsoft Teams und zur Erweiterungs-Plattform wissen.
  • Ehrlichkeit – Lassen Sie uns über alles reden. Was Ihnen gefällt und auch was nicht.

Agenda (Die Vorträge finden in englischer Sprache statt):

Mo. 30.10., 09:00 – 16:00

  • 09:00 - Eintreffen und Kennenlernen der Teilnehmer, Möglichkeit für individuelle Gespräche
  • 10:00 - Overview of the Teams platform
    • Teams background and customer value
    • Teams functionality (demo)
    • Teams/Skype for Business convergence
  • 11:00 – Overview of Teams extensibility options
    • Making your app shine on Teams via Tabs, Bots Connectors, Compose Extensions
    • Office store submission process
  • 12:00 – Real life cases
  • 13:00 – Mittagspause
  • 14:00 – Office 365 Developer Platform and Microsoft Graph
  • 15:00 – Next steps, resources available

Zusätzlich und Ergänzend zum Microsoft Teams Envisioning Workshop am Montag den 30. Oktober in Wien findet in Kooperation mit den deutschen Kollegen in der Woche vom 6. - 9. November ein Microsoft Teams Hackfest in München statt. Bei diesem Hackfest können Kunden und Partner gemeinsam mit dem Microsoft Ingenieuren Ihre eigenen Microsoft Teams Erweiterungen entwickeln und in den Store stellen.

Martin Richter: Sicherheitsupdates von Microsoft führen zu Problemen mit dem ODBC Export nach Excel

Eigentlich habe ich nichts gegen Sicherheitsupdates. Genaugenommen bin ich ein Verfechter, dass Sicherheitsupdates sofort und umgehend installiert werden sollen.

Eigentlich ging das die letzten Jahre bei unserer Software ziemlich gut. Aber heute hat es uns voll erwischt. Dazu auf allen Plattformen von Windows 7, Windows 8.1 bis Windows 10.

Es kommt jetzt beim Anlegen einer Excel Datei über ODBC zu einer Fehlermeldung in der Form:

Reservierter Fehler (-5016); es gibt keine Meldung für diesen Fehler.
Ungültiges Attribut für die Verbindungszeichenfolge. CREATE_DB
Ungültiges Attribut für die Verbindungszeichenfolge. CREATE_DB
Ungültiges Attribut für die Verbindungszeichenfolge. CREATE_DB
Ungültiges Attribut für die Verbindungszeichenfolge. CREATE_DB
Allgemeine Warnung Registrierungsschlüssel ‚Temporary (volatile) Jet DSN for process 0x2c44 Thread 0x37f4 DBC 0x5b84d54 Excel‘ kann nicht geöffnet werden.
Ungültiges Attribut für die Verbin

Aktuell kann ich die Ursache nur in den folgenden Windows Updates sehen:

Windows 7 KB4041681
Windows 8.1 KB40416393
Windows 10 KB4040724
KB4041676

Einen entsprechenden Anfrage an andere Entwickler habe ich über Stackoverflow gestellt.

Als exMVP oder rMVP hat man jedoch nicht mehr die schönen Kanäle von früher… <seufz/> 🙁

Ich frage mich, wie solche Fehler durch eine Qualitätskontrolle schlüpfen können…?


Copyright © 2017 Martin Richter
Dieser Feed ist nur für den persönlichen, nicht gewerblichen Gebrauch bestimmt. Eine Verwendung dieses Feeds bzw. der hier veröffentlichten Beiträge auf anderen Webseiten bedarf der ausdrücklichen Genehmigung des Autors.
(Digital Fingerprint: bdafe67664ea5aacaab71f8c0a581adf)

MSDN Team Blog AT [MS]: Connect(); 2017

Neben der BUILD Konferenz ist die Connect(); die zweite wichtige Konferenz für Cloud & Software Entwicklungsthemen bei Microsoft.

Die Connect(); 2017 wird von 15. bis 17 November stattfinden - nehmen Sie online Teil und erfahren Sie direkt vom Microsoft Azure und Visual Studio Produktteam die neuesten Entwicklungen und Industrietrends.

"Save the Date" für Ihren Kalender.

AIT: Neu in TFS 2018: Secure Files Library

In unserer Blog-Serie Neu in TFS 2018 wurden bereits Änderungen und auch Neuerungen präsentiert. In die Kategorie neuer Features reiht sich die Secure Files Library ein.

Secure Files Library

Bisher ließen sich im Build-Prozess ausschließlich die hinterlegten Variablen wie z.B. Passwörter verschlüsselt ablegen und waren ausschließlich im Build- und Release-Prozess verfügbar. Mit der Einführung vom TFS 2018 gibt es nun die Möglichkeit, dies auch um verschlüsselte Dateien zu erweitern. Das bietet den Vorteil, dass u.a. Signierungszertifikate und Provisierungsprofile nicht mehr in der Quellcodeverwaltung vorgehalten werden müssen.

Secure Files Library

 

Build Tasks mit Secure Files

Die hinterlegten Dateien lassen sich nun direkt in den Build-Prozess integrieren. Hierzu gibt es die zwei neuen Build Tasks: „Install Apple Certificate“ und „Install Apple Provisioning Profile“. Für die Integration in eigene Custom Build Tasks gibt es hierfür nun den neuen Input Typ secureFile.

Build Task mit Secure Files

 

Hinweis

Zu beachten gilt, dass der hinterlegte Build Agent mindestens in der Version 2.116.0 laufen muss. Zudem ist der Upload von Dateien auf 10 MB je Datei begrenzt.

André Krämer: Speicherplatz auf dem Mac optimieren für Xamarin Entwickler

Wer eine Weile auf dem Mac mit Xamarin gearbeitet hat, wird irgendwann feststellen, dass der Festplattenplatz knapp wird. Dann heißt es natürlich Aufräumen. Die Frage ist nur: Was kann weg? Bei mir war es gestern mal wieder so weit. Um herauszufinden, welche Dateien und Verzeichnisse besonders viel Platz benötigen, habe ich Daisy Disk genutzt. Das kleine Tool, das aktuell ca. 11 € kostet, zeigt übersichtlich an, welche Ordner und Dateien besonders speicherhungrig sind.

Golo Roden: DDD & Co., Teil 10: Hallo wolkenkit

Seit der vergangenen Folge ist der fachliche Code für TodoMVC vollständig. Allerdings fehlt der gesamte technische Rahmen, der die Funktionalität über eine REST- und WebSocket-API anbietet, die fachlichen Ereignisse persistiert und die Anwendung bei Bedarf skaliert. Das Open-Source-Framework wolkenkit schafft Abhilfe.

Manfred Steyer: Meine Unterlagen von den Angular Days in Berlin, Oktober 2016

Diese Woche habe ich drei Workshops auf den Angular Days in Berlin gemacht. Nachfolgend finden sich die Unterlagen dazu:

AIT: Neu in TFS 2018: Task Groups für Build und Release

Mit TFS 2015 wurde ein komplett neues Build- und Releasemanagement-System eingeführt. Über einen Teil der enthaltenen Neuerungen, wie z.B. das Importieren und Exportieren von Builddefinitionen konnten Sie bereits in vorhergehenden Beiträgen unserer Blogserie lesen. In diesem Blogpost möchten wir Ihnen nun einen weiteren großen Schritt vorstellen, den Microsoft im Ausbau des neuen Systems geht: Task Groups.

Jede Build- und jede Releasedefinition besteht aus einer Menge an Tasks, welche nacheinander ausgeführt werden. Jeder dieser Tasks definiert dabei eine auszuführende Aktion, wie z.B. das Bauen einer Anwendung, das Ausführen von Tests, u.v.m. In vielen Fällen wird die gleiche Abfolge an Tasks in mehreren Build- oder Releasedefinitionen verwendet um z.B. firmenspezifische Abläufe abzubilden, und unterscheidet sich dabei nur durch die Parameterwerte, welche als Input an den einzelnen Tasks spezifiziert werden. In diesem Fall bieten Task Groups eine einfache Möglichkeit, die Definition und Eigenschaften von solchen Abfolgen zentral zu verwalten. Task Groups fassen also eine Reihe von Tasks zusammen und stellen diese dann als einen einzigen, wiederverwendbaren Task den Build- und Releasedefinitionen zur Verfügung. Dies birgt folgende Vorteile:

  • Zentralisierung von wiederkehrenden Abläufen
  • Verringerung von Redundanzen
  • Änderungen an Task Groups werden auf alle Definitionen angewendet, welche die Task Group referenzieren

Um eine neue Task Group anzulegen, kann innerhalb einer Build- oder Releasedefinition eine Abfolge an Tasks ausgewählt werden und anschließend im Kontextmenü der Eintrag Create task group gewählt werden (vgl. Abb. 1).

SNAGHTML11d7c79f

Abbildung 1: Erstellen einer Task Group

Beim Anlegen sind folgende Punkte zu beachten:

  • Task Groups sind Team Projekt-spezifisch.
  • Für die zu erstellende Task Group muss ein Name spezifiziert werden (vgl. Abb. 2 (1)) sowie die Gruppe des Task Katalogs, in welchem die Task Group enthalten sein soll (vgl. Abb. 2 (3)). Des Weiteren kann eine Beschreibung angegeben werden. (vgl. Abb. 2 (2)).
  • Alle Parameterwerte, welche innerhalb der einzelnen Tasks verwendet werden und durch Variablen spezifiziert sind, werden automatisch auch als Variablen an der Task Group abgebildet. Der zugehörige Variablenwert wird als Default-Wert gesetzt, kann jedoch im Nachhinein noch geändert werden (vgl. Abb. 2 (4)).
  • Alle Parameterwerte, welche innerhalb der einzelnen Tasks verwendet werden und durch fest Werte (d.h. keine Variablen spezifiziert sind) werden auch als feste Werte für die Task Group übernommen. Diese fixen Werte können beim Referenzieren der Task Group nicht von außen angepasst werden.
  • Aus den beiden vorhergehenden Punkten folgt: Alle Parameterwerte, welche beim Referenzieren der Task Group veränderbar sein sollen, müssen als Variable in den enthaltenen Tasks definiert sein.

image

Abbildung 2: Eigenschaften beim Erstellen einer Task Group

Nach der Erstellung können Task Groups zentral im Task Groups-Tab des Build & Release-Hubs verwaltet und editiert werden. Jede Task Group verfügt darin vier Tabs:

  • In den Properties können die allgemeinen Eigenschaften Name, Description und Category angepasst werden. Darüber hinaus sind alle Task Group Variablen mitsamt ihrer Defaultwerte und Beschreibungen enthalten.
  • Der Tab Tasks enthält die Abfolge der Tasks. Hier können fixe Parameterwerte zu Variablen geändert werden, neue Tasks hinzugefügt werden, …
  • Im Tab History können alle Änderungen, welche an der Task Group vorgenommen wurden, nachverfolgt und untereinander verglichen werden (vgl. Abb. 3).
  • Der Tab References enthält die Liste alle Build- und Releasedefinitionen sowie alle Eltern-Task Groups, welche die Task Group referenzieren.

image

Abbildung 3: Änderungsnachverfolgung in Task Groups

Der History-Tab einer Task Group bietet schon einige Sicherheit bei Änderungen. Da sich Änderungen an einer Task Group jedoch sofort in allen Build- und Releasedefinitionen durchschlagen ist das Risiko von unerwünschten Effekten immer noch relativ hoch. Dies kann durch die Verwendung von Versionierung weiter reduziert werden: Änderungen an Task Groups können zunächst als Draft abgespeichert werden (vgl. Abb.4 (1)). Damit kann die letzte stabile Version zunächst weiter in den bestehenden Prozessen verwendet werden und die Änderungen nur in ausgewählten Definitionen getestet werden. Nach erfolgreichem Testen der Änderungen können diese dann in die ursprüngliche Task Group gepublished werden (vgl. Abb. 4 (2)). Änderungen, welche nicht abwärtskompatibel sind, können dabei als Preview gepublished werden. Dies erzeugt eine neue Version der Task Group. Damit können verschiedene Versionen einer Task Group zur selben Zeit im Einsatz sein. Die Preview-Version kann dann zu einem späteren Zeitpunkt wiederum als offizielle neue Version gepublished werden.

image

Abbildung 4: Versionierung von Task Groups via Drafts

Abgerundet wird das Konstrukt durch die Möglichkeit Task Groups zu exportieren (vgl. Abb. 5 (1)) und Importieren (vgl. Abb. 5 (2)). Oftmals werden Änderungen an Prozessen zunächst in einer separaten Testinstanz durchgeführt und getestet. Es existieren auch Build- und Releaseabläufe, welche über den Team Projekt-Kontext hinweg gleich sind. Durch den Export und Import von Task Groups entfällt in solchen Szenarien die Notwendigkeit dieselben Änderungen an verschiedenen Stellen machen zu müssen und eliminiert damit auch das Risiko von Fehlern beim manuellen Übertragen von Änderungen.

image

Abbildung 5: Export und Import von Task Groups

Fazit

Das Konzept von Task Groups bietet eine schöne und durchdachte Möglichkeit wiederkehrende Abfolgen in Build- und Releasedefinitionen zentral zu definieren und zu verwalten. Das mit TFS 2015 neu eingeführte System macht damit einen weiteren großen Schritt auf dem Weg zum Erwachsenwerden.

Weitere Schritte auf diesem Weg stellen wir Ihnen auch in nachfolgenden Beiträgen unserer Blogserie vor. Bleiben Sie also dran und freuen Sie sich auf spannende Neuerungen mit TFS 2018.

Golo Roden: Einführung in React, Folge 2: Ein tieferer Einblick

React basiert auf vielen Konzepten der funktionalen Programmierung. Besonders wichtig ist die Unveränderlichkeit von Datentypen, die in React die Grundlage für das effiziente Rendern der Anzeige bildet. Wie funktioniert das für eigenen Code, und worauf gilt es in JSX außerdem zu achten?

AIT: Berechtigungen für Fortgeschrittene – Berechtigungen in der Quellcode- verwaltung für Admins einschränken

Bei Entwicklungsteams, welche im stark regulierten Umfeld tätig sind, entstehen an das Berechtigungskonzept für die Quellcodeverwaltung aus diversen Gründen spezielle Anforderungen. Eine Anforderung aus diesen Umfeld lautet beispielsweise:

Ist es möglich einem Teil der TFS-Administratoren des Leserecht auf den Quellcode in einem besonders zu schützenden Bereich der Quellcodeverwaltung zu entziehen?

Die Kurzfassung der Antwort lautet: Ja, es ist möglich. Kommt noch der Faktor Revisionssicherheit der Lösung hinzu, dann müssen noch zusätzliche Schritte eingeplant werden.

Zu Beginn steht die Frage im Raum: Git oder Team Foundation Version Control (TFVC)? Für das beschriebene Szenario ist Git weniger gut geeignet, da hier nur eine Berechtigungsvergabe auf das komplette Repository möglich ist. TFVC hingegen unterstützt diverse Berechtigungsarten und eine feingranulare Steuerung bis auf Dateiebene herab.

Im Folgenden wird die Umsetzung der eingangs genannten Anforderung Schritt für Schritt auf Basis von TFVC beschrieben.

Um die Berechtigungen für den zu schützenden Quellcode-Ordner einzugrenzen, wird im ersten Schritt die einzugrenzende TFS‑Berechtigungsgruppe (z.B. TFS_Admins_Deny) hinzugefügt (siehe Screenshot 1).

Als nächster Schritt wird dieser Gruppe dann das Leserecht explizit verweigert (siehe Screenshot 2)

Mit einem „Verweigern“(Deny) werden den Mitgliedern einer Gruppe die Rechte explizit entzogen, auch wenn sie an anderer Stelle (z.B. durch eine andere Gruppe) gegeben wurden. Es gilt der Grundsatz: Verweigern ist stärker als zulassen.

Ein Sonderfall sind die Gruppen „Team Foundation Administrators“ und „Project Collection Administrators“. Diese Gruppen haben grundsätzlich automatisch überall in der Quellcodeverwaltung das Leserecht.

Für TFS Admin-Gruppen gilt das explizite Verweigern in den meisten Bereichen des TFS nicht. Es wird damit verhindert, dass sich Administratoren selbst aussperren können. Von dieser Regelung gibt es aber Ausnahmen. Eine dieser Ausnahmen betrifft die Quellcodeverwaltung. Es ist sozusagen die Ausnahme von der Ausnahme.

In version control permissions, explicit deny takes precedence over administrator group permissions.” Quelle: https://www.visualstudio.com/de-de/docs/setup-admin/permissions#tfvc-permissions-object-level

Die Folge ist deshalb, dass ein Administrator als Mitglieder der TFS Gruppe TFS_Admins_Deny kein Read-Recht auf den schützenswerten Quellcode-Ordner hat.

Im Fall von z.B. Anwendersupport, kann ihm aber das Leserecht auf den Quellcode, kurzfristig wieder gewährt werden. Eine Möglichkeit wäre, den Administrator temporär aus der Gruppe TFS_Admins_Deny zu entfernen.

Damit ist die Hälfte der Anforderung umgesetzt. TFS Administratoren können tatsächlich aus der Quellcodeverwaltung ausgeschlossen werden. Das ist aber noch nicht revisionssicher, da ein eingeschränkter Administrator jederzeit wieder der TFS-Gruppe das Read-Recht geben oder die Gruppenmitgliedschaft ändern kann.

Es gibt zwei Möglichkeiten die fehlende Anforderung umzusetzen. Die zwei Möglichkeiten sind in nachfolgender Tabelle dargestellt:

Legende:

  1. Bei der ersten Lösung werden alle Administratoren der TFS-Gruppe TFS_Admins_Deny hinzugefügt. Dieser Gruppe muss das Read-Recht auf den zu schützenden Quellcode verweigert werden.
  2. Bei der zweiten Lösung wird auch die Gruppe TFS_Admins_Deny erstellt, aber nur die Limited-Administrators hinzugefügt. Dieser Gruppe muss auch das Recht „Manage permissions“ verweigert werden. Es wird noch eine weitere Administratoren Gruppe benötigt, die TFS_Limited_Admins. Dieser Gruppe wird der entsprechende Personenkreis hinzugefügt und alle Rechte wie der originalen Gruppe Team Foundation Administrators gegeben, bis auf die Rechte „Edit collection-level information“ und „Edit project-level information“. Im Anschluss werden alle Benutzer der TFS_Limited_Admins aus der Gruppe Team Foundation Administrators entfernt. Mit dieser Lösung ist sichergestellt, dass beide Personenkreise die benötigten Berechtigungen für die tägliche Arbeit besitzen und mit minimalem Aufwand auch für den geschützten Quellcodebereich temporär berechtigt werden können.

Für die erste Lösung ist es unabdingbar, dass Berechtigungsänderungen und Änderungen der Gruppenmitgliedschaften protokolliert werden. Nur dann kann nachvollzogen werden, wer wann wem welches Recht eingeräumt hat. Dies lässt sich nicht mit Board-Mitteln bewerkstelligen. Für diese Anforderung ist es notwendig eine serverseitige TFS-Erweiterung zu entwickeln. Die Sicherheits-Events müssen am serverseitigen Eventhandler (TFS Requestfilter) über die TFS API revisionssicher in einer extra Datenbank protokolliert werden.
Bei der zweiten Lösung ist eine Protokollierung der Gruppenmitgliedschaften nicht zwingend erforderlich. „Echte“ Administratoren besitzen hier sowieso nur noch als einzige das Recht Berechtigungen ändern zu dürfen und verfügen aufgrund des TFS Designs sowieso über einen Vollzugriff auf alle TFS Daten.

Das besprochene Szenario kann man noch erweitern. Am Ende dieses Blogposts soll dies aber lediglich als Anregung dienen: Unabhängig ob die Lösung 1) oder 2) gewählt wurde bleibt noch die Frage offen:

Auf welche Teile des Quellcodes haben eingeschränkte Administratoren während ihres Anwendersupportfalls zugegriffen?

An diesen Stellen bietet sich auch wieder der Weg über die TFS API in Kombination mit serverseitigen Eventhandler (TFS Requestfilter) an, damit die entsprechenden Zugriffe protokolliert werden können. Dies ist hier schon fast zwingend notwendig, damit das ganze Szenario rund wird und die Protokollierung lückenlos und durchgängig ist.

Fazit

Es ist mit ein paar Handgriffen möglich Administratoren aus definierten Bereichen der Quellcodeverwaltung (TFVC) auszuschließen. Die notwendigen Zugriffe lassen sich sowohl bei der Rechtevergabe, als auch beim Zugriff der Quellcodeverwaltung protokollieren. Der Aufwand darf nicht unterschätzt werden, da für einige Teile der Lösung eigene Erweiterungen (serverseitiger TFSRequestfilter) geschrieben werden müssen.
Unabhängig von dem aktuellen Blog-Post sollte bei dieser extremen Art von Anpassungen immer kritisch hinterfragt werden, ob der Aufwand in einem angemessenen Verhältnis zum Schutzbedürfnis steht. Anpassungen können schnell zu neuen Problemen, erhöhten Aufwänden für andere Anpassungen (Bsp. Builds) und unnötigen Fehleranalysen führen.
Für ein umfassendes Sicherheitskonzept ist die Problemstellung aus diesen Blog-Post nur ein kleiner Baustein einer Sicherheitsarchitektur. Weitere Themenfelder für eine solche Architektur und ungewollte Zugriffe auf TFS Informationen sind exemplarisch: direkter Remotezugriff auf den Server via Remote Desktop, bekannte Servicekonten und -Passwörter oder direkter Datenbankzugriff.

Der Nutzen von einem Sicherheitstor bleibt schnell auf der Strecke, wenn links und rechts davon nur ein gewöhnlicher Gartenzaun um das Haus gebaut wurde.

Uli Armbruster: Workshop: Conquer your Codebase – Bewährter Clean Code aus der Praxis

Am Developer Open Space 2017 halt ich einen 1-tägigen Workshop zu obigem Thema. In reduzierter, kompakter Form werde ich dazu bewährte Inhalte aus meinem 3-tägigen Seminar nehmen und die Ursachen für folgende Probleme adressieren:

  • Unverständlicher bzw. schlecht wartbarer Code
  • Bugs
  • Skalierungsprobleme
  • Go Live Probleme
  • Verpasste Deadlines und lange Entwicklungszeiten

 

Wir werden uns anschauen wie es dazu kommen kann, z.B. weil

  • die Infrastruktur nicht wiederverwendbar ist,
  • die Domänenlogik nicht erweiterbar ist,
  • eine falsche Nutzung der API möglich ist,
  • der Code nicht ausdrucksstark ist,
  • oder starke Abhängigkeiten bestehen.

 

Wir werden das Open Closed Principle genauer besprechen und Seperation of Concerns am konkreten Beispiel umsetzen. Speziell für Freunde der Objektorientierung werde ich je nach verfügbare Zeit praktische Lösungen zur Vermeidung von If-Else-Zweigen und NULL-Checks zeigen.

 

Ein Laptop mit Visual Studio oder Visual Studio Code und .NET 4.6 wären wünschenswert. Prinzipiell ist der Workshop aber für alle Entwickler des objektorientierten Paradigmas geeignet, da bis auf Delegaten (Action/ Func) und Erweiterungsmethoden kaum Sprachspezifika verwendet werden. Darüber hinaus können Teilnehmer auch ohne Hardware beiwohnen, weil wir beim Live Coding am Präsentations-PC mit Code Monkey Runden arbeiten werden.

Gerne dürfen die Teilnehmer mir vorab Fragen und Probleme z.B. hier in Form von Kommentaren oder per E-Mail zukommen lassen.


Einsortiert unter:Architecture, Development, Events, German Tagged: Clean Code, Open Space, Workshop

Don't contact us via this (fleischfalle@alphasierrapapa.com) email address.