Compare commits

..

2 commits

Author SHA1 Message Date
df2e7495c7
Add missing license headers 2024-06-24 03:48:07 -05:00
bf2c4ec99a
Unify project indentation style 2024-06-24 03:46:18 -05:00
10 changed files with 345 additions and 226 deletions

View file

@ -1,3 +1,20 @@
/*
UNIT_CA5 - Stream management bot
Copyright (C) 2024 digimint
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package db package db
import modules.uac.UACModule import modules.uac.UACModule
@ -8,34 +25,34 @@ import slick.jdbc.JdbcProfile
import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext
class DatabaseLayer( class DatabaseLayer(
val uac: UACModule, val uac: UACModule,
val exec: ExecutorModule val exec: ExecutorModule
) )
object DatabaseLayer: object DatabaseLayer:
def setup( def setup(
profile: JdbcProfile, profile: JdbcProfile,
config_loc: String = "db_conf" config_loc: String = "db_conf"
)(implicit ec: ExecutionContext): Future[DatabaseLayer] = )(implicit ec: ExecutionContext): Future[DatabaseLayer] =
import profile.api._ import profile.api._
val database = Database.forConfig(config_loc) val database = Database.forConfig(config_loc)
// Construct and initialize modules // Construct and initialize modules
val uacModule = UACModule(profile, database) val uacModule = UACModule(profile, database)
val uacSetupActions = uacModule.setup val uacSetupActions = uacModule.setup
val executorModule = ExecutorModule(profile, database) val executorModule = ExecutorModule(profile, database)
val executorSetupOptions = executorModule.setup val executorSetupOptions = executorModule.setup
// Run initialization actions // Run initialization actions
val initActions = DBIO.seq( val initActions = DBIO.seq(
uacSetupActions, uacSetupActions,
executorSetupOptions executorSetupOptions
) )
database.run(initActions).map(_ => database.run(initActions).map(_ =>
DatabaseLayer(uacModule, executorModule) DatabaseLayer(uacModule, executorModule)
) )

View file

@ -1,3 +1,20 @@
/*
UNIT_CA5 - Stream management bot
Copyright (C) 2024 digimint
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package db.internal package db.internal
class NotFoundException extends Throwable class NotFoundException extends Throwable

View file

@ -1,7 +1,24 @@
/*
UNIT_CA5 - Stream management bot
Copyright (C) 2024 digimint
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package db.modules package db.modules
import slick.dbio.DBIO import slick.dbio.DBIO
import slick.jdbc.JdbcProfile import slick.jdbc.JdbcProfile
trait DatabaseModule: trait DatabaseModule:
def setup: DBIO[Unit] def setup: DBIO[Unit]

View file

@ -1,3 +1,20 @@
/*
UNIT_CA5 - Stream management bot
Copyright (C) 2024 digimint
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package db.modules.executor package db.modules.executor
import slick.jdbc.JdbcProfile import slick.jdbc.JdbcProfile
@ -5,12 +22,12 @@ import db.modules.DatabaseModule
import scala.concurrent.Future import scala.concurrent.Future
class ExecutorModule( class ExecutorModule(
profile: JdbcProfile, profile: JdbcProfile,
database: profile.backend.Database database: profile.backend.Database
) extends DatabaseModule: ) extends DatabaseModule:
import profile.api._ import profile.api._
def execute[A](query: DBIO[A]): Future[A] = def execute[A](query: DBIO[A]): Future[A] =
database.run(query) database.run(query)
def setup: DBIO[Unit] = DBIO.successful(None) def setup: DBIO[Unit] = DBIO.successful(None)

View file

@ -1,3 +1,20 @@
/*
UNIT_CA5 - Stream management bot
Copyright (C) 2024 digimint
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package db.modules.uac package db.modules.uac
import db.internal.NotFoundException import db.internal.NotFoundException
@ -9,121 +26,121 @@ import slick.jdbc.JdbcProfile
import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext
class UACAPI( class UACAPI(
val profile: JdbcProfile, val profile: JdbcProfile,
val queries: UACQueries, val queries: UACQueries,
val database: profile.backend.Database val database: profile.backend.Database
): ):
import profile.api._ import profile.api._
def setup: DBIO[Unit] = def setup: DBIO[Unit] =
DBIO.seq( DBIO.seq(
(queries.UACBase.schema ++ queries.tokenScopeBase.schema).createIfNotExists (queries.UACBase.schema ++ queries.tokenScopeBase.schema).createIfNotExists
) )
def create(cred: UserAuthenticationCredential)(implicit ec: ExecutionContext): DBIO[Option[Int]] = def create(cred: UserAuthenticationCredential)(implicit ec: ExecutionContext): DBIO[Option[Int]] =
queries.UACBase.returning(queries.UACBase.map(_.id)).+=( queries.UACBase.returning(queries.UACBase.map(_.id)).+=(
queries.schema.RawUAC( queries.schema.RawUAC(
0, 0,
cred.userId, cred.userId,
cred.accessToken, cred.accessToken,
cred.refreshToken, cred.refreshToken,
cred.expires cred.expires
) )
).flatMap(uacId => ).flatMap(uacId =>
queries.tokenScopeBase.++=( queries.tokenScopeBase.++=(
cred.scopes.map(scope => cred.scopes.map(scope =>
queries.schema.UACTokenScope( queries.schema.UACTokenScope(
uacId, uacId,
scope.uid scope.uid
) )
) )
) )
) )
def retrieve(user: TwitchUID)(implicit ec: ExecutionContext): DBIO[UserAuthenticationCredential] = def retrieve(user: TwitchUID)(implicit ec: ExecutionContext): DBIO[UserAuthenticationCredential] =
// Construct query for base UAC data // Construct query for base UAC data
queries.UACBase.filter( queries.UACBase.filter(
_.userId === user _.userId === user
).take(1) ).take(1)
.result.headOption // Take only the first UAC that matches (there should never be more than one) .result.headOption // Take only the first UAC that matches (there should never be more than one)
.flatMap(res => // With the result of that query: .flatMap(res => // With the result of that query:
res match res match
case None => slick.dbio.FailureAction(NotFoundException()) // Don't attempt to retrieve scopes if there's no matching UAC case None => slick.dbio.FailureAction(NotFoundException()) // Don't attempt to retrieve scopes if there's no matching UAC
case Some(uac) => case Some(uac) =>
// Construct a query to retrieve relevant scopes // Construct a query to retrieve relevant scopes
queries.tokenScopeBase.filter( scope => queries.tokenScopeBase.filter( scope =>
scope.tokenId === uac.id scope.tokenId === uac.id
).result.map(res => ).result.map(res =>
val convertedScopes = res.map(scope => val convertedScopes = res.map(scope =>
// Convert UIDs to TokenScope enums // Convert UIDs to TokenScope enums
TokenScope.fromUid(scope.scopeId) TokenScope.fromUid(scope.scopeId)
).filter(mappedScope => ).filter(mappedScope =>
// Filter any UIDs that didn't match TokenScopes. // Filter any UIDs that didn't match TokenScopes.
mappedScope match mappedScope match
case None => false case None => false
case _ => true case _ => true
).map(convertedScope => ).map(convertedScope =>
// Strip the Options. // Strip the Options.
convertedScope.get convertedScope.get
) )
UserAuthenticationCredential( UserAuthenticationCredential(
uac.userId, uac.userId,
uac.accessToken, uac.accessToken,
uac.refreshToken, uac.refreshToken,
uac.expires, uac.expires,
convertedScopes.toList convertedScopes.toList
) )
) )
) )
// val action = database.run(query).flatMap( res => // val action = database.run(query).flatMap( res =>
// res match // res match
// case None => Future{None} // case None => Future{None}
// case Some(uac) => // case Some(uac) =>
// // Construct query for // // Construct query for
// val scopesQuery = queries.tokenScopeBase.filter( scope => // val scopesQuery = queries.tokenScopeBase.filter( scope =>
// scope.tokenId === uac.id // scope.tokenId === uac.id
// ).result.map(res => // ).result.map(res =>
// res.map(scope => // res.map(scope =>
// // Convert UIDs to TokenScope enums // // Convert UIDs to TokenScope enums
// TokenScope.fromUid(scope.scopeId) // TokenScope.fromUid(scope.scopeId)
// ).filter(mappedScope => // ).filter(mappedScope =>
// // Filter any UIDs that didn't match TokenScopes. // // Filter any UIDs that didn't match TokenScopes.
// mappedScope match // mappedScope match
// case None => false // case None => false
// case _ => true // case _ => true
// ).map(convertedScope => // ).map(convertedScope =>
// // Strip the Options. // // Strip the Options.
// convertedScope.get // convertedScope.get
// ) // )
// ) // )
// database.run(scopesQuery).map(res => // database.run(scopesQuery).map(res =>
// UserAuthenticationCredential( // UserAuthenticationCredential(
// uac.userId, // uac.userId,
// uac.accessToken, // uac.accessToken,
// uac.refreshToken, // uac.refreshToken,
// uac.expires, // uac.expires,
// res.toList // res.toList
// ) // )
// ) // )
// ) // )
// .map((res) => // .map((res) =>
// val queryResult: Option[queries.schema.RawUAC] = // val queryResult: Option[queries.schema.RawUAC] =
// if res.length > 0 then Some(res[0]) else None // if res.length > 0 then Some(res[0]) else None
// if queryResult == None then // if queryResult == None then
// throw Exception // throw Exception
// val scopesQuery = queries.tokenScopeBase.filter( // val scopesQuery = queries.tokenScopeBase.filter(
// _.tokenId === queryResult.accessToken // _.tokenId === queryResult.accessToken
// ).take(1).result // ).take(1).result
// ) // )
// action // action
def delete(user: TwitchUID): Future[Unit] = ??? def delete(user: TwitchUID): Future[Unit] = ???

View file

@ -1,3 +1,20 @@
/*
UNIT_CA5 - Stream management bot
Copyright (C) 2024 digimint
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package db.modules.uac package db.modules.uac
import db.modules.DatabaseModule import db.modules.DatabaseModule
@ -7,12 +24,12 @@ import slick.jdbc.JdbcProfile
import slick.dbio.DBIO import slick.dbio.DBIO
class UACModule( class UACModule(
val profile: JdbcProfile, val profile: JdbcProfile,
val database: profile.backend.Database val database: profile.backend.Database
) extends DatabaseModule: ) extends DatabaseModule:
val schema = UACSchema(profile) val schema = UACSchema(profile)
val queries = UACQueries(profile, schema) val queries = UACQueries(profile, schema)
val api = UACAPI(profile, queries, database) val api = UACAPI(profile, queries, database)
def setup: DBIO[Unit] = def setup: DBIO[Unit] =
api.setup api.setup

View file

@ -1,3 +1,20 @@
/*
UNIT_CA5 - Stream management bot
Copyright (C) 2024 digimint
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package db.modules.uac package db.modules.uac
import db.modules.uac.UACSchema import db.modules.uac.UACSchema
@ -5,9 +22,9 @@ import db.modules.uac.UACSchema
import slick.jdbc.JdbcProfile import slick.jdbc.JdbcProfile
class UACQueries( class UACQueries(
val profile: JdbcProfile, val profile: JdbcProfile,
val schema: UACSchema val schema: UACSchema
): ):
import profile.api._ import profile.api._
val UACBase = schema._uacTable val UACBase = schema._uacTable
val tokenScopeBase = schema._uacTokenScopeTable val tokenScopeBase = schema._uacTokenScopeTable

View file

@ -1,19 +1,19 @@
/* /*
UNIT_CA5 - Stream management bot UNIT_CA5 - Stream management bot
Copyright (C) 2024 digimint Copyright (C) 2024 digimint
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details. GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package db.modules.uac package db.modules.uac
@ -25,64 +25,64 @@ import java.time.Instant
import slick.jdbc.JdbcProfile import slick.jdbc.JdbcProfile
class UACSchema(val profile: JdbcProfile): class UACSchema(val profile: JdbcProfile):
import profile.api._ import profile.api._
case class RawUAC( case class RawUAC(
id: Long = 0, id: Long = 0,
userId: TwitchUID, userId: TwitchUID,
accessToken: AccessToken, accessToken: AccessToken,
refreshToken: RefreshToken, refreshToken: RefreshToken,
expires: Instant expires: Instant
) )
case class UACTokenScope( case class UACTokenScope(
tokenId: Long, tokenId: Long,
scopeId: Long scopeId: Long
) )
class UACTable(tag: Tag) extends Table[RawUAC](tag, "USER_TOKENS"): class UACTable(tag: Tag) extends Table[RawUAC](tag, "USER_TOKENS"):
def id = column[Long]("TKN_ID", O.PrimaryKey, O.AutoInc) def id = column[Long]("TKN_ID", O.PrimaryKey, O.AutoInc)
def userId = column[TwitchUID]("USER_ID") def userId = column[TwitchUID]("USER_ID")
def accessToken = column[AccessToken]("ACCESS_TKN") def accessToken = column[AccessToken]("ACCESS_TKN")
def refreshToken = column[RefreshToken]("REFRESH_TKN") def refreshToken = column[RefreshToken]("REFRESH_TKN")
def expires = column[Instant]("EXPIRES") def expires = column[Instant]("EXPIRES")
def * = (id, userId, accessToken, refreshToken, expires).mapTo[RawUAC] def * = (id, userId, accessToken, refreshToken, expires).mapTo[RawUAC]
val _uacTable = TableQuery[UACTable] val _uacTable = TableQuery[UACTable]
class UACTokenScopeTable(tag: Tag) extends Table[UACTokenScope](tag, "USER_TOKEN_SCOPES"): class UACTokenScopeTable(tag: Tag) extends Table[UACTokenScope](tag, "USER_TOKEN_SCOPES"):
def tokenId = column[Long]("TKN_ID") def tokenId = column[Long]("TKN_ID")
def scopeId = column[Long]("SCOPE") def scopeId = column[Long]("SCOPE")
def token = foreignKey( def token = foreignKey(
"TKN", "TKN",
tokenId, tokenId,
_uacTable _uacTable
)( )(
_.id, _.id,
onDelete=ForeignKeyAction.Cascade onDelete=ForeignKeyAction.Cascade
) )
def * = (tokenId, scopeId).mapTo[UACTokenScope] def * = (tokenId, scopeId).mapTo[UACTokenScope]
val _uacTokenScopeTable = TableQuery[UACTokenScopeTable] val _uacTokenScopeTable = TableQuery[UACTokenScopeTable]
// object api: // object api:
// def setup: DBIO[Unit] = // def setup: DBIO[Unit] =
// DBIO.seq( // DBIO.seq(
// (queries.UACBase.schema ++ queries.tokenScopeBase.schema).createIfNotExists // (queries.UACBase.schema ++ queries.tokenScopeBase.schema).createIfNotExists
// ) // )
// val queryScopesForCredential = // val queryScopesForCredential =
// tokenScopesQuery.join(rawUACs).on(_.tokenId === _.id) // tokenScopesQuery.join(rawUACs).on(_.tokenId === _.id)
// .groupBy( // .groupBy(
// (token, cred) => cred.userId // (token, cred) => cred.userId
// ) // )
// .map( // .map(
// (userId, group) => userId -> group.map( // (userId, group) => userId -> group.map(
// (scope, user) => scope.scopeId // (scope, user) => scope.scopeId
// ) // )
// ) // )

View file

@ -1,19 +1,19 @@
/* /*
UNIT_CA5 - Stream management bot UNIT_CA5 - Stream management bot
Copyright (C) 2024 digimint Copyright (C) 2024 digimint
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details. GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package twitch.api package twitch.api

View file

@ -1,9 +1,9 @@
// For more information on writing tests, see // For more information on writing tests, see
// https://scalameta.org/munit/docs/getting-started.html // https://scalameta.org/munit/docs/getting-started.html
class MySuite extends munit.FunSuite { class MySuite extends munit.FunSuite {
test("example test that succeeds") { test("example test that succeeds") {
val obtained = 42 val obtained = 42
val expected = 42 val expected = 42
assertEquals(obtained, expected) assertEquals(obtained, expected)
} }
} }