blob: 3b9d3cac25a5b584ae3b32204666e3873eb61611 [file] [log] [blame]
// Package tc provides structures, constants, and functions that are used
// throughout the components of Apache Traffic Control.
//
// In general, the symbols defined here should be used by more than one
// component of Traffic Control - otherwise it can just appear in the only
// component that needs it. Most often this means that the symbols herein
// defined are referring to objects and/or concepts exposed through the Traffic
// Ops API, and usually serve to define payloads of HTTP requests and
// responses.
//
// Enumerated Types
//
// Enumerated types - which should typically go in enum.go - should be treated
// as enumerables, and MUST NOT be cast as anything else (integer, strings,
// etc.). Enums MUST NOT be compared to strings or integers via casting.
// Enumerable data SHOULD be stored as the enumeration, not as a string or
// number. The *only* reason they are internally represented as strings, is to
// make them implicitly serialize to human-readable JSON. They should not be
// treated as strings. Casting or storing strings or numbers defeats the
// purpose of enum safety and conveniences.
//
// An example of an enumerated string type 'Foo' and an enumerated integral (or
// arbitrary) type 'Bar' are shown below:
//
// type Foo string
// const (
// FooA Foo = "A"
// FooB Foo = "B"
// )
//
// type Bar int
// const (
// BarTest Bar = iota
// BarQuest
// )
//
// Note the way each member of the "enum" is prefixed with the type name, to
// help avoid collisions. Also note how, for string enumerations, the type must
// be repeated with each assignment, whereas for an arbitrary enumeration, you
// can make use of the special 'iota' language constant to start with some
// arbitrary value and then just make names on subsequent lines to implicitly
// increment from there, while maintaining proper type.
//
// Enumerables that need to be serialized and deserialized in JSON should use
// strings to make them easiest to understand and work with (serialization will
// work out-of-the-box that way). One way to implement type safety for
// enumerated types that require serialization support is to implement the
// encoding/json.Unmarshaler interface and return an error for unsupported
// values. However, note that this causes the unmarshaling to halt immediately,
// which is fast but if there are other errors in the JSON document that would
// be encountered later, they will not be reported. Therefore, types used in
// structures that the Traffic Ops API unmarshals should generally instead use
// an "invalid" value that indicates the problem but allows parsing to
// continue. The way this is normally done is with a 'FromString' method like
// so:
//
// type Foo string
// const (
// FooA Foo = "A"
// FooB Foo = "B"
// FooInvalid = ""
// )
//
// func FooFromString(foo string) Foo {
// switch foo {
// case FooA:
// fallthrough
// case FooB:
// return Foo(foo)
// }
// return FooInvalid
// }
//
// However, this requires postprocessing after deserialization, so one might
// instead implement encoding/json.Unmarshaler:
//
// import "errors"
//
// type Foo string
// const (
// FooA Foo = "A"
// FooB Foo = "B"
// FooInvalid = ""
// )
//
// func (f *Foo) UnmarshalJSON(data []byte) error {
// if string(data) == "null" {
// return errors.New("'null' is not a valid 'Foo'")
// }
// s := strings.Trim(string(data), "\"")
// switch s {
// case FooA:
// fallthrough
// case FooB:
// *f = Foo(s)
// return
// }
// // This is an invalid *value* not a parse error, so we don't return
// // an error and instead just make it clear that the value was
// // invalid.
// *f = FooInvalid
// return nil
// }
//
// Though in this case the original, raw, string value of the 'Foo' is lost. Be
// aware of the limitations of these two designs during implementation.
//
// When storing enumumerable data in memory, it SHOULD be converted to and
// stored as an enum via the corresponding `FromString` function, checked
// whether the conversion failed and Invalid values handled, and valid data
// stored as the enum. This guarantees stored data is valid, and catches
// invalid input as soon as possible.
//
// Conversion functions, whether they be a 'FromString' function or an
// UnmarshalJSON method, should not be case-insensitive.
//
// When adding new enum types, enums should be internally stored as strings, so
// they implicitly serialize as human-readable JSON, unless the performance or
// memory of integers is necessary (it almost certainly isn't). Enums should
// always have the "invalid" value as the empty string (or 0), so
// default-initialized enums are invalid.
//
// Enums should always have a String() method, that way they implement
// fmt.Stringer and can be easily represented in a human-readable way.
//
// When to Use Custom Types
//
// A type should be defined whenever there is a need to represent a data
// *structure* (in a 'struct'), or whenever the type has some enforced
// semantics. For example, enumerated types have the attached semantic that
// there is a list of valid values, and generally there are methods and/or
// functions associated with those types that act on that limitation. On the
// otherhand, if there is a type 'Foo' that has some property 'Bar' that
// contains arbitrary textual data with no other semantics or limitations,
// then 'string' is the appropriate type; there is no need to make a type
// 'FooBar' to express the relationship between a Foo and its Bar - that's the
// job of the Go language itself.
//
// Similarly, try to avoid duplication. For example, Parameters can be assigned
// to Profiles or (in legacy code that may have been removed by the time this
// is being read) Cache Groups. It was not necessary to create a type
// CacheGroupParameter that contains all of the same data as a regular
// Parameter simply because of this relationship.
//
// Versioning Structures
//
// Structures used by the Traffic Ops API may change over time as the API
// itself changes. Typically, each new major API version will cause some
// breaking change to some structure in this package. This means that a new
// structure will need to be created to avoid breaking old clients that are
// deprecated but still supported. The naming convention for a new version of a
// structure that represents a Foo object in the new version - say, 2.0 - is to
// call it FooV20. Then, a more general type alias should be made for the API
// major version for use in client methods (so that they can be silently
// upgraded for non-breaking changes) - in this case: FooV2. A deprecation
// notice should then be added to the legacy type (which is hopefully but not
// necessarily named FooV1/FooV11 etc.) indicating the new structure. For
// example:
//
// // FooV11 represents a Foo in TO APIv1.1.
// //
// // Deprecated: TO APIv1.1 is deprecated; upgrade to FooV2.
// type FooV11 struct {
// Bar string `json:"bar"`
// }
// // FooV1 represents a Foo in the latest minor version of TO APIv1.
// //
// // Deprecated: TO APIv1 is deprecated; upgrade to FooV2.
// type FooV1 = FooV11
//
// // FooV20 represents a Foo in TO APIv2.0.
// type FooV20 struct {
// Bar string `json:"bar"`
// }
// // FooV2 represents a Foo in the latest minor version of TO APIv2.
// type FooV2 = FooV20
//
// Note that there is no type alias simply named "Foo" - that is exactly how it
// should be.
//
// Legacy types may not include version information - newer versions should add
// it as described above. Legacy types may also include the word "Nullable"
// somewhere - strip this out! That word included in a type name is only to
// differentiate between older structures that were not "nullable" and the
// newer ones. Newly added types should only have one structure that is as
// "nullable" or "non-nullable" as it needs to be, so there is no need to
// specify.
package tc
/*
* 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.
*/