Skip to content

Commit

Permalink
Merge pull request #3 from mnbjhu/Adding_tests
Browse files Browse the repository at this point in the history
Adding tests
  • Loading branch information
mnbjhu authored May 5, 2023
2 parents 942c4c0 + bff37c8 commit 1557360
Show file tree
Hide file tree
Showing 46 changed files with 1,013 additions and 318 deletions.
8 changes: 2 additions & 6 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,16 @@ on:

jobs:
build:

runs-on: ubuntu-latest
services:
surreal:
image: surrealdb/surrealdb:latest
ports:
- 8000:8000
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- name: Start Surreal
run: sh ./scripts/start_surreal.sh
- name: Build with Gradle
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
with:
Expand Down
1 change: 1 addition & 0 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
kotlin("jvm")
kotlin("plugin.serialization") version "1.8.0"
`maven-publish`
}

group = "uk.gibby.dsl"
Expand Down
124 changes: 109 additions & 15 deletions core/src/main/kotlin/uk.gibby.dsl/core/Schema.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package uk.gibby.dsl.core

import uk.gibby.dsl.scopes.CodeBlockScope
import uk.gibby.dsl.scopes.TransactionScope
import uk.gibby.dsl.types.*
import uk.gibby.dsl.types.BooleanType.Companion.FALSE
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.KTypeProjection
import kotlin.reflect.full.createType
import kotlin.reflect.full.isSubtypeOf
import kotlin.time.Duration

