A Code Review is an action taken by a senior team member to review code
commits submitted by other team members, looking to ensure that all code
in the commit meets the Coding Standards. The purpose of a Code Review
is to ensure that no new code is accepted which would go against the
patterns, naming conventions and aesthetics of how we want the solution
code to appear. If a particular code commit does not meet certain
expectations, the commit can be pushed back to the developer with notes
to make specific corrections.
Code Reviews play a significant part of the Gated Checkin process, where
the code actually can't be pushed to the main branch without an approver
signing off on the code.
CI process should handle the first part of a Code Review, checking that
the Unit Tests cover the new code and that all Unit Tests pass. If a
unit test fails, the Commit should be automatically kicked back to the
user.
Additional points we can add to the CI process include running StyleCop,
FxCop and ReSharper analysis with Rule-Sets to check for changes and
provide a detailed report to the user if any rule should fail.
The last part should include the senior team member either directly
reviewing the new code if it is a simple change or sitting with the
developer for more complex changes. In the code review, the reviewer
should be checking the code for things that don't coincide with the
overall plan of the platform and/or the clients that it is intended for.
NOTE: We will be adding the step-by-step instructions for doing this in
TortoiseGit, VS and VSO once the Gated Checkin process is finalized.
Until this time, you may review any and all code commits for any version
via the CEF branch pages in Stash:
http://stash:7990/projects/CEF/repos/framework/commits?until=refs%2Fheads%2FRelease4_1
See
CEF/Developing/CodingInProjects
This project contains all the ServiceStack code to generate an API for
communication between the UI and Business Workflows. This project's
folder is referenced as a virtual application in the website and a
configuration setting in the web.config must be updated to the path
where the virtual application full path is set. This VA allows the
website to directly access all the services endpoints here without
having to move them into a particular website folder, create a symbolic
link using Windows itself, or create a separate IIS instance with a
different subdomain.
Example Routes
namespace Clarity.Ecommerce.Framework.Accounts {
using System;
using System.Collections.Generic;
using Models.Account;
using Models.Attribute;
using Models.Generic;
using ServiceStack;
[Route("/AccountTypes/", "GET", Summary = @"Use to get a account types")]
public class GetAccountTypes : IReturn<AccountTypeModel> { }
[Route("/Account/Create", "POST", Summary = @"Use to create a new account")]
public class CreateAccount : AccountModel, IReturn<AccountModel> { }
[Route("/Account/{ID}", "GET", Summary = @"Use to get a specific account")]
public class GetAccount : IReturn<AccountModel>
{
[ApiMember(Name = "ID", Description = "Account System Identification", IsRequired = true)]
public int ID { get; set; }
}
[Authenticate]
[Route("/Account/Accounts/", "GET", Summary = @"Get All Accounts")]
public class GetAccounts : IReturn<List<AccountModel>>
{
[ApiMember(Name = "CustomKey", Description = "Account Key", IsRequired = false)]
public string CustomKey { get; set; }
[ApiMember(Name = "Name", Description = "Account Name", IsRequired = false)]
public string Name { get; set; }
[ApiMember(Name = "AttributesToInclude", Description = "Attributes To Include", IsRequired = false)]
public List<string> AttributesToInclude { get; set; }
[ApiMember(Name = "ModifiedSince", Description = "Modified Since", IsRequired = false)]
public DateTime? ModifiedSince { get; set; }
}
[Authenticate]
[Route("/Account/{ID}", "POST", Summary = @"Use to update a specific account")]
public class UpdateAccount : AccountModel, IReturn<AccountModel> { }
[Authenticate]
[Route("/Account/{ID}", "POST", Summary = @"Use to update the account associated to the user currently logged into the system")]
public class UpdateCurrentAccount : IReturn<AccountModel>
{
[ApiMember(Name = "ID", Description = "Account System Identification", IsRequired = true)]
public int ID { get; set; }
[ApiMember(Name = "Name", Description = "Account Name", IsRequired = true)]
public string Name { get; set; }
[ApiMember(Name = "Email", Description = "Account Email", IsRequired = true)]
public string Email { get; set; }
[ApiMember(Name = "Website", Description = "Account Website", IsRequired = false)]
public string Website { get; set; }
[ApiMember(Name = "Fax", Description = "Main Fax For Account", IsRequired = false)]
public string Fax { get; set; }
[ApiMember(Name = "Phone", Description = "Main Phone Number For Account", IsRequired = false)]
public string Phone { get; set; }
[ApiMember(Name = "City", Description = "Account City", IsRequired = false)]
public string City { get; set; }
[ApiMember(Name = "PostalCode", Description = "Account PostalCode", IsRequired = false)]
public string PostalCode { get; set; }
[ApiMember(Name = "State", Description = "Account State", IsRequired = false)]
public int State { get; set; }
[ApiMember(Name = "Country", Description = "Account Country", IsRequired = false)]
public int Country { get; set; }
[ApiMember(Name = "Address1", Description = "Account Address 1", IsRequired = false)]
public string Address1 { get; set; }
[ApiMember(Name = "Address2", Description = "Account Address 2", IsRequired = false)]
public string Address2 { get; set; }
[ApiMember(Name = "ImageFileAttributes", Description = "Image File Attributes", IsRequired = false)]
public List<FileModel> ImageFileAttributes { get; set; }
[ApiMember(Name = "Attributes", Description = "Attributes", IsRequired = false)]
public List<AttributeModel> Attributes { get; set; }
}
[Authenticate]
[Route("/Account/Current/", "GET", Summary = @"Get account for the current user logged in")]
public class GetCurrentAccount : IReturn<AccountModel> { }
[Authenticate]
[Route("/Account/Current/Remove", "POST", Summary = @"Remove account for the account of the user currently logged into the system")]
public class RemoveCurrentAccount { }
[Authenticate]
[Route("/Account/{ID}/Remove", "POST", Summary = @"Remove a specific account from the system")]
public class RemoveAccount
{
[ApiMember(Name = "ID", Description = "Account System Identification", IsRequired = true)]
public int ID { get; set; }
}
[Authenticate]
[Route("/Account/Receivable/", "GET", Summary = @"Get All Accounts Receivable")]
public class GetAccountsReceivable : IReturn<List<AccountsReceivableSearchResultModel>> { }
[Authenticate]
[Route("/Account/Payable/", "GET", Summary = @"Get All Accounts Payable")]
public class GetAccountsPayable : IReturn<List<AccountsPayableSearchResultModel>> { }
}
Example Services
namespace Clarity.Ecommerce.Framework.Accounts {
using System;
using System.Collections.Generic;
using System.Linq;
using Models.Account;
using Models.Generic;
using Shared;
using ServiceStack;
public class AccountService : ClarityEcommerceServiceBase
{
#region Read
public AccountModel Any(GetAccount request)
{
return Workflows.Accounts.Get(request.ID);
}
public List<AccountModel> Any(GetAccounts request)
{
return Workflows.Accounts.Search(request.CustomKey,request.Name);
}
public List<AccountTypeModel> Any(GetAccountTypes request)
{
return Workflows.AccountTypes.Search();
}
public AccountModel Any(GetCurrentAccount request)
{
return Workflows.Accounts.Get(CurrentUserName);
}
public List<AccountsReceivableSearchResultModel> Any(GetAccountsPayable request)
{
return Workflows.AccountsReceivable.Search(new AccountsReceivableSearchModel());
}
public List<AccountsPayableSearchResultModel> Any(GetAccountsReceivable request)
{
return Workflows.AccountsPayable.Search(new AccountsPayableSearchModel());
}
#endregion
#region Create
public AccountModel Any(CreateAccount request)
{
return Workflows.Accounts.Create(request);
}
#endregion
#region Update
public AccountModel Any(UpdateAccount request)
{
return Workflows.Accounts.Update(request);
}
public AccountModel Any(UpdateCurrentAccount request)
{
var currentUserName = CurrentUserName;
if (!string.IsNullOrWhiteSpace(currentUserName))
{
var account = Workflows.Accounts.Get(currentUserName);
if (request.ImageFileAttributes == null)
{
request.ImageFileAttributes = new List<FileModel>();
}
if (account == null)
{
var curAccount = new AccountModel
{
Name = request.Name,
CustomKey = currentUserName,
Attributes = request.Attributes,
Images = request.ImageFileAttributes.Select(i => new FileModel
{
ID = i.ID,
Name = i.Name,
FileName = i.FileName,
IsDefault = i.IsDefault
}).ToList()
};
List<FileModel> accountImages;
lock (RecentUploadedFiles)
{
accountImages = RecentUploadedFiles.Where(f => f.Type == FileEntityType.AccountImage).ToList();
RecentUploadedFiles =
RecentUploadedFiles.Where(f => f.Type != FileEntityType.AccountImage).ToList();
}
curAccount.Images.AddRange(accountImages);
Workflows.Accounts.Create(curAccount);
}
else
{
var curAccount = new AccountModel
{
ID = account.ID,
Name = request.Name,
Attributes = request.Attributes,
Images = request.ImageFileAttributes.Select(i => new FileModel
{
ID = i.ID,
Name = i.Name,
FileName = i.FileName,
IsDefault = i.IsDefault
}).ToList()
};
List<FileModel> accountImages;
lock (RecentUploadedFiles)
{
accountImages = RecentUploadedFiles.Where(f => f.Type == FileEntityType.AccountImage).ToList();
RecentUploadedFiles = RecentUploadedFiles.Where(f => f.Type != FileEntityType.AccountImage).ToList();
}
curAccount.Images.AddRange(accountImages);
return Workflows.Accounts.Update(curAccount);
}
}
return null;
}
#endregion
#region Deactivate
public void Any(RemoveAccount request)
{
Workflows.Accounts.Delete(request.ID);
}
public void Any(RemoveCurrentAccount request)
{
throw new NotImplementedException();
}
#endregion
}
}
This project contains the core UI code for both the store and admin
portals and their related Angular directives. This project's folder is
referenced as a virtual directory in the website and a configuration
setting in the web.config must be updated to the path where the virtual
directory is set. This VD allows the website to directly access all the
files here without having to move them into a particular website folder
or create a symbolic link using Windows itself.
Client specific code should not be input to this project, instead all
storefront controls have a Transclude process where-in the front end
developer can specify a different location to pull the HTML layout
content file which overrides the base path. The base path file must
contain only the bare minimum content of what tags are available, though
not in any particular order or layout.
What is a Summary Tag? A requirement in the Coding Standards is that all
C# code must have Summary Information Tags. A Summary Information Tag
(Summary Tag) is a form of formal documentation used by developers to
outline what a piece of code (file, property, class or function) is for.
These tags are readable by various IDEs like VS's Intellisense feature
and are often compiled into external resources to assist with creating
Help documentation.
What is Atomineer? Atomineer is one of several plugins that assist
developers with the tedium of documenting their code. The difference
between Atomineer and several of their competitors is that Atomineer
uses strong, customizable language heuristics to generate summary tags
for you, as well as ensuring that the existing tags are uniformly
formatted throughout your full solution. While Clarity only utilizes
Atomineer for the DocXml standard of documenting C# code, they support
a variety of languages and a variety of standards.
Atomineer is a Licensed product meaning that each developer which needs
a license to use it (after a 30 day trial) will need to purchase a
license. Clarity will order licenses for all Back End developers and
ensure that the licensing subscription is maintained. Please contact
Chris or James G. for assistance with getting a license after your 30
day trial is over.
Example Summary Tag Open a C# solution in Visual Studio and create a
new C# class file. Paste the following contents in that file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
154 155 namespace RepositoryPattern.BusinessWorkflows.Authors {
using System;
using System.Collections.Generic;
using System.Linq;
using Interfaces.BusinessWorkflows;
using Interfaces.DataModels;
using Interfaces.Mappers;
using Interfaces.Models;
using Interfaces.Repositories;
using Interfaces.SearchModels;
public class AuthorsBusinessWorkflow : IAuthorsBusinessWorkflow
{
public AuthorsBusinessWorkflow(IAuthorsRepository authorsRepository, IAuthorMapper authorMapper)
{
AuthorsRepository = authorsRepository;
AuthorMapper = authorMapper;
}
#region Private Variables
private IAuthorMapper AuthorMapper { get; }
private IAuthorsRepository AuthorsRepository { get; }
#endregion
#region Read
public IAuthorModel Get(int id)
{
BusinessWorkflowBase.ValidateRequiredID(id);
return AuthorMapper.MapToModel(AuthorsRepository.Get(id));
}
public IAuthorModel Get(string key)
{
BusinessWorkflowBase.ValidateRequiredKey(key);
return AuthorMapper.MapToModel(AuthorsRepository.Get(key));
}
public List<IAuthorModel> Search(IAuthorSearchModel searchModel, bool asListing = false)
{
var results = AuthorsRepository.Search(searchModel);
return asListing
? results.Select(AuthorMapper.MapToModelListing).ToList()
: results.Select(AuthorMapper.MapToModelLite).ToList();
}
#endregion
#region Create
public IAuthorModel Create(IAuthorModel model)
{
// Validate model
BusinessWorkflowBase.ValidateIDIsNull(model.ID);
BusinessWorkflowBase.ValidateRequiredString(model.Name, nameof(model.Name));
// Search for an Existing Record (Don't allow Duplicates
var results = Search(AuthorMapper.MapToSearchModel(model));
if (results.Any()) { return results.First(); } // Return the first that matches
// Map model to a new entity
var newEntity = AuthorMapper.MapToEntity(model);
newEntity.CreatedDate = BusinessWorkflowBase.GenDateTime;
newEntity.UpdatedDate = null;
newEntity.Active = true;
// Add it
AuthorsRepository.Add(newEntity);
// Try to Save Changes
AuthorsRepository.SaveChanges();
// Return the new value
return Get(newEntity.ID);
}
#endregion
#region Update
public IAuthorModel Update(IAuthorModel model)
{
// Validate model
BusinessWorkflowBase.ValidateRequiredNullableID(model.ID);
BusinessWorkflowBase.ValidateRequiredString(model.Name, nameof(model.Name));
// Find existing entity
// ReSharper disable once PossibleInvalidOperationException
var existingEntity = AuthorsRepository.Get(model.ID.Value);
// Check if we would be applying identical information, if we are, just return the original
// ReSharper disable once SuspiciousTypeConversion.Global
if (AuthorMapper.AreEqual(model, existingEntity))
{
return AuthorMapper.MapToModel(existingEntity);
}
// Map model to an existing entity
AuthorMapper.MapToEntity(model, ref existingEntity);
existingEntity.UpdatedDate = BusinessWorkflowBase.GenDateTime;
// Update it
AuthorsRepository.Update(AuthorMapper.MapToEntity(model));
// Try to Save Changes
AuthorsRepository.SaveChanges();
// Return the new value
return Get(existingEntity.ID);
}
#endregion
#region Deactivate
public bool Deactivate(int id)
{
BusinessWorkflowBase.ValidateRequiredID(id);
// Find existing Entity
var existingEntity = AuthorsRepository.Get(id);
if (existingEntity == null)
{
throw new InvalidOperationException($"Could not find an entity with id {id} to deactivate it");
}
// Do the Deactivate
return Deactivate(existingEntity);
}
public bool Deactivate(string key)
{
BusinessWorkflowBase.ValidateRequiredKey(key);
// Find existing Entity
var existingEntity = AuthorsRepository.Get(key);
if (existingEntity == null)
{
throw new InvalidOperationException($"Could not find an entity with key {key} to deactivate it");
}
// Do the Deactivate
return Deactivate(existingEntity);
}
protected bool Deactivate(IAuthor entity)
{
// Deactivate it
AuthorsRepository.Deactivate(entity);
// Try to Save Changes
AuthorsRepository.SaveChanges();
// Finished!
return true;
}
#endregion
#region Remove
public bool Remove(int id)
{
BusinessWorkflowBase.ValidateRequiredID(id);
// Find existing Entity
var existingEntity = AuthorsRepository.Get(id);
// Do the Remove
return Remove(existingEntity);
}
public bool Remove(string key)
{
BusinessWorkflowBase.ValidateRequiredKey(key);
// Find existing Entity
var existingEntity = AuthorsRepository.Get(key);
// Do the Remove
return Remove(existingEntity);
}
protected bool Remove(IAuthor entity)
{
if (entity == null) { return true; } // No entity found to remove, consider it passed
// Remove it
AuthorsRepository.Remove(entity);
// Try to Save Changes
AuthorsRepository.SaveChanges();
// Finished!
return true;
}
#endregion
}
} Don't worry about all the unrecognizable symbols, they don't impact
this example.
You can see here we have some comments inside the functions but no
summary tags. Though this code is well-formed and appears
well-documented, it would actually fail Code Review because there are no
Summary Tags.
To go about adding the first Summary Tag. Click on line 12, where there
is a blank space between the Usings and the Class declaration. Press
Enter to make a new line and then press / three times in a row. By
default, without Atomineer, the following text will be created by Visual
Studio:
1 2 3 4 /// <summary> /// /// </summary> public class
AuthorsBusinessWorkflow : IAuthorsBusinessWorkflow You can see that this
is just an empty summary tag. You would now need to type in a summary of
what this class is for between the <summary> and </summary> tags like
this.
1 2 3 4 /// <summary> /// A business logic layer for Authors. ///
</summary> public class AuthorsBusinessWorkflow :
IAuthorsBusinessWorkflow Now, imagine you need to do this for every
Class, Property, Function, Enum, etc in the entire code-base when there
are none. That would be tedious and time-consuming. In fact, a lot of
what you would write is really just an expanded form of what the item's
name is. It's really only where there is special logic happening that
you need to expand the documentation.
With Atomineer installed, you could click on the Class name (or anywhere
on that line) and instead of pressing / three times in a row, you would
press Ctrl+Shift+D. This would trigger Atomineer to document the line
using it's heuristics instead. Here is what Atomineer writes if I remove
the summary tag we just made and let it do it itself:
1 2 3 /// <summary>The authors business workflow.</summary>
/// <seealso cref="T:RepositoryPattern.Interfaces.BusinessWorkflows.IAuthorsBusinessWorkflow"/>
public class AuthorsBusinessWorkflow : IAuthorsBusinessWorkflow Ok, so
not the same as what we would have written in the summary tag itself but
you can see it directly relates to what the class name is. Also, it's
telling you in documentation that this class implements a specific
interface and where tho find that information with a <seealso/> tag. If
you go to another place in the code and start typing var w = new
AuthorsBusinessWorkflow(); in Visual Studio, the Intellisense would come
up automatically and provide a tooltip that says "The authors business
workflow." with a second part of the tooltip that would like over to the
documentation coming from the Interface that this is implementing and
its summary tag's content.
So now we see more information getting pre-populated by Atomineer than
we would have done or even known to do on our own. However, that just
tagged this one item of thousands throughout the solution. To document
more, Atomineer has friendly functions for doing more at the same time.
To document the entirety of the current file, press Ctrl+Shift+A then
Ctrl+Shift+F. Suddenly, this entire file is now documented with summary
tags. We can review the tags to make sure there's nothing extra we need
to add and save the file. There are additional commands which can be run
from the Tools > Atomineer Pro Documentation menu such as deleting
all the tags from a file and documenting an entire project or solution
at once (these can take a few minutes).
Atomineer provides this functionality as well as other customizations
like an entire dictionary of abbreviations to expand, such as Dnn to
DotNetNuke. Atomineer uses a set of settings files to maintain all of
the customized documentation setups. To get a copy of these settings,
please see James G.
Other projects in the solution must be handled on a case-by-case basis.
Continuous integration (CI) is the practice, in software engineering, of
merging all developer working copies to a shared mainline several times
a day. It was first named and proposed by Grady Booch in his 1991
method, although Booch did not advocate integrating several times a day.
It was adopted as part of extreme programming (XP), which did advocate
integrating more than once per day, perhaps as many as tens of times per
day. The main aim of CI is to prevent integration problems, referred to
as "integration hell" in early descriptions of XP. CI isn't universally
accepted as an improvement over frequent integration, so it is important
to distinguish between the two as there is disagreement about the
virtues of each.
In XP, CI was intended to be used in combination with automated unit
tests written through the practices of test-driven development.
Initially this was conceived of as running all unit tests in the
developer's local environment and verifying they all passed before
committing to the mainline. This helps avoid one developer's
work-in-progress breaking another developer's copy. If necessary,
partially complete features can be disabled before committing using
feature toggles.
Later elaborations of the concept introduced build servers, which
automatically ran the unit tests periodically or even after every commit
and report the results to the developers. The use of build servers (not
necessarily running unit tests) had already been practiced by some teams
outside the XP community. Nowadays, many organisations have adopted CI
without adopting all of XP.
In addition to automated unit tests, organisations using CI typically
use a build server to implement continuous processes of applying quality
control in general - small pieces of effort, applied frequently. In
addition to running the unit and integration tests, such processes run
additional static and dynamic tests, measure and profile performance,
extract and format documentation from the source code and facilitate
manual QA processes. This continuous application of quality control aims
to improve the quality of software, and to reduce the time taken to
deliver it, by replacing the traditional practice of applying quality
control after completing all development. This is very similar to the
original idea of integrating more frequently to make integration easier,
only applied to QA processes.
In the same vein, the practice of continuous delivery further extends CI
by making sure the software checked in on the mainline is always in a
state that can be deployed to users and makes the actual deployment
process very rapid.
The following child pages outline CI process instructions for TeamCity,
Visual Studio Online (VSO) and Team Foundation Server (TFS):
(Video) Continuous
Integration
Continuous Integration with
TeamCity
Continuous Integration with VSO-TFS
Set up a CI build Your team can minimize errors and increase quality by
integrating the code as frequently as possible and then building and
testing the result. You can define a build process to support this
strategy, known as continuous integration (CI). After this is done, you
and your team can determine as quickly as possible that a check-in has
broken the build or caused a test to fail. Define a build process to
support continuous integration
Improve the function and performance of the build process
Take next steps
Dig deeper
Define a Build Process to Support Continuous Integration In Team
Explorer, make sure you are connected to the team project (Keyboard:
Ctrl + 0, C), and then open theBuilds page (Keyboard: Ctrl + 0, B).
Choose the New Build Definition link or select a build, open its context
menu, and choose Edit Build Definition. Tip Tip If a TF225001 error
message appears, configure a build controller.
On the Trigger tab, choose Continuous Integration.
Tip Tip If your developers have to wait too long for their check-ins to
build, you might want to choose Rolling builds instead. This trigger
causes the build system to build multiple check-ins together. See Use
the Rolling builds trigger.
On the Source Settings tab:
TFVC icon TFVC: In the Working folders table, specify the
version-control folders that contain the files that your build process
requires.
Tip Tip To ensure that your build process functions correctly and to
improve performance, include all folders, and only these folders, that
contain files that your build process requires. For more information
about how to specify these folders, see Work with build workspaces.
Git icon Git: In the Monitored branches list, specify the repository and
the branches that contain the files that your build process requires.
You can use wildcards. For example, you could
specifyrefs/heads/feature* to monitor the refs/heads/featureA and
refs/heads/featureB branches.
To improve performance, on the Build Defaults tab, choose This build
does not copy output files to a drop folder.
On the Process tab, in the Build process parameters table under Build,
specify the solutions or code projects that you want to build.
On the Process tab, set the build process parameters to ensure that
check-ins meet the specific standards of code quality for your team
without delaying your developers unnecessarily.
For more information, see Improve build process function and performance
later in this topic.
Specify build process options on the other tabs. For more information,
see Create or edit a build definition.
Improve build process function and performance To minimize the time that
is required to process the build, you should consider following these
guidelines when you specify values for the build process parameters on
the Process tab.
TF Version Control or Git Clean workspace or Clean repository: For
faster performance, set this value to False. This setting might cause
your team to miss some types of defects, such as those introduced during
refactoring.
Build
Configurations: If you leave this parameter empty, the default platform
and configuration is used for each solution and project. To optimize
performance, adhere to the following guidelines:
If a platform-configuration pair builds more quickly than other pairs,
specify it in this parameter.
Specify as few platform-configuration pairs as possible.
Clean build For faster performance, set this parameter to False. This
setting might cause your team to miss some types of defects, such as
those introduced during refactoring.
Build, Advanced
Perform Code Analysis: For faster performance, set this value to Never.
Test, Advanced
Disable tests:
For faster performance, select True.
If your code must pass certain tests, select False, and then define a
set of tests to run in the build. You can improve performance by running
only the tests that you require. To designate those tests, filter them
by either category or priority. For more information, see Run tests in
your build process.
Publish Symbols
Path to publish symbols: For faster performance, leave this value empty.
Advanced
Agent Settings
Name Filter –or– Tags Filter: Use either a build agent name or a tag to
bind this build definition to a build agent that is designed
specifically for running this build. The build agent should run on
hardware that is sufficiently powerful to process this build quickly
enough to meet your team's performance expectations.
Maximum Execution Time: Set this value to a reasonably small number. For
example, 15 minutes might work for your team, but eight hours is
probably too long.
For more information about Default Template build process parameters,
see Use the Default Template for your build process.
Try this next Make sure everyone on your team checks in (TFVC) or pushes
(Git version control) early and often.
Run tests in your build process
Dig deeper Set up build notifications if you want to be notified when a
CI build is completed.
Use a gated check-in build process to validate changes if you want to
block check-ins that would break the build or fail your tests.
Overview This document is intended to provide guidelines for the
development processes of the eCommerce platform. The process is designed
to be light weight and efficient while adhering to current industry
standards. Although this document may not capture 100% of the scenarios
that may be encountered throughout the development process, this
document is to be considered a living document that may be updated in
the future to document and unforeseen. The format of this document will
focus on the high level processes initially then explore the details of
each process in sequential order. Any miscellaneous, but relevant
processes will be amended towards the end unless another logic order is
more ideal.
Life-cycle The development lifecycle is modeled after agile product
development practices with a scrum management process. In the figure
below, 10 key high level processes are identified for managing the
development processes of the eCommerce platform starting with platform
road mapping. For each key process, the roles that are involved in that
process are identified in the figure below.
https://clarityventures.sharepoint.com/Product/SiteAssets/CEF Clarity eCommerce Framework Wiki/Development Lifecycle %26 Processes/development_lifecycle.jpg
development_lifecycle.jpg
Roles Sales The sales role is filled by either the active Vice President
of Sales or a sales team member tasked with providing input for the
direction the platform from the sales team. The primary function of this
role is to provide feedback in the product development process that will
help shape the platform into the ideal product to sell in the target
market. Sales also follows up with the client currently which will
provide feedback for the improvements on existing features that are
needed.
Marketing The marketing role is filled by the active executive from the
marketing department. This role is intended to provide insight on the
competitive landscape and the ideal target market to develop feature or
to fill voids in certain market areas.
Firm Partners Firm partners are roles that have equity interest in the
firm, such as the CEO. The partner roles are required for the platform
development process to ensure firm resources are used efficiently and
the product direction is in line with the vision for the firm.
Platform Lead The role of the platform lead is to be the deciding factor
in the technical direction of the platform. This role also manages the
key parts of the development process to ensure quality, consistency, and
technical support or guidance to other development members. This role is
intended to help knock down development roadblocks to help ensure
progress.
Management Management includes development managers for the front and
backend to ensure adequate resources are allocated to meet timelines.
Project Management The project management role is to ensure timelines
and expectations are within reason.
Senior Development Senior development roles are any developer positions
containing senior titles. These roles are vital in the process to
provide key architectural and technical insights for the products
technical direction. These roles also help in the development the
features of the platform and improve development processes.
Developers Developers provide the technical skill sets to create the
product features.
QA Members QA members review the finish development of all features to
ensure product issues do not make it to clients.
Stakeholders The stakeholders are responsible for providing insight and
details for specific tasks. This role could be comprised of either an
internal employee or designated customer.
Key Process Platform Development Road Mapping The road mapping process
is designed to identify all features need for a commercially viable
product and prioritize them based off business and technical needs. This
process takes the form of a quarterly meeting between all roles
involved. Prior to the meeting each individual is required to compose a
list of newly identified features and send them to all participants.
Each newly identified feature should contain a short description of the
business needs, a priority, and a description of the functionality
required by each feature. During each meeting the group reviews,
discusses, and approves new features. The group will also review
existing features and decide whether to demote them from the roadmap.
Once the list is approved each road mapping iteration the top priority
features will be grouped in to 2 or 3 sequential releases in which the
releases will be developed.
Release Cycle A release cycle is the development process to complete a
group of features from the roadmap that has already been defined for the
release by the road mapping committee. Each release starts off with
reviewing the individual features assigned to the release and then
defined into tasks with functional and technical details in VSO, the
release management tool. Once all features of the release have been
broken into concise and manageable tasks, the task are then organized
into two or more sprints, about two weeks long each, which make up the
release. Based on the amount of effort and resources available a
timeline will be determined for the series of sprints to be completed by
to calculate a release date. After the sprints and timeline are
solidified a sprint cycle may begin.
Sprint Cycle The sprint cycle is the actual development process of the
tasks assigned to a sprint. The cycle starts off with a planning meeting
to finalize the details of the sprint then moves into a setup of the
sprint and feature development. After each feature has been developed, a
QA member or stakeholder will review the wok completed to make sure it
functionally satisfies the business needs. Once the business needs have
been met for a feature, the code developed is reviewed by peers to
ensure quality and best practices. When all the features have been
completed, the new features developed in the sprint will be reviewed one
more time prior to updating the release branch with the latest. After
the sprint development has been completed, a sprint retrospect is
performed to assess what worked well, what didn't work well, and what
needs to change for the next sprint.
Planning During the sprint planning tasks are reviewed to make sure the
timeline is realistic. Any adjustments to the sprint are made at this
time. The only exceptions to adjusting the items being worked on in a
sprint are if urgent business needs have been submitted to crash the
development schedule or sprint items have been completed ahead of time.
When reviewing the sprint items, there are several things that need to
be reviewed or provided. Below is a validation list for the sprint items
in VSO.
Sprint Item Validation Check List Brief summary of the task is provided
A subsystem is identified Priority is established A detailed description
or sub tasks that make up the task are provided Estimate (Original
Estimate in VSO) Stakeholder must be provided that is the business
expert for the requested feature Sprint assigned Epic link provided
After all items have been reviewed and pass the criteria, the time
required to develop each item of the sprint and available resources
should be reviewed to verify enough resources have been allocated to the
sprint. When all the details have been solidified and risks have been
identified then the sprint may begin.
Pre-Sprint Setup The sprint set up is composed of a few tasks that need
to be performed prior to each sprint. Below is a list that details the
tasks need prior to starting a sprint.
Pre-Sprint Setup Start Sprint Verify current release state is build-able
and tagged Create new release branch if start of new release Verify
environments need are set up prior (DB, Dev servers) Development
Features Feature developers are responsible for the actual development
and implementation of the requested feature. In order to start
developing a feature, the sprint must be started and an item must be
assigned.
QA / UAT During the QA process, a QA member or stakeholder will review
the newly completed feature and verify that the expected functionality
is working correctly. Ideally a separate environment is set up for this
review for the QA member or stakeholder to review the feature. During
this time, adjustments may be needed in order for the feature to be
acceptable for a completed status.
Peer Review / Sign off When the Task has been marked for completion, the
developer of the feature will need to send an email to one or more peers
detailing the change and files. If the peer, approves of the
implementation then the code can be merged into the sprint. If the peer
does not approve, then the peer needs to detail a more ideal
implementation or note any adjustments. Once the code has been deemed to
be in good standing then it can be merged back into the release branch.
Retrospect After all tasks have been completed or the sprint timeline
has finish, then a sprint retrospect needs to be completed. This can
take the form of a meeting or an email, but the following items need to
be detailed out by the team prior to starting another sprint cycle.
Retrospect items What worked well? What didn't work well? What do we
need to change? Or how can we fix what did not go well. Tag Release
Branch After each sprint is completed, the platform development lead
needs to ensure all features of a sprint have been merged into the
release branch. If the sprint is the last sprint of a release then the
platform lead needs to merge the release branch into the trunk. At this
time, a tag needs to label the completion of each release and sprint in
source control to easily identify it for future reference. The Platform
lead needs to ensure each merge into release or trunk is buildable. All
trunk merges must be production ready.
(Video) Development & QA Cycles During a
Project
(Video) What A Good QA Testing Plan Should
Include
The below guide will help team members with the eCommerce Website QA
process
Step-by-step guide Recommended areas to confirm eCommerce Site
functionality:
API Can you access the API URL / endpoints - for example
www.devsite.us/DesktopModules/ClarityeCommerce/API Storefront Sample
Data Does the product page show demo products Catalog Is there a working
product catalog page Does the category selector work Can you link
directly to a product category from the nav Can you click on a product
and be taken to the product detail page Does the page selector work Do
the Items per page work Product Detail Does the product detail page show
the correct price and product attribute selectors Does the product zoom
work Do the related products appear Does the product gallery work Does
the add to cart button work Cart Does the view cart template work and
update when an Item is added Can you increase quantity and clear the
cart Does the go to checkout link work Checkout Can you complete the
checkout process Can you log back in with the user used for checkout
User Dashboard Can you login and see the user dashboard after logging in
Admin Can you access the CEF admin dashboard Product Can you add a
product Can you associate to a category/categories Can you add related
products Can you assign product attributes Can you add product images
Category Can you create a category Sales Order Can you view past sales
orders Can you update a sales order Can you modify the payment status or
process payment Can you add new line items or process an RMA Accounts
Can you add/remove accounts Reports Can you access the reports Do the
reports provide meaningful/accurate data
Bug Reporting
Use the bug reporting standards to issue bug reports using the tools for
that particular project
Note that typically the more information provided the better someone can
debug quickly
Key items are browser version, dimensions, page url, IP and date/time,
highlight of the screen / screenshot, and notes
Recommended Resources:
How Usability Works - Lynda.com Foundations of Programming - Software
Quality Assurance - Lynda.com Debugging Tips
Recommended Debugging Resources:
Chrome Dev Tools - Chrome.com Introduction to Chrome dev tools -
HTML5Rocks.com Recommended Uses:
Typically if there is an issue with the platform it will show within the
console log of the inspection tool. If you can identify the request and
response detail it will typically help the development team resolve the
issue quickly if it's a simpler error for them. Note that you can use
Firefox, Chrome or other inspector tools to get the same information.
Important Links
Key Pages Within DNN:
www.devsite.us/Admin/Pages Look on the left column of the page list for
key pages such as Cart, Checkout, Product Details, Product Listing,
Dashboard, Order Detail, Invoice Detail www.devsite.us/login - Use this
url path to login Common logins are Clarity or Host and qpzm9731 or
4rfvBGT% www.devsite.us/home.aspx?ctl=logoff - Use this url path to
logoff Key Pages Within Sitefinity:
www.devsite.us/Sitefinity - use this path to login
Clone Cloning is really the default behavior you'd expect from SVN. This
is how you'll create your local working copy. You don't need permission
via pull request to merge back (assuming you have r+w to the parent
repository). You should clone a repository if you plan to push your
changes back to the same repository where you have read+write access
privileges. You should also clone a repository if you are building a new
repository that is unique.
Fork If you want to contribute code enhancements to an existing
repository, you should create a new repository by creating a fork of the
original. For example, if you want to contribute minor bug fixes,
enhancements, or new features to another git project, the easiest way
for those changes to be merged into the original repository is with a
Pull Request from a forked repository. See Make a Pull Request on
GitHub.
Fork Documentation from Atlassian
Note: When you fork a repository, all metadata associated with the
repository such as commit messages, branches, tags, etc. are copied over
to the new repository so that it's an exhaustive duplicate of the
repository and all of its contents.
Commits Amending a commit
git commit --amend -m "New commit message" Git and Stash How To's Use
these links to get basic overviews:
http://blog.sourcetreeapp.com/
https://confluence.atlassian.com/display/STASH/Stash+Documentation+Home
http://stackoverflow.com/questions/507343/using-git-with-visual-studio
http://www.hanselman.com/blog/GitSupportForVisualStudioGitTFSAndVSPutIntoContext.aspx
http://net.tutsplus.com/articles/general/from-ftp-to-git-a-deployment-story/
Table of Contents Requirements Instructions Tips & Warnings Related
PurposeThis How To will explain how to use GIT and Stash at Clarity
Requirements Login access to the Clarity network and access to
http://stash:7990 or via http://www.claritygit.com:7990 (note you
can create a host entry for stash = 10.10.30.90 if your DNS isn't
resolving). Instructions Review the links above (top of the page) for
intro to and how to use GIT. Login to your Stash account on the
http://stash:7990 url and confirm access. Create a simple test
repository and clone to a folder on a development server or your local
machine. Confirm your ability to "Commit" and then "Push" changes - you
may want to use the SourceTree app or Visual Studio integration. You can
now access Clarity's Git repository to make commits. Please let your
team lead or a partner know if you need help getting access. Tips &
Warnings Some team members may not have full access right away. Your
team lead or a partner can resolve this quickly. You may want to ask a
lead who has done work with the tool set before for a basic walk through
before you begin. It would be helpful to make sure you understand the
fundamentals of the GIT and Stash tools by reviewing their sites and
online documentation to ensure you're up to speed.
Define Understanding Setting up environment
Select Team Explorer At the top, hover over the grey text to the right
of Home. Select Projects and My Teams Select Connect to Team Projects…
Click Select Team Projects… Select the project you want to connect to.
Click Connect Double-click the project name in the list under
clarity-ventures.visualstudio.com Click the link in the yellow banner to
clone the project. When done, double-click the appropriate solution from
the Solutions list. The solutions tab will populate with the local copy
of your file structure. Yes. It IS that easy. ;P
Pulling down When should someone pull down the branch?
When setting up initial clone. When code is ready to be sync'd with
branch (aka no breaks & ready to be merged onto other people's systems)
Steps for pulling down a branch
Save your work (if any) Commit your work (if any) Click on Sync in the
yellow banner after commit completes After reviewing Outgoing Commits,
click the Pull button
Commits How often should I make a commit? Any time you would hit the
save button. Really, you can't have too many of these
How should I label my commit? Don't worry about dates. Git takes care of
that for us, but do tell us what you're doing short and sweet… but not
too short.
Good note implementing javascript fixes on MegaMenu mobile toggles'
Bad note "I did stuff to make things happen"
Bad note "It little profits that an idle king, By this still hearth,
among these barren crags, Match'd with an aged wife, I mete and dole
Unequal laws unto a savage race, That hoard, and sleep, and feed, and
know not me. I cannot rest from travel: I will drink Life to the lees:
All times I have enjoy'd Greatly, have suffer'd greatly, both with those
That loved me, and alone, on shore, and when Thro' scudding drifts the
rainy Hyades Vext the dim sea: I am become a name; For always roaming
with a hungry heart Much have I seen and known; cities of men And
manners, climates, councils, governments, Myself not least, but honour'd
of them all; And drunk delight of battle with my peers, Far on the
ringing plains of windy Troy. I am a part of all that I have met; Yet
all experience is an arch wherethro' Gleams that untravell'd world whose
margin fades For ever and forever when I move. How dull it is to pause,
to make an end, To rust unburnish'd, not to shine in use! As tho' to
breathe were life! Life piled on life Were all too little, and of one to
me Little remains: but every hour is saved From that eternal silence,
something more, A bringer of new things; and vile it were For some three
suns to store and hoard myself, And this gray spirit yearning in desire
To follow knowledge like a sinking star, Beyond the utmost bound of
human thought. This is my son, mine own Telemachus, To whom I leave the
sceptre and the isle,— Well-loved of me, discerning to fulfil This
labour, by slow prudence to make mild A rugged people, and thro' soft
degrees Subdue them to the useful and the good. Most blameless is he,
centred in the sphere Of common duties, decent not to fail In offices of
tenderness, and pay Meet adoration to my household gods, When I am gone.
He works his work, I mine. There lies the port; the vessel puffs her
sail: There gloom the dark, broad seas. My mariners, Souls that have
toil'd, and wrought, and thought with me— That ever with a frolic
welcome took The thunder and the sunshine, and opposed Free hearts, free
foreheads—you and I are old; Old age hath yet his honour and his toil;
Death closes all: but something ere the end, Some work of noble note,
may yet be done, Not unbecoming men that strove with Gods. The lights
begin to twinkle from the rocks: The long day wanes: the slow moon
climbs: the deep Moans round with many voices. Come, my friends, 'T is
not too late to seek a newer world. Push off, and sitting well in order
smite The sounding furrows; for my purpose holds To sail beyond the
sunset, and the baths Of all the western stars, until I die. It may be
that the gulfs will wash us down: It may be we shall touch the Happy
Isles, And see the great Achilles, whom we knew. Tho' much is taken,
much abides; and tho' We are not now that strength which in old days
Moved earth and heaven, that which we are, we are; One equal temper of
heroic hearts, Made weak by time and fate, but strong in will To strive,
to seek, to find, and not to yield."
Don't be like Ulysses . It sucks to be Ulysses.
Pushing When to push? Pushing code should only happen when it is
complete and ready to go to live. This means you have tested it on your
system and it works as expected.
When not to push When you're just wanting to save your work or someone
wants to see it. Commits are for saving and deadlines are for show (this
part may be argued, but legit serious about the commit part).
Branching What branch to work off of? Your branch should be a copy of
main.
When to create a new branch? If you want to create your own
sub-branches, you are welcome to do so. This is usually done if you're
working on multiple features/functions and you want to have the ability
to push one at a time to main. It helps elminate merge conflicts. Thank
Eric for that suggestion.
Why branches are used? So everyone can break their own stuff. It's like
the lock that keeps your sister's out of your room so they can't break
your toys.
How to resolve conflicts When 2 developers work on the same file,
probably for different reasons, sometimes to fix the same bug,
eventually both persons will try to commit that file's changes. The
first person which gets it in will succeed normally, and the second will
be forced to pull the other commit down and Resolve a Conflict. Various
tools exist which can allow conflict resolution.
In most cases, if the two developers didn't alter the same set of lines
(dev A could have edited the top part while dev B did the bottom part),
then the conflict will basically resolve itself. You may even not have
to use the Conflict Merge tool in some of those cases. However, when 2
people have worked on the same part of the same file in different ways,
it must be manually merged. This is called Resolving a Conflict.
https://mcuoneclipse.files.wordpress.com/2013/03/component-conflict-in-processorexpert-pe.png
You can see in the image above a common conflict merge tool and the
views it displays:
The top left is 'Their' file, the file as it came from the Git server
The top right is 'My' file, with my local changes The bottom shows the
'Merged Result' file, you will see a red area with a ton of ?'s. It's
telling you it doesn't know what to do with that area of a file. You can
review both the left and right files to figure out an appropriate
solution. You can either:
Pick the left side's code and call it done Pick the right side's code
and call it done Pick both with either side being first Write a totally
different answer in the bottom file When all the Conflicts in a file
have been resolved (the bottom file should have no red in it), you can
now save the file and 'Mark the file Resolved'. This tells Git the file
is ready to be pushed back up to the server with a Resolved Conflict.
From here, make your commit normally and push it to the server.
Sometimes when coding you need to make a change that will affect how
other developers need to pull this change into their environments beyond
the standard pull-merge-continue method. When this happens, clear
documentation must be included on what actions must be taken to perform
the upgrade at the time of pull-down and steps must be taken to share
this information with the team.
Example Steven makes a commit that includes a new EF Code-First Database
Migration because he added a column to the table schema for Products.
His local copy of the database has already been upgraded to the new
schema but no other copy of the database, such as the Staging
environment or Emily's local copy has been updated. When Stephen makes
his commit, he needs to add the following to his Commit Notes:
You can see above that he's informing developers exactly what needs to
be done and we have a permanent record to review that.
The following key factors should be considered when prioritizing or
ranking bug fixes. A bug report that meets any of the following criteria
should typically be treated with urgency:
Is it a bug that effects multiple concurrent installations of the store?
Is it a bug that prevents a user from purchasing an item? Is it a bug
that prevents an administrator from performing fundamental store
functions such as: updating products, changing payment information or
viewing transactions? Does this bug prevent development in other (or
all) key areas of a project or projects? Will this bug have an immediate
effect on an important client deadline? Was this bug submitted or
sponsored by a team lead? Some Notes on Project Resolution Time
How long a bug fix is expected to take should be considered along with
other factors. For example, a bug fix that meets the criteria for urgent
intervention and that can be resolved very quickly should almost always
be near the very top of the list. Alternately, an issue that is not
particularly urgent and that will furthermore take a long time to
address should be near the bottom of any list.
Lower Priority Factors
Most of these can be readily inferred from the urgent list.
The bug only effects a single instance. The bug can be worked around,
other work can continue while it is in the dock. The bug relates to a
feature that does not prevent item purchase or basic store
administration.
Please use this process below to document any "bugs" with the CEF
platform.
Step-by-step guide to add a bug to VSO Login to VSTS:
https://clarity-ventures.visualstudio.com/ Find the project and go
into the Work tab within the project For the CEF-Product you can click
this link to be taken directly to the backlog:
https://clarity-ventures.visualstudio.com/DefaultCollection/CEF-Product/_backlogs
Click New Item and change the type to Bug Type the Title of your new Bug
and press Enter Open your new Bug by double-clicking on the new row in
the Backlog and perform the following modifications to the Bug: Assign
to Brittney Betbeze Add the project name as a Tag using the
auto-complete feature to match associated items: eg- Protec, UMC Select
the best fitting Area in the Area field Set the Severity of this Bug as
it pertains to your project. If it is blocking the ability to implement
a critical site feature, mark it as 1 - Critical.
NOTE: DO NOT SET PRIORITY. That will be handled by Brittney.
Write the reproduction steps with any pertinent information to the Repro
Steps field Best Practices Provide detailed steps to replicate the bug
being sure to include any information that might be relevant. Provide
full urls where applicable to any web pages referenced. Provide full log
in information if it is needed to replicate your bug. Include any steps
you have taken to attempt to remedy or work around the problem. If it's
a very higher priority for you, explain why in your bug report. If
anyone has special knowledge of this bug, or has worked on it in the
past mention them so it can be assigned appropriately.
In Scrum, the sprint planning meeting is attended by the product owner,
ScrumMaster and the entire Scrum team. Outside stakeholders may attend
by invitation of the team, although this is rare in most companies.
During the sprint planning meeting, the product owner describes the
highest priority features to the team. The team asks enough questions
that they can turn a high-level user story of the product backlog into
the more detailed tasks of the sprint backlog.
The product owner doesn't have to describe every item being tracked on
the product backlog. A good guideline is for the product owner to come
to the sprint planning meeting prepared to talk about two sprint's worth
of product backlog items. To make an example really simple, suppose a
team always finishes five product backlog items. Their product owner
should enter the meeting prepared to talk about the top 10 priorities.
There are two defined artifacts that result from a sprint planning
meeting:
A sprint goal A sprint backlog A sprint goal is a short, one- or
two-sentence, description of what the team plans to achieve during the
sprint. It is written collaboratively by the team and the product owner.
The following are example sprint goals on an eCommerce application:
Implement basic shopping cart functionality including add, remove, and
update quantities. Develop the checkout process: pay for an order, pick
shipping, order gift wrapping, etc. The sprint goal can be used for
quick reporting to those outside the sprint. There are always
stakeholders who want to know what the team is working on, but who do
not need to hear about each product backlog item (user story) in detail.
The success of the sprint will later be assessed during the sprint
review meeting against the sprint goal, rather than against each
specific item selected from the product backlog.
The sprint backlog is the other output of sprint planning. A sprint
backlog is a list of the product backlog items the team commits to
delivering plus the list of tasks necessary to delivering those product
backlog items. Each task on the sprint backlog is also usually
estimated.
An important point to reiterate here is that it's the team that selects
how much work they can do in the coming sprint. The product owner does
not get to say, "We have four sprints left so you need to do one-fourth
of everything I need." We can hope the team does that much (or more),
but it's up to the team to determine how much they can do in the sprint.
CREDIT FOR CONTENT ABOVE:
http://www.mountaingoatsoftware.com/agile/scrum/sprint-planning-meeting
What is it? A Software Change Notice (SCN), or change notice, is a
document which records or authorizes a change to a specific design. The
reasons for the change should also be recorded.
Following sound engineering principles, control and documentation are
necessary to ensure that changes are built upon a known foundation and
approved by relevant authorities.
"[A] document approved by the design activity that describes and
authorizes the implementation of an engineering change to the product
and its approved configuration documentation".
An SCN must contain at least this information:
Identification of what needs to be changed. This should include the part
number and name of the component and reference to the drawings that show
the component in detail or assembly. Reason(s) for the change.
Description of the change. This includes a drawing of the component
before and after the change. Generally, these drawings are only of the
detail affected by the change. List of documents and departments
affected by the change. The most important part of making a change is to
see that all pertinent groups are notified and all documents updated.
Approval of the change. As with the detail and assembly drawings, the
changes must be approved by management. Instruction about when to
introduce the change—immediately (scrapping current inventory), during
the next production run, or at some other milestone. The term “change”
includes changes to hardware, software, and firmware that occur over the
entire life of a product. Product changes include those considered
report-able and non-report-able. These changes may be applied by a
supplier, a customer, or a contractor retained by the customer,
depending on negotiated agreements. Fundamentally, the customer’s goal
is to ensure there is a process by which there is accurate and efficient
tracking and reporting of changes to products.
Changes are considered report-able when they affect the performance or
life span of a product. Such changes include any that affect the form,
fit, function, or the product technical specification (i.e.,
documentation) of the product. The desire for supplier or customer
trace-ability may result in a report-able change.
Example SCN Note: The following is just an example and not part of a
real change notification
Simple Note for Release Change-log: Product schema now stores kit
information and relationship with a Kit Parent Product Full Details: To
support Product Kits as designed by Microsoft Dynamics GP's ERP system
which Clarity Connect supports pulling product and kit information from,
some changes needed to occur in the CEF schema and workflows.
Three data points have been added to the Product table:
KitParentId: The identifier of the core item that will be part of this
kit. For instance if the kit is 2 T-Shirts, one red, one white. This
value would point at the Red One as sold individually (if sold ind., if
not then a product record would still exist just not visible as part of
the store for users). A second record would point at the White one
KitQuantityOfParent: How many of the product that KitParentId points to
would be included in this kit. For instance a kit of 4 hard drives would
specify 4 here. KitUnitOfMeasure: The GP specified Unit of Measure, such
as Each or Case. If the UofM is Case and the Quantity is 4, then the kit
contains 4 cases of the product specified by KitParentId instead of 4
Units Workflows now support pulling the product kit information from GP
to CEF and creating the appropriate relationships between the records.
The store should display the Kit item with specialized UI for stating
that child items are part of the Kit. Only the Kit item should be added
to the cart as the ERP system will assign the quantities of the kit
parts for distribution after sale.
This schema and workflows change requires a Database update which must
be issued via EntityFramework Code First Update-Database method, state a
Target of GPAdjustments02 to specifically implement these changes.
Why can't I commit my code to Dev-Master anymore?
We have implemented Gated Checkins (branch policies) on Master,
Dev-Master, and the Client branches. No one, not even the senior team
members, can commit directly to those branches anymore. A separate
branch must be created and a Pull Request made via VSTS to get your
updates pushed into Dev-Master. Once a Pull Request is made, the
appropriate reviewers will review and validate the code being submitted
and Approve or Reject the Pull Request. If we Reject it, we will be
providing reasons why in the Discussion on the Request.
When you go to make your own branch to work in, please follow this
standardized naming convention: <type>_<name> for example:
Feat_PayPalExpressCheckout BugFix_VSTS12345
Client branches should be named: Client_<Client Code> for example:
Client_P4H Client_UMC
You will not perform any work in the Client branches as they are also
locked. In rare circumstances, a branch off of the Client branch may be
approved to insert work that cannot be applied to Core CEF.
A requirement in the Coding Standards is that all C# code must have
Summary Information Tags. A Summary Information Tag (Summary Tag) is a
form of formal documentation used by developers to outline what a piece
of code (file, property, class or function) is for. These tags are
readable by various IDEs like VS's Intellisense feature and are often
compiled into external resources to assist with creating Help
documentation.
Atomineer is one of several plugins that assist developers with the
tedium of documenting their code. The difference between Atomineer and
several of their competitors is that Atomineer uses strong, customizable
language heuristics to generate summary tags for you, as well as
ensuring that the existing tags are uniformly formatted throughout your
full solution. While Clarity only utilizes Atomineer for the DocXml
standard of documenting C# code, they support a variety of languages
and a variety of standards.
Atomineer is a Licensed product meaning that each developer which needs
a license to use it (after a 30 day trial) will need to purchase a
license. Clarity will order licenses for all Back End developers and
ensure that the licensing subscription is maintained. Please contact
Chris or James G. for assistance with getting a license after your 30
day trial is over.
Open a C# solution in Visual Studio and create a new C# class file.
Paste the following contents in that file:
namespace RepositoryPattern.BusinessWorkflows.Authors
{
using System;
using System.Collections.Generic;
using System.Linq;
using Interfaces.BusinessWorkflows;
using Interfaces.DataModels;
using Interfaces.Mappers;
using Interfaces.Models;
using Interfaces.Repositories;
using Interfaces.SearchModels;
public class AuthorsBusinessWorkflow : IAuthorsBusinessWorkflow
{
public AuthorsBusinessWorkflow(IAuthorsRepository authorsRepository, IAuthorMapper authorMapper)
{
AuthorsRepository = authorsRepository;
AuthorMapper = authorMapper;
}
#region Private Variables
private IAuthorMapper AuthorMapper { get; }
private IAuthorsRepository AuthorsRepository { get; }
#endregion
#region Read
public IAuthorModel Get(int id)
{
BusinessWorkflowBase.ValidateRequiredID(id);
return AuthorMapper.MapToModel(AuthorsRepository.Get(id));
}
public IAuthorModel Get(string key)
{
BusinessWorkflowBase.ValidateRequiredKey(key);
return AuthorMapper.MapToModel(AuthorsRepository.Get(key));
}
public List<IAuthorModel> Search(IAuthorSearchModel searchModel, bool asListing = false)
{
var results = AuthorsRepository.Search(searchModel);
return asListing
? results.Select(AuthorMapper.MapToModelListing).ToList()
: results.Select(AuthorMapper.MapToModelLite).ToList();
}
#endregion
#region Create
public IAuthorModel Create(IAuthorModel model)
{
// Validate model
BusinessWorkflowBase.ValidateIDIsNull(model.ID);
BusinessWorkflowBase.ValidateRequiredString(model.Name, nameof(model.Name));
// Search for an Existing Record (Don't allow Duplicates
var results = Search(AuthorMapper.MapToSearchModel(model));
if (results.Any()) { return results.First(); } // Return the first that matches
// Map model to a new entity
var newEntity = AuthorMapper.MapToEntity(model);
newEntity.CreatedDate = BusinessWorkflowBase.GenDateTime;
newEntity.UpdatedDate = null;
newEntity.Active = true;
// Add it
AuthorsRepository.Add(newEntity);
// Try to Save Changes
AuthorsRepository.SaveChanges();
// Return the new value
return Get(newEntity.ID);
}
#endregion
#region Update
public IAuthorModel Update(IAuthorModel model)
{
// Validate model
BusinessWorkflowBase.ValidateRequiredNullableID(model.ID);
BusinessWorkflowBase.ValidateRequiredString(model.Name, nameof(model.Name));
// Find existing entity
// ReSharper disable once PossibleInvalidOperationException
var existingEntity = AuthorsRepository.Get(model.ID.Value);
// Check if we would be applying identical information, if we are, just return the original
// ReSharper disable once SuspiciousTypeConversion.Global
if (AuthorMapper.AreEqual(model, existingEntity))
{
return AuthorMapper.MapToModel(existingEntity);
}
// Map model to an existing entity
AuthorMapper.MapToEntity(model, ref existingEntity);
existingEntity.UpdatedDate = BusinessWorkflowBase.GenDateTime;
// Update it
AuthorsRepository.Update(AuthorMapper.MapToEntity(model));
// Try to Save Changes
AuthorsRepository.SaveChanges();
// Return the new value
return Get(existingEntity.ID);
}
#endregion
#region Deactivate
public bool Deactivate(int id)
{
BusinessWorkflowBase.ValidateRequiredID(id);
// Find existing Entity
var existingEntity = AuthorsRepository.Get(id);
if (existingEntity == null)
{
throw new InvalidOperationException($"Could not find an entity with id {id} to deactivate it");
}
// Do the Deactivate
return Deactivate(existingEntity);
}
public bool Deactivate(string key)
{
BusinessWorkflowBase.ValidateRequiredKey(key);
// Find existing Entity
var existingEntity = AuthorsRepository.Get(key);
if (existingEntity == null)
{
throw new InvalidOperationException($"Could not find an entity with key {key} to deactivate it");
}
// Do the Deactivate
return Deactivate(existingEntity);
}
protected bool Deactivate(IAuthor entity)
{
// Deactivate it
AuthorsRepository.Deactivate(entity);
// Try to Save Changes
AuthorsRepository.SaveChanges();
// Finished!
return true;
}
#endregion
#region Remove
public bool Remove(int id)
{
BusinessWorkflowBase.ValidateRequiredID(id);
// Find existing Entity
var existingEntity = AuthorsRepository.Get(id);
// Do the Remove
return Remove(existingEntity);
}
public bool Remove(string key)
{
BusinessWorkflowBase.ValidateRequiredKey(key);
// Find existing Entity
var existingEntity = AuthorsRepository.Get(key);
// Do the Remove
return Remove(existingEntity);
}
protected bool Remove(IAuthor entity)
{
if (entity == null) { return true; } // No entity found to remove, consider it passed
// Remove it
AuthorsRepository.Remove(entity);
// Try to Save Changes
AuthorsRepository.SaveChanges();
// Finished!
return true;
}
#endregion
}
}
Don't worry about all the unrecognizable symbols, they don't impact this
example.
You can see here we have some comments inside the functions but no
summary tags. Though this code is well-formed and appears
well-documented, it would actually fail Code Review because there are no
Summary Tags.
To go about adding the first Summary Tag. Click on line 12, where there
is a blank space between the Usings and the Class declaration. Press
Enter to make a new line and then press / three times in a row. By
default, without Atomineer, the following text will be created by Visual
Studio:
/// <summary>
///
/// </summary>
public class AuthorsBusinessWorkflow : IAuthorsBusinessWorkflow
You can see that this is just an empty summary tag. You would now need
to type in a summary of what this class is for between the <summary> and
</summary> tags like this.
/// <summary>
/// A business logic layer for Authors.
/// </summary>
public class AuthorsBusinessWorkflow : IAuthorsBusinessWorkflow
Now, imagine you need to do this for every Class, Property, Function,
Enum, etc in the entire code-base when there are none. That would be
tedious and time-consuming. In fact, a lot of what you would write is
really just an expanded form of what the item's name is. It's really
only where there is special logic happening that you need to expand the
documentation.
With Atomineer installed, you could click on the Class name (or anywhere
on that line) and instead of pressing / three times in a row, you would
press Ctrl+Shift+D. This would trigger Atomineer to document the line
using it's heuristics instead. Here is what Atomineer writes if I remove
the summary tag we just made and let it do it itself:
/// <summary>The authors business workflow.</summary>
/// <seealso cref="T:RepositoryPattern.Interfaces.BusinessWorkflows.IAuthorsBusinessWorkflow"/>
public class AuthorsBusinessWorkflow : IAuthorsBusinessWorkflow
Ok, so not the same as what we would have written in the summary tag
itself but you can see it directly relates to what the class name is.
Also, it's telling you in documentation that this class implements a
specific interface and where tho find that information with a <seealso/>
tag. If you go to another place in the code and start typing var w = new
AuthorsBusinessWorkflow(); in Visual Studio, the Intellisense would come
up automatically and provide a tooltip that says "The authors business
workflow." with a second part of the tooltip that would like over to the
documentation coming from the Interface that this is implementing and
its summary tag's content.
So now we see more information getting pre-populated by Atomineer than
we would have done or even known to do on our own. However, that just
tagged this one item of thousands throughout the solution. To document
more, Atomineer has friendly functions for doing more at the same time.
To document the entirety of the current file, press Ctrl+Shift+A then
Ctrl+Shift+F. Suddenly, this entire file is now documented with summary
tags. We can review the tags to make sure there's nothing extra we need
to add and save the file. There are additional commands which can be run
from the Tools > Atomineer Pro Documentation menu such as deleting
all the tags from a file and documenting an entire project or solution
at once (these can take a few minutes).
Atomineer provides this functionality as well as other customizations
like an entire dictionary of abbreviations to expand, such as Dnn to
DotNetNuke. Atomineer uses a set of settings files to maintain all of
the customized documentation setups. To get a copy of these settings,
please see James G.