Skip to content

v2.0.0

Compare
Choose a tag to compare
@benhalstead benhalstead released this 14 Apr 19:50
· 218 commits to master since this release

Granitic 2.0 Release Notes

Granitic 2.0 is a major release of Granitic focusing on YAML support, Go module support, streamlining of web service code, improvements to grnc-bind, code quality and documentation.

This release is not backwards compatible, refer to the migration guide at the bottom of these notes for more information.

YAML

Granitic now supports YAML for configuration and component definition files. This involves the use of a third-party YAML parser and so to preserve Granitic's principle of having no dependencies, you must download the additional granitic-yaml project from GitHub or add it as a Go module dependency (see below).

The default file format for Granitic is currently still JSON and all documentation and examples will continue to use JSON, but YAML versions of the tutorial source code are now available.

Note you will need to change your application's main function to

func main() {
	granitic-yaml.StartGraniticWithYaml(bindings.Components())
}

New default locations for files

The previous default locations for application configuration (resource/config) and component definitions (resource/components) were too verbose and have been deprecated. The new preferred locations are config and comp-def.

grnc-project has been modified to create projects with these new locations. The empty files it creates are now config/base.json and comp-def/common.json and the generated entrypoint file is main.go

The old locations are still respected, but you will see a warning when you use them. Support for the old locations will be removed in a future version of Granitic.

Reference documentation

Granitic now has a reference manual, intended to compliment the information in the Godoc.
You can find this manual in doc/ref/index.md or on the Granitic website.

Go modules

Grantic 2 is compatible with the requirements of Go modules including
semantic import verisioning. As such Granitic
now requires the use of Go 1.11 or later.

Your applications should declare their dependency on Granitic in their go.mod file with:

    require github.com/graniticio/granitic/v2 v2

or (if you are using YAML configuration and component files)

    require github.com/graniticio/granitic-yaml/v2 v2

Wherever your code (or component definition files) imports Granitic types, the import statements should be of
the form:

    import "github.com/graniticio/granitic/v2/packageName"

Tool support

grnc-project and grnc-yaml-project now generate working Granitic projects using modules. A go.mod will automatically
be created for you.

grnc-bind and grnc-yaml-bind have additional support for modules. These features are explained below.

Web service streamlining

Your web service logic component no longer needs to implement handler.WsRequestProcessor or
handler.WsUnmarshallTarget. Instead it just needs
to declare a method with the signature:

  ProcessPayload(context.Context, *ws.Request, *ws.Response, *YourStruct)  

Where *YourStruct is a pointer to any type that you want the HTTP request's payload to be parsed into.

If you do not need any body, path or query parameter data parsed (e.g. a simple GET or HEAD), you should continue to implement
handler.WsRequestProcessor.

If you need more control over the struct that is created as your parsing target (e.g. you need to pre-populate it), you
should continue to implement handler.WsUnmarshallTarget.

Request instrumentation

Granitic now includes integration points for adding instrumentation to a web service request and
supporting for propagating that instrumentation to downstream web service requests. A common
use-case for this is to add timing traces to service calls.

See the instrument package documentation for more details.

Request unique identifiers

Granitic now includes integration points for generating unique IDs for web service requests
and having them injected into that request's context.Context. You need to create a component that implements
IdentifiedRequestContextBuilder

Code quality

Granitic 2 sees significant improvements in file and statement unit test coverage. This release
also see the start of Granitic committing to abiding by all of the advice from the go vet and
golint tools.

This has resulted in changes to the names of a number of exported types and the names of some Granitic
facilities. See the migration guide at the bottom of these notes for information on how this might affect your
application.

RDBMS client is now an interface

To make it easier to provide mock implementations for testing, the struct rdbms.RdbmsClient is now the interface
rdbms.Client

grnc-bind

grnc-bind (and its YAML equivalent grnc-yaml-bind) have gained a number of additional features
and quality of life improvements.

Validation and error handling

Rather than exit as soon as an error is found,grnc-bind attempts to continue parsing your
component definition files for as long as possible. This means that multiple errors are now reported
in a single run.

Additional validation has been applied to increase the number of problems detected during the bind
phase instead of during go build

Logging

