All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.appengine.api.datastore.dev.NamespacePseudoKind Maven / Gradle / Ivy

Go to download

SDK for dev_appserver (local development) with some of the dependencies shaded (repackaged)

There is a newer version: 2.0.31
Show newest version
/*
 * Copyright 2021 Google LLC
 *
 * Licensed 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
 *
 *     https://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 com.google.appengine.api.datastore.dev;

import static com.google.appengine.api.datastore.Entities.NAMESPACE_METADATA_KIND;
import static com.google.appengine.api.datastore.dev.Utils.checkRequest;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.dev.LocalDatastoreService.Extent;
import com.google.appengine.api.datastore.dev.LocalDatastoreService.Profile;
import com.google.apphosting.api.DatastorePb.Query;
import com.google.appengine.repackaged.com.google.common.collect.Lists;
import com.google.storage.onestore.v3.OnestoreEntity.EntityProto;
import com.google.storage.onestore.v3.OnestoreEntity.Path;
import com.google.storage.onestore.v3.OnestoreEntity.Reference;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Pseudo-kind named __namespace__ that returns namespaces used by an application.
 *
 */
class NamespacePseudoKind extends KeyFilteredPseudoKind {
  static final long EMPTY_NAMESPACE_ID = 1;

  NamespacePseudoKind(LocalDatastoreService localDatastore) {
    super(localDatastore);
  }

  @Override
  public String getKindName() {
    return NAMESPACE_METADATA_KIND;
  }

  /**
   * Returns the namespace specified by {@code key}, or throws an exception if the key's format is
   * invalid.
   */
  private String namespaceKeyToString(Key key) {
    if (key == null) {
      return null;
    }
    checkRequest(
        key.getParent() == null,
        String.format(
            "Key filter on %s is invalid (key has parent) - received %s",
            NAMESPACE_METADATA_KIND, key));
    checkRequest(
        key.getKind().equals(NAMESPACE_METADATA_KIND),
        String.format(
            "Key filter on %s is invalid (must be a key for %s) - received %s",
            NAMESPACE_METADATA_KIND, NAMESPACE_METADATA_KIND, key));
    if (key.getName() != null) {
      return key.getName();
    } else {
      checkRequest(
          key.getId() == EMPTY_NAMESPACE_ID,
          String.format(
              "Key filter on %s is invalid (key must be a name or the number %d)"
                  + " - received %s",
              NAMESPACE_METADATA_KIND, EMPTY_NAMESPACE_ID, key));
      return "";
    }
  }

  @Override
  List runQuery(
      Query query, Key startKey, boolean startInclusive, Key endKey, boolean endInclusive) {
    /* Ancestor has no meaning in namespace queries. This also has the desirable side effect that
     * schema queries cannot live in transactions. */
    checkRequest(
        !query.hasAncestor(), "ancestor queries on " + NAMESPACE_METADATA_KIND + " not allowed");

    String app = query.getApp();
    String startNamespace = namespaceKeyToString(startKey);
    String endNamespace = namespaceKeyToString(endKey);
    Profile profile = getDatastore().getOrCreateProfile(app);
    Map extents = profile.getExtents();
    Set namespaceSet = new HashSet();

    synchronized (extents) {
      // Just collect all namespaces that are in the selected range
      for (Map.Entry entry : extents.entrySet()) {
        for (EntityProto entity : entry.getValue().getAllEntityProtos()) {
          String namespace = entity.getKey().getNameSpace();

          // Apply filters.
          if (startNamespace != null) {
            int namespacesCompared = namespace.compareTo(startNamespace);
            if ((startInclusive && namespacesCompared < 0)
                || (!startInclusive && namespacesCompared <= 0)) {
              continue;
            }
          }
          if (endNamespace != null) {
            int namespacesCompared = namespace.compareTo(endNamespace);
            if ((endInclusive && namespacesCompared > 0)
                || (!endInclusive && namespacesCompared >= 0)) {
              continue;
            }
          }
          namespaceSet.add(namespace);
        }
      }
    }
    return makeNamespaceEntities(namespaceSet, app, query.getNameSpace());
  }

  private List makeNamespaceEntities(
      Set namespaceSet, String app, String executionNamespace) {
    List namespaces = Lists.newArrayListWithCapacity(namespaceSet.size());
    for (String namespace : namespaceSet) {
      // Create namespace entity and set its key based on the namespace
      EntityProto namespaceEntity = new EntityProto();
      namespaces.add(namespaceEntity);

      Path path = new Path();
      // Empty namespaces use an EMPTY_NAMESPACE_ID key
      if (namespace.isEmpty()) {
        path.addElement().setType(NAMESPACE_METADATA_KIND).setId(EMPTY_NAMESPACE_ID);
      } else {
        path.addElement().setType(NAMESPACE_METADATA_KIND).setName(namespace);
      }
      Reference key = new Reference().setApp(app).setPath(path);
      if (executionNamespace.length() > 0) {
        key.setNameSpace(executionNamespace);
      }
      namespaceEntity.setKey(key);
      // EntityProto.entity_group is a required PB field.
      namespaceEntity.getMutableEntityGroup().addElement(path.getElement(0));
    }
    return namespaces;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy