go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/service/datastore/meta/namespaces.go (about)

     1  // Copyright 2015 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package meta
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"strings"
    21  
    22  	ds "go.chromium.org/luci/gae/service/datastore"
    23  )
    24  
    25  // NamespacesCallback is the callback type used with Namespaces. The callback
    26  // will be invoked with each identified namespace.
    27  //
    28  // If the callback returns an error, iteration will stop. If the error is
    29  // datastore.Stop, Namespaces will stop iterating and return nil. Otherwise,
    30  // the error will be forwarded.
    31  type NamespacesCallback func(string) error
    32  
    33  // Namespaces returns a list of all of the namespaces in the datastore.
    34  //
    35  // This is done by issuing a datastore query for kind "__namespace__". The
    36  // resulting keys will have IDs for the namespaces, namely:
    37  //   - The empty namespace will have integer ID 1. This will be forwarded to the
    38  //     callback as "".
    39  //   - Other namespaces will have non-zero string IDs.
    40  func Namespaces(c context.Context, cb NamespacesCallback) error {
    41  	q := ds.NewQuery("__namespace__").KeysOnly(true)
    42  
    43  	// Query our datastore for the full set of namespaces.
    44  	return ds.Run(c, q, func(k *ds.Key) error {
    45  		switch {
    46  		case k.IntID() == 1:
    47  			return cb("")
    48  		case k.IntID() != 0:
    49  			return fmt.Errorf("unexpected namepsace integer key (%d)", k.IntID())
    50  		default:
    51  			return cb(k.StringID())
    52  		}
    53  	})
    54  }
    55  
    56  // NamespacesWithPrefix runs Namespaces, returning only namespaces beginning
    57  // with the supplied prefix string.
    58  func NamespacesWithPrefix(c context.Context, p string, cb NamespacesCallback) error {
    59  	// TODO: https://go.chromium.org/luci/gae/issues/49 : When inequality filters are
    60  	// supported, implement this using a "Gte" filter.
    61  	any := false
    62  	return Namespaces(c, func(ns string) error {
    63  		if !strings.HasPrefix(ns, p) {
    64  			if any {
    65  				return ds.Stop
    66  			}
    67  			return nil
    68  		}
    69  
    70  		any = true
    71  		return cb(ns)
    72  	})
    73  }
    74  
    75  // NamespacesCollector exposes a NamespacesCallback function that aggregates
    76  // resulting namespaces into the collector slice.
    77  type NamespacesCollector []string
    78  
    79  // Callback is a NamespacesCallback which adds each namespace to the collector.
    80  func (c *NamespacesCollector) Callback(v string) error {
    81  	*c = append(*c, v)
    82  	return nil
    83  }