/* * Copyright 2016 Sai Pullabhotla. * * 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 * * 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 com.jmethods.catatumbo.impl; import java.util.ArrayList; import java.util.List; import com.google.cloud.datastore.Cursor; import com.google.cloud.datastore.Datastore; import com.google.cloud.datastore.DatastoreException; import com.google.cloud.datastore.GqlQuery; import com.google.cloud.datastore.Key; import com.google.cloud.datastore.Query; import com.google.cloud.datastore.Query.ResultType; import com.google.cloud.datastore.QueryResults; import com.jmethods.catatumbo.DatastoreCursor; import com.jmethods.catatumbo.DatastoreMetadata; import com.jmethods.catatumbo.DatastoreProperty; import com.jmethods.catatumbo.DefaultDatastoreCursor; import com.jmethods.catatumbo.DefaultDatastoreKey; import com.jmethods.catatumbo.DefaultQueryResponse; import com.jmethods.catatumbo.EntityManagerException; import com.jmethods.catatumbo.EntityQueryRequest; import com.jmethods.catatumbo.QueryResponse; /** * Default implementation of {@link DatastoreMetadata}. * * @author Sai Pullabhotla * */ public class DefaultDatastoreMetadata implements DatastoreMetadata { /** * Name of the System entity that contains the namespaces */ private static final String ENTITY_NAMESPACES = "__namespace__"; /** * Name of the System entity that contains the Kinds */ private static final String ENTITY_KINDS = "__kind__"; /** * Name of the System entity that contains the Properties of a Kind */ private static final String ENTITY_PROPERTIES = "__property__"; /** * Reference to the entity manager that created this object */ private DefaultEntityManager entityManager; /** * Creates a new instance of <code>DefaultDatastoreMetadata</code>. * * @param entityManager * the entity manager that created this metadata object. */ public DefaultDatastoreMetadata(DefaultEntityManager entityManager) { this.entityManager = entityManager; } @Override public List<String> getNamespaces() { return getNamespaces(0).getResults(); } @Override public QueryResponse<String> getNamespaces(int limit) { return getNamespaces(new DefaultDatastoreCursor(null), limit); } @Override public QueryResponse<String> getNamespaces(DatastoreCursor fromCursor, int limit) { try { String query = "SELECT __key__ FROM " + ENTITY_NAMESPACES + " ORDER BY __key__"; if (limit > 0) { query += " LIMIT @Limit"; } query += " OFFSET @Offset"; GqlQuery.Builder<Key> gqlQueryBuilder = Query.newGqlQueryBuilder(ResultType.KEY, query); if (limit > 0) { gqlQueryBuilder.setBinding("Limit", limit); } gqlQueryBuilder.setBinding("Offset", Cursor.fromUrlSafe(fromCursor.getEncoded())); GqlQuery<Key> gqlQuery = gqlQueryBuilder.build(); Datastore datastore = entityManager.getDatastore(); QueryResults<Key> results = datastore.run(gqlQuery); DefaultQueryResponse<String> response = new DefaultQueryResponse<>(); List<String> namespaces = new ArrayList<>(Math.max(limit, 50)); response.setStartCursor(new DefaultDatastoreCursor(results.getCursorAfter().toUrlSafe())); while (results.hasNext()) { Key key = results.next(); String name = key.getName(); namespaces.add(name == null ? "" : name); } response.setResults(namespaces); response.setEndCursor(new DefaultDatastoreCursor(results.getCursorAfter().toUrlSafe())); return response; } catch (DatastoreException exp) { throw new EntityManagerException(exp); } } @Override public List<String> getKinds() { return getKinds(false); } @Override public List<String> getKinds(boolean excludeSystemKinds) { try { String query = "SELECT __key__ FROM " + ENTITY_KINDS + " ORDER BY __key__"; GqlQuery.Builder<Key> gqlQueryBuilder = Query.newGqlQueryBuilder(ResultType.KEY, query); gqlQueryBuilder.setNamespace(entityManager.getEffectiveNamespace()); GqlQuery<Key> gqlQuery = gqlQueryBuilder.build(); QueryResults<Key> results = entityManager.getDatastore().run(gqlQuery); List<String> kinds = new ArrayList<>(50); while (results.hasNext()) { Key key = results.next(); String kind = key.getName(); if (excludeSystemKinds && kind.startsWith("__")) { continue; } kinds.add(key.getName()); } return kinds; } catch (DatastoreException exp) { throw new EntityManagerException(exp); } } @Override public List<DatastoreProperty> getProperties(String kind) { try { Key nativeKey = entityManager.newNativeKeyFactory().setKind(ENTITY_KINDS).newKey(kind); DefaultDatastoreKey key = new DefaultDatastoreKey(nativeKey); String query = "SELECT * FROM " + ENTITY_PROPERTIES + " WHERE __key__ HAS ANCESTOR @1 ORDER BY __key__"; EntityQueryRequest request = entityManager.createEntityQueryRequest(query); request.addPositionalBinding(key); QueryResponse<DatastoreProperty> response = entityManager .executeEntityQueryRequest(DatastoreProperty.class, request); return response.getResults(); } catch (DatastoreException exp) { throw new EntityManagerException(exp); } } }