abstract class Schema {
abstract class Schema(tables: List<Table<*, *>>) {

constructor(vararg tables: Table<*, *>): this(tables.toList())
fun init(){
if(!isInitialized){
with(SchemaScope()){
Expand All @@ -21,8 +24,8 @@ abstract class Schema {
}

private var isInitialized = false
abstract val tables: List<TableDefinition>
abstract val scopes: List<Scope<*, *, *, *, *, *>>
val tables: List<TableDefinition> = tables.map { it.getDefinition() }
open val scopes: List<Scope<*, *, *, *, *, *>> = listOf()
fun getDefinitionQuery(): String {
init()
var definition = "BEGIN TRANSACTION;\n"
Expand All @@ -35,21 +38,80 @@ abstract class Schema {
return "$definition\nCOMMIT TRANSACTION;"
}
open fun SchemaScope.configure() {}

inner class SchemaScope {
fun <T, U: RecordType<T>, c, C: RecordType<c>>Table<T, U>.permissions(`for`: Scope<*, *, *, *, c, C>, vararg types: PermissionType, `when`: C.() -> BooleanType){
println(tables)

fun <T, U: RecordType<T>>Table<T, U>.configureFields(
configuration: context(TableDefinitionScope) U.() -> Unit
) {
configuration(TableDefinitionScope(this), recordType)
}
fun <T, U: RecordType<T>, c, C: RecordType<c>>Table<T, U>.permissions(`for`: Scope<*, *, *, *, c, C>, vararg types: PermissionType, `when`: U.(C) -> BooleanType): Table<T, U>{
val definition = tables.first { it.name == name }
types.forEach {
val current = definition.permissions.getOrPut(it) {
val current = definition.permissions.getOrPut(it) { "" }
val token = recordType.createReference("\$auth") as C
definition.permissions[it] = current + "IF (\$scope == \"${`for`.name}\") THEN ${recordType.`when`(token).getReference()} ELSE "
}
return this
}

fun <T, U: RecordType<T>>Table<T, U>.noPermissionsFor(`for`: Scope<*, *, *, *, *, *>, vararg types: PermissionType): Table<T, U> {
val definition = tables.first { it.name == name }
types.forEach {
val current = definition.permissions.getOrPut(it) { "" }
definition.permissions[it] = current + "IF (\$scope == \"${`for`.name}\") THEN FALSE ELSE "
}
return this
}

fun <T, U: RecordType<T>>Table<T, U>.fullPermissionsFor(`for`: Scope<*, *, *, *, *, *>, vararg types: PermissionType): Table<T, U> {
val definition = tables.first { it.name == name }
types.forEach {
val current = definition.permissions.getOrPut(it) { "" }
definition.permissions[it] = current + "IF (\$scope == \"${`for`.name}\") THEN TRUE ELSE "
}
return this
}
/*
fun <T, U: RecordType<T>>Table<T, U>.assert(){
val definition = tables.first { it.name == name }
definition
}
*/
}

inner class TableDefinitionScope(private val definition: TableDefinition) {
constructor(table: Table<*, *>): this(tables.first { it.name == table.name })
fun <T, U: Reference<T>>U.assert(condition: (U) -> BooleanType): U {
val assertion = condition(createReference("\$value") as U).getReference()
definition.fields[getReference()]!!.assertions.add(assertion)
return this
}

fun defineIndex(name: String, vararg fields: Reference<*>) {
definition.indexes.add("DEFINE INDEX $name ON ${definition.name} FIELDS ${fields.joinToString { it.getReference() }};")
}
fun defineUniqueIndex(name: String, vararg fields: Reference<*>) {
definition.indexes.add("DEFINE INDEX $name ON ${definition.name} FIELDS ${fields.joinToString { it.getReference() }} UNIQUE;")
}

fun <T, U: Reference<T>, c, C: RecordType<c>>U.permissions(`for`: Scope<*, *, *, *, c, C>, vararg types: PermissionType, `when`: (C) -> BooleanType){
val fieldDefinition = definition.fields[getReference()]!!
types.forEach {
val current = fieldDefinition.permissions.getOrPut(it) {
""
}
val token = recordType.createReference("\$auth") as C
definition.permissions[it] = current + "IF (\$scope == \"${`for`.name}\") THEN ${token.`when`().getReference()} ELSE "
val token = `for`.tokenTable.recordType.createReference("\$auth") as C
fieldDefinition.permissions[it] = current + "IF (\$scope == \"${`for`.name}\") THEN ${`when`(token).getReference()} ELSE "
}
}
}

}



fun getTableDefinition(name: String, type: KType, reference: Reference<*>): TableDefinition {
val definition = mutableMapOf<String, FieldDefinition>()
val clazz = reference::class
Expand All @@ -61,7 +123,7 @@ fun getTableDefinition(name: String, type: KType, reference: Reference<*>): Tabl
.forEach { definition.putAll(getFieldDefinition(it.name, it.returnType, it.call(reference) as Reference<*>)) }
return TableDefinition(name, definition)
}
inline fun <reified T, U: RecordType<T>>Table<T, U>.getDefinition(): TableDefinition {
fun Table<*, *>.getDefinition(): TableDefinition {
return getTableDefinition(name, recordType::class.createType(), recordType)
}

Expand Down Expand Up @@ -116,15 +178,17 @@ fun getFieldDefinition(name: String, type: KType, reference: Reference<*>): Map<

class TableDefinition(
val name: String,
private val fields: Map<String, FieldDefinition>,
val fields: MutableMap<String, FieldDefinition>,
val permissions: MutableMap<PermissionType, String> = mutableMapOf(),
val indexes: MutableList<String> = mutableListOf()
) {
fun getDefinition(): String {
return "DEFINE TABLE $name SCHEMAFULL" +
( if(permissions.isNotEmpty())
"\nPERMISSIONS \n${permissions.entries.joinToString("\n"){ "FOR ${it.key.text} WHERE ${it.value}FALSE END" }}" else "" ) +
";" +
fields.entries.joinToString("\n"){ it.value.getDefinition(it.key, name) }
";\n" +
fields.entries.joinToString("\n"){ it.value.getDefinition(it.key, name) } + "\n" +
indexes.joinToString("\n")
}
}

Expand All @@ -143,31 +207,61 @@ data class FieldDefinition(
object ARRAY: Type("array")
object OBJECT: Type("object")
class RecordLink(tableName: String): Type("record($tableName)")

}
fun getDefinition(name: String, tableName: String): String {
return "DEFINE FIELD $name ON TABLE $tableName TYPE ${type.text}" +
( if(assertions.isNotEmpty()) "\nASSERT ${assertions.joinToString(" AND "){ it }}" else "" ) +
( if(permissions.isNotEmpty())
"\nPERMISSIONS \n${permissions.entries.joinToString("\n"){ "FOR ${it.key.name}\n${it.value}" }}" else "" ) +
"\nPERMISSIONS \n${permissions.entries.joinToString("\n"){ "FOR ${it.key.text} WHERE ${it.value}FALSE END" }}" else "" ) +
";"

}
}


fun <a, A: Reference<a>, b, B: Reference<b>, c, C: RecordType<c>>scopeOf(
name: String,
sessionDuration: Duration,
signupType: A,
signInType: B,
tokenTable: Table<c, C>,
signUp: context(TransactionScope) (A) -> C,
signIn: context(TransactionScope) (B) -> ListType<c, C>,
) = object: Scope<a, A, b, B, c, C>(
name,
sessionDuration,
signupType,
signInType,
tokenTable,

) {
override fun signUp(auth: A): ListType<c, C> {
return signUp(TransactionScope(), auth).run {
list(this).createReference(getReference().removePrefix("RETURN "))
}
}

override fun signIn(auth: B): ListType<c, C> {
return signIn(TransactionScope(), auth)
}

}

abstract class Scope<a, A: Reference<a>, b, B: Reference<b>, c, C: RecordType<c>>(
val name: String,
private val sessionDuration: Duration,
private val signupType: A,
private val signInType: B,
private val tokenTable: Table<c, C>
internal val tokenTable: Table<c, C>
) {
abstract fun signUp(auth: A): ListType<c, C>
abstract fun signIn(auth: B): ListType<c, C>

fun getDefinition(): String {
val signUpToken = signUp(signupType.createReference("\$creds") as A)

val signInToken = signIn(signInType.createReference("\$auth") as B)
val signInToken = signIn(signInType.createReference("\$creds") as B)

return "DEFINE SCOPE $name\n" +
"SESSION $sessionDuration\n" +
Expand Down
53 changes: 29 additions & 24 deletions core/src/main/kotlin/uk.gibby.dsl/core/Table.kt
Original file line number Diff line number Diff line change
@@ -1,49 +1,54 @@
package uk.gibby.dsl.core

import kotlinx.serialization.encodeToString
import uk.gibby.dsl.driver.surrealJson
import uk.gibby.dsl.scopes.FilterScope
import uk.gibby.dsl.scopes.FilterScopeImpl
import uk.gibby.dsl.scopes.SetScope
import uk.gibby.dsl.scopes.*
import uk.gibby.dsl.types.*

data class Table<T, U: RecordType<T>>(val name: String, val recordType: U) {
fun delete(deleteScope: context(FilterScope) U.() -> Unit = {}): ListType<String?, NullableType<String, StringType>> {
val filter = FilterScopeImpl()
filter.deleteScope(recordType)
return ListType(NullableType("_", stringType), "DELETE FROM $name${filter.getFilterString()}")
context(TransactionScope)
fun delete(): ListType<String?, NullableType<String, StringType>> {
return ListType(NullableType("_", stringType), "(DELETE FROM $name)")
}
context(TransactionScope)
fun <a, A: Reference<a>>delete(deleteScope: context(FilterScope, ReturningScope<T, U>) U.() -> A): ListType<String?, NullableType<String, StringType>> {
val filter = FilterScopeImpl(recordType)
val returned = filter.deleteScope(ReturningScopeImpl(recordType), recordType)
return ListType(NullableType("_", stringType), "(DELETE FROM $name${filter.getFilterString()} RETURN ${returned.getReference()})")
}

context(TransactionScope)
fun selectAll(selectScope: context(FilterScope) U.() -> Unit = {}): ListType<T, U> {
val filter = FilterScopeImpl()
val filter = FilterScopeImpl(recordType)
with(filter) { selectScope(recordType) }
return ListType(recordType, "SELECT * FROM $name${filter.getFilterString()}")
return ListType(recordType, "(SELECT * FROM $name${filter.getFilterString()})")
}

context(TransactionScope)
fun <r, R: Reference<r>>select(projection: context(FilterScope) U.() -> R): ListType<r, R> {
val filter = FilterScopeImpl()
val filter = FilterScopeImpl(recordType)
val toSelect = with(filter) {
projection(recordType)
}
return ListType(
toSelect,
"SELECT VALUE ${toSelect.getReference()} FROM $name${filter.getFilterString()}"
"(SELECT VALUE ${toSelect.getReference()} FROM $name${filter.getFilterString()})"
)
TODO()
}

fun update(updateScope: context(SetScope, FilterScope) U.() -> Unit): ListType<T, U> {
context(TransactionScope)
fun <a, A: Reference<a>>update(updateScope: context(SetScope, FilterScope, ReturningScope<T, U>) U.() -> A): ListType<a, A> {
val setScope = SetScope()
val filterScope = FilterScopeImpl()
updateScope(setScope, filterScope, recordType)

return ListType(recordType, "UPDATE $name ${setScope.getSetString()} ${filterScope.getFilterString()}")
val filterScope = FilterScopeImpl(recordType)
val returned = updateScope(setScope, filterScope, ReturningScopeImpl(recordType), recordType)
return ListType(returned, "UPDATE $name ${setScope.getSetString()} ${filterScope.getFilterString()} " +
"RETURN ${when(val ref = returned.getReference()){
"AFTER" -> "AFTER"
"BEFORE" -> "BEFORE"
"NONE" -> "NONE"
else -> "VALUE $ref"
}}")
}
operator fun get(id: String): Table<T, U> {
return Table("$name:$id", recordType)
operator fun get(id: String): TableId<T, U> {
return TableId("$name:$id", recordType)
}
}

inline fun <reified T, U: RecordType<T>>Table<T, U>.insert(vararg items: T): ListType<T, U>{
return ListType(recordType, "INSERT INTO $name ${surrealJson.encodeToString(items.toList())}")
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ class DatabaseConnection(private val host: String, private val port: Int = 8000)
suspend fun defineDatabase(ns: String, db: String) {
query("USE NS $ns; DEFINE DATABASE $db;")
}
suspend fun removeDatabase(ns: String, db: String) {
query("USE NS $ns; REMOVE DATABASE $db;")
}
suspend fun removeNamespace(name: String) {
query("REMOVE NAMESPACE $name;")
}

suspend fun removeDatabase(name: String) {
query("REMOVE DATABASE $name;")
}
private suspend fun sendRequest(method: String, params: JsonArray): JsonElement {
val id = count++.toString()
val request = RpcRequest(id, method, params).also { println(it) }
Expand Down
13 changes: 12 additions & 1 deletion core/src/main/kotlin/uk.gibby.dsl/functions/Validation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,15 @@ object Is {
fun numeric(value: Reference<*>) = booleanType.createReference("is::numeric(${value.getReference()})")
fun semver(value: Reference<*>) = booleanType.createReference("is::semver(${value.getReference()})")
fun uuid(value: Reference<*>) = booleanType.createReference("is::uuid(${value.getReference()})")
}
}
fun Reference<*>.isAlphaNum() = booleanType.createReference("is::alphanum(${getReference()})")
fun Reference<*>.isAlpha() = booleanType.createReference("is::alpha(${getReference()})")
fun Reference<*>.isAscii() = booleanType.createReference("is::ascii(${getReference()})")
fun Reference<*>.isDomain() = booleanType.createReference("is::domain(${getReference()})")
fun Reference<*>.isEmail() = booleanType.createReference("is::email(${getReference()})")
fun Reference<*>.isHexadecimal() = booleanType.createReference("is::hexadecimal(${getReference()})")
fun Reference<*>.isLatitude() = booleanType.createReference("is::latitude(${getReference()})")
fun Reference<*>.isLongitude() = booleanType.createReference("is::longitude(${getReference()})")
fun Reference<*>.isNumeric() = booleanType.createReference("is::numeric(${getReference()})")
fun Reference<*>.isSemver() = booleanType.createReference("is::semver(${getReference()})")
fun Reference<*>.isUuid() = booleanType.createReference("is::uuid(${getReference()})")
4 changes: 0 additions & 4 deletions core/src/main/kotlin/uk.gibby.dsl/scopes/CodeBlockScope.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import uk.gibby.dsl.types.Reference
import kotlin.reflect.KProperty


open class StatementScope {
}


class CodeBlockScope {
private var generated: String = "{\n"
operator fun Reference<*>.unaryPlus(){
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/kotlin/uk.gibby.dsl/scopes/FilterScope.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import uk.gibby.dsl.types.RecordType

interface FilterScope {
fun getFilterString(): String
fun where(condition: BooleanType)
fun <T, U: RecordType<T>>fetch(items: ListType<Linked<T>, RecordLink<T, U>>)
fun <T, U: RecordType<T>>U.where(condition: BooleanType): UnitType
fun <T, U: RecordType<T>>fetch(items: ListType<Linked<T>, RecordLink<T, U>>): UnitType
}
Loading

0 comments on commit 1557360

Please sign in to comment.