Skip to content

Commit

Permalink
Add goDoc documentation (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
krzysztofreczek authored Dec 2, 2020
1 parent 090c172 commit eceec4e
Show file tree
Hide file tree
Showing 16 changed files with 324 additions and 96 deletions.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Scraper may be instantiated in one of two ways:
In order to instantiate the scraper you need to provide scraper configuration which contains a slice of prefixes of packages that you want to reflect. Types that do not match any of the given prefixes will not be traversed.
```go
config := scraper.NewConfiguration(
"github.com/krzysztofreczek/pkg",
"github.com/org/pkg",
)
s := scraper.NewScraper(config)
```
Expand All @@ -47,7 +47,7 @@ Each rule consists of:

```go
r, err := scraper.NewRule().
WithPkgRegexps("github.com/krzysztofreczek/pkg/foo/.*").
WithPkgRegexps("github.com/org/pkg/foo/.*").
WithNameRegexp("^(.*)Client$").
WithApplyFunc(
func(name string, _ ...string) model.Info {
Expand All @@ -62,7 +62,7 @@ The apply function has two arguments: name and groups matched from the name regu
See the example:
```go
r, err := scraper.NewRule().
WithPkgRegexps("github.com/krzysztofreczek/pkg/foo/.*").
WithPkgRegexps("github.com/org/pkg/foo/.*").
WithNameRegexp(`^(\w*)\.(\w*)Client$`).
WithApplyFunc(
func(_ string, groups ...string) model.Info {
Expand All @@ -79,12 +79,12 @@ Alternatively, you can instantiate the scraper form YAML configuration file:
// go-structurizr.yml
configuration:
pkgs:
- "github.com/krzysztofreczek/pkg"
- "github.com/org/pkg"

rules:
- name_regexp: "^(.*)Client$"
pkg_regexps:
- "github.com/krzysztofreczek/pkg/foo/.*"
- "github.com/org/pkg/foo/.*"
component:
description: "foo client"
technology: "gRPC"
Expand All @@ -97,7 +97,7 @@ Regex groups may also be used within yaml rule definition. Here you can find an
rules:
- name_regexp: "(\\w*)\\.(\\w*)Client$"
pkg_regexps:
- "github.com/krzysztofreczek/pkg/foo/.*"
- "github.com/org/pkg/foo/.*"
component:
name: "Client of external {1} service"
description: "foo client"
Expand All @@ -112,12 +112,12 @@ s, err := scraper.NewScraperFromConfigFile("./go-structurizr.yml")

Eventually, having the scraper instantiated and configured you can use it to scrape any structure you want. Scraper returns a struct `model.Structure`.
```go
structure := s.Scrap(app)
structure := s.Scrape(app)
```

### View

Similarly to the scraper, view may be instantiated in one of two ways:
Similarly, to the scraper, view may be instantiated in one of two ways:
* from the code
* from the YAML file

Expand Down Expand Up @@ -173,7 +173,7 @@ defer func() {
_ = outFile.Close()
}()

err = v.RenderTo(structure, outFile)
err = v.RenderStructureTo(structure, outFile)
```

## Good practices
Expand Down
18 changes: 18 additions & 0 deletions pkg/model/info.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package model

// HasInfo wraps simple getter method returning component information.
//
// HasInfo interface informs that the type is able to provide component
// information on its own.
// All the types that implement the interface are automatically detected
// by default implementation of the scraper.
type HasInfo interface {
Info() Info
}
Expand All @@ -8,6 +14,14 @@ const (
infoKindComponent = "component"
)

// Info struct contains all component information details.
//
// Name is a component name.
// Kind is a type that reflects component level in terms of C4 diagrams.
// Description explains the responsibility of the component.
// Technology describes technology that the component is based on.
// Tags is a set of generic string tags that may be used as reference
// to a group of components.
type Info struct {
Name string
Kind string
Expand All @@ -16,6 +30,8 @@ type Info struct {
Tags []string
}

// ComponentInfo instantiates a new component of predefined kind "component".
// Variadic arguments are assigned to the rest of Info properties one-by-one.
func ComponentInfo(s ...string) Info {
return info(infoKindComponent, s...)
}
Expand Down Expand Up @@ -47,6 +63,8 @@ func info(kind string, s ...string) Info {
return info
}

// IsZero informs if the component is empty.
// If component has no kind specified it is considered as empty.
func (i Info) IsZero() bool {
return i.Kind == ""
}
17 changes: 17 additions & 0 deletions pkg/model/structure.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
package model

// Component is an open structure that represents details of scraped component.
//
// ID is a unique identifier of the component.
// Kind is a type that reflects component level in terms of C4 diagrams.
// Name is a component name.
// Description explains the responsibility of the component.
// Technology describes technology that the component is based on.
// Tags is a set of generic string tags that may be used as reference
// to a group of components.
type Component struct {
ID string
Kind string
Expand All @@ -9,18 +18,26 @@ type Component struct {
Tags []string
}

// Structure is an open stricture that represents whole scraped structure.
//
// Components contains all the scraped components by its IDs.
// Relations contains all the connections between components by its IDs.
type Structure struct {
Components map[string]Component
Relations map[string]map[string]struct{}
}

// NewStructure instantiates an empty structure.
func NewStructure() Structure {
return Structure{
Components: make(map[string]Component),
Relations: make(map[string]map[string]struct{}),
}
}

// AddComponent adds component and corresponding relation to its parent.
//
// In case a parent of given ID does not exist relation will not be created.
func (s Structure) AddComponent(c Component, parentID string) {
s.Components[c.ID] = c
if parentID != "" {
Expand Down
13 changes: 11 additions & 2 deletions pkg/scraper/config.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
package scraper

// Configuration is an open structure that contains scraper configuration.
//
// Packages contain prefixes of packages for scraper to scrape.
// Each object of package that does not match any of predefined
// package prefixes is omitted and its internal structure is not scraped.
// When no package prefix is provided, the scraper will stop
// scraping given structure on a root level.
type Configuration struct {
packages []string
Packages []string
}

// NewConfiguration instantiates Configuration with a set of package
// prefixes provided with variadic argument.
func NewConfiguration(packages ...string) Configuration {
return Configuration{
packages: packages,
Packages: packages,
}
}
83 changes: 74 additions & 9 deletions pkg/scraper/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,24 @@ var (
matchAllRegexp = regexp.MustCompile("^.*$")
)

// RuleApplyFunc defines a signature of method returning
// a component information of type model.Info.
//
// Arguments:
// - name is a scraped name of the type in format `package.TypeName`
// - groups is a slice of sub-groups resolved from the rule name
// regular expression
type RuleApplyFunc func(
name string,
groups ...string,
) model.Info

// Rule defines an interface of any rule that maybe registered within scraper.
//
// Applies informs if rule should be applied to the given component
// considering its full package name and type name in format `package.TypeName`.
// Apply returns a component information of type model.Info based
// on the type name in format `package.TypeName`.
type Rule interface {
Applies(
pkg string,
Expand Down Expand Up @@ -70,13 +83,27 @@ func newRule(
}, nil
}

// Applies informs if rule should be applied to the given component
// considering its full package name and type name in format `package.TypeName`.
//
// Component will be recognised as applicable when all of the following
// conditions are met:
// - package matches at least one of the rule package regular expressions
// - name matches the rule name regular expression
func (r rule) Applies(
pkg string,
name string,
) bool {
return r.nameApplies(name) && r.pkgApplies(pkg)
}

// Apply returns a component information of type model.Info based
// on the type name in format `package.TypeName`.
// Apply will return result of registered RuleApplyFunc application function
// passing the following arguments:
// - name is a scraped name of the type in format `package.TypeName`
// - groups is a slice of sub-groups resolved from the rule name
// regular expression
func (r rule) Apply(
name string,
) model.Info {
Expand Down Expand Up @@ -106,40 +133,73 @@ func (r rule) nameApplies(name string) bool {
return r.nameRegex.MatchString(name)
}

type RuleBuilder struct {
// Builder simplifies instantiation of default Rule implementation.
//
// WithPkgRegexps sets a list of package regular expressions.
// WithNameRegexp sets name regular expression.
// WithApplyFunc sets rule application function RuleApplyFunc.
//
// Build returns Rule implementation constructed from the provided expressions
// and application function.
// Build will return an error if at least one of the provided expressions
// is invalid and cannot be compiled.
// Build will return an error if application function RuleApplyFunc is missing.
type Builder interface {
WithPkgRegexps(rgx ...string) Builder
WithNameRegexp(rgx string) Builder
WithApplyFunc(f RuleApplyFunc) Builder

Build() (Rule, error)
}

type builder struct {
pkgRegexes []string
nameRegex string
applyFunc RuleApplyFunc
}

func NewRule() *RuleBuilder {
return &RuleBuilder{}
// NewRule returns an empty Builder.
func NewRule() Builder {
return &builder{}
}

func (b *RuleBuilder) WithPkgRegexps(rgx ...string) *RuleBuilder {
// WithPkgRegexps sets a list of package regular expressions.
func (b *builder) WithPkgRegexps(rgx ...string) Builder {
for _, r := range rgx {
b.pkgRegexes = append(b.pkgRegexes, r)
}
return b
}

func (b *RuleBuilder) WithNameRegexp(rgx string) *RuleBuilder {
// WithNameRegexp sets name regular expression.
func (b *builder) WithNameRegexp(rgx string) Builder {
b.nameRegex = rgx
return b
}

func (b *RuleBuilder) WithApplyFunc(f RuleApplyFunc) *RuleBuilder {
// WithApplyFunc sets rule application function RuleApplyFunc.
func (b *builder) WithApplyFunc(f RuleApplyFunc) Builder {
b.applyFunc = f
return b
}

func (b RuleBuilder) Build() (Rule, error) {
// Build returns Rule implementation constructed from the provided expressions
// and application function.
//
// In case no regular expression is provided either for name or package,
// those will be filled with regular expression matching all string "^.*$".
//
// Build will return an error if at least one of the provided expressions
// is invalid and cannot be compiled.
// Build will return an error if application function RuleApplyFunc is missing.
func (b builder) Build() (Rule, error) {
pkgRegexes := make([]*regexp.Regexp, 0)
for _, rgx := range b.pkgRegexes {
r, err := regexp.Compile(rgx)
if err != nil {
return nil, errors.Wrapf(err,
"could not compile package expression `%s` as correct regular expression", rgx)
"could not compile package expression `%s` "+
"as correct regular expression", rgx)
}
pkgRegexes = append(pkgRegexes, r)
}
Expand All @@ -153,11 +213,16 @@ func (b RuleBuilder) Build() (Rule, error) {
r, err := regexp.Compile(b.nameRegex)
if err != nil {
return nil, errors.Wrapf(err,
"could not compile name expression `%s` as correct regular expression", b.nameRegex)
"could not compile name expression `%s` "+
"as correct regular expression", b.nameRegex)
}
nameRegex = r
}

if b.applyFunc == nil {
return nil, errors.New("apply function must be provided")
}

return newRule(
pkgRegexes,
nameRegex,
Expand Down
Loading

0 comments on commit eceec4e

Please sign in to comment.