/*
 * 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.
 */
package org.apache.tinkerpop.gremlin.server.auth.groovy.plugin;

import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.mindrot.jbcrypt.BCrypt;

import java.util.concurrent.atomic.AtomicInteger;

import static org.apache.tinkerpop.gremlin.server.auth.groovy.plugin.CredentialGraphTokens.PROPERTY_PASSWORD;
import static org.apache.tinkerpop.gremlin.server.auth.groovy.plugin.CredentialGraphTokens.PROPERTY_USERNAME;
import static org.apache.tinkerpop.gremlin.server.auth.groovy.plugin.CredentialGraphTokens.VERTEX_LABEL_USER;

/**
 * A DSL for managing a "credentials graph" used by Gremlin Server for simple authentication functions.
 *
 * @author Stephen Mallette (http://stephen.genoprime.com)
 */
public class CredentialGraph {

    private final Graph graph;
    private final GraphTraversalSource g;

    public CredentialGraph(final Graph graph) {
        this.graph = graph;
        g = graph.traversal();
    }

    /**
     * Finds a user by username and return {@code null} if one could not be found.
     *
     * @throws IllegalStateException if there is more than one user with a particular username.
     */
    public Vertex findUser(final String username) {
        final GraphTraversal<Vertex,Vertex> t = g.V().has(PROPERTY_USERNAME, username);
        final Vertex v = t.hasNext() ? t.next() : null;
        if (t.hasNext()) throw new IllegalStateException(String.format("Multiple users with username %s", username));
        return v;
    }

    /**
     * Creates a new user.
     *
     * @return the newly created user vertex
     */
    public Vertex createUser(final String username, final String password) {
        if (findUser(username) != null) throw new IllegalStateException("User with this name already exists");
        return graph.addVertex(T.label, VERTEX_LABEL_USER,
                               PROPERTY_USERNAME, username,
                               PROPERTY_PASSWORD, BCrypt.hashpw(password, BCrypt.gensalt()));
    }

    /**
     * Removes a user by name.
     *
     * @return the number of users removed (which should be one or zero)
     */
    public long removeUser(final String username) {
        final AtomicInteger counter = new AtomicInteger(0);
        g.V().has(PROPERTY_USERNAME, username).sideEffect(v -> counter.incrementAndGet()).drop().iterate();
        return counter.get();
    }

    public static CredentialGraph credentials(final Graph graph) {
        return new CredentialGraph(graph);
    }
}

@@ -1233,51 +1233,80 @@
 ort 
-java.util.concurrent.atomic.AtomicInteger;%0A
+static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.drop;
 %0Aimp
@@ -1740,16 +1740,114 @@
 nctions.
+  If the%0A * %7B@link Graph%7D is transactional, new transactions will be started for each method call.
 %0A *%0A * @
@@ -2009,16 +2009,64 @@
 ource g;
+%0A    private final boolean supportsTransactions;
 %0A%0A    pu
@@ -2162,24 +2162,104 @@
 raversal();%0A
+        supportsTransactions = graph.features().graph().supportsTransactions();%0A
     %7D%0A%0A    /
@@ -2499,32 +2499,85 @@
 ing username) %7B%0A
+        if (supportsTransactions) g.tx().rollback();%0A
         final Gr
@@ -3138,22 +3138,109 @@
         
-return
+if (supportsTransactions) graph.tx().rollback();%0A%0A        try %7B%0A            final Vertex v = 
  graph.a
@@ -3299,32 +3299,47 @@
                 
+       
+        
 PROPERTY_USERNAM
@@ -3351,16 +3351,31 @@
 ername,%0A
+               
         
@@ -3468,394 +3468,928 @@
     
-%7D%0A%0A    /**%0A     * Removes a user by name.%0A     *%0A     * @return the number of users removed (which should be one or zero)%0A     */%0A    public long removeUser(final String username) %7B%0A        final AtomicInteger counter = new AtomicInteger(0);%0A        g.V().has(PROPERTY_USERNAME, username).sideEffect(v -%3E counter.incrementAndGet()).drop().iterate();%0A        return counter.get(
+        if (supportsTransactions) graph.tx().commit();%0A            return v;%0A        %7D catch (Exception ex) %7B%0A            if (supportsTransactions) graph.tx().rollback();%0A            throw new RuntimeException(ex);%0A        %7D%0A    %7D%0A%0A    /**%0A     * Removes a user by name.%0A     *%0A     * @return the number of users removed (which should be one or zero)%0A     */%0A    public long removeUser(final String username) %7B%0A        if (supportsTransactions) graph.tx().rollback();%0A        try %7B%0A            final long count = g.V().has(PROPERTY_USERNAME, username).sideEffect(drop()).count().next();%0A            if (supportsTransactions) graph.tx().commit();%0A            return count;%0A        %7D catch (Exception ex) %7B%0A            if (supportsTransactions) graph.tx().rollback();%0A            throw new RuntimeException(ex
 );%0A    
+ 
-%7D%0A
+   %7D%0A    %7D%0A%0A    /**%0A     * Wrap up any %7B@link Graph%7D instance in the %7B@code CredentialGraph%7D DSL.%0A     */
 %0A   

Support transactions in CredentialsGraph DSL.