func NewProject(fs afero.Fs, fullQualifiedProjectFolderName string, projectFolderName string, apis map[string]api.Api, projectRootFolder string) (Project, error) {
	var configs = make([]config.Config, 0)
	projectRootFolder = strings.Trim(projectRootFolder, string(os.PathSeparator))
	builder := projectBuilder{
		projectRootFolder: projectRootFolder,
		projectId:         fullQualifiedProjectFolderName,
		configs:           configs,
		apis:              apis,
		configFactory:     config.NewConfigFactory(),
		fs:                fs,
	}
	err := builder.readFolder(fullQualifiedProjectFolderName, true)
	if err != nil {
		return nil, err
	}
	err = builder.sortConfigsAccordingToDependencies()
	if err != nil {
		return nil, err
	}
	warnIfProjectNameClashesWithApiName(projectFolderName, apis, projectRootFolder)
	return &projectImpl{
		id:      fullQualifiedProjectFolderName,
		configs: builder.configs,
	}, nil
}
NewProject loads a new project from folder. Returns either project or a reading/sorting error respectively.
package project
import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"github.com/dynatrace-oss/dynatrace-monitoring-as-code/pkg/api"
	"github.com/dynatrace-oss/dynatrace-monitoring-as-code/pkg/config"
	"github.com/dynatrace-oss/dynatrace-monitoring-as-code/pkg/util"
	"github.com/pkg/errors"
	"github.com/spf13/afero"
)
type Project interface {
	HasDependencyOn(project Project) bool
	GetConfigs() []config.Config
	GetConfig(id string) (config.Config, error)
	GetId() string
}
type projectImpl struct {
	id      string
	configs []config.Config
}
type projectBuilder struct {
	projectRootFolder string
	projectId         string
	configs           []config.Config
	apis              map[string]api.Api
	configFactory     config.ConfigFactory
	fs                afero.Fs
}