For one project we use a Gremlin DB in Azure (Cosmos DB). Various libraries are available for use. Unfortunately, no REST is supported, which is why a corresponding simple interface had to be provided here.
The most elegant way would be to set up an Azure API Management and provide a REST API. The request could then be converted internally with policies (C # code) and sent to the Cosmos DB. Unfortunately, no additional libraries can be loaded in the APIM, only a few standard libraries can be used.
Therefore, an Azure Function was created that receives a POST request with a Gremlin Query. The idea was described here by Ritu Raj, for example. However, the following adjustments were made during implementation:
- the project was implemented as a remote-container project in Visual Studio Code and the Tinkerpop Console was also installed
- some bugs in the code have been fixed and
add
calls have been prohibited
The source code with the instructions for configuration and use is stored in the Github project AzGremlinRestApi.
Using Remote-Container in VS Code
With the remote-container extension the development environment with all dependencies and libraries is relocated to a Docker container. The VS runs locally and uses all data and the mounted source code from the container. Therefore every developer has the same environment in an extremely short time.
By activating remote-container a folder .devcontainer
is created (2). I have selected (1) Azure Functions & C# (.NET Core 3.1) as the development container (therefore mcr.microsoft.com/azure-functions/dotnet:3.0-dotnet3-core-tools
is used as the base image in the Dockerfile). If the project is restarted, the extension recognizes the folder and the configuration and offers to reopen in the container:
The Dockerfile has been adapted accordingly to configure the access to Gremlin. These properties are used in the function and also correspond to the environment variables that would be set by the Function App Service:
# config for function ENV GRAPHDBHOSTNAME=<COSMOSDBACCOUNT>.gremlin.cosmos.azure.com ENV GRAPHDBKEY=<KEY> ENV GRAPHDBNAME=<COSMOSDB> ENV GRAPHDBCOLL=<GRAPHCOLLECTION>
To install the Tinkerpop Console, the Dockefile contains the following lines:
# install java RUN sudo apt update && sudo apt install -y default-jdk # install tinkerpop gremlin console in /workspaces/tinkerpop-cmd WORKDIR /workspaces RUN sudo chmod 777 -R /workspaces/ \ && mkdir tinkerpop-cmd \ && wget https://apache.mivzakim.net/tinkerpop/3.4.8/apache-tinkerpop-gremlin-console-3.4.8-bin.zip \ && unzip apache-tinkerpop-gremlin-console-3.4.8-bin.zip \ && mv apache-tinkerpop-gremlin-console-3.4.8/* tinkerpop-cmd/ \ && rmdir apache-tinkerpop-gremlin-console-3.4.8
The Tinerkerpop Console can also be used to send Gremlin queries via the shell. This is particularly good for checking the query results beforehand. The configuration is defined in the remote-security.yaml
file.
Azure Function for calling Gremlin Cosmos DB
The source code is pretty simple. The parameters are loaded (line 3-7) and the GremlinServer and GremlinClient objects are created (line 30-33). Before this, it is checked in line 21 that a query has been transmitted in the POST body and that this query does not try to create a new node or new edge.
In the original POST, the resultSet
was looped through and only the last value was returned. Here the entire resultSet
is returned directly (line 36-37) as JSON.
public static class gremgraphapi { private static string hostname = Environment.GetEnvironmentVariable("GRAPHDBHOSTNAME"); private static int port = 443; private static string authKey = Environment.GetEnvironmentVariable("GRAPHDBKEY"); private static string database = Environment.GetEnvironmentVariable("GRAPHDBNAME"); private static string collection = Environment.GetEnvironmentVariable("GRAPHDBCOLL"); [FunctionName("gremlinquery")] public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation("C# HTTP trigger function processed a request."); string query=""; if (req.Method.ToLower() == "post") { query = JObject.Parse(await new StreamReader(req.Body).ReadToEndAsync())?["query"]?.ToString(); } if (String.IsNullOrEmpty(query) || (query.Contains("add"))) return new BadRequestObjectResult("Please pass a read query in the POST body"); log.LogInformation($"Query: {query}"); log.LogInformation($"hostname: {hostname}"); string connection = "/dbs/" + database + "/colls/" + collection; log.LogInformation($"username: {connection}"); log.LogInformation($"key: {authKey.Substring(0, 4)}...{authKey.Substring(authKey.Length-4, 4)}"); string jRes=""; var gremlinServer = new GremlinServer(hostname, port, enableSsl: true, username: connection, password: authKey); using (var gremlinClient = new GremlinClient(gremlinServer, new GraphSON2Reader(), new GraphSON2Writer(), GremlinClient.GraphSON2MimeType)) { // Create async task to execute the Gremlin query. var resultSet = gremlinClient.SubmitAsync<dynamic>(query).Result; jRes = JsonConvert.SerializeObject(resultSet).ToString(); } return new OkObjectResult(jRes); } }
Schreibe einen Kommentar