github.com/tilt-dev/tilt@v0.36.0/web/src/labels.ts (about)

     1  // Helper functions for working with labels and resource groups
     2  
     3  import Features, { Flag } from "./feature"
     4  import { UIResource } from "./types"
     5  
     6  export const UNLABELED_LABEL = "unlabeled"
     7  export const TILTFILE_LABEL = "Tiltfile"
     8  
     9  export type GroupByLabelView<T> = {
    10    labels: string[]
    11    labelsToResources: { [key: string]: T[] }
    12    tiltfile: T[]
    13    unlabeled: T[]
    14  }
    15  
    16  /**
    17   * The generated type for labels is a generic object,
    18   * but in reality, it's an object with string keys and values.
    19   * (This is a little bit of typescript gymnastics.)
    20   *
    21   * `isUILabels` is a type predicate function that asserts
    22   * whether or not its input is the `UILabels` type
    23   *
    24   * `asUILabels` safely casts its input into a `UILabels` type
    25   */
    26  type UILabelsGenerated = Pick<Proto.v1ObjectMeta, "labels">
    27  
    28  interface UILabels extends UILabelsGenerated {
    29    labels: { [key: string]: string } | undefined
    30  }
    31  
    32  function isUILabels(
    33    labelsWrapper: UILabelsGenerated
    34  ): labelsWrapper is UILabels {
    35    return (
    36      labelsWrapper.labels === undefined ||
    37      typeof labelsWrapper.labels === "object"
    38    )
    39  }
    40  
    41  function asUILabels(labels: UILabelsGenerated): UILabels {
    42    if (isUILabels(labels)) {
    43      return labels
    44    }
    45  
    46    return { labels: undefined } as UILabels
    47  }
    48  
    49  // Following k8s practices, we treat labels with prefixes as
    50  // added by external tooling and not relevant to the user
    51  export function getResourceLabels(resource: UIResource): string[] {
    52    // Safely cast and extract labels from a resource
    53    const { labels: labelsMap } = asUILabels({
    54      labels: resource.metadata?.labels,
    55    })
    56    if (!labelsMap) {
    57      return []
    58    }
    59  
    60    // Return the labels in the form of a list, not a map
    61    return Object.keys(labelsMap)
    62      .filter((label) => {
    63        const labelHasPrefix = label.includes("/")
    64        return !labelHasPrefix
    65      })
    66      .map((label) => labelsMap[label])
    67  }
    68  
    69  // Order labels alphabetically A - Z
    70  export function orderLabels(labels: string[]) {
    71    return [...labels].sort((a, b) => a.localeCompare(b))
    72  }
    73  
    74  // This helper function takes a template type for the resources
    75  // and a label accessor function
    76  export function resourcesHaveLabels<T>(
    77    features: Features,
    78    resources: T[] | undefined,
    79    getLabels: (resource: T) => string[]
    80  ): boolean {
    81    // Labels on resources are ignored if feature is not enabled
    82    if (!features.isEnabled(Flag.Labels)) {
    83      return false
    84    }
    85  
    86    if (resources === undefined) {
    87      return false
    88    }
    89  
    90    return resources.some((r) => getLabels(r).length > 0)
    91  }