Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
5b6e251
Generate per-table Id newtypes (UserId, ProjectId, etc.) instead of p…
mpscholten Feb 21, 2026
97a79db
Merge remote-tracking branch 'origin/master' into per-table-id-newtypes
mpscholten Feb 21, 2026
8a910e6
Fix remaining test suite compilation errors for per-table Id newtypes
mpscholten Feb 21, 2026
9dead0c
Remove unused IHP.ModelSupport import from QueryBuilderSpec
mpscholten Feb 21, 2026
a20b68b
Fix ihp-job-dashboard: use genericFetchIdOne instead of removed Fetch…
mpscholten Feb 21, 2026
1ebceef
Add hashable dependency for generated PrimaryKeys module
mpscholten Feb 21, 2026
e13cf1b
Fix ihp-datasync: use Show (Id' ...) instead of Show (PrimaryKey ...)
mpscholten Feb 21, 2026
1cd4a76
Fix ihp-datasync test: add TestUserId newtype for test_users table
mpscholten Feb 21, 2026
d64b84a
Fix generated PrimaryKeys module: derive Show, qualify Prelude functions
mpscholten Feb 21, 2026
9970490
Remove redundant Id' import from ihp-typed-sql TypeMapping
mpscholten Feb 21, 2026
a42770b
Remove redundant Id'(..) import from ihp-typed-sql Decoders
mpscholten Feb 21, 2026
373af00
Export CollectionFetchRelated classes from IHP.FetchRelated
mpscholten Feb 21, 2026
deecac0
Fix ihp-typed-sql tests: add per-table Id newtypes for test tables
mpscholten Feb 21, 2026
9b66347
Fix tableNameToIdName: avoid ClassyPrelude's init/last
mpscholten Feb 22, 2026
b0b3cec
Fix IsString instance in typed-sql tests: use Text.Read.readMaybe
mpscholten Feb 22, 2026
b943b2f
Merge master into per-table-id-newtypes
mpscholten Mar 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion NixSupport/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ library
, hasql-postgresql-types
, hasql-pool
, unordered-containers
, hashable
, postgresql-types
exposed-modules:
CABAL_EOF
Expand Down Expand Up @@ -123,7 +124,7 @@ CABAL_EOF
# Inline mkDerivation instead of callCabal2nix to avoid IFD (Import From Derivation).
# The dependencies here must match the .cabal template generated in modelsPackageSrc above.
modelsPackage = pkgs.haskell.lib.disableLibraryProfiling (pkgs.haskell.lib.dontHaddock (
ghc.callPackage ({ mkDerivation, base, ihp, basic-prelude, text, bytestring, time, uuid, aeson, postgresql-simple, deepseq, data-default, scientific, string-conversions, hasql, hasql-dynamic-statements, hasql-implicits, hasql-mapping, hasql-postgresql-types, hasql-pool, unordered-containers, postgresql-types }: mkDerivation {
ghc.callPackage ({ mkDerivation, base, ihp, basic-prelude, text, bytestring, time, uuid, aeson, postgresql-simple, deepseq, data-default, scientific, string-conversions, hasql, hasql-dynamic-statements, hasql-implicits, hasql-mapping, hasql-postgresql-types, hasql-pool, unordered-containers, hashable, postgresql-types }: mkDerivation {
pname = "${appName}-models";
version = "0.1.0";
src = modelsPackageSrc;
Expand All @@ -148,6 +149,7 @@ CABAL_EOF
hasql-postgresql-types
hasql-pool
unordered-containers
hashable
postgresql-types
];
license = pkgs.lib.licenses.free;
Expand Down
2 changes: 1 addition & 1 deletion ihp-datasync/IHP/DataSync/Controller.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import IHP.DataSync.ControllerImpl (runDataSyncController)
import IHP.DataSync.DynamicQueryCompiler (camelCaseRenamer)

instance (
Show (PrimaryKey (GetTableName CurrentUserRecord))
Show (Id' (GetTableName CurrentUserRecord))
, HasNewSessionUrl CurrentUserRecord
, Typeable CurrentUserRecord
, HasField "id" CurrentUserRecord (Id' (GetTableName CurrentUserRecord))
Expand Down
10 changes: 5 additions & 5 deletions ihp-datasync/IHP/DataSync/ControllerImpl.hs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ runDataSyncController ::
, ?state :: IORef DataSyncController
, Typeable CurrentUserRecord
, HasNewSessionUrl CurrentUserRecord
, Show (PrimaryKey (GetTableName CurrentUserRecord))
, Show (Id' (GetTableName CurrentUserRecord))
) => Hasql.Pool.Pool -> EnsureRLSEnabledFn -> InstallTableChangeTriggerFn -> IO ByteString -> SendJSONFn -> HandleCustomMessageFn -> (Text -> Renamer) -> IO ()
runDataSyncController hasqlPool ensureRLSEnabled installTableChangeTriggers receiveData sendJSON handleCustomMessage renamer = do
setState DataSyncReady { subscriptions = HashMap.empty, transactions = HashMap.empty }
Expand Down Expand Up @@ -111,7 +111,7 @@ buildMessageHandler ::
, ?state :: IORef DataSyncController
, Typeable CurrentUserRecord
, HasNewSessionUrl CurrentUserRecord
, Show (PrimaryKey (GetTableName CurrentUserRecord))
, Show (Id' (GetTableName CurrentUserRecord))
)
=> Hasql.Pool.Pool -> EnsureRLSEnabledFn -> InstallTableChangeTriggerFn -> SendJSONFn -> HandleCustomMessageFn -> (Text -> Renamer) -> (Text -> IO ColumnTypeInfo) -> IO (DataSyncMessage -> IO ())
buildMessageHandler hasqlPool ensureRLSEnabled installTableChangeTriggers sendJSON handleCustomMessage renamer columnTypeLookup = do
Expand Down Expand Up @@ -512,7 +512,7 @@ encodePatchToSetSql ren columnTypes patch =

sqlQueryWithRLSAndTransactionId ::
( ?context :: ControllerContext
, Show (PrimaryKey (GetTableName CurrentUserRecord))
, Show (Id' (GetTableName CurrentUserRecord))
, HasNewSessionUrl CurrentUserRecord
, Typeable CurrentUserRecord
, HasField "id" CurrentUserRecord (Id' (GetTableName CurrentUserRecord))
Expand All @@ -531,7 +531,7 @@ sqlQueryWithRLSAndTransactionId pool Nothing statement = runSession pool (sqlQue
-- to return results (e.g. wrapped with 'wrapDynamicQuery').
sqlQueryWriteWithRLSAndTransactionId ::
( ?context :: ControllerContext
, Show (PrimaryKey (GetTableName CurrentUserRecord))
, Show (Id' (GetTableName CurrentUserRecord))
, HasNewSessionUrl CurrentUserRecord
, Typeable CurrentUserRecord
, HasField "id" CurrentUserRecord (Id' (GetTableName CurrentUserRecord))
Expand All @@ -546,7 +546,7 @@ sqlQueryWriteWithRLSAndTransactionId pool Nothing statement = runSession pool (s

sqlExecWithRLSAndTransactionId ::
( ?context :: ControllerContext
, Show (PrimaryKey (GetTableName CurrentUserRecord))
, Show (Id' (GetTableName CurrentUserRecord))
, HasNewSessionUrl CurrentUserRecord
, Typeable CurrentUserRecord
, HasField "id" CurrentUserRecord (Id' (GetTableName CurrentUserRecord))
Expand Down
2 changes: 1 addition & 1 deletion ihp-datasync/IHP/DataSync/REST/Controller.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import qualified Hasql.Decoders as Decoders
import qualified Hasql.Statement as Hasql

instance (
Show (PrimaryKey (GetTableName CurrentUserRecord))
Show (Id' (GetTableName CurrentUserRecord))
, HasNewSessionUrl CurrentUserRecord
, Typeable CurrentUserRecord
, HasField "id" CurrentUserRecord (Id' (GetTableName CurrentUserRecord))
Expand Down
18 changes: 9 additions & 9 deletions ihp-datasync/IHP/DataSync/RowLevelSecurity.hs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ ensureRLSEnabledSession table = do
-- (e.g. after a manual @BEGIN@).
setRLSConfigSession ::
( ?context :: ControllerContext
, Show (PrimaryKey (GetTableName CurrentUserRecord))
, Show (Id' (GetTableName CurrentUserRecord))
, HasNewSessionUrl CurrentUserRecord
, Typeable CurrentUserRecord
, HasField "id" CurrentUserRecord (Id' (GetTableName CurrentUserRecord))
Expand All @@ -71,7 +71,7 @@ setRLSConfigSession = Session.statement (Role.authenticatedRole, encodedUserId)

sqlQueryWithRLSSession ::
( ?context :: ControllerContext
, Show (PrimaryKey (GetTableName CurrentUserRecord))
, Show (Id' (GetTableName CurrentUserRecord))
, HasNewSessionUrl CurrentUserRecord
, Typeable CurrentUserRecord
, HasField "id" CurrentUserRecord (Id' (GetTableName CurrentUserRecord))
Expand All @@ -92,7 +92,7 @@ sqlQueryWithRLSSession statement =
-- to return results (e.g. wrapped with 'wrapDynamicQuery').
sqlQueryWriteWithRLSSession ::
( ?context :: ControllerContext
, Show (PrimaryKey (GetTableName CurrentUserRecord))
, Show (Id' (GetTableName CurrentUserRecord))
, HasNewSessionUrl CurrentUserRecord
, Typeable CurrentUserRecord
, HasField "id" CurrentUserRecord (Id' (GetTableName CurrentUserRecord))
Expand All @@ -109,7 +109,7 @@ sqlQueryWriteWithRLSSession statement =

sqlExecWithRLSSession ::
( ?context :: ControllerContext
, Show (PrimaryKey (GetTableName CurrentUserRecord))
, Show (Id' (GetTableName CurrentUserRecord))
, HasNewSessionUrl CurrentUserRecord
, Typeable CurrentUserRecord
, HasField "id" CurrentUserRecord (Id' (GetTableName CurrentUserRecord))
Expand All @@ -126,7 +126,7 @@ sqlExecWithRLSSession statement =

sqlQueryScalarWithRLSSession ::
( ?context :: ControllerContext
, Show (PrimaryKey (GetTableName CurrentUserRecord))
, Show (Id' (GetTableName CurrentUserRecord))
, HasNewSessionUrl CurrentUserRecord
, Typeable CurrentUserRecord
, HasField "id" CurrentUserRecord (Id' (GetTableName CurrentUserRecord))
Expand All @@ -145,7 +145,7 @@ sqlQueryScalarWithRLSSession statement =

sqlQueryWithRLS ::
( ?context :: ControllerContext
, Show (PrimaryKey (GetTableName CurrentUserRecord))
, Show (Id' (GetTableName CurrentUserRecord))
, HasNewSessionUrl CurrentUserRecord
, Typeable CurrentUserRecord
, HasField "id" CurrentUserRecord (Id' (GetTableName CurrentUserRecord))
Expand All @@ -159,7 +159,7 @@ sqlQueryWithRLS pool statement = runSession pool (sqlQueryWithRLSSession stateme
-- to return results (e.g. wrapped with 'wrapDynamicQuery').
sqlQueryWriteWithRLS ::
( ?context :: ControllerContext
, Show (PrimaryKey (GetTableName CurrentUserRecord))
, Show (Id' (GetTableName CurrentUserRecord))
, HasNewSessionUrl CurrentUserRecord
, Typeable CurrentUserRecord
, HasField "id" CurrentUserRecord (Id' (GetTableName CurrentUserRecord))
Expand All @@ -169,7 +169,7 @@ sqlQueryWriteWithRLS pool statement = runSession pool (sqlQueryWriteWithRLSSessi

sqlExecWithRLS ::
( ?context :: ControllerContext
, Show (PrimaryKey (GetTableName CurrentUserRecord))
, Show (Id' (GetTableName CurrentUserRecord))
, HasNewSessionUrl CurrentUserRecord
, Typeable CurrentUserRecord
, HasField "id" CurrentUserRecord (Id' (GetTableName CurrentUserRecord))
Expand All @@ -179,7 +179,7 @@ sqlExecWithRLS pool statement = runSession pool (sqlExecWithRLSSession statement

sqlQueryScalarWithRLS ::
( ?context :: ControllerContext
, Show (PrimaryKey (GetTableName CurrentUserRecord))
, Show (Id' (GetTableName CurrentUserRecord))
, HasNewSessionUrl CurrentUserRecord
, Typeable CurrentUserRecord
, HasField "id" CurrentUserRecord (Id' (GetTableName CurrentUserRecord))
Expand Down
13 changes: 10 additions & 3 deletions ihp-datasync/Test/DataSync/DataSyncIntegrationSpec.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE UndecidableInstances, DerivingStrategies, GeneralizedNewtypeDeriving #-}
module Test.DataSync.DataSyncIntegrationSpec where

import Test.Hspec
Expand All @@ -18,7 +18,7 @@ import IHP.RequestVault (pgListenerVaultKey, frameworkConfigVaultKey)
import IHP.Controller.Context (newControllerContext, putContext, freeze)
import IHP.LoginSupport.Types (HasNewSessionUrl(..), CurrentUserRecord)
import qualified IHP.ModelSupport as ModelSupport
import IHP.ModelSupport.Types (Id'(..), PrimaryKey)
import IHP.ModelSupport.Types (PrimaryKey)
import qualified IHP.PGListener as PGListener
import IHP.FrameworkConfig (buildFrameworkConfig)
import IHP.FrameworkConfig.Types
Expand All @@ -37,7 +37,14 @@ import Control.Concurrent (threadDelay)
import qualified IHP.Log as Log

-- | Define CurrentUserRecord for this test module
data TestUser = TestUser { id :: Id' "test_users" }
newtype TestUserId = TestUserId UUID
deriving newtype (Eq, Ord, Show)
type instance Id' "test_users" = TestUserId
instance IdNewtype TestUserId UUID where
toId = TestUserId
fromId (TestUserId x) = x

data TestUser = TestUser { id :: TestUserId }
deriving (Show, Typeable)

type instance CurrentUserRecord = TestUser
Expand Down
Loading