diff --git a/plugins/host/scrobble_retriever.go b/plugins/host/scrobble_retriever.go index 569dc8641..173989c04 100644 --- a/plugins/host/scrobble_retriever.go +++ b/plugins/host/scrobble_retriever.go @@ -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) } diff --git a/plugins/host_scrobbleretriever_test.go b/plugins/host_scrobbleretriever_test.go index 8175c7d06..031bda154 100644 --- a/plugins/host_scrobbleretriever_test.go +++ b/plugins/host_scrobbleretriever_test.go @@ -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()) diff --git a/plugins/pdk/go/host/nd_host_scrobbleretriever.go b/plugins/pdk/go/host/nd_host_scrobbleretriever.go index c73ff5073..efe981ac3 100644 --- a/plugins/pdk/go/host/nd_host_scrobbleretriever.go +++ b/plugins/pdk/go/host/nd_host_scrobbleretriever.go @@ -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{ diff --git a/plugins/pdk/go/host/nd_host_scrobbleretriever_stub.go b/plugins/pdk/go/host/nd_host_scrobbleretriever_stub.go index 12bc46887..c6de90ebd 100644 --- a/plugins/pdk/go/host/nd_host_scrobbleretriever_stub.go +++ b/plugins/pdk/go/host/nd_host_scrobbleretriever_stub.go @@ -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) } diff --git a/plugins/pdk/rust/nd-pdk-host/src/nd_host_scrobbleretriever.rs b/plugins/pdk/rust/nd-pdk-host/src/nd_host_scrobbleretriever.rs index c3062ab4d..709e40ac8 100644 --- a/plugins/pdk/rust/nd-pdk-host/src/nd_host_scrobbleretriever.rs +++ b/plugins/pdk/rust/nd-pdk-host/src/nd_host_scrobbleretriever.rs @@ -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, } +/// 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, } +/// 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) -> Json; } -/// 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, 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, 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