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
import modules.uac.UACModule
@ -8,34 +25,34 @@ import slick.jdbc.JdbcProfile
import scala.concurrent.ExecutionContext
class DatabaseLayer(
val uac: UACModule,
val exec: ExecutorModule
val uac: UACModule,
val exec: ExecutorModule
)
object DatabaseLayer:
def setup(
profile: JdbcProfile,
config_loc: String = "db_conf"
)(implicit ec: ExecutionContext): Future[DatabaseLayer] =
import profile.api._
val database = Database.forConfig(config_loc)
def setup(
profile: JdbcProfile,
config_loc: String = "db_conf"
)(implicit ec: ExecutionContext): Future[DatabaseLayer] =
import profile.api._
val database = Database.forConfig(config_loc)
// Construct and initialize modules
val uacModule = UACModule(profile, database)
val uacSetupActions = uacModule.setup
// Construct and initialize modules
val uacModule = UACModule(profile, database)
val uacSetupActions = uacModule.setup
val executorModule = ExecutorModule(profile, database)
val executorSetupOptions = executorModule.setup
val executorModule = ExecutorModule(profile, database)
val executorSetupOptions = executorModule.setup
// Run initialization actions
val initActions = DBIO.seq(
uacSetupActions,
executorSetupOptions
)
// Run initialization actions
val initActions = DBIO.seq(
uacSetupActions,
executorSetupOptions
)
database.run(initActions).map(_ =>
DatabaseLayer(uacModule, executorModule)
)
database.run(initActions).map(_ =>
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
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
import slick.dbio.DBIO
import slick.jdbc.JdbcProfile
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
import slick.jdbc.JdbcProfile
@ -5,12 +22,12 @@ import db.modules.DatabaseModule
import scala.concurrent.Future
class ExecutorModule(
profile: JdbcProfile,
database: profile.backend.Database
profile: JdbcProfile,
database: profile.backend.Database
) extends DatabaseModule:
import profile.api._
import profile.api._
def execute[A](query: DBIO[A]): Future[A] =
database.run(query)
def execute[A](query: DBIO[A]): Future[A] =
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
import db.internal.NotFoundException
@ -9,121 +26,121 @@ import slick.jdbc.JdbcProfile
import scala.concurrent.ExecutionContext
class UACAPI(
val profile: JdbcProfile,
val queries: UACQueries,
val database: profile.backend.Database
val profile: JdbcProfile,
val queries: UACQueries,
val database: profile.backend.Database
):
import profile.api._
def setup: DBIO[Unit] =
DBIO.seq(
(queries.UACBase.schema ++ queries.tokenScopeBase.schema).createIfNotExists
)
import profile.api._
def setup: DBIO[Unit] =
DBIO.seq(
(queries.UACBase.schema ++ queries.tokenScopeBase.schema).createIfNotExists
)
def create(cred: UserAuthenticationCredential)(implicit ec: ExecutionContext): DBIO[Option[Int]] =
queries.UACBase.returning(queries.UACBase.map(_.id)).+=(
queries.schema.RawUAC(
0,
cred.userId,
cred.accessToken,
cred.refreshToken,
cred.expires
)
).flatMap(uacId =>
queries.tokenScopeBase.++=(
cred.scopes.map(scope =>
queries.schema.UACTokenScope(
uacId,
scope.uid
)
)
)
)
def create(cred: UserAuthenticationCredential)(implicit ec: ExecutionContext): DBIO[Option[Int]] =
queries.UACBase.returning(queries.UACBase.map(_.id)).+=(
queries.schema.RawUAC(
0,
cred.userId,
cred.accessToken,
cred.refreshToken,
cred.expires
)
).flatMap(uacId =>
queries.tokenScopeBase.++=(
cred.scopes.map(scope =>
queries.schema.UACTokenScope(
uacId,
scope.uid
)
)
)
)
def retrieve(user: TwitchUID)(implicit ec: ExecutionContext): DBIO[UserAuthenticationCredential] =
// Construct query for base UAC data
queries.UACBase.filter(
_.userId === user
).take(1)
.result.headOption // Take only the first UAC that matches (there should never be more than one)
.flatMap(res => // With the result of that query:
res match
case None => slick.dbio.FailureAction(NotFoundException()) // Don't attempt to retrieve scopes if there's no matching UAC
case Some(uac) =>
// Construct a query to retrieve relevant scopes
queries.tokenScopeBase.filter( scope =>
scope.tokenId === uac.id
).result.map(res =>
val convertedScopes = res.map(scope =>
// Convert UIDs to TokenScope enums
TokenScope.fromUid(scope.scopeId)
).filter(mappedScope =>
// Filter any UIDs that didn't match TokenScopes.
mappedScope match
case None => false
case _ => true
).map(convertedScope =>
// Strip the Options.
convertedScope.get
)
def retrieve(user: TwitchUID)(implicit ec: ExecutionContext): DBIO[UserAuthenticationCredential] =
// Construct query for base UAC data
queries.UACBase.filter(
_.userId === user
).take(1)
.result.headOption // Take only the first UAC that matches (there should never be more than one)
.flatMap(res => // With the result of that query:
res match
case None => slick.dbio.FailureAction(NotFoundException()) // Don't attempt to retrieve scopes if there's no matching UAC
case Some(uac) =>
// Construct a query to retrieve relevant scopes
queries.tokenScopeBase.filter( scope =>
scope.tokenId === uac.id
).result.map(res =>
val convertedScopes = res.map(scope =>
// Convert UIDs to TokenScope enums
TokenScope.fromUid(scope.scopeId)
).filter(mappedScope =>
// Filter any UIDs that didn't match TokenScopes.
mappedScope match
case None => false
case _ => true
).map(convertedScope =>
// Strip the Options.
convertedScope.get
)
UserAuthenticationCredential(
uac.userId,
uac.accessToken,
uac.refreshToken,
uac.expires,
convertedScopes.toList
)
)
)
UserAuthenticationCredential(
uac.userId,
uac.accessToken,
uac.refreshToken,
uac.expires,
convertedScopes.toList
)
)
)
// val action = database.run(query).flatMap( res =>
// res match
// case None => Future{None}
// case Some(uac) =>
// // Construct query for
// val scopesQuery = queries.tokenScopeBase.filter( scope =>
// scope.tokenId === uac.id
// ).result.map(res =>
// res.map(scope =>
// // Convert UIDs to TokenScope enums
// TokenScope.fromUid(scope.scopeId)
// ).filter(mappedScope =>
// // Filter any UIDs that didn't match TokenScopes.
// mappedScope match
// case None => false
// case _ => true
// ).map(convertedScope =>
// // Strip the Options.
// convertedScope.get
// )
// )
// val action = database.run(query).flatMap( res =>
// res match
// case None => Future{None}
// case Some(uac) =>
// // Construct query for
// val scopesQuery = queries.tokenScopeBase.filter( scope =>
// scope.tokenId === uac.id
// ).result.map(res =>
// res.map(scope =>
// // Convert UIDs to TokenScope enums
// TokenScope.fromUid(scope.scopeId)
// ).filter(mappedScope =>
// // Filter any UIDs that didn't match TokenScopes.
// mappedScope match
// case None => false
// case _ => true
// ).map(convertedScope =>
// // Strip the Options.
// convertedScope.get
// )
// )
// database.run(scopesQuery).map(res =>
// UserAuthenticationCredential(
// uac.userId,
// uac.accessToken,
// uac.refreshToken,
// uac.expires,
// res.toList
// )
// )
// )
// .map((res) =>
// val queryResult: Option[queries.schema.RawUAC] =
// if res.length > 0 then Some(res[0]) else None
// database.run(scopesQuery).map(res =>
// UserAuthenticationCredential(
// uac.userId,
// uac.accessToken,
// uac.refreshToken,
// uac.expires,
// res.toList
// )
// )
// )
// .map((res) =>
// val queryResult: Option[queries.schema.RawUAC] =
// if res.length > 0 then Some(res[0]) else None
// if queryResult == None then
// throw Exception
// if queryResult == None then
// throw Exception
// val scopesQuery = queries.tokenScopeBase.filter(
// _.tokenId === queryResult.accessToken
// ).take(1).result
// )
// val scopesQuery = queries.tokenScopeBase.filter(
// _.tokenId === queryResult.accessToken
// ).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
import db.modules.DatabaseModule
@ -7,12 +24,12 @@ import slick.jdbc.JdbcProfile
import slick.dbio.DBIO
class UACModule(
val profile: JdbcProfile,
val database: profile.backend.Database
val profile: JdbcProfile,
val database: profile.backend.Database
) extends DatabaseModule:
val schema = UACSchema(profile)
val queries = UACQueries(profile, schema)
val api = UACAPI(profile, queries, database)
val schema = UACSchema(profile)
val queries = UACQueries(profile, schema)
val api = UACAPI(profile, queries, database)
def setup: DBIO[Unit] =
api.setup
def setup: DBIO[Unit] =
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
import db.modules.uac.UACSchema
@ -5,9 +22,9 @@ import db.modules.uac.UACSchema
import slick.jdbc.JdbcProfile
class UACQueries(
val profile: JdbcProfile,
val schema: UACSchema
val profile: JdbcProfile,
val schema: UACSchema
):
import profile.api._
val UACBase = schema._uacTable
val tokenScopeBase = schema._uacTokenScopeTable
import profile.api._
val UACBase = schema._uacTable
val tokenScopeBase = schema._uacTokenScopeTable

View file

@ -1,19 +1,19 @@
/*
UNIT_CA5 - Stream management bot
Copyright (C) 2024 digimint
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 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.
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/>.
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
@ -25,64 +25,64 @@ import java.time.Instant
import slick.jdbc.JdbcProfile
class UACSchema(val profile: JdbcProfile):
import profile.api._
case class RawUAC(
id: Long = 0,
userId: TwitchUID,
accessToken: AccessToken,
refreshToken: RefreshToken,
expires: Instant
)
import profile.api._
case class RawUAC(
id: Long = 0,
userId: TwitchUID,
accessToken: AccessToken,
refreshToken: RefreshToken,
expires: Instant
)
case class UACTokenScope(
tokenId: Long,
scopeId: Long
)
case class UACTokenScope(
tokenId: Long,
scopeId: Long
)
class UACTable(tag: Tag) extends Table[RawUAC](tag, "USER_TOKENS"):
def id = column[Long]("TKN_ID", O.PrimaryKey, O.AutoInc)
def userId = column[TwitchUID]("USER_ID")
def accessToken = column[AccessToken]("ACCESS_TKN")
def refreshToken = column[RefreshToken]("REFRESH_TKN")
def expires = column[Instant]("EXPIRES")
class UACTable(tag: Tag) extends Table[RawUAC](tag, "USER_TOKENS"):
def id = column[Long]("TKN_ID", O.PrimaryKey, O.AutoInc)
def userId = column[TwitchUID]("USER_ID")
def accessToken = column[AccessToken]("ACCESS_TKN")
def refreshToken = column[RefreshToken]("REFRESH_TKN")
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"):
def tokenId = column[Long]("TKN_ID")
def scopeId = column[Long]("SCOPE")
class UACTokenScopeTable(tag: Tag) extends Table[UACTokenScope](tag, "USER_TOKEN_SCOPES"):
def tokenId = column[Long]("TKN_ID")
def scopeId = column[Long]("SCOPE")
def token = foreignKey(
"TKN",
tokenId,
_uacTable
)(
_.id,
onDelete=ForeignKeyAction.Cascade
)
def token = foreignKey(
"TKN",
tokenId,
_uacTable
)(
_.id,
onDelete=ForeignKeyAction.Cascade
)
def * = (tokenId, scopeId).mapTo[UACTokenScope]
def * = (tokenId, scopeId).mapTo[UACTokenScope]
val _uacTokenScopeTable = TableQuery[UACTokenScopeTable]
// object api:
// def setup: DBIO[Unit] =
// DBIO.seq(
// (queries.UACBase.schema ++ queries.tokenScopeBase.schema).createIfNotExists
// )
val _uacTokenScopeTable = TableQuery[UACTokenScopeTable]
// object api:
// def setup: DBIO[Unit] =
// DBIO.seq(
// (queries.UACBase.schema ++ queries.tokenScopeBase.schema).createIfNotExists
// )
// val queryScopesForCredential =
// tokenScopesQuery.join(rawUACs).on(_.tokenId === _.id)
// .groupBy(
// (token, cred) => cred.userId
// )
// .map(
// (userId, group) => userId -> group.map(
// (scope, user) => scope.scopeId
// )
// )
// val queryScopesForCredential =
// tokenScopesQuery.join(rawUACs).on(_.tokenId === _.id)
// .groupBy(
// (token, cred) => cred.userId
// )
// .map(
// (userId, group) => userId -> group.map(
// (scope, user) => scope.scopeId
// )
// )

View file

@ -1,19 +1,19 @@
/*
UNIT_CA5 - Stream management bot
Copyright (C) 2024 digimint
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 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.
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/>.
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 twitch.api

View file

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