Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a JS driver #1486

Merged
merged 25 commits into from
Feb 7, 2020
Merged

Implement a JS driver #1486

merged 25 commits into from
Feb 7, 2020

Conversation

tokou
Copy link
Contributor

@tokou tokou commented Oct 27, 2019

This is a tentative implementation of a JS driver. It uses sql.js as it looked like the most used and efficient solution.

This is still a WIP and will not pass CI.

The implementation is done.
I also added a web sample (and fixed the other samples). (Run ./gradlew :sample:web:jsBrowserWebpack and then serve the sample/web/build/distributions/index.html file)

The main issue I have is with tests. I duplicated all the tests and adapted them (quite naively).

The database can only be created in an asynchronous fashion in sql.js. This requires adapting all the tests to run with async code.

In the duplicated tests, I implemented the workaround descibed here to run async tests. However there's still an issue as @BeforeTest and @AfterTest are not handled properly as reported here.

Edit: I just submitted a PR in order to fix this and make possible asynchronous beofre and after methods.

Tests in :drivers:sqljs-driver:jsTest pass but they are duplicated.
Some tests in :sample:common:jsTest fail because I don't think it's possible to ignore common tests for a particular target. I duplicated them and fixed them for async code and they pass.

I also duplicated the runTest method in a couple of files for now.

What approach do you recommend for fixing the tests ?

Also, I am not very familiar with publishing of artifacts. I'm sure some steps are missing or maybe wrong to ensure the proper publishing of the JS driver and also some CI steps maybe.

Fixes #1350
Fixes #1294
Closes #1302

package com.squareup.sqldelight.drivers.sqljs

import org.khronos.webgl.Uint8Array
import kotlin.js.*
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit no * imports, you can search for star imports in intellij preferences to turn off

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


external interface QueryResults {
var columns: Array<String>
var values: Array<Array<dynamic /* Number | String | Uint8Array | Nothing? */>>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does this work O_O

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean
You declare the interface you expect from the JS lib, and then you can use it from Kotlin with type checking etc

return js("new (Function.prototype.bind.apply(type, argsArray))")
}

suspend fun initSql(config: Config? = js("{}")): SqlJsStatic = initSqlJs(config).await()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way we can remove the dependency on coroutines from the driver? It should ideally be all blocking straightforward APIs that we can wrap in coroutines if necessary

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would have to expose this as a Promise, which I think is fine. The consumer could then await() using the built-in extension for it if they're using coroutines.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea I like that more

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does Promise have a normal blocking await()? if so you could probably just use that then in your tests @tokou to get around the issues you're currently seeing

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. You would have to do something like

@Test fun myTest() = initDb().then {
  test stuff
}

which returns the Promise to the test runner which it will then wait on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, indeed it's much better now

README.md Outdated
```kotlin
val sql = initSql()
val db = sql.Database()
val driver: SqlDriver = JsSqlDriver(db)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can the constructor for JsSqlDriver() just initSql and get the database itself instead of doing these three steps explicitly? Or is the expectation that consumers would use the results of initSql and Database() multiple times

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some helper functions to initialize the driver directly

@AlecKazakova
Copy link
Collaborator

this is impressive

I'm going to update the sample code to 1.2.0 today and then we'll need to rebase this pr on that. It should at least clean up the diff that amount. Really love that you also created a sample for the web side, thank you

@AlecKazakova
Copy link
Collaborator

With regard to publishing we're going to do the kotlin 1.3.60 release, then merge all these new drivers for windows mac and js then fix the publishing/travis story because its a nightmare, then do another release

so it might be a while before this all gets released because theres a lot of work that has to go into it

Copy link
Collaborator

@JakeWharton JakeWharton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall great stuff 👍

@@ -0,0 +1,79 @@
apply plugin: 'org.jetbrains.kotlin.multiplatform'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason to use multiplatform in this module instead of only the js plugin?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm no, I just switched to the js plugin

dependsOn 'publishJsPublicationToTestRepository'
}
// NOTE: We do not alias uploadArchives because CI runs it on Linux and we only want to run it on Mac OS.
//tasks.register('uploadArchives').dependsOn('publishKotlinMultiplatformPublicationToMavenRepository')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we need this commented out since it should work on all host OSs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed all publishing related code for now, as per Alec's comment

kotlin {

js {
useCommonJs()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this prevents use in a module system and we should be using UMD (since Kotlin can't emit ES6 module files).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

tearDown()
}

@JsName("SqlResultSet_getters_return_null_if_the_column_values_are_NULL")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just name the tests like this directly. They only run on JS, so there's no point duplicating them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@tokou
Copy link
Contributor Author

tokou commented Feb 4, 2020

@AlecStrong Just rebased on the latest master.

  • Everything seems to work fine (ran ./gradlew clean :drivers:sqljs-driver:test :sample:common:jsTest :sample:web:browserWebpack)
  • stately does not provide a js for now so running ./gradlew :runtime:jsTest will fail (along with gradle sync in IntelliJ)

@AlecKazakova
Copy link
Collaborator

i thought stately was only a dep of the native artifact - interesting that it fails the js runtime compilation

@tokou
Copy link
Contributor Author

tokou commented Feb 4, 2020

stately-collection is a dependency on the commonTest source set. See here

@tokou
Copy link
Contributor Author

tokou commented Feb 4, 2020

I could simply replace the class with an expect/actual if you want and remove the dependency on stately in common

@AlecKazakova
Copy link
Collaborator

if possible i think that makes sense

@tokou
Copy link
Contributor Author

tokou commented Feb 4, 2020

I fixed the CI and updated the README
The only thing left is the publishing of the js driver I guess

@AlecKazakova
Copy link
Collaborator

dope, will review shortly. We can figure out publishing later

@@ -4,6 +4,7 @@ import kotlin.test.Test
import kotlin.test.assertTrue

class SchemaTest : BaseTest() {
/*
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this still need to be commented out?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. I missed these. Just pushed a fix. There's no way do disable running common tests on a specific target apparently so I duplicated the tests (the alternative would be to expect a boolean from each target to decide if we should run the test or not).

@AlecKazakova AlecKazakova merged commit 43ca376 into sqldelight:master Feb 7, 2020
@AlecKazakova
Copy link
Collaborator

huge contribution. thanks for doing this

targets {
targetFromPreset(presets.jvm, 'jvm')
targetFromPreset(presets.js, 'js') {
tasks.getByName(compilations.main.compileKotlinTaskName).kotlinOptions {
moduleKind = 'umd'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was replaced with the js block to define the target
Sorry I'm not sure why I did this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

sqldelight for Kotlin multiplatform JS target Sample still on 1.0.2, broken with 1.1.1
3 participants