age icon indicating copy to clipboard operation
age copied to clipboard

Write Swift Driver for AGE

Open eyab opened this issue 1 year ago • 14 comments

eyab avatar Dec 28 '22 18:12 eyab

  • Ensure the Swift driver is quality controlled and secure
  • Check for consistency and robustness

eyab avatar Dec 28 '22 18:12 eyab

I can help with a Swift Driver. Been developing iOS apps in ObjC/C/C++ and Swift since way back. Some of my iOS libs are used in 1000s of iOS apps, like this one: https://www.appsight.io/sdk/typhoon

Besides the driver, can help to set up an executable specification (BDD style tests) to ensure the two points above.

jasperblues avatar Dec 29 '22 20:12 jasperblues

Hey, I am Fahad Zaheer, excited to work on this project

FahadZaheerfzr avatar Jan 11 '23 14:01 FahadZaheerfzr

Hi this is Steve Shin from Vancouver Team.

Steves452 avatar Jan 12 '23 00:01 Steves452

Is there a short way to prepare the statements and execute them, if you are trying to run multiple queries and you are not concerned with the results that are generated by those statements.

Forexample I want to execute these two statements, is there any short way to write these two statements and execute them.

do {
        let load_age_statement = try connection.prepareStatement(text: "Load 'age';")
        let set_path_statement = try connection.prepareStatement(text: "SET search_path = ag_catalog, '$user', public;")
    
        var cursor = try load_age_statement.execute()
        load_age_statement.close()
        cursor  = try set_path_statement.execute()
        
}catch{
        print(error)
}

FahadZaheerfzr avatar Jan 21 '23 11:01 FahadZaheerfzr

Greetings! I just finished some initial work on a Swift driver for Apache AGE. The core project includes the antlr4 parsing with a visitor to create the Swift data structures. I translated some test cases from other projects as well. Project can be found here: https://github.com/joshjacob/SwiftAge

I also have a sample app using the PostgresNIO project. This is the underlying project to the Vapor ORM package but doesn't contain any ORM code. However, it is written with SwiftNIO which would be more performant in a server environment than other Swift PostgreSQL drivers. The example code has some code to take the data from PostgresNIO and pass it through the parser. This would ideally be part of the core project and implemented as an extension to PostgresNIO similar to other drivers. It's a start and does connect, query and parse AGE. Here is the examples project: https://github.com/joshjacob/SwiftAgeExamples

joshjacob avatar Mar 08 '23 02:03 joshjacob

@joshjacob that's great!! I'm eager to take a look. awesome

dehowef avatar Mar 08 '23 16:03 dehowef

I just pushed an update to https://github.com/joshjacob/SwiftAge that includes extension methods to run graph queries and return parsed Agtype results. The README and test cases show examples of running and handling results from these extension methods.

joshjacob avatar Mar 14 '23 00:03 joshjacob

@joshjacob awesome, great work. do you have further stuff you hope to add down the line?

dehowef avatar Mar 14 '23 09:03 dehowef

@dehowef The parsing of returned data is in a good spot. There are a few outstanding issues with a Path object and some better error/exception handling left to do.

Currently I'm looking at how some of the other drivers use parameters and binding. I'm still in the initial stages of researching how that works.

Once those functional aspects are complete I would plan for some more examples/demos. Any input/feedback would be appreciated.

joshjacob avatar Mar 15 '23 01:03 joshjacob

Good to hear. One thing I'd pay particular attention to is how the query string from user input is handled/constructed by the driver-- it's best to do so in a way that prevents malicious/unintended user input. The Python and Go Drivers both ensure that the user-inputted strings are sanitized to protect against attacks. You can look at the buildCypher function in the Python code as an example of how we did that using PsycoPG's included mogrify function. Cheers~

dehowef avatar Mar 15 '23 02:03 dehowef

@dehowef For safe parameter handling, the solution I landed on is built on PostgresNIO's string interpolation. This code:

let params: Dictionary<String,AGValue> = ["newName": "Little'Bobby'Tables"]
let paramsWrapper: AGValueWrapper = AGValueWrapper.init(value: params)
let agRows = try await connection.execCypher(
	"SELECT * FROM cypher('test_graph_1', $$ CREATE (v:Person {name: $newName}) RETURN v $$, \( paramsWrapper )) as (v agtype);",
	logger: logger)

will send the following query to Postgres:

SELECT * FROM cypher('test_graph_1', $$ CREATE (v:Person {name: $newName}) RETURN v $$, $1) as (v agtype);

with the $1 parameter being a jsonb encoding of that Dictionary. Let me know if you see any concerns with that approach.

joshjacob avatar Mar 31 '23 17:03 joshjacob

@dehowef For safe parameter handling, the solution I landed on is built on PostgresNIO's string interpolation. This code:

let params: Dictionary<String,AGValue> = ["newName": "Little'Bobby'Tables"]
let paramsWrapper: AGValueWrapper = AGValueWrapper.init(value: params)
let agRows = try await connection.execCypher(
	"SELECT * FROM cypher('test_graph_1', $$ CREATE (v:Person {name: $newName}) RETURN v $$, \( paramsWrapper )) as (v agtype);",
	logger: logger)

will send the following query to Postgres:

SELECT * FROM cypher('test_graph_1', $$ CREATE (v:Person {name: $newName}) RETURN v $$, $1) as (v agtype);

with the $1 parameter being a jsonb encoding of that Dictionary. Let me know if you see any concerns with that approach.

That's awesome to know. Great work! Thank you for your contribution

dehowef avatar Apr 14 '23 00:04 dehowef

We also have a way to directly pass in parameters to the cypher function. Just fyi.

jrgemignani avatar Apr 14 '23 00:04 jrgemignani