blob: bae3a3d4f94ef7aa53fa6501bdba8fdf39fd0ad2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package commands
import (
"errors"
"fmt"
"github.com/apache/incubator-openwhisk-cli/wski18n"
"github.com/apache/incubator-openwhisk-client-go/whisk"
"strings"
)
type QualifiedName struct {
namespace string // namespace. does not include leading '/'. may be "" (i.e. default namespace)
packageName string // package. may be "". does not include leading/trailing '/'
entity string // entity. should not be ""
EntityName string // pkg+entity
}
///////////////////////////
// QualifiedName Methods //
///////////////////////////
// GetFullQualifiedName() returns a full qualified name in proper string format
// from qualifiedName with proper syntax.
// Example: /namespace/[package/]entity
func (qualifiedName *QualifiedName) GetFullQualifiedName() string {
output := []string{}
if len(qualifiedName.GetNamespace()) > 0 {
output = append(output, "/", qualifiedName.GetNamespace(), "/")
}
if len(qualifiedName.GetPackageName()) > 0 {
output = append(output, qualifiedName.GetPackageName(), "/")
}
output = append(output, qualifiedName.GetEntity())
return strings.Join(output, "")
}
// GetPackageName() returns the package name from qualifiedName without a
// leading '/'
func (qualifiedName *QualifiedName) GetPackageName() string {
return qualifiedName.packageName
}
// GetEntityName() returns the entity name ([package/]entity) of qualifiedName
// without a leading '/'
func (qualifiedName *QualifiedName) GetEntityName() string {
return qualifiedName.EntityName
}
// GetEntity() returns the name of entity in qualifiedName without a leading '/'
func (qualifiedName *QualifiedName) GetEntity() string {
return qualifiedName.entity
}
// GetNamespace() returns the name of the namespace in qualifiedName without
// a leading '/'
func (qualifiedName *QualifiedName) GetNamespace() string {
return qualifiedName.namespace
}
// NewQualifiedName(name) initializes and constructs a (possibly fully qualified)
// QualifiedName struct.
//
// NOTE: If the given qualified name is None, then this is a default qualified
// name and it is resolved from properties.
// NOTE: If the namespace is missing from the qualified name, the namespace
// is also resolved from the property file.
//
// Examples:
// foo => qualifiedName {namespace: "_", entityName: foo}
// pkg/foo => qualifiedName {namespace: "_", entityName: pkg/foo}
// /ns/foo => qualifiedName {namespace: ns, entityName: foo}
// /ns/pkg/foo => qualifiedName {namespace: ns, entityName: pkg/foo}
func NewQualifiedName(name string) (*QualifiedName, error) {
qualifiedName := new(QualifiedName)
// If name has a preceding delimiter (/), or if it has two delimiters with a
// leading non-empty string, then it contains a namespace. Otherwise the name
// does not specify a namespace, so default the namespace to the namespace
// value set in the properties file; if that is not set, use "_"
name = addLeadSlash(name)
parts := strings.Split(name, "/")
if strings.HasPrefix(name, "/") {
qualifiedName.namespace = parts[1]
if len(parts) < 2 || len(parts) > 4 {
return qualifiedName, qualifiedNameNotSpecifiedErr()
}
for i := 1; i < len(parts); i++ {
if len(parts[i]) == 0 || parts[i] == "." {
return qualifiedName, qualifiedNameNotSpecifiedErr()
}
}
qualifiedName.EntityName = strings.Join(parts[2:], "/")
if len(parts) == 4 {
qualifiedName.packageName = parts[2]
}
qualifiedName.entity = parts[len(parts)-1]
} else {
if len(name) == 0 || name == "." {
return qualifiedName, qualifiedNameNotSpecifiedErr()
}
qualifiedName.entity = parts[len(parts)-1]
if len(parts) == 2 {
qualifiedName.packageName = parts[0]
}
qualifiedName.EntityName = name
qualifiedName.namespace = getNamespaceFromProp()
}
whisk.Debug(whisk.DbgInfo, "Qualified pkg+entity (EntityName): %s\n", qualifiedName.GetEntityName())
whisk.Debug(whisk.DbgInfo, "Qualified namespace: %s\n", qualifiedName.GetNamespace())
whisk.Debug(whisk.DbgInfo, "Qualified package: %s\n", qualifiedName.GetPackageName())
whisk.Debug(whisk.DbgInfo, "Qualified entity: %s\n", qualifiedName.GetEntity())
return qualifiedName, nil
}
/////////////////////
// Error Functions //
/////////////////////
// qualifiedNameNotSpecifiedErr() returns generic whisk error for
// invalid qualified names detected while building a new
// QualifiedName struct.
func qualifiedNameNotSpecifiedErr() error {
whisk.Debug(whisk.DbgError, "A valid qualified name was not detected\n")
errStr := wski18n.T("A valid qualified name must be specified.")
return whisk.MakeWskError(errors.New(errStr), whisk.NOT_ALLOWED, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
}
// NewQualifiedNameError(entityName, err) returns specific whisk error
// for invalid qualified names.
func NewQualifiedNameError(entityName string, err error) error {
whisk.Debug(whisk.DbgError, "NewQualifiedName(%s) failed: %s\n", entityName, err)
errMsg := wski18n.T(
"'{{.name}}' is not a valid qualified name: {{.err}}",
map[string]interface{}{
"name": entityName,
"err": err,
})
return whisk.MakeWskError(errors.New(errMsg), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
}
///////////////////////////
// Helper/Misc Functions //
///////////////////////////
// addLeadSlash(name) returns a (possibly fully qualified) resource name,
// inserting a leading '/' if it is of 3 parts (namespace/package/action)
// and lacking the leading '/'.
func addLeadSlash(name string) string {
parts := strings.Split(name, "/")
if len(parts) == 3 && parts[0] != "" {
name = "/" + name
}
return name
}
// getNamespaceFromProp() returns a namespace from Properties if one exists,
// else defaults to returning "_"
func getNamespaceFromProp() string {
namespace := "_"
if Properties.Namespace != "" {
namespace = Properties.Namespace
}
return namespace
}
// getQualifiedName(name, namespace) returns a fully qualified name given a
// (possibly fully qualified) resource name and optional namespace.
//
// Examples:
// (foo, None) => /_/foo
// (pkg/foo, None) => /_/pkg/foo
// (foo, ns) => /ns/foo
// (/ns/pkg/foo, None) => /ns/pkg/foo
// (/ns/pkg/foo, otherns) => /ns/pkg/foo
func getQualifiedName(name string, namespace string) string {
name = addLeadSlash(name)
if strings.HasPrefix(name, "/") {
return name
} else if strings.HasPrefix(namespace, "/") {
return fmt.Sprintf("%s/%s", namespace, name)
} else {
if len(namespace) == 0 {
namespace = Properties.Namespace
}
return fmt.Sprintf("/%s/%s", namespace, name)
}
}