You can now provide a -l LOGLEVEL flag to grnc-bind for more detailed output. Valid
values for LOGLEVEL are TRACE, DEBUG, INFO, WARN, ERROR and FATAL (case
insensitive). Default is WARN

New symbols for dependency and configuration promises

In addition to the c:, conf:, r: and ref: prefixes for indicating configuration promises
and component dependencies, you can now also use the symbols $ and + as an alternative.

For example:

"ComponentName": {
  "type": "some.Type",
  "A": "$some.config.path",
  "B": "+someOtherComponent"
}

If strings in your component definition file start with these new symbols, you can escape them with $$ or ++

Nested components

You can now define components in a nested manner under the field of the parent component
that the nested component should be injected into.

For example:

  "artistHandler": {
    "type": "handler.WsHandler",
    "PathPattern": "$paths.getArtist(^/artist)",
    "HTTPMethod": "GET",
    "Logic": {
      "type": "endpoint.ArtistLogic"
    }
}

The only requirement is that you specify a type or a parent template for the nested component.
If you want to be able to refer to the nested component from other components, you need to
provide a name field, e.g.:

   "Logic": {
     "type": "endpoint.ArtistLogic",
     "name": "artistLogic"
   }
       

Default values

You can now provide a default value along with a configuration promise by including the
default in brackets after the config path. This value will be used if no configuration
is provided to your application that overrides that config path.

For example:

"ComponentName": {
  "type": "some.Type",
  "A": "$some.config.path(true)",
  "B": "$some.other.path(1.2"
}

Note that default values will only be type checked against the fields you are trying to
inject them into at application run time (not during the build phase).

Package aliases

If your component definition files import two packages that clash (because the final part of the
package name is the same), you can define an alias for one of the packages, then refer to the
alias when defining types.

For example:

{
  "packages": [
    "github.com/graniticio/granitic/v2/ws/handler"
  ],

  "packageAliases": {
    "mh": "myproject/handler"
  },
  
  "SomeComponent": {
    "type": "mh.SomeType"  
  }
}

Find Granitic installation from go.mod (experimental)

grnc-bind serialises a copy of Granitic's facility configuration files into your application.
As such it needs to know where to find a copy of Granitic on disk. Previously this required
you to set the $GRANITIC_HOME environment variable or check Granitic out to a standard location.

In Granitic 2, the go.mod file can instead be used to automatically work out where to find
Granitic (but you still need to set your $GOPATH environment variable correctly).

This feature has not been fully tested with module proxies so should be considered experimental.

Bug fixes

  • Component definition files containing a slice of float64 could not be bound if the first element was parseable as an int.
  • The ServiceErrorManager was not respecting the value of ErrorCodeUser.ValidateMissing() when complaining about missing error codes.
  • Using ConfigAccessor to try and push configuration into an unsupported type of target field was not returning an error.
  • Some configuration parsing errors were causing Granitic to exit rather than return an error

Granitic 1.x to 2.0 migration

Support for Go modules and implementing the recommendations
of golint means that you will need to make changes to your application in order to run it with
Granitic 2.

Import paths

Semantic import verisioning support
means that you need to include the major version number of Granitic in your import paths.
This will affect your Go source co and your component definition files.

Go code

  import "github.com/graniticio/granitic/logging"

would need to change to

  import "github.com/graniticio/granitic/v2/logging"

Component definition files

{
  "packages": [
    "github.com/graniticio/granitic/ws/handler"
  ]
}

would need to change to:

{
  "packages": [
    "github.com/graniticio/granitic/v2/ws/handler"
  ]
}

Capitalisation of abbreviations

Common abbreviations in type names (Id, Http, Sql, Json, Xml) are now capitalised
(ID, HTTP, SQL, JSON, XML)

For example the type xml.GraniticXmlErrorFormatter is now xml.GraniticXMLErrorFormatter

Facilities

This has also affected the names of the facilities HttpServer, JsonWs and XmlWs which
are now HTTPServer, JSONWs and XMLWs respectively. You will need to update any references to
these in your application's configuration files.

Stuttering

Types where the type name started with the name of the package have been 'de-stuttered'. For example
ws.WsRequest is now ws.Request.

This is most likely to affect your application code where it uses Granitic types in the ws and rdbms
packages.

Default file locations

Move your configuration files from resource/config to config and your component definition files from resource/components
to comp-def