add docs, test for rejected user

This commit is contained in:
Kendall Garner 2026-07-20 21:39:59 -07:00
parent 6b72ce3c0d
commit cd2643f83e
No known key found for this signature in database
GPG Key ID: 9355F387FE765C94
5 changed files with 200 additions and 30 deletions

View File

@ -2,26 +2,46 @@ package host
import "context"
// ScrobbleList is a list of scrobbles, plus an optional timestamp
// that can be used as a cursor for the next fetch
type ScrobbleList struct {
Scrobbles []ScrobbleRef `json:"scrobbles"`
NextTimestamp *int64 `json:"nextTimestamp,omitempty"`
// The scrobbles in a given range
Scrobbles []ScrobbleRef `json:"scrobbles"`
// If additional items are available, the timestamp of the next scrobble to fetch
NextTimestamp *int64 `json:"nextTimestamp,omitempty"`
}
// ScrobbleRef represents one instance of a scrobble (instance id, file id, submission time)
type ScrobbleRef struct {
ID int64 `json:"id"`
MediaFileID string `json:"mediaFileId"`
SubmissionTime int64 `json:"submissionTime"`
// The ID of the scrobble. Useful if duplicate scrobbles happen for the same time
ID int64 `json:"id"`
// The ID of the MediaFile submitted at this time
MediaFileID string `json:"mediaFileId"`
// The UNIX timestamp this scrobble was submitted
SubmissionTime int64 `json:"submissionTime"`
}
// ScrobbleOptions carries optional parameters for retrieving user scrobbles
type ScrobbleOptions struct {
// The starting unix timestamp to query for scrobbles (inclusive).
// If not specified, start from the first scrobble
FromTimestamp *int64 `json:"fromTimestamp,omitempty"`
ToTimestamp *int64 `json:"toTimestamp,omitempty"`
MaxItems int `json:"maxItems"`
// The ending unix timestamp to query for scrobbles (inclusive).
// If not specified, go up to the last scrobble
ToTimestamp *int64 `json:"toTimestamp,omitempty"`
// The maximum number of items to retrieve. This is capped at 5000, the
// default if not specified
MaxItems int `json:"maxItems"`
}
// ScrobbleCountOptions carries optional parameters for counting user scrobbles
type ScrobbleCountOptions struct {
// The starting unix timestamp to query for scrobbles (inclusive).
// If not specified, start from the first scrobble
FromTimestamp *int64 `json:"fromTimestamp,omitempty"`
ToTimestamp *int64 `json:"toTimestamp,omitempty"`
// The ending unix timestamp to query for scrobbles (inclusive).
// If not specified, go up to the last scrobble
ToTimestamp *int64 `json:"toTimestamp,omitempty"`
}
// ScrobbleRetrieverService allows a plugin to retrieve scrobbles for one or more authorized users.
@ -30,17 +50,43 @@ type ScrobbleCountOptions struct {
//
//nd:hostservice name=ScrobbleRetriever permission=scrobbleRetriever
type ScrobbleRetrieverService interface {
// GetFirstTimestamp returns the unix timestamp of the oldest scrobble for the user
// GetFirstTimestamp returns the unix timestamp of the oldest scrobble for the user.
// If the user has no scrobbles, returns nil
//nd:hostfunc
GetFirstTimestamp(ctx context.Context, username string) (*int64, error)
// GetLastTimestamp returns the unix timestamp of the most recent scrobble for the user
// If the user has no scrobbles, return nil
//nd:hostfunc
GetLastTimestamp(ctx context.Context, username string) (*int64, error)
// GetScrobbles returns scrobbles for a user.
//
// Parameters:
// - username: the user to query for scrobbles
// - options.FromTimestamp: If specified, the first UNIX timestamp to start fetching scrobbles (inclusive). Otherwise, start from the first scrobble
// - options.ToTimestamp: If specified, the last UNIX timestamp to fetch (inclusive). Otherwise, end at the last scrobble
// - options.MaxItems: The maximum number of items to retrieve. The maximum value (and default) if not specified is 5000
//
// Returns:
// - Scrobbles: A list of scrobbles within the constraints given (if any). The order
// of the items depends on the options: if ToTimestamp is specified AND
// FromTImestamp is not specified, the order is in descending submission time.
// Otherwise, the scrobbles are returned in ascending submission time.
// - NextTimestamp: If there are additional items to retrieve in the range, the timestamp
// of the next scrobble that would be retrieved in the order (asc or desc)
//nd:hostfunc
GetScrobbles(ctx context.Context, username string, options ScrobbleOptions) (*ScrobbleList, error)
// GetScrobbleCount returns the number of scrobbles for a user in a given range
//
// Parameters:
// - username: the user to query for scrobbles
// - options.FromTimestamp: If specified, the first UNIX timestamp to start fetching scrobbles (inclusive). Otherwise, start from the first scrobble
// - options.ToTimestamp: If specified, the last UNIX timestamp to fetch (inclusive). Otherwise, end at the last scrobble
//
// Returns:
// - the number of scrobbles in the given range, or 0
//nd:hostfunc
GetScrobbleCount(ctx context.Context, username string, options ScrobbleCountOptions) (int64, error)
}

View File

@ -130,16 +130,58 @@ var _ = Describe("Scrobbble Retriever Host Function", Ordered, func() {
})
})
Describe("no items", func() {
var plugin *plugin
var plugin *plugin
BeforeEach(func() {
manager.mu.RLock()
plugin = manager.plugins["test-scrobble-retriever"]
manager.mu.RUnlock()
Expect(plugin).ToNot(BeNil())
BeforeEach(func() {
manager.mu.RLock()
plugin = manager.plugins["test-scrobble-retriever"]
manager.mu.RUnlock()
Expect(plugin).ToNot(BeNil())
})
Describe("not authorized", func() {
It("rejects first timestamp", func() {
instance, err := plugin.instance(GinkgoT().Context())
Expect(err).ToNot(HaveOccurred())
defer instance.Close(GinkgoT().Context())
exit, _, err := instance.Call("call_get_first_timestamp", []byte("baduser"))
Expect(err).To(HaveOccurred())
Expect(exit).To(Equal(uint32(1)))
})
It("rejects last timestamp", func() {
instance, err := plugin.instance(GinkgoT().Context())
Expect(err).ToNot(HaveOccurred())
defer instance.Close(GinkgoT().Context())
exit, _, err := instance.Call("call_get_last_timestamp", []byte("baduser"))
Expect(err).To(HaveOccurred())
Expect(exit).To(Equal(uint32(1)))
})
It("rejects scrobbles", func() {
instance, err := plugin.instance(GinkgoT().Context())
Expect(err).ToNot(HaveOccurred())
defer instance.Close(GinkgoT().Context())
exit, _, err := instance.Call("call_get_scrobbles", []byte(`{"username":"baduser"}`))
Expect(err).To(HaveOccurred())
Expect(exit).To(Equal(uint32(1)))
})
It("rejects scrobbles", func() {
instance, err := plugin.instance(GinkgoT().Context())
Expect(err).ToNot(HaveOccurred())
defer instance.Close(GinkgoT().Context())
exit, _, err := instance.Call("call_get_scrobbles_count", []byte(`{"username":"baduser"}`))
Expect(err).To(HaveOccurred())
Expect(exit).To(Equal(uint32(1)))
})
})
Describe("no items", func() {
It("calls get first timestamp", func() {
instance, err := plugin.instance(GinkgoT().Context())
Expect(err).ToNot(HaveOccurred())
@ -189,8 +231,6 @@ var _ = Describe("Scrobbble Retriever Host Function", Ordered, func() {
})
Describe("with items", func() {
var plugin *plugin
p := func(val int64) *int64 {
return &val
}
@ -210,13 +250,6 @@ var _ = Describe("Scrobbble Retriever Host Function", Ordered, func() {
}
})
BeforeEach(func() {
manager.mu.RLock()
plugin = manager.plugins["test-scrobble-retriever"]
manager.mu.RUnlock()
Expect(plugin).ToNot(BeNil())
})
It("calls get first timestamp", func() {
instance, err := plugin.instance(GinkgoT().Context())
Expect(err).ToNot(HaveOccurred())

View File

@ -15,18 +15,22 @@ import (
)
// ScrobbleCountOptions represents the ScrobbleCountOptions data structure.
// ScrobbleCountOptions carries optional parameters for counting user scrobbles
type ScrobbleCountOptions struct {
FromTimestamp *int64 `json:"fromTimestamp"`
ToTimestamp *int64 `json:"toTimestamp"`
}
// ScrobbleList represents the ScrobbleList data structure.
// ScrobbleList is a list of scrobbles, plus an optional timestamp
// that can be used as a cursor for the next fetch
type ScrobbleList struct {
Scrobbles []ScrobbleRef `json:"scrobbles"`
NextTimestamp *int64 `json:"nextTimestamp"`
}
// ScrobbleOptions represents the ScrobbleOptions data structure.
// ScrobbleOptions carries optional parameters for retrieving user scrobbles
type ScrobbleOptions struct {
FromTimestamp *int64 `json:"fromTimestamp"`
ToTimestamp *int64 `json:"toTimestamp"`
@ -34,6 +38,7 @@ type ScrobbleOptions struct {
}
// ScrobbleRef represents the ScrobbleRef data structure.
// ScrobbleRef represents one instance of a scrobble (instance id, file id, submission time)
type ScrobbleRef struct {
ID int64 `json:"id"`
MediaFileID string `json:"mediaFileId"`
@ -99,7 +104,8 @@ type scrobbleRetrieverGetScrobbleCountResponse struct {
}
// ScrobbleRetrieverGetFirstTimestamp calls the scrobbleretriever_getfirsttimestamp host function.
// GetFirstTimestamp returns the unix timestamp of the oldest scrobble for the user
// GetFirstTimestamp returns the unix timestamp of the oldest scrobble for the user.
// If the user has no scrobbles, returns nil
func ScrobbleRetrieverGetFirstTimestamp(username string) (*int64, error) {
// Marshal request to JSON
req := scrobbleRetrieverGetFirstTimestampRequest{
@ -135,6 +141,7 @@ func ScrobbleRetrieverGetFirstTimestamp(username string) (*int64, error) {
// ScrobbleRetrieverGetLastTimestamp calls the scrobbleretriever_getlasttimestamp host function.
// GetLastTimestamp returns the unix timestamp of the most recent scrobble for the user
// If the user has no scrobbles, return nil
func ScrobbleRetrieverGetLastTimestamp(username string) (*int64, error) {
// Marshal request to JSON
req := scrobbleRetrieverGetLastTimestampRequest{
@ -169,6 +176,21 @@ func ScrobbleRetrieverGetLastTimestamp(username string) (*int64, error) {
}
// ScrobbleRetrieverGetScrobbles calls the scrobbleretriever_getscrobbles host function.
// GetScrobbles returns scrobbles for a user.
//
// Parameters:
// - username: the user to query for scrobbles
// - options.FromTimestamp: If specified, the first UNIX timestamp to start fetching scrobbles (inclusive). Otherwise, start from the first scrobble
// - options.ToTimestamp: If specified, the last UNIX timestamp to fetch (inclusive). Otherwise, end at the last scrobble
// - options.MaxItems: The maximum number of items to retrieve. The maximum value (and default) if not specified is 5000
//
// Returns:
// - Scrobbles: A list of scrobbles within the constraints given (if any). The order
// of the items depends on the options: if ToTimestamp is specified AND
// FromTImestamp is not specified, the order is in descending submission time.
// Otherwise, the scrobbles are returned in ascending submission time.
// - NextTimestamp: If there are additional items to retrieve in the range, the timestamp
// of the next scrobble that would be retrieved in the order (asc or desc)
func ScrobbleRetrieverGetScrobbles(username string, options ScrobbleOptions) (*ScrobbleList, error) {
// Marshal request to JSON
req := scrobbleRetrieverGetScrobblesRequest{
@ -204,6 +226,15 @@ func ScrobbleRetrieverGetScrobbles(username string, options ScrobbleOptions) (*S
}
// ScrobbleRetrieverGetScrobbleCount calls the scrobbleretriever_getscrobblecount host function.
// GetScrobbleCount returns the number of scrobbles for a user in a given range
//
// Parameters:
// - username: the user to query for scrobbles
// - options.FromTimestamp: If specified, the first UNIX timestamp to start fetching scrobbles (inclusive). Otherwise, start from the first scrobble
// - options.ToTimestamp: If specified, the last UNIX timestamp to fetch (inclusive). Otherwise, end at the last scrobble
//
// Returns:
// - the number of scrobbles in the given range, or 0
func ScrobbleRetrieverGetScrobbleCount(username string, options ScrobbleCountOptions) (int64, error) {
// Marshal request to JSON
req := scrobbleRetrieverGetScrobbleCountRequest{

View File

@ -13,18 +13,22 @@ import (
)
// ScrobbleCountOptions represents the ScrobbleCountOptions data structure.
// ScrobbleCountOptions carries optional parameters for counting user scrobbles
type ScrobbleCountOptions struct {
FromTimestamp *int64 `json:"fromTimestamp"`
ToTimestamp *int64 `json:"toTimestamp"`
}
// ScrobbleList represents the ScrobbleList data structure.
// ScrobbleList is a list of scrobbles, plus an optional timestamp
// that can be used as a cursor for the next fetch
type ScrobbleList struct {
Scrobbles []ScrobbleRef `json:"scrobbles"`
NextTimestamp *int64 `json:"nextTimestamp"`
}
// ScrobbleOptions represents the ScrobbleOptions data structure.
// ScrobbleOptions carries optional parameters for retrieving user scrobbles
type ScrobbleOptions struct {
FromTimestamp *int64 `json:"fromTimestamp"`
ToTimestamp *int64 `json:"toTimestamp"`
@ -32,6 +36,7 @@ type ScrobbleOptions struct {
}
// ScrobbleRef represents the ScrobbleRef data structure.
// ScrobbleRef represents one instance of a scrobble (instance id, file id, submission time)
type ScrobbleRef struct {
ID int64 `json:"id"`
MediaFileID string `json:"mediaFileId"`
@ -54,7 +59,8 @@ func (m *mockScrobbleRetrieverService) GetFirstTimestamp(username string) (*int6
}
// ScrobbleRetrieverGetFirstTimestamp delegates to the mock instance.
// GetFirstTimestamp returns the unix timestamp of the oldest scrobble for the user
// GetFirstTimestamp returns the unix timestamp of the oldest scrobble for the user.
// If the user has no scrobbles, returns nil
func ScrobbleRetrieverGetFirstTimestamp(username string) (*int64, error) {
return ScrobbleRetrieverMock.GetFirstTimestamp(username)
}
@ -67,6 +73,7 @@ func (m *mockScrobbleRetrieverService) GetLastTimestamp(username string) (*int64
// ScrobbleRetrieverGetLastTimestamp delegates to the mock instance.
// GetLastTimestamp returns the unix timestamp of the most recent scrobble for the user
// If the user has no scrobbles, return nil
func ScrobbleRetrieverGetLastTimestamp(username string) (*int64, error) {
return ScrobbleRetrieverMock.GetLastTimestamp(username)
}
@ -78,6 +85,21 @@ func (m *mockScrobbleRetrieverService) GetScrobbles(username string, options Scr
}
// ScrobbleRetrieverGetScrobbles delegates to the mock instance.
// GetScrobbles returns scrobbles for a user.
//
// Parameters:
// - username: the user to query for scrobbles
// - options.FromTimestamp: If specified, the first UNIX timestamp to start fetching scrobbles (inclusive). Otherwise, start from the first scrobble
// - options.ToTimestamp: If specified, the last UNIX timestamp to fetch (inclusive). Otherwise, end at the last scrobble
// - options.MaxItems: The maximum number of items to retrieve. The maximum value (and default) if not specified is 5000
//
// Returns:
// - Scrobbles: A list of scrobbles within the constraints given (if any). The order
// of the items depends on the options: if ToTimestamp is specified AND
// FromTImestamp is not specified, the order is in descending submission time.
// Otherwise, the scrobbles are returned in ascending submission time.
// - NextTimestamp: If there are additional items to retrieve in the range, the timestamp
// of the next scrobble that would be retrieved in the order (asc or desc)
func ScrobbleRetrieverGetScrobbles(username string, options ScrobbleOptions) (*ScrobbleList, error) {
return ScrobbleRetrieverMock.GetScrobbles(username, options)
}
@ -89,6 +111,15 @@ func (m *mockScrobbleRetrieverService) GetScrobbleCount(username string, options
}
// ScrobbleRetrieverGetScrobbleCount delegates to the mock instance.
// GetScrobbleCount returns the number of scrobbles for a user in a given range
//
// Parameters:
// - username: the user to query for scrobbles
// - options.FromTimestamp: If specified, the first UNIX timestamp to start fetching scrobbles (inclusive). Otherwise, start from the first scrobble
// - options.ToTimestamp: If specified, the last UNIX timestamp to fetch (inclusive). Otherwise, end at the last scrobble
//
// Returns:
// - the number of scrobbles in the given range, or 0
func ScrobbleRetrieverGetScrobbleCount(username string, options ScrobbleCountOptions) (int64, error) {
return ScrobbleRetrieverMock.GetScrobbleCount(username, options)
}

View File

@ -6,6 +6,7 @@
use extism_pdk::*;
use serde::{Deserialize, Serialize};
/// ScrobbleCountOptions carries optional parameters for counting user scrobbles
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScrobbleCountOptions {
@ -15,6 +16,8 @@ pub struct ScrobbleCountOptions {
pub to_timestamp: Option<i64>,
}
/// ScrobbleList is a list of scrobbles, plus an optional timestamp
/// that can be used as a cursor for the next fetch
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScrobbleList {
@ -23,6 +26,7 @@ pub struct ScrobbleList {
pub next_timestamp: Option<i64>,
}
/// ScrobbleOptions carries optional parameters for retrieving user scrobbles
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScrobbleOptions {
@ -33,6 +37,7 @@ pub struct ScrobbleOptions {
pub max_items: i32,
}
/// ScrobbleRef represents one instance of a scrobble (instance id, file id, submission time)
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScrobbleRef {
@ -111,7 +116,8 @@ extern "ExtismHost" {
fn scrobbleretriever_getscrobblecount(input: Json<ScrobbleRetrieverGetScrobbleCountRequest>) -> Json<ScrobbleRetrieverGetScrobbleCountResponse>;
}
/// GetFirstTimestamp returns the unix timestamp of the oldest scrobble for the user
/// GetFirstTimestamp returns the unix timestamp of the oldest scrobble for the user.
/// If the user has no scrobbles, returns nil
///
/// # Arguments
/// * `username` - String parameter.
@ -136,6 +142,7 @@ pub fn get_first_timestamp(username: &str) -> Result<Option<i64>, Error> {
}
/// GetLastTimestamp returns the unix timestamp of the most recent scrobble for the user
/// If the user has no scrobbles, return nil
///
/// # Arguments
/// * `username` - String parameter.
@ -159,7 +166,21 @@ pub fn get_last_timestamp(username: &str) -> Result<Option<i64>, Error> {
Ok(response.0.result)
}
/// Calls the scrobbleretriever_getscrobbles host function.
/// GetScrobbles returns scrobbles for a user.
///
/// Parameters:
/// - username: the user to query for scrobbles
/// - options.FromTimestamp: If specified, the first UNIX timestamp to start fetching scrobbles (inclusive). Otherwise, start from the first scrobble
/// - options.ToTimestamp: If specified, the last UNIX timestamp to fetch (inclusive). Otherwise, end at the last scrobble
/// - options.MaxItems: The maximum number of items to retrieve. The maximum value (and default) if not specified is 5000
///
/// Returns:
/// - Scrobbles: A list of scrobbles within the constraints given (if any). The order
/// of the items depends on the options: if ToTimestamp is specified AND
/// FromTImestamp is not specified, the order is in descending submission time.
/// Otherwise, the scrobbles are returned in ascending submission time.
/// - NextTimestamp: If there are additional items to retrieve in the range, the timestamp
/// of the next scrobble that would be retrieved in the order (asc or desc)
///
/// # Arguments
/// * `username` - String parameter.
@ -185,7 +206,15 @@ pub fn get_scrobbles(username: &str, options: ScrobbleOptions) -> Result<Option<
Ok(response.0.result)
}
/// Calls the scrobbleretriever_getscrobblecount host function.
/// GetScrobbleCount returns the number of scrobbles for a user in a given range
///
/// Parameters:
/// - username: the user to query for scrobbles
/// - options.FromTimestamp: If specified, the first UNIX timestamp to start fetching scrobbles (inclusive). Otherwise, start from the first scrobble
/// - options.ToTimestamp: If specified, the last UNIX timestamp to fetch (inclusive). Otherwise, end at the last scrobble
///
/// Returns:
/// - the number of scrobbles in the given range, or 0
///
/// # Arguments
/// * `username` - String parameter.