Filter consent records by version number
Replace the opaque cookieBannerVersionId filter with an integer version filter. The SQL filter now resolves the version number via a subquery against cookie_banner_versions. Also fix the CookieBannerVersion resolver on consent records to load the full version from the database instead of returning a stub with only the ID set (which caused the version to always display as 0). Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -64,7 +64,7 @@ const consentRecordsFragment = graphql`
|
|||||||
last: { type: "Int", defaultValue: null }
|
last: { type: "Int", defaultValue: null }
|
||||||
action: { type: "CookieConsentAction", defaultValue: null }
|
action: { type: "CookieConsentAction", defaultValue: null }
|
||||||
visitorId: { type: "String", defaultValue: null }
|
visitorId: { type: "String", defaultValue: null }
|
||||||
cookieBannerVersionId: { type: "ID", defaultValue: null }
|
version: { type: "Int", defaultValue: null }
|
||||||
) {
|
) {
|
||||||
consentRecords(
|
consentRecords(
|
||||||
first: $first
|
first: $first
|
||||||
@@ -75,7 +75,7 @@ const consentRecordsFragment = graphql`
|
|||||||
filter: {
|
filter: {
|
||||||
action: $action
|
action: $action
|
||||||
visitorId: $visitorId
|
visitorId: $visitorId
|
||||||
cookieBannerVersionId: $cookieBannerVersionId
|
version: $version
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@connection(
|
@connection(
|
||||||
@@ -109,7 +109,8 @@ export default function CookieBannerConsentRecordsPage({
|
|||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
const [actionFilter, setActionFilter] = useState<CookieConsentAction | null>(null);
|
const [actionFilter, setActionFilter] = useState<CookieConsentAction | null>(null);
|
||||||
const [visitorIdFilter, setVisitorIdFilter] = useState<string>("");
|
const [visitorIdFilter, setVisitorIdFilter] = useState<string>("");
|
||||||
const [versionIdFilter, setVersionIdFilter] = useState<string>("");
|
const [versionFilter, setVersionFilter] = useState<string>("");
|
||||||
|
const [versionError, setVersionError] = useState(false);
|
||||||
|
|
||||||
const { data: fragmentData, ...pagination } = usePaginationFragment<
|
const { data: fragmentData, ...pagination } = usePaginationFragment<
|
||||||
CookieBannerConsentRecordsPageRefetchQuery,
|
CookieBannerConsentRecordsPageRefetchQuery,
|
||||||
@@ -124,7 +125,7 @@ export default function CookieBannerConsentRecordsPage({
|
|||||||
{
|
{
|
||||||
action: actionFilter,
|
action: actionFilter,
|
||||||
visitorId: visitorIdFilter || null,
|
visitorId: visitorIdFilter || null,
|
||||||
cookieBannerVersionId: versionIdFilter || null,
|
version: versionFilter ? parseInt(versionFilter, 10) : null,
|
||||||
...overrides,
|
...overrides,
|
||||||
},
|
},
|
||||||
{ fetchPolicy: "network-only" },
|
{ fetchPolicy: "network-only" },
|
||||||
@@ -142,8 +143,13 @@ export default function CookieBannerConsentRecordsPage({
|
|||||||
refetchFilters({ visitorId: visitorIdFilter || null });
|
refetchFilters({ visitorId: visitorIdFilter || null });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleVersionIdSubmit = () => {
|
const handleVersionSubmit = () => {
|
||||||
refetchFilters({ cookieBannerVersionId: versionIdFilter || null });
|
if (versionFilter && !/^\d+$/.test(versionFilter)) {
|
||||||
|
setVersionError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setVersionError(false);
|
||||||
|
refetchFilters({ version: versionFilter ? parseInt(versionFilter, 10) : null });
|
||||||
};
|
};
|
||||||
|
|
||||||
const refetchWithFilters: ComponentProps<typeof SortableTable>["refetch"] = ({ order }) => {
|
const refetchWithFilters: ComponentProps<typeof SortableTable>["refetch"] = ({ order }) => {
|
||||||
@@ -151,7 +157,7 @@ export default function CookieBannerConsentRecordsPage({
|
|||||||
order: { direction: order.direction, field: order.field as CookieConsentRecordOrderField },
|
order: { direction: order.direction, field: order.field as CookieConsentRecordOrderField },
|
||||||
action: actionFilter,
|
action: actionFilter,
|
||||||
visitorId: visitorIdFilter || null,
|
visitorId: visitorIdFilter || null,
|
||||||
cookieBannerVersionId: versionIdFilter || null,
|
version: versionFilter ? parseInt(versionFilter, 10) : null,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -177,11 +183,15 @@ export default function CookieBannerConsentRecordsPage({
|
|||||||
className="w-48"
|
className="w-48"
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
placeholder={__("Version ID")}
|
placeholder={__("Banner version")}
|
||||||
value={versionIdFilter}
|
value={versionFilter}
|
||||||
onChange={e => setVersionIdFilter(e.target.value)}
|
invalid={versionError}
|
||||||
onKeyDown={e => e.key === "Enter" && handleVersionIdSubmit()}
|
onChange={(e) => {
|
||||||
onBlur={handleVersionIdSubmit}
|
setVersionFilter(e.target.value);
|
||||||
|
setVersionError(false);
|
||||||
|
}}
|
||||||
|
onKeyDown={e => e.key === "Enter" && handleVersionSubmit()}
|
||||||
|
onBlur={handleVersionSubmit}
|
||||||
className="w-48"
|
className="w-48"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -198,7 +208,7 @@ export default function CookieBannerConsentRecordsPage({
|
|||||||
<Tr>
|
<Tr>
|
||||||
<Th>{__("Visitor ID")}</Th>
|
<Th>{__("Visitor ID")}</Th>
|
||||||
<Th>{__("Action")}</Th>
|
<Th>{__("Action")}</Th>
|
||||||
<Th>{__("Version")}</Th>
|
<Th>{__("Banner Version")}</Th>
|
||||||
<Th>{__("IP Address")}</Th>
|
<Th>{__("IP Address")}</Th>
|
||||||
<Th>{__("SDK Version")}</Th>
|
<Th>{__("SDK Version")}</Th>
|
||||||
<Th>{__("Consent Data")}</Th>
|
<Th>{__("Consent Data")}</Th>
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ export function ConsentRecordRow({ recordKey }: ConsentRecordRowProps) {
|
|||||||
{record.cookieBannerVersion
|
{record.cookieBannerVersion
|
||||||
? (
|
? (
|
||||||
<span className="font-mono text-sm">
|
<span className="font-mono text-sm">
|
||||||
v
|
|
||||||
{record.cookieBannerVersion.version}
|
{record.cookieBannerVersion.version}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,25 +16,23 @@ package coredata
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
|
|
||||||
"go.probo.inc/probo/pkg/gid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CookieConsentRecordFilter struct {
|
type CookieConsentRecordFilter struct {
|
||||||
action *CookieConsentAction
|
action *CookieConsentAction
|
||||||
visitorID *string
|
visitorID *string
|
||||||
cookieBannerVersionID *gid.GID
|
version *int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCookieConsentRecordFilter(
|
func NewCookieConsentRecordFilter(
|
||||||
action *CookieConsentAction,
|
action *CookieConsentAction,
|
||||||
visitorID *string,
|
visitorID *string,
|
||||||
cookieBannerVersionID *gid.GID,
|
version *int,
|
||||||
) *CookieConsentRecordFilter {
|
) *CookieConsentRecordFilter {
|
||||||
return &CookieConsentRecordFilter{
|
return &CookieConsentRecordFilter{
|
||||||
action: action,
|
action: action,
|
||||||
visitorID: visitorID,
|
visitorID: visitorID,
|
||||||
cookieBannerVersionID: cookieBannerVersionID,
|
version: version,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,8 +56,12 @@ AND
|
|||||||
AND
|
AND
|
||||||
(
|
(
|
||||||
CASE
|
CASE
|
||||||
WHEN @filter_cookie_banner_version_id::text IS NOT NULL THEN
|
WHEN @filter_version::int IS NOT NULL THEN
|
||||||
cookie_banner_version_id = @filter_cookie_banner_version_id
|
cookie_banner_version_id = (
|
||||||
|
SELECT id FROM cookie_banner_versions
|
||||||
|
WHERE cookie_banner_id = cookie_consent_records.cookie_banner_id
|
||||||
|
AND version = @filter_version
|
||||||
|
)
|
||||||
ELSE TRUE
|
ELSE TRUE
|
||||||
END
|
END
|
||||||
)`
|
)`
|
||||||
@@ -67,9 +69,9 @@ AND
|
|||||||
|
|
||||||
func (f *CookieConsentRecordFilter) SQLArguments() pgx.StrictNamedArgs {
|
func (f *CookieConsentRecordFilter) SQLArguments() pgx.StrictNamedArgs {
|
||||||
args := pgx.StrictNamedArgs{
|
args := pgx.StrictNamedArgs{
|
||||||
"filter_action": nil,
|
"filter_action": nil,
|
||||||
"filter_visitor_id": nil,
|
"filter_visitor_id": nil,
|
||||||
"filter_cookie_banner_version_id": nil,
|
"filter_version": nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.action != nil {
|
if f.action != nil {
|
||||||
@@ -80,8 +82,8 @@ func (f *CookieConsentRecordFilter) SQLArguments() pgx.StrictNamedArgs {
|
|||||||
args["filter_visitor_id"] = *f.visitorID
|
args["filter_visitor_id"] = *f.visitorID
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.cookieBannerVersionID != nil {
|
if f.version != nil {
|
||||||
args["filter_cookie_banner_version_id"] = f.cookieBannerVersionID.String()
|
args["filter_version"] = *f.version
|
||||||
}
|
}
|
||||||
|
|
||||||
return args
|
return args
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"go.gearno.de/kit/log"
|
"go.gearno.de/kit/log"
|
||||||
"go.probo.inc/probo/pkg/cookiebanner"
|
"go.probo.inc/probo/pkg/cookiebanner"
|
||||||
"go.probo.inc/probo/pkg/coredata"
|
"go.probo.inc/probo/pkg/coredata"
|
||||||
"go.probo.inc/probo/pkg/gid"
|
|
||||||
"go.probo.inc/probo/pkg/page"
|
"go.probo.inc/probo/pkg/page"
|
||||||
"go.probo.inc/probo/pkg/probo"
|
"go.probo.inc/probo/pkg/probo"
|
||||||
"go.probo.inc/probo/pkg/server/api/console/v1/schema"
|
"go.probo.inc/probo/pkg/server/api/console/v1/schema"
|
||||||
@@ -144,17 +143,17 @@ func (r *cookieBannerResolver) ConsentRecords(ctx context.Context, obj *types.Co
|
|||||||
scope := coredata.NewScopeFromObjectID(obj.ID)
|
scope := coredata.NewScopeFromObjectID(obj.ID)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
action *coredata.CookieConsentAction
|
action *coredata.CookieConsentAction
|
||||||
visitorID *string
|
visitorID *string
|
||||||
cookieBannerVersionID *gid.GID
|
version *int
|
||||||
)
|
)
|
||||||
if filter != nil {
|
if filter != nil {
|
||||||
action = filter.Action
|
action = filter.Action
|
||||||
visitorID = filter.VisitorID
|
visitorID = filter.VisitorID
|
||||||
cookieBannerVersionID = filter.CookieBannerVersionID
|
version = filter.Version
|
||||||
}
|
}
|
||||||
|
|
||||||
coredataFilter := coredata.NewCookieConsentRecordFilter(action, visitorID, cookieBannerVersionID)
|
coredataFilter := coredata.NewCookieConsentRecordFilter(action, visitorID, version)
|
||||||
|
|
||||||
records, err := r.cookieBanner.ListCookieConsentRecordsForBanner(ctx, scope, obj.ID, cursor, coredataFilter)
|
records, err := r.cookieBanner.ListCookieConsentRecordsForBanner(ctx, scope, obj.ID, cursor, coredataFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ package console_v1
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"go.gearno.de/kit/log"
|
"go.gearno.de/kit/log"
|
||||||
|
"go.probo.inc/probo/pkg/cookiebanner"
|
||||||
"go.probo.inc/probo/pkg/coredata"
|
"go.probo.inc/probo/pkg/coredata"
|
||||||
"go.probo.inc/probo/pkg/probo"
|
"go.probo.inc/probo/pkg/probo"
|
||||||
"go.probo.inc/probo/pkg/server/api/console/v1/schema"
|
"go.probo.inc/probo/pkg/server/api/console/v1/schema"
|
||||||
@@ -23,7 +25,32 @@ func (r *cookieConsentRecordResolver) CookieBanner(ctx context.Context, obj *typ
|
|||||||
|
|
||||||
// CookieBannerVersion is the resolver for the cookieBannerVersion field.
|
// CookieBannerVersion is the resolver for the cookieBannerVersion field.
|
||||||
func (r *cookieConsentRecordResolver) CookieBannerVersion(ctx context.Context, obj *types.CookieConsentRecord) (*types.CookieBannerVersion, error) {
|
func (r *cookieConsentRecordResolver) CookieBannerVersion(ctx context.Context, obj *types.CookieConsentRecord) (*types.CookieBannerVersion, error) {
|
||||||
return obj.CookieBannerVersion, nil
|
if obj.CookieBannerVersion == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.authorize(ctx, obj.CookieBannerVersion.ID, probo.ActionCookieBannerVersionGet); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
scope := coredata.NewScopeFromObjectID(obj.CookieBannerVersion.ID)
|
||||||
|
|
||||||
|
version, err := r.cookieBanner.GetCookieBannerVersion(ctx, scope, obj.CookieBannerVersion.ID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, cookiebanner.ErrVersionNotFound) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
r.logger.ErrorCtx(ctx, "cannot get cookie banner version", log.Error(err))
|
||||||
|
return nil, gqlutils.Internal(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.CookieBannerVersion{
|
||||||
|
ID: version.ID,
|
||||||
|
Version: version.Version,
|
||||||
|
State: string(version.State),
|
||||||
|
CreatedAt: version.CreatedAt,
|
||||||
|
UpdatedAt: version.UpdatedAt,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TotalCount is the resolver for the totalCount field.
|
// TotalCount is the resolver for the totalCount field.
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ input CookieConsentRecordOrder
|
|||||||
input CookieConsentRecordFilter {
|
input CookieConsentRecordFilter {
|
||||||
action: CookieConsentAction
|
action: CookieConsentAction
|
||||||
visitorId: String
|
visitorId: String
|
||||||
cookieBannerVersionId: ID
|
version: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
type CookieConsentRecord implements Node {
|
type CookieConsentRecord implements Node {
|
||||||
|
|||||||
Reference in New Issue
Block a user