diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index 101efd1..8873c66 100755 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -3,18 +3,18 @@ id: e742591b-391d-4f4e-8484-d01a093b32ec management: docChecksum: e34dac84738ebf2d447ea2b9055a6eeb docVersion: 0.0.3 - speakeasyVersion: 1.207.1 - generationVersion: 2.280.6 - releaseVersion: 0.6.2 - configChecksum: e5f2c8fc4f178691477fc9628ef17cf9 + speakeasyVersion: 1.215.1 + generationVersion: 2.286.4 + releaseVersion: 0.7.0 + configChecksum: d39fbfbc5f1443a36c3b73aa6888a696 repoURL: https://github.com/LukeHagar/plexterraform.git repoSubDirectory: . published: true features: terraform: constsAndDefaults: 0.1.4 - core: 3.10.3 - globalSecurity: 2.81.5 + core: 3.11.1 + globalSecurity: 2.81.6 globalServerURLs: 2.82.1 methodServerURLs: 2.82.1 nameOverrides: 2.81.1 @@ -34,7 +34,89 @@ generatedFiles: - internal/sdk/statistics.go - internal/sdk/sessions.go - internal/sdk/updater.go - - internal/sdk/sdk.go + - internal/sdk/plexapi.go + - internal/sdk/models/errors/sdkerror.go + - internal/sdk/types/bigint.go + - internal/sdk/types/date.go + - internal/sdk/types/datetime.go + - internal/sdk/types/decimal.go + - internal/sdk/types/pointers.go + - internal/sdk/internal/utils/contenttype.go + - internal/sdk/internal/utils/form.go + - internal/sdk/internal/utils/headers.go + - internal/sdk/internal/utils/json.go + - internal/sdk/internal/utils/pathparams.go + - internal/sdk/internal/utils/queryparams.go + - internal/sdk/internal/utils/requestbody.go + - internal/sdk/internal/utils/retries.go + - internal/sdk/internal/utils/security.go + - internal/sdk/internal/utils/utils.go + - internal/sdk/models/operations/getservercapabilities.go + - internal/sdk/models/operations/getserverpreferences.go + - internal/sdk/models/operations/getavailableclients.go + - internal/sdk/models/operations/getdevices.go + - internal/sdk/models/operations/getserveridentity.go + - internal/sdk/models/operations/getmyplexaccount.go + - internal/sdk/models/operations/getresizedphoto.go + - internal/sdk/models/operations/getserverlist.go + - internal/sdk/models/operations/markplayed.go + - internal/sdk/models/operations/markunplayed.go + - internal/sdk/models/operations/updateplayprogress.go + - internal/sdk/models/operations/gettimeline.go + - internal/sdk/models/operations/startuniversaltranscode.go + - internal/sdk/models/operations/getserveractivities.go + - internal/sdk/models/operations/cancelserveractivities.go + - internal/sdk/models/operations/getbutlertasks.go + - internal/sdk/models/operations/startalltasks.go + - internal/sdk/models/operations/stopalltasks.go + - internal/sdk/models/operations/starttask.go + - internal/sdk/models/operations/stoptask.go + - internal/sdk/models/operations/getglobalhubs.go + - internal/sdk/models/operations/getlibraryhubs.go + - internal/sdk/models/operations/performsearch.go + - internal/sdk/models/operations/performvoicesearch.go + - internal/sdk/models/operations/getsearchresults.go + - internal/sdk/models/operations/getfilehash.go + - internal/sdk/models/operations/getrecentlyadded.go + - internal/sdk/models/operations/getlibraries.go + - internal/sdk/models/operations/getlibrary.go + - internal/sdk/models/operations/deletelibrary.go + - internal/sdk/models/operations/getlibraryitems.go + - internal/sdk/models/operations/refreshlibrary.go + - internal/sdk/models/operations/searchlibrary.go + - internal/sdk/models/operations/getmetadata.go + - internal/sdk/models/operations/getmetadatachildren.go + - internal/sdk/models/operations/getondeck.go + - internal/sdk/models/operations/logline.go + - internal/sdk/models/operations/logmultiline.go + - internal/sdk/models/operations/enablepapertrail.go + - internal/sdk/models/operations/getpin.go + - internal/sdk/models/operations/gettoken.go + - internal/sdk/models/operations/createplaylist.go + - internal/sdk/models/operations/getplaylists.go + - internal/sdk/models/operations/getplaylist.go + - internal/sdk/models/operations/deleteplaylist.go + - internal/sdk/models/operations/updateplaylist.go + - internal/sdk/models/operations/getplaylistcontents.go + - internal/sdk/models/operations/clearplaylistcontents.go + - internal/sdk/models/operations/addplaylistcontents.go + - internal/sdk/models/operations/uploadplaylist.go + - internal/sdk/models/operations/gettransienttoken.go + - internal/sdk/models/operations/getsourceconnectioninformation.go + - internal/sdk/models/operations/getstatistics.go + - internal/sdk/models/operations/getsessions.go + - internal/sdk/models/operations/getsessionhistory.go + - internal/sdk/models/operations/gettranscodesessions.go + - internal/sdk/models/operations/stoptranscodesession.go + - internal/sdk/models/operations/getupdatestatus.go + - internal/sdk/models/operations/checkforupdates.go + - internal/sdk/models/operations/applyupdates.go + - internal/sdk/models/shared/security.go + - internal/sdk/.gitignore + - internal/sdk/models/operations/options.go + - internal/sdk/.gitattributes + - internal/sdk/internal/hooks/registration.go + - internal/sdk/internal/hooks/hooks.go - examples/README.md - go.mod - go.sum @@ -63,22 +145,6 @@ generatedFiles: - internal/provider/reflect/slice.go - internal/provider/reflect/struct.go - internal/provider/utils.go - - internal/sdk/pkg/models/sdkerrors/sdkerror.go - - internal/sdk/pkg/types/bigint.go - - internal/sdk/pkg/types/date.go - - internal/sdk/pkg/types/datetime.go - - internal/sdk/pkg/types/decimal.go - - internal/sdk/pkg/types/pointers.go - - internal/sdk/pkg/utils/contenttype.go - - internal/sdk/pkg/utils/form.go - - internal/sdk/pkg/utils/headers.go - - internal/sdk/pkg/utils/json.go - - internal/sdk/pkg/utils/pathparams.go - - internal/sdk/pkg/utils/queryparams.go - - internal/sdk/pkg/utils/requestbody.go - - internal/sdk/pkg/utils/retries.go - - internal/sdk/pkg/utils/security.go - - internal/sdk/pkg/utils/utils.go - internal/validators/DateValidator.go - internal/validators/ExactlyOneChild.go - internal/validators/JSONParseValidator.go @@ -95,69 +161,7 @@ generatedFiles: - main.go - terraform-registry-manifest.json - tools/tools.go - - internal/sdk/pkg/models/operations/getservercapabilities.go - - internal/sdk/pkg/models/operations/getserverpreferences.go - - internal/sdk/pkg/models/operations/getavailableclients.go - - internal/sdk/pkg/models/operations/getdevices.go - - internal/sdk/pkg/models/operations/getserveridentity.go - - internal/sdk/pkg/models/operations/getmyplexaccount.go - - internal/sdk/pkg/models/operations/getresizedphoto.go - - internal/sdk/pkg/models/operations/getserverlist.go - - internal/sdk/pkg/models/operations/markplayed.go - - internal/sdk/pkg/models/operations/markunplayed.go - - internal/sdk/pkg/models/operations/updateplayprogress.go - - internal/sdk/pkg/models/operations/gettimeline.go - - internal/sdk/pkg/models/operations/startuniversaltranscode.go - - internal/sdk/pkg/models/operations/getserveractivities.go - - internal/sdk/pkg/models/operations/cancelserveractivities.go - - internal/sdk/pkg/models/operations/getbutlertasks.go - - internal/sdk/pkg/models/operations/startalltasks.go - - internal/sdk/pkg/models/operations/stopalltasks.go - - internal/sdk/pkg/models/operations/starttask.go - - internal/sdk/pkg/models/operations/stoptask.go - - internal/sdk/pkg/models/operations/getglobalhubs.go - - internal/sdk/pkg/models/operations/getlibraryhubs.go - - internal/sdk/pkg/models/operations/performsearch.go - - internal/sdk/pkg/models/operations/performvoicesearch.go - - internal/sdk/pkg/models/operations/getsearchresults.go - - internal/sdk/pkg/models/operations/getfilehash.go - - internal/sdk/pkg/models/operations/getrecentlyadded.go - - internal/sdk/pkg/models/operations/getlibraries.go - - internal/sdk/pkg/models/operations/getlibrary.go - - internal/sdk/pkg/models/operations/deletelibrary.go - - internal/sdk/pkg/models/operations/getlibraryitems.go - - internal/sdk/pkg/models/operations/refreshlibrary.go - - internal/sdk/pkg/models/operations/searchlibrary.go - - internal/sdk/pkg/models/operations/getmetadata.go - - internal/sdk/pkg/models/operations/getmetadatachildren.go - - internal/sdk/pkg/models/operations/getondeck.go - - internal/sdk/pkg/models/operations/logline.go - - internal/sdk/pkg/models/operations/logmultiline.go - - internal/sdk/pkg/models/operations/enablepapertrail.go - - internal/sdk/pkg/models/operations/getpin.go - - internal/sdk/pkg/models/operations/gettoken.go - - internal/sdk/pkg/models/operations/createplaylist.go - - internal/sdk/pkg/models/operations/getplaylists.go - - internal/sdk/pkg/models/operations/getplaylist.go - - internal/sdk/pkg/models/operations/deleteplaylist.go - - internal/sdk/pkg/models/operations/updateplaylist.go - - internal/sdk/pkg/models/operations/getplaylistcontents.go - - internal/sdk/pkg/models/operations/clearplaylistcontents.go - - internal/sdk/pkg/models/operations/addplaylistcontents.go - - internal/sdk/pkg/models/operations/uploadplaylist.go - - internal/sdk/pkg/models/operations/gettransienttoken.go - - internal/sdk/pkg/models/operations/getsourceconnectioninformation.go - - internal/sdk/pkg/models/operations/getstatistics.go - - internal/sdk/pkg/models/operations/getsessions.go - - internal/sdk/pkg/models/operations/getsessionhistory.go - - internal/sdk/pkg/models/operations/gettranscodesessions.go - - internal/sdk/pkg/models/operations/stoptranscodesession.go - - internal/sdk/pkg/models/operations/getupdatestatus.go - - internal/sdk/pkg/models/operations/checkforupdates.go - - internal/sdk/pkg/models/operations/applyupdates.go - - internal/sdk/pkg/models/shared/security.go - USAGE.md - internal/provider/provider.go - examples/provider/provider.tf - - internal/sdk/pkg/models/operations/options.go - .gitattributes diff --git a/.speakeasy/gen.yaml b/.speakeasy/gen.yaml index 5da659c..c5683d0 100644 --- a/.speakeasy/gen.yaml +++ b/.speakeasy/gen.yaml @@ -12,7 +12,7 @@ generation: auth: oAuth2ClientCredentialsEnabled: false terraform: - version: 0.6.2 + version: 0.7.0 author: LukeHagar imports: option: openapi diff --git a/README.md b/README.md index c36dae4..80bd5cc 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ terraform { required_providers { PlexAPI = { source = "LukeHagar/PlexAPI" - version = "0.6.2" + version = "0.7.0" } } } diff --git a/RELEASES.md b/RELEASES.md index b042c37..79f9e22 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -134,4 +134,12 @@ Based on: - OpenAPI Doc 0.0.3 - Speakeasy CLI 1.207.1 (2.280.6) https://github.com/speakeasy-api/speakeasy ### Generated -- [terraform v0.6.2] . \ No newline at end of file +- [terraform v0.6.2] . + +## 2024-03-21 01:05:16 +### Changes +Based on: +- OpenAPI Doc +- Speakeasy CLI 1.215.1 (2.286.4) https://github.com/speakeasy-api/speakeasy +### Generated +- [terraform v0.7.0] . \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 876f036..94b30b1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -17,7 +17,7 @@ terraform { required_providers { PlexAPI = { source = "LukeHagar/PlexAPI" - version = "0.6.2" + version = "0.7.0" } } } diff --git a/examples/provider/provider.tf b/examples/provider/provider.tf index ae3d326..e977809 100644 --- a/examples/provider/provider.tf +++ b/examples/provider/provider.tf @@ -2,7 +2,7 @@ terraform { required_providers { PlexAPI = { source = "LukeHagar/PlexAPI" - version = "0.6.2" + version = "0.7.0" } } } diff --git a/internal/provider/provider.go b/internal/provider/provider.go index a806194..dde3711 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -5,7 +5,7 @@ package provider import ( "context" "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/shared" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/shared" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/schema" diff --git a/internal/sdk/.gitattributes b/internal/sdk/.gitattributes new file mode 100644 index 0000000..e6a9944 --- /dev/null +++ b/internal/sdk/.gitattributes @@ -0,0 +1,2 @@ +# This allows generated code to be indexed correctly +*.go linguist-generated=false \ No newline at end of file diff --git a/internal/sdk/.gitignore b/internal/sdk/.gitignore new file mode 100644 index 0000000..d3c2f59 --- /dev/null +++ b/internal/sdk/.gitignore @@ -0,0 +1 @@ +# .gitignore diff --git a/internal/sdk/activities.go b/internal/sdk/activities.go index f67c663..800f14f 100644 --- a/internal/sdk/activities.go +++ b/internal/sdk/activities.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Activities are awesome. They provide a way to monitor and control asynchronous operations on the server. In order to receive real-time updates for activities, a client would normally subscribe via either EventSource or Websocket endpoints. @@ -34,24 +35,61 @@ func newActivities(sdkConfig sdkConfiguration) *Activities { // GetServerActivities - Get Server Activities // Get Server Activities func (s *Activities) GetServerActivities(ctx context.Context) (*operations.GetServerActivitiesResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/activities" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getServerActivities", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/activities") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetServerActivitiesResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -61,17 +99,10 @@ func (s *Activities) GetServerActivities(ctx context.Context) (*operations.GetSe httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetServerActivitiesResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetServerActivitiesResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -79,12 +110,12 @@ func (s *Activities) GetServerActivities(ctx context.Context) (*operations.GetSe res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetServerActivitiesActivitiesResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -92,8 +123,10 @@ func (s *Activities) GetServerActivities(ctx context.Context) (*operations.GetSe res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -102,27 +135,61 @@ func (s *Activities) GetServerActivities(ctx context.Context) (*operations.GetSe // CancelServerActivities - Cancel Server Activities // Cancel Server Activities func (s *Activities) CancelServerActivities(ctx context.Context, request operations.CancelServerActivitiesRequest) (*operations.CancelServerActivitiesResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "cancelServerActivities", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/activities/{activityUUID}", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/activities/{activityUUID}", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "DELETE", url, nil) + req, err := http.NewRequestWithContext(ctx, "DELETE", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.CancelServerActivitiesResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -132,20 +199,13 @@ func (s *Activities) CancelServerActivities(ctx context.Context, request operati httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.CancelServerActivitiesResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.CancelServerActivitiesResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -153,8 +213,10 @@ func (s *Activities) CancelServerActivities(ctx context.Context, request operati res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/sdk/authentication.go b/internal/sdk/authentication.go index 9c74f6b..e5cdd69 100644 --- a/internal/sdk/authentication.go +++ b/internal/sdk/authentication.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Authentication - API Calls regarding authentication for Plex Media Server @@ -28,28 +29,65 @@ func newAuthentication(sdkConfig sdkConfiguration) *Authentication { // GetTransientToken - Get a Transient Token. // This endpoint provides the caller with a temporary token with the same access level as the caller's token. These tokens are valid for up to 48 hours and are destroyed if the server instance is restarted. func (s *Authentication) GetTransientToken(ctx context.Context, request operations.GetTransientTokenRequest) (*operations.GetTransientTokenResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/security/token" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getTransientToken", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/security/token") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetTransientTokenResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -59,20 +97,13 @@ func (s *Authentication) GetTransientToken(ctx context.Context, request operatio httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetTransientTokenResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetTransientTokenResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -80,8 +111,10 @@ func (s *Authentication) GetTransientToken(ctx context.Context, request operatio res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -91,28 +124,65 @@ func (s *Authentication) GetTransientToken(ctx context.Context, request operatio // If a caller requires connection details and a transient token for a source that is known to the server, for example a cloud media provider or shared PMS, then this endpoint can be called. This endpoint is only accessible with either an admin token or a valid transient token generated from an admin token. // Note: requires Plex Media Server >= 1.15.4. func (s *Authentication) GetSourceConnectionInformation(ctx context.Context, request operations.GetSourceConnectionInformationRequest) (*operations.GetSourceConnectionInformationResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/security/resources" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getSourceConnectionInformation", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/security/resources") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetSourceConnectionInformationResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -122,20 +192,13 @@ func (s *Authentication) GetSourceConnectionInformation(ctx context.Context, req httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetSourceConnectionInformationResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetSourceConnectionInformationResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -143,8 +206,10 @@ func (s *Authentication) GetSourceConnectionInformation(ctx context.Context, req res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/sdk/butler.go b/internal/sdk/butler.go index e3e3217..3fd0259 100644 --- a/internal/sdk/butler.go +++ b/internal/sdk/butler.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Butler is the task manager of the Plex Media Server Ecosystem. @@ -28,24 +29,61 @@ func newButler(sdkConfig sdkConfiguration) *Butler { // GetButlerTasks - Get Butler tasks // Returns a list of butler tasks func (s *Butler) GetButlerTasks(ctx context.Context) (*operations.GetButlerTasksResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/butler" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getButlerTasks", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/butler") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetButlerTasksResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -55,17 +93,10 @@ func (s *Butler) GetButlerTasks(ctx context.Context) (*operations.GetButlerTasks httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetButlerTasksResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetButlerTasksResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -73,12 +104,12 @@ func (s *Butler) GetButlerTasks(ctx context.Context) (*operations.GetButlerTasks res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetButlerTasksButlerResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -86,8 +117,10 @@ func (s *Butler) GetButlerTasks(ctx context.Context) (*operations.GetButlerTasks res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -100,24 +133,61 @@ func (s *Butler) GetButlerTasks(ctx context.Context) (*operations.GetButlerTasks // 3. If a task is configured to run at a random time during the configured window and we are within that window, the task will be scheduled at a random time within the window. // 4. If we are outside the configured window, the task will start immediately. func (s *Butler) StartAllTasks(ctx context.Context) (*operations.StartAllTasksResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/butler" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "startAllTasks", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "POST", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/butler") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "POST", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.StartAllTasksResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -127,20 +197,13 @@ func (s *Butler) StartAllTasks(ctx context.Context) (*operations.StartAllTasksRe httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.StartAllTasksResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.StartAllTasksResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -148,8 +211,10 @@ func (s *Butler) StartAllTasks(ctx context.Context) (*operations.StartAllTasksRe res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -158,24 +223,61 @@ func (s *Butler) StartAllTasks(ctx context.Context) (*operations.StartAllTasksRe // StopAllTasks - Stop all Butler tasks // This endpoint will stop all currently running tasks and remove any scheduled tasks from the queue. func (s *Butler) StopAllTasks(ctx context.Context) (*operations.StopAllTasksResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/butler" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "stopAllTasks", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "DELETE", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/butler") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "DELETE", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.StopAllTasksResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -185,20 +287,13 @@ func (s *Butler) StopAllTasks(ctx context.Context) (*operations.StopAllTasksResp httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.StopAllTasksResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.StopAllTasksResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -206,8 +301,10 @@ func (s *Butler) StopAllTasks(ctx context.Context) (*operations.StopAllTasksResp res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -220,27 +317,61 @@ func (s *Butler) StopAllTasks(ctx context.Context) (*operations.StopAllTasksResp // 3. If a task is configured to run at a random time during the configured window and we are within that window, the task will be scheduled at a random time within the window. // 4. If we are outside the configured window, the task will start immediately. func (s *Butler) StartTask(ctx context.Context, request operations.StartTaskRequest) (*operations.StartTaskResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "startTask", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/butler/{taskName}", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/butler/{taskName}", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "POST", url, nil) + req, err := http.NewRequestWithContext(ctx, "POST", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.StartTaskResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -250,13 +381,6 @@ func (s *Butler) StartTask(ctx context.Context, request operations.StartTaskRequ httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.StartTaskResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough @@ -265,7 +389,7 @@ func (s *Butler) StartTask(ctx context.Context, request operations.StartTaskRequ case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.StartTaskResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -273,8 +397,10 @@ func (s *Butler) StartTask(ctx context.Context, request operations.StartTaskRequ res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -283,27 +409,61 @@ func (s *Butler) StartTask(ctx context.Context, request operations.StartTaskRequ // StopTask - Stop a single Butler task // This endpoint will stop a currently running task by name, or remove it from the list of scheduled tasks if it exists. See the section above for a list of task names for this endpoint. func (s *Butler) StopTask(ctx context.Context, request operations.StopTaskRequest) (*operations.StopTaskResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "stopTask", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/butler/{taskName}", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/butler/{taskName}", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "DELETE", url, nil) + req, err := http.NewRequestWithContext(ctx, "DELETE", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.StopTaskResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -313,13 +473,6 @@ func (s *Butler) StopTask(ctx context.Context, request operations.StopTaskReques httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.StopTaskResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough @@ -328,7 +481,7 @@ func (s *Butler) StopTask(ctx context.Context, request operations.StopTaskReques case httpRes.StatusCode == 404: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.StopTaskResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -336,8 +489,10 @@ func (s *Butler) StopTask(ctx context.Context, request operations.StopTaskReques res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/sdk/hubs.go b/internal/sdk/hubs.go index b6dd1f5..c7319d7 100644 --- a/internal/sdk/hubs.go +++ b/internal/sdk/hubs.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Hubs are a structured two-dimensional container for media, generally represented by multiple horizontal rows. @@ -28,28 +29,65 @@ func newHubs(sdkConfig sdkConfiguration) *Hubs { // GetGlobalHubs - Get Global Hubs // Get Global Hubs filtered by the parameters provided. func (s *Hubs) GetGlobalHubs(ctx context.Context, request operations.GetGlobalHubsRequest) (*operations.GetGlobalHubsResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/hubs" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getGlobalHubs", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/hubs") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetGlobalHubsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -59,17 +97,10 @@ func (s *Hubs) GetGlobalHubs(ctx context.Context, request operations.GetGlobalHu httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetGlobalHubsResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetGlobalHubsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -77,12 +108,12 @@ func (s *Hubs) GetGlobalHubs(ctx context.Context, request operations.GetGlobalHu res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetGlobalHubsHubsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -90,8 +121,10 @@ func (s *Hubs) GetGlobalHubs(ctx context.Context, request operations.GetGlobalHu res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -100,31 +133,65 @@ func (s *Hubs) GetGlobalHubs(ctx context.Context, request operations.GetGlobalHu // GetLibraryHubs - Get library specific hubs // This endpoint will return a list of library specific hubs func (s *Hubs) GetLibraryHubs(ctx context.Context, request operations.GetLibraryHubsRequest) (*operations.GetLibraryHubsResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getLibraryHubs", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/hubs/sections/{sectionId}", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/hubs/sections/{sectionId}", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetLibraryHubsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -134,17 +201,10 @@ func (s *Hubs) GetLibraryHubs(ctx context.Context, request operations.GetLibrary httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetLibraryHubsResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetLibraryHubsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -152,12 +212,12 @@ func (s *Hubs) GetLibraryHubs(ctx context.Context, request operations.GetLibrary res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetLibraryHubsHubsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -165,8 +225,10 @@ func (s *Hubs) GetLibraryHubs(ctx context.Context, request operations.GetLibrary res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/sdk/internal/hooks/hooks.go b/internal/sdk/internal/hooks/hooks.go new file mode 100644 index 0000000..ee97182 --- /dev/null +++ b/internal/sdk/internal/hooks/hooks.go @@ -0,0 +1,144 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package hooks + +import ( + "context" + "errors" + "net/http" +) + +type FailEarly struct { + Cause error +} + +var _ error = (*FailEarly)(nil) + +func (f *FailEarly) Error() string { + return f.Cause.Error() +} + +// HTTPClient provides an interface for supplying the SDK with a custom HTTP client +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) +} + +type HookContext struct { + Context context.Context + OperationID string + OAuth2Scopes []string + SecuritySource func(context.Context) (interface{}, error) +} + +type BeforeRequestContext struct { + HookContext +} + +type AfterSuccessContext struct { + HookContext +} + +type AfterErrorContext struct { + HookContext +} + +// sdkInitHook is called when the SDK is initializing. The hook can modify and return a new baseURL and HTTP client to be used by the SDK. +type sdkInitHook interface { + SDKInit(baseURL string, client HTTPClient) (string, HTTPClient) +} + +// beforeRequestHook is called before the SDK sends a request. The hook can modify the request before it is sent or return an error to stop the request from being sent. +type beforeRequestHook interface { + BeforeRequest(hookCtx BeforeRequestContext, req *http.Request) (*http.Request, error) +} + +// afterSuccessHook is called after the SDK receives a response. The hook can modify the response before it is handled or return an error to stop the response from being handled. +type afterSuccessHook interface { + AfterSuccess(hookCtx AfterSuccessContext, res *http.Response) (*http.Response, error) +} + +// afterErrorHook is called after the SDK encounters an error, or a non-successful response. The hook can modify the response if available otherwise modify the error. +// All afterErrorHook hooks are called and returning an error won't stop the other hooks from being called. But if you want to stop the other hooks from being called, you can return a FailEarly error wrapping your error. +type afterErrorHook interface { + AfterError(hookCtx AfterErrorContext, res *http.Response, err error) (*http.Response, error) +} + +type Hooks struct { + sdkInitHooks []sdkInitHook + beforeRequestHook []beforeRequestHook + afterSuccessHook []afterSuccessHook + afterErrorHook []afterErrorHook +} + +func New() *Hooks { + h := &Hooks{ + sdkInitHooks: []sdkInitHook{}, + beforeRequestHook: []beforeRequestHook{}, + afterSuccessHook: []afterSuccessHook{}, + afterErrorHook: []afterErrorHook{}, + } + + initHooks(h) + + return h +} + +// registerSDKInitHook registers a hook to be used by the SDK for the initialization event. +func (h *Hooks) registerSDKInitHook(hook sdkInitHook) { + h.sdkInitHooks = append(h.sdkInitHooks, hook) +} + +// registerBeforeRequestHook registers a hook to be used by the SDK for the before request event. +func (h *Hooks) registerBeforeRequestHook(hook beforeRequestHook) { + h.beforeRequestHook = append(h.beforeRequestHook, hook) +} + +// registerAfterSuccessHook registers a hook to be used by the SDK for the after success event. +func (h *Hooks) registerAfterSuccessHook(hook afterSuccessHook) { + h.afterSuccessHook = append(h.afterSuccessHook, hook) +} + +// registerAfterErrorHook registers a hook to be used by the SDK for the after error event. +func (h *Hooks) registerAfterErrorHook(hook afterErrorHook) { + h.afterErrorHook = append(h.afterErrorHook, hook) +} + +func (h *Hooks) SDKInit(baseURL string, client HTTPClient) (string, HTTPClient) { + for _, hook := range h.sdkInitHooks { + baseURL, client = hook.SDKInit(baseURL, client) + } + return baseURL, client +} + +func (h *Hooks) BeforeRequest(hookCtx BeforeRequestContext, req *http.Request) (*http.Request, error) { + for _, hook := range h.beforeRequestHook { + var err error + req, err = hook.BeforeRequest(hookCtx, req) + if err != nil { + return req, err + } + } + return req, nil +} + +func (h *Hooks) AfterSuccess(hookCtx AfterSuccessContext, res *http.Response) (*http.Response, error) { + for _, hook := range h.afterSuccessHook { + var err error + res, err = hook.AfterSuccess(hookCtx, res) + if err != nil { + return res, err + } + } + return res, nil +} + +func (h *Hooks) AfterError(hookCtx AfterErrorContext, res *http.Response, err error) (*http.Response, error) { + for _, hook := range h.afterErrorHook { + res, err = hook.AfterError(hookCtx, res, err) + var fe *FailEarly + if errors.As(err, &fe) { + return nil, fe.Cause + } + } + return res, err +} diff --git a/internal/sdk/internal/hooks/registration.go b/internal/sdk/internal/hooks/registration.go new file mode 100644 index 0000000..e68bf4c --- /dev/null +++ b/internal/sdk/internal/hooks/registration.go @@ -0,0 +1,15 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package hooks + +/* + * This file is only ever generated once on the first generation and then is free to be modified. + * Any hooks you wish to add should be registered in the InitHooks function. Feel free to define them + * in this file or in separate files in the hooks package. + */ + +func initHooks(h *Hooks) { + // Add hooks by calling h.register{SDKInit/BeforeRequest/AfterSuccess/AfterError}Hook + // with an instance of a hook that implements that specific Hook interface + // Hooks are registered per SDK instance, and are valid for the lifetime of the SDK instance +} diff --git a/internal/sdk/pkg/utils/contenttype.go b/internal/sdk/internal/utils/contenttype.go similarity index 100% rename from internal/sdk/pkg/utils/contenttype.go rename to internal/sdk/internal/utils/contenttype.go diff --git a/internal/sdk/pkg/utils/form.go b/internal/sdk/internal/utils/form.go similarity index 97% rename from internal/sdk/pkg/utils/form.go rename to internal/sdk/internal/utils/form.go index d5091c4..52de257 100644 --- a/internal/sdk/pkg/utils/form.go +++ b/internal/sdk/internal/utils/form.go @@ -12,7 +12,7 @@ import ( "github.com/ericlagergren/decimal" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/types" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/types" ) func populateForm(paramName string, explode bool, objType reflect.Type, objValue reflect.Value, delimiter string, getFieldName func(reflect.StructField) string) url.Values { diff --git a/internal/sdk/pkg/utils/headers.go b/internal/sdk/internal/utils/headers.go similarity index 100% rename from internal/sdk/pkg/utils/headers.go rename to internal/sdk/internal/utils/headers.go diff --git a/internal/sdk/pkg/utils/json.go b/internal/sdk/internal/utils/json.go similarity index 99% rename from internal/sdk/pkg/utils/json.go rename to internal/sdk/internal/utils/json.go index e4cc0ea..3546816 100644 --- a/internal/sdk/pkg/utils/json.go +++ b/internal/sdk/internal/utils/json.go @@ -12,7 +12,7 @@ import ( "time" "unsafe" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/types" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/types" "github.com/ericlagergren/decimal" ) diff --git a/internal/sdk/pkg/utils/pathparams.go b/internal/sdk/internal/utils/pathparams.go similarity index 98% rename from internal/sdk/pkg/utils/pathparams.go rename to internal/sdk/internal/utils/pathparams.go index 688d21a..45e1427 100644 --- a/internal/sdk/pkg/utils/pathparams.go +++ b/internal/sdk/internal/utils/pathparams.go @@ -13,7 +13,7 @@ import ( "github.com/ericlagergren/decimal" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/types" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/types" ) func GenerateURL(ctx context.Context, serverURL, path string, pathParams interface{}, globals map[string]map[string]map[string]interface{}) (string, error) { diff --git a/internal/sdk/pkg/utils/queryparams.go b/internal/sdk/internal/utils/queryparams.go similarity index 100% rename from internal/sdk/pkg/utils/queryparams.go rename to internal/sdk/internal/utils/queryparams.go diff --git a/internal/sdk/pkg/utils/requestbody.go b/internal/sdk/internal/utils/requestbody.go similarity index 94% rename from internal/sdk/pkg/utils/requestbody.go rename to internal/sdk/internal/utils/requestbody.go index 950f39a..322b8b3 100644 --- a/internal/sdk/pkg/utils/requestbody.go +++ b/internal/sdk/internal/utils/requestbody.go @@ -26,6 +26,19 @@ var ( ) func SerializeRequestBody(ctx context.Context, request interface{}, nullable, optional bool, requestFieldName, serializationMethod, tag string) (io.Reader, string, error) { + bodyReader, contentType, err := serializeRequestBody(ctx, request, nullable, optional, requestFieldName, serializationMethod, tag) + if err != nil { + return nil, "", fmt.Errorf("error serializing request body: %w", err) + } + + if bodyReader == nil && !optional { + return nil, "", fmt.Errorf("request body is required") + } + + return bodyReader, contentType, nil +} + +func serializeRequestBody(ctx context.Context, request interface{}, nullable, optional bool, requestFieldName, serializationMethod, tag string) (io.Reader, string, error) { requestStructType := reflect.TypeOf(request) requestValType := reflect.ValueOf(request) diff --git a/internal/sdk/pkg/utils/retries.go b/internal/sdk/internal/utils/retries.go similarity index 100% rename from internal/sdk/pkg/utils/retries.go rename to internal/sdk/internal/utils/retries.go diff --git a/internal/sdk/pkg/utils/security.go b/internal/sdk/internal/utils/security.go similarity index 63% rename from internal/sdk/pkg/utils/security.go rename to internal/sdk/internal/utils/security.go index ea1d4b2..8023225 100644 --- a/internal/sdk/pkg/utils/security.go +++ b/internal/sdk/internal/utils/security.go @@ -11,10 +11,6 @@ import ( "strings" ) -type HTTPClient interface { - Do(req *http.Request) (*http.Response, error) -} - const ( securityTagKey = "security" ) @@ -27,73 +23,24 @@ type securityTag struct { SubType string } -type securityConfig struct { - headers map[string]string - queryParams map[string]string -} - -type SecurityClient struct { - HTTPClient - security func(ctx context.Context) (interface{}, error) -} - -func newSecurityClient(client HTTPClient, security func(ctx context.Context) (interface{}, error)) *SecurityClient { - return &SecurityClient{ - HTTPClient: client, - security: security, +func PopulateSecurity(ctx context.Context, req *http.Request, securitySource func(context.Context) (interface{}, error)) error { + if securitySource == nil { + return nil } -} -func (c *SecurityClient) Do(req *http.Request) (*http.Response, error) { - securityCtx, err := c.security(req.Context()) + security, err := securitySource(ctx) if err != nil { - return nil, err + return err } - ctx := securityConfig{ - headers: make(map[string]string), - queryParams: make(map[string]string), - } - parseSecurityStruct(&ctx, securityCtx) + headers := make(map[string]string) + queryParams := make(map[string]string) - for k, v := range ctx.headers { - req.Header.Set(k, v) - } - - queryParams := req.URL.Query() - - for k, v := range ctx.queryParams { - queryParams.Add(k, v) - } - - req.URL.RawQuery = queryParams.Encode() - - return c.HTTPClient.Do(req) -} - -func ConfigureSecurityClient(c HTTPClient, security func(ctx context.Context) (interface{}, error)) *SecurityClient { - return newSecurityClient(c, security) -} - -func trueReflectValue(val reflect.Value) reflect.Value { - kind := val.Type().Kind() - for kind == reflect.Interface || kind == reflect.Ptr { - innerVal := val.Elem() - if !innerVal.IsValid() { - break - } - val = innerVal - kind = val.Type().Kind() - } - return val -} - -func parseSecurityStruct(c *securityConfig, security interface{}) { securityValType := trueReflectValue(reflect.ValueOf(security)) securityStructType := securityValType.Type() if isNil(securityStructType, securityValType) { - return + return nil } if securityStructType.Kind() == reflect.Ptr { @@ -118,25 +65,37 @@ func parseSecurityStruct(c *securityConfig, security interface{}) { secTag := parseSecurityTag(fieldType) if secTag != nil { if secTag.Option { - handleSecurityOption(c, valType.Interface()) + handleSecurityOption(headers, queryParams, valType.Interface()) } else if secTag.Scheme { // Special case for basic auth which could be a flattened struct if secTag.SubType == "basic" && kind != reflect.Struct { - parseSecurityScheme(c, secTag, security) + parseSecurityScheme(headers, queryParams, secTag, security) } else { - parseSecurityScheme(c, secTag, valType.Interface()) + parseSecurityScheme(headers, queryParams, secTag, valType.Interface()) } } } } + + for key, value := range headers { + req.Header.Add(key, value) + } + + query := req.URL.Query() + for key, value := range queryParams { + query.Add(key, value) + } + req.URL.RawQuery = query.Encode() + + return nil } -func handleSecurityOption(c *securityConfig, option interface{}) error { +func handleSecurityOption(headers, queryParams map[string]string, option interface{}) { optionValType := trueReflectValue(reflect.ValueOf(option)) optionStructType := optionValType.Type() if isNil(optionStructType, optionValType) { - return nil + return } for i := 0; i < optionStructType.NumField(); i++ { @@ -145,14 +104,12 @@ func handleSecurityOption(c *securityConfig, option interface{}) error { secTag := parseSecurityTag(fieldType) if secTag != nil && secTag.Scheme { - parseSecurityScheme(c, secTag, valType.Interface()) + parseSecurityScheme(headers, queryParams, secTag, valType.Interface()) } } - - return nil } -func parseSecurityScheme(client *securityConfig, schemeTag *securityTag, scheme interface{}) { +func parseSecurityScheme(headers, queryParams map[string]string, schemeTag *securityTag, scheme interface{}) { schemeVal := trueReflectValue(reflect.ValueOf(scheme)) schemeType := schemeVal.Type() @@ -162,7 +119,7 @@ func parseSecurityScheme(client *securityConfig, schemeTag *securityTag, scheme if schemeType.Kind() == reflect.Struct { if schemeTag.Type == "http" && schemeTag.SubType == "basic" { - handleBasicAuthScheme(client, schemeVal.Interface()) + handleBasicAuthScheme(headers, schemeVal.Interface()) return } @@ -183,34 +140,36 @@ func parseSecurityScheme(client *securityConfig, schemeTag *securityTag, scheme return } - parseSecuritySchemeValue(client, schemeTag, secTag, valType.Interface()) + parseSecuritySchemeValue(headers, queryParams, schemeTag, secTag, valType.Interface()) } } else { - parseSecuritySchemeValue(client, schemeTag, schemeTag, schemeVal.Interface()) + parseSecuritySchemeValue(headers, queryParams, schemeTag, schemeTag, schemeVal.Interface()) } } -func parseSecuritySchemeValue(client *securityConfig, schemeTag *securityTag, secTag *securityTag, val interface{}) { +func parseSecuritySchemeValue(headers, queryParams map[string]string, schemeTag *securityTag, secTag *securityTag, val interface{}) { switch schemeTag.Type { case "apiKey": switch schemeTag.SubType { case "header": - client.headers[secTag.Name] = valToString(val) + headers[secTag.Name] = valToString(val) case "query": - client.queryParams[secTag.Name] = valToString(val) + queryParams[secTag.Name] = valToString(val) case "cookie": - client.headers["Cookie"] = fmt.Sprintf("%s=%s", secTag.Name, valToString(val)) + headers["Cookie"] = fmt.Sprintf("%s=%s", secTag.Name, valToString(val)) default: panic("not supported") } case "openIdConnect": - client.headers[secTag.Name] = prefixBearer(valToString(val)) + headers[secTag.Name] = prefixBearer(valToString(val)) case "oauth2": - client.headers[secTag.Name] = prefixBearer(valToString(val)) + if schemeTag.SubType != "client_credentials" { + headers[secTag.Name] = prefixBearer(valToString(val)) + } case "http": switch schemeTag.SubType { case "bearer": - client.headers[secTag.Name] = prefixBearer(valToString(val)) + headers[secTag.Name] = prefixBearer(valToString(val)) default: panic("not supported") } @@ -227,7 +186,7 @@ func prefixBearer(authHeaderValue string) string { return fmt.Sprintf("Bearer %s", authHeaderValue) } -func handleBasicAuthScheme(client *securityConfig, scheme interface{}) { +func handleBasicAuthScheme(headers map[string]string, scheme interface{}) { schemeStructType := reflect.TypeOf(scheme) schemeValType := reflect.ValueOf(scheme) @@ -250,7 +209,7 @@ func handleBasicAuthScheme(client *securityConfig, scheme interface{}) { } } - client.headers["Authorization"] = fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))) + headers["Authorization"] = fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))) } func parseSecurityTag(field reflect.StructField) *securityTag { @@ -296,3 +255,16 @@ func parseSecurityTag(field reflect.StructField) *securityTag { SubType: securitySubType, } } + +func trueReflectValue(val reflect.Value) reflect.Value { + kind := val.Type().Kind() + for kind == reflect.Interface || kind == reflect.Ptr { + innerVal := val.Elem() + if !innerVal.IsValid() { + break + } + val = innerVal + kind = val.Type().Kind() + } + return val +} diff --git a/internal/sdk/pkg/utils/utils.go b/internal/sdk/internal/utils/utils.go similarity index 83% rename from internal/sdk/pkg/utils/utils.go rename to internal/sdk/internal/utils/utils.go index 8c8161a..5508c5f 100644 --- a/internal/sdk/pkg/utils/utils.go +++ b/internal/sdk/internal/utils/utils.go @@ -8,6 +8,7 @@ import ( "math/big" "reflect" "regexp" + "strconv" "strings" "time" @@ -63,6 +64,29 @@ func Contains(slice []string, item string) bool { return false } +func MatchStatusCodes(expectedCodes []string, statusCode int) bool { + for _, codeStr := range expectedCodes { + code, err := strconv.Atoi(codeStr) + if err == nil { + if code == statusCode { + return true + } + continue + } + + codeRange, err := strconv.Atoi(string(codeStr[0])) + if err != nil { + continue + } + + if statusCode >= (codeRange*100) && statusCode < ((codeRange+1)*100) { + return true + } + } + + return false +} + func parseStructTag(tagKey string, field reflect.StructField) map[string]string { tag := field.Tag.Get(tagKey) if tag == "" { @@ -154,6 +178,14 @@ func populateFromGlobals(fieldType reflect.StructField, valType reflect.Value, p } func isNil(typ reflect.Type, val reflect.Value) bool { + // `reflect.TypeOf(nil) == nil` so calling typ.Kind() will cause a nil pointer + // dereference panic. Catch it and return early. + // https://github.com/golang/go/issues/51649 + // https://github.com/golang/go/issues/54208 + if typ == nil { + return true + } + if typ.Kind() == reflect.Ptr || typ.Kind() == reflect.Map || typ.Kind() == reflect.Slice || typ.Kind() == reflect.Interface { return val.IsNil() } diff --git a/internal/sdk/library.go b/internal/sdk/library.go index ddb74aa..23767fb 100644 --- a/internal/sdk/library.go +++ b/internal/sdk/library.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Library - API Calls interacting with Plex Media Server Libraries @@ -28,28 +29,65 @@ func newLibrary(sdkConfig sdkConfiguration) *Library { // GetFileHash - Get Hash Value // This resource returns hash values for local files func (s *Library) GetFileHash(ctx context.Context, request operations.GetFileHashRequest) (*operations.GetFileHashResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/library/hashes" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getFileHash", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/library/hashes") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetFileHashResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -59,20 +97,13 @@ func (s *Library) GetFileHash(ctx context.Context, request operations.GetFileHas httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetFileHashResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetFileHashResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -80,8 +111,10 @@ func (s *Library) GetFileHash(ctx context.Context, request operations.GetFileHas res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -90,24 +123,61 @@ func (s *Library) GetFileHash(ctx context.Context, request operations.GetFileHas // GetRecentlyAdded - Get Recently Added // This endpoint will return the recently added content. func (s *Library) GetRecentlyAdded(ctx context.Context) (*operations.GetRecentlyAddedResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/library/recentlyAdded" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getRecentlyAdded", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/library/recentlyAdded") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetRecentlyAddedResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -117,17 +187,10 @@ func (s *Library) GetRecentlyAdded(ctx context.Context) (*operations.GetRecently httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetRecentlyAddedResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetRecentlyAddedResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -135,12 +198,12 @@ func (s *Library) GetRecentlyAdded(ctx context.Context) (*operations.GetRecently res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetRecentlyAddedLibraryResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -148,8 +211,10 @@ func (s *Library) GetRecentlyAdded(ctx context.Context) (*operations.GetRecently res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -163,24 +228,61 @@ func (s *Library) GetRecentlyAdded(ctx context.Context) (*operations.GetRecently // Libraries have features beyond just being a collection of media; for starters, they include information about supported types, filters and sorts. // This allows a client to provide a rich interface around the media (e.g. allow sorting movies by release year). func (s *Library) GetLibraries(ctx context.Context) (*operations.GetLibrariesResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/library/sections" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getLibraries", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/library/sections") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetLibrariesResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -190,17 +292,10 @@ func (s *Library) GetLibraries(ctx context.Context) (*operations.GetLibrariesRes httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetLibrariesResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetLibrariesResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -208,12 +303,12 @@ func (s *Library) GetLibraries(ctx context.Context) (*operations.GetLibrariesRes res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetLibrariesLibraryResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -221,8 +316,10 @@ func (s *Library) GetLibraries(ctx context.Context) (*operations.GetLibrariesRes res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -269,31 +366,65 @@ func (s *Library) GetLibraries(ctx context.Context) (*operations.GetLibrariesRes // // > **Note**: Filters and sorts are optional; without them, no filtering controls are rendered. func (s *Library) GetLibrary(ctx context.Context, request operations.GetLibraryRequest) (*operations.GetLibraryResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getLibrary", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/library/sections/{sectionId}", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/library/sections/{sectionId}", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetLibraryResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -303,17 +434,10 @@ func (s *Library) GetLibrary(ctx context.Context, request operations.GetLibraryR httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetLibraryResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetLibraryResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -321,12 +445,12 @@ func (s *Library) GetLibrary(ctx context.Context, request operations.GetLibraryR res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetLibraryLibraryResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -334,8 +458,10 @@ func (s *Library) GetLibrary(ctx context.Context, request operations.GetLibraryR res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -344,27 +470,61 @@ func (s *Library) GetLibrary(ctx context.Context, request operations.GetLibraryR // DeleteLibrary - Delete Library Section // Delate a library using a specific section func (s *Library) DeleteLibrary(ctx context.Context, request operations.DeleteLibraryRequest) (*operations.DeleteLibraryResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "deleteLibrary", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/library/sections/{sectionId}", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/library/sections/{sectionId}", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "DELETE", url, nil) + req, err := http.NewRequestWithContext(ctx, "DELETE", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.DeleteLibraryResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -374,20 +534,13 @@ func (s *Library) DeleteLibrary(ctx context.Context, request operations.DeleteLi httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.DeleteLibraryResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.DeleteLibraryResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -395,8 +548,10 @@ func (s *Library) DeleteLibrary(ctx context.Context, request operations.DeleteLi res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -424,27 +579,61 @@ func (s *Library) DeleteLibrary(ctx context.Context, request operations.DeleteLi // - `firstCharacter`: Items categorized by the first letter. // - `folder`: Items categorized by folder. func (s *Library) GetLibraryItems(ctx context.Context, request operations.GetLibraryItemsRequest) (*operations.GetLibraryItemsResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getLibraryItems", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/library/sections/{sectionId}/{tag}", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/library/sections/{sectionId}/{tag}", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetLibraryItemsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -454,17 +643,10 @@ func (s *Library) GetLibraryItems(ctx context.Context, request operations.GetLib httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetLibraryItemsResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetLibraryItemsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -472,8 +654,10 @@ func (s *Library) GetLibraryItems(ctx context.Context, request operations.GetLib res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -482,27 +666,61 @@ func (s *Library) GetLibraryItems(ctx context.Context, request operations.GetLib // RefreshLibrary - Refresh Library // This endpoint Refreshes the library. func (s *Library) RefreshLibrary(ctx context.Context, request operations.RefreshLibraryRequest) (*operations.RefreshLibraryResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "refreshLibrary", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/library/sections/{sectionId}/refresh", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/library/sections/{sectionId}/refresh", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.RefreshLibraryResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -512,20 +730,13 @@ func (s *Library) RefreshLibrary(ctx context.Context, request operations.Refresh httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.RefreshLibraryResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.RefreshLibraryResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -533,8 +744,10 @@ func (s *Library) RefreshLibrary(ctx context.Context, request operations.Refresh res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -560,31 +773,65 @@ func (s *Library) RefreshLibrary(ctx context.Context, request operations.Refresh // // > **Note**: Filters and sorts are optional; without them, no filtering controls are rendered. func (s *Library) SearchLibrary(ctx context.Context, request operations.SearchLibraryRequest) (*operations.SearchLibraryResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "searchLibrary", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/library/sections/{sectionId}/search", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/library/sections/{sectionId}/search", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.SearchLibraryResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -594,17 +841,10 @@ func (s *Library) SearchLibrary(ctx context.Context, request operations.SearchLi httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.SearchLibraryResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.SearchLibraryResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -612,8 +852,10 @@ func (s *Library) SearchLibrary(ctx context.Context, request operations.SearchLi res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -622,27 +864,61 @@ func (s *Library) SearchLibrary(ctx context.Context, request operations.SearchLi // GetMetadata - Get Items Metadata // This endpoint will return the metadata of a library item specified with the ratingKey. func (s *Library) GetMetadata(ctx context.Context, request operations.GetMetadataRequest) (*operations.GetMetadataResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getMetadata", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/library/metadata/{ratingKey}", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/library/metadata/{ratingKey}", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetMetadataResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -652,17 +928,10 @@ func (s *Library) GetMetadata(ctx context.Context, request operations.GetMetadat httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetMetadataResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetMetadataResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -670,12 +939,12 @@ func (s *Library) GetMetadata(ctx context.Context, request operations.GetMetadat res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetMetadataLibraryResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -683,8 +952,10 @@ func (s *Library) GetMetadata(ctx context.Context, request operations.GetMetadat res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -693,27 +964,61 @@ func (s *Library) GetMetadata(ctx context.Context, request operations.GetMetadat // GetMetadataChildren - Get Items Children // This endpoint will return the children of of a library item specified with the ratingKey. func (s *Library) GetMetadataChildren(ctx context.Context, request operations.GetMetadataChildrenRequest) (*operations.GetMetadataChildrenResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getMetadataChildren", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/library/metadata/{ratingKey}/children", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/library/metadata/{ratingKey}/children", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetMetadataChildrenResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -723,17 +1028,10 @@ func (s *Library) GetMetadataChildren(ctx context.Context, request operations.Ge httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetMetadataChildrenResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetMetadataChildrenResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -741,12 +1039,12 @@ func (s *Library) GetMetadataChildren(ctx context.Context, request operations.Ge res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetMetadataChildrenLibraryResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -754,8 +1052,10 @@ func (s *Library) GetMetadataChildren(ctx context.Context, request operations.Ge res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -764,24 +1064,61 @@ func (s *Library) GetMetadataChildren(ctx context.Context, request operations.Ge // GetOnDeck - Get On Deck // This endpoint will return the on deck content. func (s *Library) GetOnDeck(ctx context.Context) (*operations.GetOnDeckResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/library/onDeck" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getOnDeck", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/library/onDeck") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetOnDeckResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -791,17 +1128,10 @@ func (s *Library) GetOnDeck(ctx context.Context) (*operations.GetOnDeckResponse, httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetOnDeckResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetOnDeckResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -809,12 +1139,12 @@ func (s *Library) GetOnDeck(ctx context.Context) (*operations.GetOnDeckResponse, res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetOnDeckLibraryResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -822,8 +1152,10 @@ func (s *Library) GetOnDeck(ctx context.Context) (*operations.GetOnDeckResponse, res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/sdk/log.go b/internal/sdk/log.go index 150fd8c..def3fab 100644 --- a/internal/sdk/log.go +++ b/internal/sdk/log.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Log - Submit logs to the Log Handler for Plex Media Server @@ -28,28 +29,65 @@ func newLog(sdkConfig sdkConfiguration) *Log { // LogLine - Logging a single line message. // This endpoint will write a single-line log message, including a level and source to the main Plex Media Server log. func (s *Log) LogLine(ctx context.Context, request operations.LogLineRequest) (*operations.LogLineResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/log" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "logLine", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/log") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.LogLineResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -59,20 +97,13 @@ func (s *Log) LogLine(ctx context.Context, request operations.LogLineRequest) (* httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.LogLineResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.LogLineResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -80,8 +111,10 @@ func (s *Log) LogLine(ctx context.Context, request operations.LogLineRequest) (* res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -110,61 +143,83 @@ func (s *Log) LogLine(ctx context.Context, request operations.LogLineRequest) (* // // Ensure each parameter is properly URL-encoded to avoid interpretation issues. func (s *Log) LogMultiLine(ctx context.Context, request string) (*operations.LogMultiLineResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "logMultiLine", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/log" + opURL, err := url.JoinPath(baseURL, "/log") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, false, false, "Request", "string", `request:"mediaType=text/plain"`) if err != nil { - return nil, fmt.Errorf("error serializing request body: %w", err) - } - if bodyReader == nil { - return nil, fmt.Errorf("request body is required") + return nil, err } - debugBody := bytes.NewBuffer([]byte{}) - debugReader := io.TeeReader(bodyReader, debugBody) - - req, err := http.NewRequestWithContext(ctx, "POST", url, debugReader) + req, err := http.NewRequestWithContext(ctx, "POST", opURL, bodyReader) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) - + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) req.Header.Set("Content-Type", reqContentType) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.LogMultiLineResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) if err != nil { return nil, fmt.Errorf("error reading response body: %w", err) } - httpRes.Request.Body = io.NopCloser(debugBody) httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.LogMultiLineResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.LogMultiLineResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -172,8 +227,10 @@ func (s *Log) LogMultiLine(ctx context.Context, request string) (*operations.Log res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -182,24 +239,61 @@ func (s *Log) LogMultiLine(ctx context.Context, request string) (*operations.Log // EnablePaperTrail - Enabling Papertrail // This endpoint will enable all Plex Media Serverlogs to be sent to the Papertrail networked logging site for a period of time. func (s *Log) EnablePaperTrail(ctx context.Context) (*operations.EnablePaperTrailResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/log/networked" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "enablePaperTrail", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/log/networked") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.EnablePaperTrailResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -209,13 +303,6 @@ func (s *Log) EnablePaperTrail(ctx context.Context) (*operations.EnablePaperTrai httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.EnablePaperTrailResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough @@ -224,7 +311,7 @@ func (s *Log) EnablePaperTrail(ctx context.Context) (*operations.EnablePaperTrai case httpRes.StatusCode == 403: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.EnablePaperTrailResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -232,8 +319,10 @@ func (s *Log) EnablePaperTrail(ctx context.Context) (*operations.EnablePaperTrai res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/sdk/media.go b/internal/sdk/media.go index 4252b23..c6478ac 100644 --- a/internal/sdk/media.go +++ b/internal/sdk/media.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Media - API Calls interacting with Plex Media Server Media @@ -28,28 +29,65 @@ func newMedia(sdkConfig sdkConfiguration) *Media { // MarkPlayed - Mark Media Played // This will mark the provided media key as Played. func (s *Media) MarkPlayed(ctx context.Context, request operations.MarkPlayedRequest) (*operations.MarkPlayedResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/:/scrobble" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "markPlayed", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/:/scrobble") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.MarkPlayedResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -59,20 +97,13 @@ func (s *Media) MarkPlayed(ctx context.Context, request operations.MarkPlayedReq httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.MarkPlayedResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.MarkPlayedResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -80,8 +111,10 @@ func (s *Media) MarkPlayed(ctx context.Context, request operations.MarkPlayedReq res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -90,28 +123,65 @@ func (s *Media) MarkPlayed(ctx context.Context, request operations.MarkPlayedReq // MarkUnplayed - Mark Media Unplayed // This will mark the provided media key as Unplayed. func (s *Media) MarkUnplayed(ctx context.Context, request operations.MarkUnplayedRequest) (*operations.MarkUnplayedResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/:/unscrobble" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "markUnplayed", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/:/unscrobble") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.MarkUnplayedResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -121,20 +191,13 @@ func (s *Media) MarkUnplayed(ctx context.Context, request operations.MarkUnplaye httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.MarkUnplayedResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.MarkUnplayedResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -142,8 +205,10 @@ func (s *Media) MarkUnplayed(ctx context.Context, request operations.MarkUnplaye res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -152,28 +217,65 @@ func (s *Media) MarkUnplayed(ctx context.Context, request operations.MarkUnplaye // UpdatePlayProgress - Update Media Play Progress // This API command can be used to update the play progress of a media item. func (s *Media) UpdatePlayProgress(ctx context.Context, request operations.UpdatePlayProgressRequest) (*operations.UpdatePlayProgressResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/:/progress" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "updatePlayProgress", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "POST", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/:/progress") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "POST", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.UpdatePlayProgressResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -183,20 +285,13 @@ func (s *Media) UpdatePlayProgress(ctx context.Context, request operations.Updat httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.UpdatePlayProgressResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.UpdatePlayProgressResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -204,8 +299,10 @@ func (s *Media) UpdatePlayProgress(ctx context.Context, request operations.Updat res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/sdk/pkg/models/sdkerrors/sdkerror.go b/internal/sdk/models/errors/sdkerror.go similarity index 97% rename from internal/sdk/pkg/models/sdkerrors/sdkerror.go rename to internal/sdk/models/errors/sdkerror.go index 5c1affd..c5a89b6 100644 --- a/internal/sdk/pkg/models/sdkerrors/sdkerror.go +++ b/internal/sdk/models/errors/sdkerror.go @@ -1,6 +1,6 @@ // Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. -package sdkerrors +package errors import ( "fmt" diff --git a/internal/sdk/pkg/models/operations/addplaylistcontents.go b/internal/sdk/models/operations/addplaylistcontents.go similarity index 100% rename from internal/sdk/pkg/models/operations/addplaylistcontents.go rename to internal/sdk/models/operations/addplaylistcontents.go diff --git a/internal/sdk/pkg/models/operations/applyupdates.go b/internal/sdk/models/operations/applyupdates.go similarity index 100% rename from internal/sdk/pkg/models/operations/applyupdates.go rename to internal/sdk/models/operations/applyupdates.go diff --git a/internal/sdk/pkg/models/operations/cancelserveractivities.go b/internal/sdk/models/operations/cancelserveractivities.go similarity index 100% rename from internal/sdk/pkg/models/operations/cancelserveractivities.go rename to internal/sdk/models/operations/cancelserveractivities.go diff --git a/internal/sdk/pkg/models/operations/checkforupdates.go b/internal/sdk/models/operations/checkforupdates.go similarity index 100% rename from internal/sdk/pkg/models/operations/checkforupdates.go rename to internal/sdk/models/operations/checkforupdates.go diff --git a/internal/sdk/pkg/models/operations/clearplaylistcontents.go b/internal/sdk/models/operations/clearplaylistcontents.go similarity index 100% rename from internal/sdk/pkg/models/operations/clearplaylistcontents.go rename to internal/sdk/models/operations/clearplaylistcontents.go diff --git a/internal/sdk/pkg/models/operations/createplaylist.go b/internal/sdk/models/operations/createplaylist.go similarity index 100% rename from internal/sdk/pkg/models/operations/createplaylist.go rename to internal/sdk/models/operations/createplaylist.go diff --git a/internal/sdk/pkg/models/operations/deletelibrary.go b/internal/sdk/models/operations/deletelibrary.go similarity index 100% rename from internal/sdk/pkg/models/operations/deletelibrary.go rename to internal/sdk/models/operations/deletelibrary.go diff --git a/internal/sdk/pkg/models/operations/deleteplaylist.go b/internal/sdk/models/operations/deleteplaylist.go similarity index 100% rename from internal/sdk/pkg/models/operations/deleteplaylist.go rename to internal/sdk/models/operations/deleteplaylist.go diff --git a/internal/sdk/pkg/models/operations/enablepapertrail.go b/internal/sdk/models/operations/enablepapertrail.go similarity index 100% rename from internal/sdk/pkg/models/operations/enablepapertrail.go rename to internal/sdk/models/operations/enablepapertrail.go diff --git a/internal/sdk/pkg/models/operations/getavailableclients.go b/internal/sdk/models/operations/getavailableclients.go similarity index 100% rename from internal/sdk/pkg/models/operations/getavailableclients.go rename to internal/sdk/models/operations/getavailableclients.go diff --git a/internal/sdk/pkg/models/operations/getbutlertasks.go b/internal/sdk/models/operations/getbutlertasks.go similarity index 100% rename from internal/sdk/pkg/models/operations/getbutlertasks.go rename to internal/sdk/models/operations/getbutlertasks.go diff --git a/internal/sdk/pkg/models/operations/getdevices.go b/internal/sdk/models/operations/getdevices.go similarity index 100% rename from internal/sdk/pkg/models/operations/getdevices.go rename to internal/sdk/models/operations/getdevices.go diff --git a/internal/sdk/pkg/models/operations/getfilehash.go b/internal/sdk/models/operations/getfilehash.go similarity index 100% rename from internal/sdk/pkg/models/operations/getfilehash.go rename to internal/sdk/models/operations/getfilehash.go diff --git a/internal/sdk/pkg/models/operations/getglobalhubs.go b/internal/sdk/models/operations/getglobalhubs.go similarity index 100% rename from internal/sdk/pkg/models/operations/getglobalhubs.go rename to internal/sdk/models/operations/getglobalhubs.go diff --git a/internal/sdk/pkg/models/operations/getlibraries.go b/internal/sdk/models/operations/getlibraries.go similarity index 100% rename from internal/sdk/pkg/models/operations/getlibraries.go rename to internal/sdk/models/operations/getlibraries.go diff --git a/internal/sdk/pkg/models/operations/getlibrary.go b/internal/sdk/models/operations/getlibrary.go similarity index 99% rename from internal/sdk/pkg/models/operations/getlibrary.go rename to internal/sdk/models/operations/getlibrary.go index fc24123..bbe2025 100644 --- a/internal/sdk/pkg/models/operations/getlibrary.go +++ b/internal/sdk/models/operations/getlibrary.go @@ -5,7 +5,7 @@ package operations import ( "encoding/json" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" "net/http" ) diff --git a/internal/sdk/pkg/models/operations/getlibraryhubs.go b/internal/sdk/models/operations/getlibraryhubs.go similarity index 99% rename from internal/sdk/pkg/models/operations/getlibraryhubs.go rename to internal/sdk/models/operations/getlibraryhubs.go index 8742142..75f26ec 100644 --- a/internal/sdk/pkg/models/operations/getlibraryhubs.go +++ b/internal/sdk/models/operations/getlibraryhubs.go @@ -5,8 +5,8 @@ package operations import ( "encoding/json" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/types" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/types" "net/http" ) diff --git a/internal/sdk/pkg/models/operations/getlibraryitems.go b/internal/sdk/models/operations/getlibraryitems.go similarity index 99% rename from internal/sdk/pkg/models/operations/getlibraryitems.go rename to internal/sdk/models/operations/getlibraryitems.go index 17a9bad..cbf619d 100644 --- a/internal/sdk/pkg/models/operations/getlibraryitems.go +++ b/internal/sdk/models/operations/getlibraryitems.go @@ -5,8 +5,8 @@ package operations import ( "encoding/json" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/types" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/types" "net/http" ) diff --git a/internal/sdk/pkg/models/operations/getmetadata.go b/internal/sdk/models/operations/getmetadata.go similarity index 99% rename from internal/sdk/pkg/models/operations/getmetadata.go rename to internal/sdk/models/operations/getmetadata.go index ba90cc6..32f0610 100644 --- a/internal/sdk/pkg/models/operations/getmetadata.go +++ b/internal/sdk/models/operations/getmetadata.go @@ -3,8 +3,8 @@ package operations import ( - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/types" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/types" "net/http" ) diff --git a/internal/sdk/pkg/models/operations/getmetadatachildren.go b/internal/sdk/models/operations/getmetadatachildren.go similarity index 100% rename from internal/sdk/pkg/models/operations/getmetadatachildren.go rename to internal/sdk/models/operations/getmetadatachildren.go diff --git a/internal/sdk/pkg/models/operations/getmyplexaccount.go b/internal/sdk/models/operations/getmyplexaccount.go similarity index 100% rename from internal/sdk/pkg/models/operations/getmyplexaccount.go rename to internal/sdk/models/operations/getmyplexaccount.go diff --git a/internal/sdk/pkg/models/operations/getondeck.go b/internal/sdk/models/operations/getondeck.go similarity index 99% rename from internal/sdk/pkg/models/operations/getondeck.go rename to internal/sdk/models/operations/getondeck.go index 41baa4d..1185ab2 100644 --- a/internal/sdk/pkg/models/operations/getondeck.go +++ b/internal/sdk/models/operations/getondeck.go @@ -3,7 +3,7 @@ package operations import ( - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" "net/http" "time" ) diff --git a/internal/sdk/pkg/models/operations/getpin.go b/internal/sdk/models/operations/getpin.go similarity index 98% rename from internal/sdk/pkg/models/operations/getpin.go rename to internal/sdk/models/operations/getpin.go index fa786a6..89e42cf 100644 --- a/internal/sdk/pkg/models/operations/getpin.go +++ b/internal/sdk/models/operations/getpin.go @@ -3,7 +3,7 @@ package operations import ( - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" "net/http" "time" ) diff --git a/internal/sdk/pkg/models/operations/getplaylist.go b/internal/sdk/models/operations/getplaylist.go similarity index 100% rename from internal/sdk/pkg/models/operations/getplaylist.go rename to internal/sdk/models/operations/getplaylist.go diff --git a/internal/sdk/pkg/models/operations/getplaylistcontents.go b/internal/sdk/models/operations/getplaylistcontents.go similarity index 99% rename from internal/sdk/pkg/models/operations/getplaylistcontents.go rename to internal/sdk/models/operations/getplaylistcontents.go index 8d49b99..deb00aa 100644 --- a/internal/sdk/pkg/models/operations/getplaylistcontents.go +++ b/internal/sdk/models/operations/getplaylistcontents.go @@ -3,8 +3,8 @@ package operations import ( - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/types" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/types" "net/http" ) diff --git a/internal/sdk/pkg/models/operations/getplaylists.go b/internal/sdk/models/operations/getplaylists.go similarity index 100% rename from internal/sdk/pkg/models/operations/getplaylists.go rename to internal/sdk/models/operations/getplaylists.go diff --git a/internal/sdk/pkg/models/operations/getrecentlyadded.go b/internal/sdk/models/operations/getrecentlyadded.go similarity index 99% rename from internal/sdk/pkg/models/operations/getrecentlyadded.go rename to internal/sdk/models/operations/getrecentlyadded.go index 607ae3a..6252bfc 100644 --- a/internal/sdk/pkg/models/operations/getrecentlyadded.go +++ b/internal/sdk/models/operations/getrecentlyadded.go @@ -3,7 +3,7 @@ package operations import ( - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" "net/http" "time" ) diff --git a/internal/sdk/pkg/models/operations/getresizedphoto.go b/internal/sdk/models/operations/getresizedphoto.go similarity index 98% rename from internal/sdk/pkg/models/operations/getresizedphoto.go rename to internal/sdk/models/operations/getresizedphoto.go index edcfd96..e745f21 100644 --- a/internal/sdk/pkg/models/operations/getresizedphoto.go +++ b/internal/sdk/models/operations/getresizedphoto.go @@ -5,7 +5,7 @@ package operations import ( "encoding/json" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" "net/http" ) diff --git a/internal/sdk/pkg/models/operations/getsearchresults.go b/internal/sdk/models/operations/getsearchresults.go similarity index 99% rename from internal/sdk/pkg/models/operations/getsearchresults.go rename to internal/sdk/models/operations/getsearchresults.go index 10718c9..72fa57e 100644 --- a/internal/sdk/pkg/models/operations/getsearchresults.go +++ b/internal/sdk/models/operations/getsearchresults.go @@ -3,7 +3,7 @@ package operations import ( - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" "net/http" "time" ) diff --git a/internal/sdk/pkg/models/operations/getserveractivities.go b/internal/sdk/models/operations/getserveractivities.go similarity index 100% rename from internal/sdk/pkg/models/operations/getserveractivities.go rename to internal/sdk/models/operations/getserveractivities.go diff --git a/internal/sdk/pkg/models/operations/getservercapabilities.go b/internal/sdk/models/operations/getservercapabilities.go similarity index 100% rename from internal/sdk/pkg/models/operations/getservercapabilities.go rename to internal/sdk/models/operations/getservercapabilities.go diff --git a/internal/sdk/pkg/models/operations/getserveridentity.go b/internal/sdk/models/operations/getserveridentity.go similarity index 100% rename from internal/sdk/pkg/models/operations/getserveridentity.go rename to internal/sdk/models/operations/getserveridentity.go diff --git a/internal/sdk/pkg/models/operations/getserverlist.go b/internal/sdk/models/operations/getserverlist.go similarity index 100% rename from internal/sdk/pkg/models/operations/getserverlist.go rename to internal/sdk/models/operations/getserverlist.go diff --git a/internal/sdk/pkg/models/operations/getserverpreferences.go b/internal/sdk/models/operations/getserverpreferences.go similarity index 100% rename from internal/sdk/pkg/models/operations/getserverpreferences.go rename to internal/sdk/models/operations/getserverpreferences.go diff --git a/internal/sdk/pkg/models/operations/getsessionhistory.go b/internal/sdk/models/operations/getsessionhistory.go similarity index 97% rename from internal/sdk/pkg/models/operations/getsessionhistory.go rename to internal/sdk/models/operations/getsessionhistory.go index da3fcbc..37d4334 100644 --- a/internal/sdk/pkg/models/operations/getsessionhistory.go +++ b/internal/sdk/models/operations/getsessionhistory.go @@ -3,8 +3,8 @@ package operations import ( - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/types" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/types" "net/http" ) diff --git a/internal/sdk/pkg/models/operations/getsessions.go b/internal/sdk/models/operations/getsessions.go similarity index 100% rename from internal/sdk/pkg/models/operations/getsessions.go rename to internal/sdk/models/operations/getsessions.go diff --git a/internal/sdk/pkg/models/operations/getsourceconnectioninformation.go b/internal/sdk/models/operations/getsourceconnectioninformation.go similarity index 100% rename from internal/sdk/pkg/models/operations/getsourceconnectioninformation.go rename to internal/sdk/models/operations/getsourceconnectioninformation.go diff --git a/internal/sdk/pkg/models/operations/getstatistics.go b/internal/sdk/models/operations/getstatistics.go similarity index 100% rename from internal/sdk/pkg/models/operations/getstatistics.go rename to internal/sdk/models/operations/getstatistics.go diff --git a/internal/sdk/pkg/models/operations/gettimeline.go b/internal/sdk/models/operations/gettimeline.go similarity index 100% rename from internal/sdk/pkg/models/operations/gettimeline.go rename to internal/sdk/models/operations/gettimeline.go diff --git a/internal/sdk/pkg/models/operations/gettoken.go b/internal/sdk/models/operations/gettoken.go similarity index 100% rename from internal/sdk/pkg/models/operations/gettoken.go rename to internal/sdk/models/operations/gettoken.go diff --git a/internal/sdk/pkg/models/operations/gettranscodesessions.go b/internal/sdk/models/operations/gettranscodesessions.go similarity index 100% rename from internal/sdk/pkg/models/operations/gettranscodesessions.go rename to internal/sdk/models/operations/gettranscodesessions.go diff --git a/internal/sdk/pkg/models/operations/gettransienttoken.go b/internal/sdk/models/operations/gettransienttoken.go similarity index 100% rename from internal/sdk/pkg/models/operations/gettransienttoken.go rename to internal/sdk/models/operations/gettransienttoken.go diff --git a/internal/sdk/pkg/models/operations/getupdatestatus.go b/internal/sdk/models/operations/getupdatestatus.go similarity index 100% rename from internal/sdk/pkg/models/operations/getupdatestatus.go rename to internal/sdk/models/operations/getupdatestatus.go diff --git a/internal/sdk/pkg/models/operations/logline.go b/internal/sdk/models/operations/logline.go similarity index 100% rename from internal/sdk/pkg/models/operations/logline.go rename to internal/sdk/models/operations/logline.go diff --git a/internal/sdk/pkg/models/operations/logmultiline.go b/internal/sdk/models/operations/logmultiline.go similarity index 100% rename from internal/sdk/pkg/models/operations/logmultiline.go rename to internal/sdk/models/operations/logmultiline.go diff --git a/internal/sdk/pkg/models/operations/markplayed.go b/internal/sdk/models/operations/markplayed.go similarity index 100% rename from internal/sdk/pkg/models/operations/markplayed.go rename to internal/sdk/models/operations/markplayed.go diff --git a/internal/sdk/pkg/models/operations/markunplayed.go b/internal/sdk/models/operations/markunplayed.go similarity index 100% rename from internal/sdk/pkg/models/operations/markunplayed.go rename to internal/sdk/models/operations/markunplayed.go diff --git a/internal/sdk/pkg/models/operations/options.go b/internal/sdk/models/operations/options.go similarity index 95% rename from internal/sdk/pkg/models/operations/options.go rename to internal/sdk/models/operations/options.go index 7089b05..7e714fa 100644 --- a/internal/sdk/pkg/models/operations/options.go +++ b/internal/sdk/models/operations/options.go @@ -4,7 +4,7 @@ package operations import ( "errors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" ) var ErrUnsupportedOption = errors.New("unsupported option") diff --git a/internal/sdk/pkg/models/operations/performsearch.go b/internal/sdk/models/operations/performsearch.go similarity index 97% rename from internal/sdk/pkg/models/operations/performsearch.go rename to internal/sdk/models/operations/performsearch.go index b067197..4eb99bb 100644 --- a/internal/sdk/pkg/models/operations/performsearch.go +++ b/internal/sdk/models/operations/performsearch.go @@ -3,7 +3,7 @@ package operations import ( - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" "net/http" ) diff --git a/internal/sdk/pkg/models/operations/performvoicesearch.go b/internal/sdk/models/operations/performvoicesearch.go similarity index 97% rename from internal/sdk/pkg/models/operations/performvoicesearch.go rename to internal/sdk/models/operations/performvoicesearch.go index 08b731b..1607907 100644 --- a/internal/sdk/pkg/models/operations/performvoicesearch.go +++ b/internal/sdk/models/operations/performvoicesearch.go @@ -3,7 +3,7 @@ package operations import ( - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" "net/http" ) diff --git a/internal/sdk/pkg/models/operations/refreshlibrary.go b/internal/sdk/models/operations/refreshlibrary.go similarity index 100% rename from internal/sdk/pkg/models/operations/refreshlibrary.go rename to internal/sdk/models/operations/refreshlibrary.go diff --git a/internal/sdk/pkg/models/operations/searchlibrary.go b/internal/sdk/models/operations/searchlibrary.go similarity index 100% rename from internal/sdk/pkg/models/operations/searchlibrary.go rename to internal/sdk/models/operations/searchlibrary.go diff --git a/internal/sdk/pkg/models/operations/startalltasks.go b/internal/sdk/models/operations/startalltasks.go similarity index 100% rename from internal/sdk/pkg/models/operations/startalltasks.go rename to internal/sdk/models/operations/startalltasks.go diff --git a/internal/sdk/pkg/models/operations/starttask.go b/internal/sdk/models/operations/starttask.go similarity index 100% rename from internal/sdk/pkg/models/operations/starttask.go rename to internal/sdk/models/operations/starttask.go diff --git a/internal/sdk/pkg/models/operations/startuniversaltranscode.go b/internal/sdk/models/operations/startuniversaltranscode.go similarity index 100% rename from internal/sdk/pkg/models/operations/startuniversaltranscode.go rename to internal/sdk/models/operations/startuniversaltranscode.go diff --git a/internal/sdk/pkg/models/operations/stopalltasks.go b/internal/sdk/models/operations/stopalltasks.go similarity index 100% rename from internal/sdk/pkg/models/operations/stopalltasks.go rename to internal/sdk/models/operations/stopalltasks.go diff --git a/internal/sdk/pkg/models/operations/stoptask.go b/internal/sdk/models/operations/stoptask.go similarity index 100% rename from internal/sdk/pkg/models/operations/stoptask.go rename to internal/sdk/models/operations/stoptask.go diff --git a/internal/sdk/pkg/models/operations/stoptranscodesession.go b/internal/sdk/models/operations/stoptranscodesession.go similarity index 100% rename from internal/sdk/pkg/models/operations/stoptranscodesession.go rename to internal/sdk/models/operations/stoptranscodesession.go diff --git a/internal/sdk/pkg/models/operations/updateplaylist.go b/internal/sdk/models/operations/updateplaylist.go similarity index 100% rename from internal/sdk/pkg/models/operations/updateplaylist.go rename to internal/sdk/models/operations/updateplaylist.go diff --git a/internal/sdk/pkg/models/operations/updateplayprogress.go b/internal/sdk/models/operations/updateplayprogress.go similarity index 100% rename from internal/sdk/pkg/models/operations/updateplayprogress.go rename to internal/sdk/models/operations/updateplayprogress.go diff --git a/internal/sdk/pkg/models/operations/uploadplaylist.go b/internal/sdk/models/operations/uploadplaylist.go similarity index 100% rename from internal/sdk/pkg/models/operations/uploadplaylist.go rename to internal/sdk/models/operations/uploadplaylist.go diff --git a/internal/sdk/pkg/models/shared/security.go b/internal/sdk/models/shared/security.go similarity index 100% rename from internal/sdk/pkg/models/shared/security.go rename to internal/sdk/models/shared/security.go diff --git a/internal/sdk/playlists.go b/internal/sdk/playlists.go index d2d9209..362474f 100644 --- a/internal/sdk/playlists.go +++ b/internal/sdk/playlists.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Playlists are ordered collections of media. They can be dumb (just a list of media) or smart (based on a media query, such as "all albums from 2017"). @@ -33,28 +34,65 @@ func newPlaylists(sdkConfig sdkConfiguration) *Playlists { // - `uri` - The content URI for what we're playing (e.g. `server://1234/com.plexapp.plugins.library/library/metadata/1`). // - `playQueueID` - To create a playlist from an existing play queue. func (s *Playlists) CreatePlaylist(ctx context.Context, request operations.CreatePlaylistRequest) (*operations.CreatePlaylistResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/playlists" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "createPlaylist", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "POST", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/playlists") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "POST", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.CreatePlaylistResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -64,17 +102,10 @@ func (s *Playlists) CreatePlaylist(ctx context.Context, request operations.Creat httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.CreatePlaylistResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.CreatePlaylistResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -82,12 +113,12 @@ func (s *Playlists) CreatePlaylist(ctx context.Context, request operations.Creat res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.CreatePlaylistPlaylistsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -95,8 +126,10 @@ func (s *Playlists) CreatePlaylist(ctx context.Context, request operations.Creat res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -105,28 +138,65 @@ func (s *Playlists) CreatePlaylist(ctx context.Context, request operations.Creat // GetPlaylists - Get All Playlists // Get All Playlists given the specified filters. func (s *Playlists) GetPlaylists(ctx context.Context, request operations.GetPlaylistsRequest) (*operations.GetPlaylistsResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/playlists" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getPlaylists", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/playlists") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetPlaylistsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -136,17 +206,10 @@ func (s *Playlists) GetPlaylists(ctx context.Context, request operations.GetPlay httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetPlaylistsResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetPlaylistsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -154,12 +217,12 @@ func (s *Playlists) GetPlaylists(ctx context.Context, request operations.GetPlay res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetPlaylistsPlaylistsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -167,8 +230,10 @@ func (s *Playlists) GetPlaylists(ctx context.Context, request operations.GetPlay res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -178,27 +243,61 @@ func (s *Playlists) GetPlaylists(ctx context.Context, request operations.GetPlay // Gets detailed metadata for a playlist. A playlist for many purposes (rating, editing metadata, tagging), can be treated like a regular metadata item: // Smart playlist details contain the `content` attribute. This is the content URI for the generator. This can then be parsed by a client to provide smart playlist editing. func (s *Playlists) GetPlaylist(ctx context.Context, request operations.GetPlaylistRequest) (*operations.GetPlaylistResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getPlaylist", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/playlists/{playlistID}", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/playlists/{playlistID}", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetPlaylistResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -208,17 +307,10 @@ func (s *Playlists) GetPlaylist(ctx context.Context, request operations.GetPlayl httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetPlaylistResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetPlaylistResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -226,12 +318,12 @@ func (s *Playlists) GetPlaylist(ctx context.Context, request operations.GetPlayl res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetPlaylistPlaylistsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -239,8 +331,10 @@ func (s *Playlists) GetPlaylist(ctx context.Context, request operations.GetPlayl res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -249,27 +343,61 @@ func (s *Playlists) GetPlaylist(ctx context.Context, request operations.GetPlayl // DeletePlaylist - Deletes a Playlist // This endpoint will delete a playlist func (s *Playlists) DeletePlaylist(ctx context.Context, request operations.DeletePlaylistRequest) (*operations.DeletePlaylistResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "deletePlaylist", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/playlists/{playlistID}", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/playlists/{playlistID}", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "DELETE", url, nil) + req, err := http.NewRequestWithContext(ctx, "DELETE", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.DeletePlaylistResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -279,20 +407,13 @@ func (s *Playlists) DeletePlaylist(ctx context.Context, request operations.Delet httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.DeletePlaylistResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.DeletePlaylistResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -300,8 +421,10 @@ func (s *Playlists) DeletePlaylist(ctx context.Context, request operations.Delet res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -310,31 +433,65 @@ func (s *Playlists) DeletePlaylist(ctx context.Context, request operations.Delet // UpdatePlaylist - Update a Playlist // From PMS version 1.9.1 clients can also edit playlist metadata using this endpoint as they would via `PUT /library/metadata/{playlistID}` func (s *Playlists) UpdatePlaylist(ctx context.Context, request operations.UpdatePlaylistRequest) (*operations.UpdatePlaylistResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "updatePlaylist", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/playlists/{playlistID}", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/playlists/{playlistID}", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "PUT", url, nil) + req, err := http.NewRequestWithContext(ctx, "PUT", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.UpdatePlaylistResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -344,20 +501,13 @@ func (s *Playlists) UpdatePlaylist(ctx context.Context, request operations.Updat httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.UpdatePlaylistResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.UpdatePlaylistResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -365,8 +515,10 @@ func (s *Playlists) UpdatePlaylist(ctx context.Context, request operations.Updat res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -378,31 +530,65 @@ func (s *Playlists) UpdatePlaylist(ctx context.Context, request operations.Updat // For example, you could use this to display a list of recently added albums vis a smart playlist. // Note that for dumb playlists, items have a `playlistItemID` attribute which is used for deleting or moving items. func (s *Playlists) GetPlaylistContents(ctx context.Context, request operations.GetPlaylistContentsRequest) (*operations.GetPlaylistContentsResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getPlaylistContents", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/playlists/{playlistID}/items", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/playlists/{playlistID}/items", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetPlaylistContentsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -412,17 +598,10 @@ func (s *Playlists) GetPlaylistContents(ctx context.Context, request operations. httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetPlaylistContentsResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetPlaylistContentsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -430,12 +609,12 @@ func (s *Playlists) GetPlaylistContents(ctx context.Context, request operations. res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetPlaylistContentsPlaylistsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -443,8 +622,10 @@ func (s *Playlists) GetPlaylistContents(ctx context.Context, request operations. res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -453,27 +634,61 @@ func (s *Playlists) GetPlaylistContents(ctx context.Context, request operations. // ClearPlaylistContents - Delete Playlist Contents // Clears a playlist, only works with dumb playlists. Returns the playlist. func (s *Playlists) ClearPlaylistContents(ctx context.Context, request operations.ClearPlaylistContentsRequest) (*operations.ClearPlaylistContentsResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "clearPlaylistContents", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/playlists/{playlistID}/items", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/playlists/{playlistID}/items", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "DELETE", url, nil) + req, err := http.NewRequestWithContext(ctx, "DELETE", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.ClearPlaylistContentsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -483,20 +698,13 @@ func (s *Playlists) ClearPlaylistContents(ctx context.Context, request operation httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.ClearPlaylistContentsResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.ClearPlaylistContentsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -504,8 +712,10 @@ func (s *Playlists) ClearPlaylistContents(ctx context.Context, request operation res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -515,31 +725,65 @@ func (s *Playlists) ClearPlaylistContents(ctx context.Context, request operation // Adds a generator to a playlist, same parameters as the POST to create. With a dumb playlist, this adds the specified items to the playlist. // With a smart playlist, passing a new `uri` parameter replaces the rules for the playlist. Returns the playlist. func (s *Playlists) AddPlaylistContents(ctx context.Context, request operations.AddPlaylistContentsRequest) (*operations.AddPlaylistContentsResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "addPlaylistContents", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/playlists/{playlistID}/items", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/playlists/{playlistID}/items", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "PUT", url, nil) + req, err := http.NewRequestWithContext(ctx, "PUT", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.AddPlaylistContentsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -549,17 +793,10 @@ func (s *Playlists) AddPlaylistContents(ctx context.Context, request operations. httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.AddPlaylistContentsResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.AddPlaylistContentsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -567,12 +804,12 @@ func (s *Playlists) AddPlaylistContents(ctx context.Context, request operations. res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.AddPlaylistContentsPlaylistsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -580,8 +817,10 @@ func (s *Playlists) AddPlaylistContents(ctx context.Context, request operations. res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -590,28 +829,65 @@ func (s *Playlists) AddPlaylistContents(ctx context.Context, request operations. // UploadPlaylist - Upload Playlist // Imports m3u playlists by passing a path on the server to scan for m3u-formatted playlist files, or a path to a single playlist file. func (s *Playlists) UploadPlaylist(ctx context.Context, request operations.UploadPlaylistRequest) (*operations.UploadPlaylistResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/playlists/upload" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "uploadPlaylist", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "POST", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/playlists/upload") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "POST", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.UploadPlaylistResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -621,20 +897,13 @@ func (s *Playlists) UploadPlaylist(ctx context.Context, request operations.Uploa httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.UploadPlaylistResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.UploadPlaylistResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -642,8 +911,10 @@ func (s *Playlists) UploadPlaylist(ctx context.Context, request operations.Uploa res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/sdk/plex.go b/internal/sdk/plex.go index aa9140e..1b42a27 100644 --- a/internal/sdk/plex.go +++ b/internal/sdk/plex.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Plex - API Calls that perform operations directly against https://Plex.tv @@ -28,6 +29,13 @@ func newPlex(sdkConfig sdkConfiguration) *Plex { // GetPin - Get a Pin // Retrieve a Pin from Plex.tv for authentication flows func (s *Plex) GetPin(ctx context.Context, request operations.GetPinRequest, opts ...operations.Option) (*operations.GetPinResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getPin", + OAuth2Scopes: []string{}, + SecuritySource: nil, + } + o := operations.Options{} supportedOptions := []string{ operations.SupportedOptionServerURL, @@ -43,14 +51,17 @@ func (s *Plex) GetPin(ctx context.Context, request operations.GetPinRequest, opt baseURL = *o.ServerURL } - url := strings.TrimSuffix(baseURL, "/") + "/pins" + opURL, err := url.JoinPath(baseURL, "/pins") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } - req, err := http.NewRequestWithContext(ctx, "POST", url, nil) + req, err := http.NewRequestWithContext(ctx, "POST", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) utils.PopulateHeaders(ctx, req, request) @@ -58,14 +69,37 @@ func (s *Plex) GetPin(ctx context.Context, request operations.GetPinRequest, opt return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.DefaultClient - - httpRes, err := client.Do(req) + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetPinResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -75,17 +109,10 @@ func (s *Plex) GetPin(ctx context.Context, request operations.GetPinRequest, opt httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetPinResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetPinResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -93,11 +120,11 @@ func (s *Plex) GetPin(ctx context.Context, request operations.GetPinRequest, opt res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetPinPlexResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -105,8 +132,10 @@ func (s *Plex) GetPin(ctx context.Context, request operations.GetPinRequest, opt res.FourHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -115,6 +144,13 @@ func (s *Plex) GetPin(ctx context.Context, request operations.GetPinRequest, opt // GetToken - Get Access Token // Retrieve an Access Token from Plex.tv after the Pin has already been authenticated func (s *Plex) GetToken(ctx context.Context, request operations.GetTokenRequest, opts ...operations.Option) (*operations.GetTokenResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getToken", + OAuth2Scopes: []string{}, + SecuritySource: nil, + } + o := operations.Options{} supportedOptions := []string{ operations.SupportedOptionServerURL, @@ -130,28 +166,51 @@ func (s *Plex) GetToken(ctx context.Context, request operations.GetTokenRequest, baseURL = *o.ServerURL } - url, err := utils.GenerateURL(ctx, baseURL, "/pins/{pinID}", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/pins/{pinID}", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) utils.PopulateHeaders(ctx, req, request) - client := s.sdkConfiguration.DefaultClient - - httpRes, err := client.Do(req) + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetTokenResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -161,18 +220,11 @@ func (s *Plex) GetToken(ctx context.Context, request operations.GetTokenRequest, httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetTokenResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: case httpRes.StatusCode == 400: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetTokenResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -180,8 +232,10 @@ func (s *Plex) GetToken(ctx context.Context, request operations.GetTokenRequest, res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/sdk/sdk.go b/internal/sdk/plexapi.go similarity index 90% rename from internal/sdk/sdk.go rename to internal/sdk/plexapi.go index c36d2fc..a7c6358 100644 --- a/internal/sdk/sdk.go +++ b/internal/sdk/plexapi.go @@ -6,8 +6,9 @@ import ( "context" "encoding/json" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/shared" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/shared" "net/http" "time" ) @@ -42,8 +43,7 @@ func Float32(f float32) *float32 { return &f } func Float64(f float64) *float64 { return &f } type sdkConfiguration struct { - DefaultClient HTTPClient - SecurityClient HTTPClient + Client HTTPClient Security func(context.Context) (interface{}, error) ServerURL string ServerIndex int @@ -54,6 +54,7 @@ type sdkConfiguration struct { GenVersion string UserAgent string RetryConfig *utils.RetryConfig + Hooks *hooks.Hooks } func (c *sdkConfiguration) GetServerDetails() (string, map[string]string) { @@ -227,13 +228,13 @@ func WithPort(port string) SDKOption { // WithClient allows the overriding of the default HTTP client used by the SDK func WithClient(client HTTPClient) SDKOption { return func(sdk *PlexAPI) { - sdk.sdkConfiguration.DefaultClient = client + sdk.sdkConfiguration.Client = client } } func withSecurity(security interface{}) func(context.Context) (interface{}, error) { return func(context.Context) (interface{}, error) { - return &security, nil + return security, nil } } @@ -265,9 +266,9 @@ func New(opts ...SDKOption) *PlexAPI { sdkConfiguration: sdkConfiguration{ Language: "go", OpenAPIDocVersion: "0.0.3", - SDKVersion: "0.6.2", - GenVersion: "2.280.6", - UserAgent: "speakeasy-sdk/go 0.6.2 2.280.6 0.0.3 PlexAPI", + SDKVersion: "0.0.1", + GenVersion: "2.286.4", + UserAgent: "speakeasy-sdk/go 0.0.1 2.286.4 0.0.3 github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk", ServerDefaults: []map[string]string{ { "protocol": "http", @@ -275,6 +276,7 @@ func New(opts ...SDKOption) *PlexAPI { "port": "32400", }, }, + Hooks: hooks.New(), }, } for _, opt := range opts { @@ -282,15 +284,15 @@ func New(opts ...SDKOption) *PlexAPI { } // Use WithClient to override the default client if you would like to customize the timeout - if sdk.sdkConfiguration.DefaultClient == nil { - sdk.sdkConfiguration.DefaultClient = &http.Client{Timeout: 60 * time.Second} + if sdk.sdkConfiguration.Client == nil { + sdk.sdkConfiguration.Client = &http.Client{Timeout: 60 * time.Second} } - if sdk.sdkConfiguration.SecurityClient == nil { - if sdk.sdkConfiguration.Security != nil { - sdk.sdkConfiguration.SecurityClient = utils.ConfigureSecurityClient(sdk.sdkConfiguration.DefaultClient, sdk.sdkConfiguration.Security) - } else { - sdk.sdkConfiguration.SecurityClient = sdk.sdkConfiguration.DefaultClient - } + + currentServerURL, _ := sdk.sdkConfiguration.GetServerDetails() + serverURL := currentServerURL + serverURL, sdk.sdkConfiguration.Client = sdk.sdkConfiguration.Hooks.SDKInit(currentServerURL, sdk.sdkConfiguration.Client) + if serverURL != currentServerURL { + sdk.sdkConfiguration.ServerURL = serverURL } sdk.Server = newServer(sdk.sdkConfiguration) diff --git a/internal/sdk/search.go b/internal/sdk/search.go index 79a959c..e88e7b9 100644 --- a/internal/sdk/search.go +++ b/internal/sdk/search.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Search - API Calls that perform search operations with Plex Media Server @@ -40,28 +41,65 @@ func newSearch(sdkConfig sdkConfiguration) *Search { // // This request is intended to be very fast, and called as the user types. func (s *Search) PerformSearch(ctx context.Context, request operations.PerformSearchRequest) (*operations.PerformSearchResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/hubs/search" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "performSearch", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/hubs/search") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.PerformSearchResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -71,20 +109,13 @@ func (s *Search) PerformSearch(ctx context.Context, request operations.PerformSe httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.PerformSearchResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.PerformSearchResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -92,8 +123,10 @@ func (s *Search) PerformSearch(ctx context.Context, request operations.PerformSe res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -105,28 +138,65 @@ func (s *Search) PerformSearch(ctx context.Context, request operations.PerformSe // Whenever possible, clients should limit the search to the appropriate type. // Results, as well as their containing per-type hubs, contain a `distance` attribute which can be used to judge result quality. func (s *Search) PerformVoiceSearch(ctx context.Context, request operations.PerformVoiceSearchRequest) (*operations.PerformVoiceSearchResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/hubs/search/voice" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "performVoiceSearch", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/hubs/search/voice") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.PerformVoiceSearchResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -136,20 +206,13 @@ func (s *Search) PerformVoiceSearch(ctx context.Context, request operations.Perf httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.PerformVoiceSearchResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.PerformVoiceSearchResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -157,8 +220,10 @@ func (s *Search) PerformVoiceSearch(ctx context.Context, request operations.Perf res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -167,28 +232,65 @@ func (s *Search) PerformVoiceSearch(ctx context.Context, request operations.Perf // GetSearchResults - Get Search Results // This will search the database for the string provided. func (s *Search) GetSearchResults(ctx context.Context, request operations.GetSearchResultsRequest) (*operations.GetSearchResultsResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/search" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getSearchResults", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/search") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetSearchResultsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -198,17 +300,10 @@ func (s *Search) GetSearchResults(ctx context.Context, request operations.GetSea httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetSearchResultsResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetSearchResultsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -216,12 +311,12 @@ func (s *Search) GetSearchResults(ctx context.Context, request operations.GetSea res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetSearchResultsSearchResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -229,8 +324,10 @@ func (s *Search) GetSearchResults(ctx context.Context, request operations.GetSea res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/sdk/server.go b/internal/sdk/server.go index 9987a4e..e6805c9 100644 --- a/internal/sdk/server.go +++ b/internal/sdk/server.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Server - Operations against the Plex Media Server System. @@ -28,24 +29,61 @@ func newServer(sdkConfig sdkConfiguration) *Server { // GetServerCapabilities - Server Capabilities // Server Capabilities func (s *Server) GetServerCapabilities(ctx context.Context) (*operations.GetServerCapabilitiesResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getServerCapabilities", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetServerCapabilitiesResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -55,17 +93,10 @@ func (s *Server) GetServerCapabilities(ctx context.Context) (*operations.GetServ httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetServerCapabilitiesResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetServerCapabilitiesResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -73,12 +104,12 @@ func (s *Server) GetServerCapabilities(ctx context.Context) (*operations.GetServ res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetServerCapabilitiesServerResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -86,8 +117,10 @@ func (s *Server) GetServerCapabilities(ctx context.Context) (*operations.GetServ res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -96,24 +129,61 @@ func (s *Server) GetServerCapabilities(ctx context.Context) (*operations.GetServ // GetServerPreferences - Get Server Preferences // Get Server Preferences func (s *Server) GetServerPreferences(ctx context.Context) (*operations.GetServerPreferencesResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/:/prefs" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getServerPreferences", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/:/prefs") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetServerPreferencesResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -123,17 +193,10 @@ func (s *Server) GetServerPreferences(ctx context.Context) (*operations.GetServe httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetServerPreferencesResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetServerPreferencesResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -141,12 +204,12 @@ func (s *Server) GetServerPreferences(ctx context.Context) (*operations.GetServe res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetServerPreferencesServerResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -154,8 +217,10 @@ func (s *Server) GetServerPreferences(ctx context.Context) (*operations.GetServe res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -164,24 +229,61 @@ func (s *Server) GetServerPreferences(ctx context.Context) (*operations.GetServe // GetAvailableClients - Get Available Clients // Get Available Clients func (s *Server) GetAvailableClients(ctx context.Context) (*operations.GetAvailableClientsResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/clients" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getAvailableClients", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/clients") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetAvailableClientsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -191,17 +293,10 @@ func (s *Server) GetAvailableClients(ctx context.Context) (*operations.GetAvaila httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetAvailableClientsResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetAvailableClientsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -209,12 +304,12 @@ func (s *Server) GetAvailableClients(ctx context.Context) (*operations.GetAvaila res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetAvailableClientsServerResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -222,8 +317,10 @@ func (s *Server) GetAvailableClients(ctx context.Context) (*operations.GetAvaila res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -232,24 +329,61 @@ func (s *Server) GetAvailableClients(ctx context.Context) (*operations.GetAvaila // GetDevices - Get Devices // Get Devices func (s *Server) GetDevices(ctx context.Context) (*operations.GetDevicesResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/devices" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getDevices", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/devices") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetDevicesResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -259,17 +393,10 @@ func (s *Server) GetDevices(ctx context.Context) (*operations.GetDevicesResponse httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetDevicesResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetDevicesResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -277,12 +404,12 @@ func (s *Server) GetDevices(ctx context.Context) (*operations.GetDevicesResponse res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetDevicesServerResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -290,8 +417,10 @@ func (s *Server) GetDevices(ctx context.Context) (*operations.GetDevicesResponse res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -300,24 +429,61 @@ func (s *Server) GetDevices(ctx context.Context) (*operations.GetDevicesResponse // GetServerIdentity - Get Server Identity // Get Server Identity func (s *Server) GetServerIdentity(ctx context.Context) (*operations.GetServerIdentityResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/identity" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getServerIdentity", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/identity") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetServerIdentityResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -327,17 +493,10 @@ func (s *Server) GetServerIdentity(ctx context.Context) (*operations.GetServerId httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetServerIdentityResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetServerIdentityResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -345,12 +504,12 @@ func (s *Server) GetServerIdentity(ctx context.Context) (*operations.GetServerId res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetServerIdentityServerResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -358,8 +517,10 @@ func (s *Server) GetServerIdentity(ctx context.Context) (*operations.GetServerId res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -368,24 +529,61 @@ func (s *Server) GetServerIdentity(ctx context.Context) (*operations.GetServerId // GetMyPlexAccount - Get MyPlex Account // Returns MyPlex Account Information func (s *Server) GetMyPlexAccount(ctx context.Context) (*operations.GetMyPlexAccountResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/myplex/account" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getMyPlexAccount", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/myplex/account") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetMyPlexAccountResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -395,17 +593,10 @@ func (s *Server) GetMyPlexAccount(ctx context.Context) (*operations.GetMyPlexAcc httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetMyPlexAccountResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetMyPlexAccountResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -413,12 +604,12 @@ func (s *Server) GetMyPlexAccount(ctx context.Context) (*operations.GetMyPlexAcc res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetMyPlexAccountServerResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -426,8 +617,10 @@ func (s *Server) GetMyPlexAccount(ctx context.Context) (*operations.GetMyPlexAcc res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -436,28 +629,65 @@ func (s *Server) GetMyPlexAccount(ctx context.Context) (*operations.GetMyPlexAcc // GetResizedPhoto - Get a Resized Photo // Plex's Photo transcoder is used throughout the service to serve images at specified sizes. func (s *Server) GetResizedPhoto(ctx context.Context, request operations.GetResizedPhotoRequest) (*operations.GetResizedPhotoResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/photo/:/transcode" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getResizedPhoto", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/photo/:/transcode") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetResizedPhotoResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -467,20 +697,13 @@ func (s *Server) GetResizedPhoto(ctx context.Context, request operations.GetResi httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetResizedPhotoResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetResizedPhotoResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -488,8 +711,10 @@ func (s *Server) GetResizedPhoto(ctx context.Context, request operations.GetResi res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -498,24 +723,61 @@ func (s *Server) GetResizedPhoto(ctx context.Context, request operations.GetResi // GetServerList - Get Server List // Get Server List func (s *Server) GetServerList(ctx context.Context) (*operations.GetServerListResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/servers" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getServerList", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/servers") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetServerListResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -525,17 +787,10 @@ func (s *Server) GetServerList(ctx context.Context) (*operations.GetServerListRe httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetServerListResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetServerListResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -543,12 +798,12 @@ func (s *Server) GetServerList(ctx context.Context) (*operations.GetServerListRe res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetServerListServerResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -556,8 +811,10 @@ func (s *Server) GetServerList(ctx context.Context) (*operations.GetServerListRe res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/sdk/sessions.go b/internal/sdk/sessions.go index 054152f..f152725 100644 --- a/internal/sdk/sessions.go +++ b/internal/sdk/sessions.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Sessions - API Calls that perform search operations with Plex Media Server Sessions @@ -28,24 +29,61 @@ func newSessions(sdkConfig sdkConfiguration) *Sessions { // GetSessions - Get Active Sessions // This will retrieve the "Now Playing" Information of the PMS. func (s *Sessions) GetSessions(ctx context.Context) (*operations.GetSessionsResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/status/sessions" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getSessions", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/status/sessions") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetSessionsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -55,17 +93,10 @@ func (s *Sessions) GetSessions(ctx context.Context) (*operations.GetSessionsResp httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetSessionsResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetSessionsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -73,12 +104,12 @@ func (s *Sessions) GetSessions(ctx context.Context) (*operations.GetSessionsResp res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetSessionsSessionsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -86,8 +117,10 @@ func (s *Sessions) GetSessions(ctx context.Context) (*operations.GetSessionsResp res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -96,24 +129,61 @@ func (s *Sessions) GetSessions(ctx context.Context) (*operations.GetSessionsResp // GetSessionHistory - Get Session History // This will Retrieve a listing of all history views. func (s *Sessions) GetSessionHistory(ctx context.Context) (*operations.GetSessionHistoryResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/status/sessions/history/all" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getSessionHistory", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/status/sessions/history/all") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetSessionHistoryResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -123,17 +193,10 @@ func (s *Sessions) GetSessionHistory(ctx context.Context) (*operations.GetSessio httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetSessionHistoryResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetSessionHistoryResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -141,12 +204,12 @@ func (s *Sessions) GetSessionHistory(ctx context.Context) (*operations.GetSessio res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetSessionHistorySessionsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -154,8 +217,10 @@ func (s *Sessions) GetSessionHistory(ctx context.Context) (*operations.GetSessio res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -164,24 +229,61 @@ func (s *Sessions) GetSessionHistory(ctx context.Context) (*operations.GetSessio // GetTranscodeSessions - Get Transcode Sessions // Get Transcode Sessions func (s *Sessions) GetTranscodeSessions(ctx context.Context) (*operations.GetTranscodeSessionsResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/transcode/sessions" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getTranscodeSessions", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/transcode/sessions") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetTranscodeSessionsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -191,17 +293,10 @@ func (s *Sessions) GetTranscodeSessions(ctx context.Context) (*operations.GetTra httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetTranscodeSessionsResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetTranscodeSessionsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -209,12 +304,12 @@ func (s *Sessions) GetTranscodeSessions(ctx context.Context) (*operations.GetTra res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetTranscodeSessionsSessionsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -222,8 +317,10 @@ func (s *Sessions) GetTranscodeSessions(ctx context.Context) (*operations.GetTra res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -232,27 +329,61 @@ func (s *Sessions) GetTranscodeSessions(ctx context.Context) (*operations.GetTra // StopTranscodeSession - Stop a Transcode Session // Stop a Transcode Session func (s *Sessions) StopTranscodeSession(ctx context.Context, request operations.StopTranscodeSessionRequest) (*operations.StopTranscodeSessionResponse, error) { + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "stopTranscodeSession", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url, err := utils.GenerateURL(ctx, baseURL, "/transcode/sessions/{sessionKey}", request, nil) + opURL, err := utils.GenerateURL(ctx, baseURL, "/transcode/sessions/{sessionKey}", request, nil) if err != nil { return nil, fmt.Errorf("error generating URL: %w", err) } - req, err := http.NewRequestWithContext(ctx, "DELETE", url, nil) + req, err := http.NewRequestWithContext(ctx, "DELETE", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.StopTranscodeSessionResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -262,20 +393,13 @@ func (s *Sessions) StopTranscodeSession(ctx context.Context, request operations. httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.StopTranscodeSessionResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 204: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.StopTranscodeSessionResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -283,8 +407,10 @@ func (s *Sessions) StopTranscodeSession(ctx context.Context, request operations. res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/sdk/statistics.go b/internal/sdk/statistics.go index 68eb0c1..7ffa812 100644 --- a/internal/sdk/statistics.go +++ b/internal/sdk/statistics.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Statistics - API Calls that perform operations with Plex Media Server Statistics @@ -28,28 +29,65 @@ func newStatistics(sdkConfig sdkConfiguration) *Statistics { // GetStatistics - Get Media Statistics // This will return the media statistics for the server func (s *Statistics) GetStatistics(ctx context.Context, request operations.GetStatisticsRequest) (*operations.GetStatisticsResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/statistics/media" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getStatistics", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/statistics/media") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetStatisticsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -59,17 +97,10 @@ func (s *Statistics) GetStatistics(ctx context.Context, request operations.GetSt httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetStatisticsResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetStatisticsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -77,12 +108,12 @@ func (s *Statistics) GetStatistics(ctx context.Context, request operations.GetSt res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetStatisticsStatisticsResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -90,8 +121,10 @@ func (s *Statistics) GetStatistics(ctx context.Context, request operations.GetSt res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/sdk/pkg/types/bigint.go b/internal/sdk/types/bigint.go similarity index 100% rename from internal/sdk/pkg/types/bigint.go rename to internal/sdk/types/bigint.go diff --git a/internal/sdk/pkg/types/date.go b/internal/sdk/types/date.go similarity index 100% rename from internal/sdk/pkg/types/date.go rename to internal/sdk/types/date.go diff --git a/internal/sdk/pkg/types/datetime.go b/internal/sdk/types/datetime.go similarity index 100% rename from internal/sdk/pkg/types/datetime.go rename to internal/sdk/types/datetime.go diff --git a/internal/sdk/pkg/types/decimal.go b/internal/sdk/types/decimal.go similarity index 100% rename from internal/sdk/pkg/types/decimal.go rename to internal/sdk/types/decimal.go diff --git a/internal/sdk/pkg/types/pointers.go b/internal/sdk/types/pointers.go similarity index 100% rename from internal/sdk/pkg/types/pointers.go rename to internal/sdk/types/pointers.go diff --git a/internal/sdk/updater.go b/internal/sdk/updater.go index e904fe0..065f13e 100644 --- a/internal/sdk/updater.go +++ b/internal/sdk/updater.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Updater - This describes the API for searching and applying updates to the Plex Media Server. @@ -29,24 +30,61 @@ func newUpdater(sdkConfig sdkConfiguration) *Updater { // GetUpdateStatus - Querying status of updates // Querying status of updates func (s *Updater) GetUpdateStatus(ctx context.Context) (*operations.GetUpdateStatusResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/updater/status" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getUpdateStatus", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/updater/status") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetUpdateStatusResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -56,17 +94,10 @@ func (s *Updater) GetUpdateStatus(ctx context.Context) (*operations.GetUpdateSta httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetUpdateStatusResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetUpdateStatusResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -74,12 +105,12 @@ func (s *Updater) GetUpdateStatus(ctx context.Context) (*operations.GetUpdateSta res.TwoHundredApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetUpdateStatusUpdaterResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -87,8 +118,10 @@ func (s *Updater) GetUpdateStatus(ctx context.Context) (*operations.GetUpdateSta res.FourHundredAndOneApplicationJSONObject = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -97,28 +130,65 @@ func (s *Updater) GetUpdateStatus(ctx context.Context) (*operations.GetUpdateSta // CheckForUpdates - Checking for updates // Checking for updates func (s *Updater) CheckForUpdates(ctx context.Context, request operations.CheckForUpdatesRequest) (*operations.CheckForUpdatesResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/updater/check" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "checkForUpdates", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "PUT", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/updater/check") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "PUT", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.CheckForUpdatesResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -128,20 +198,13 @@ func (s *Updater) CheckForUpdates(ctx context.Context, request operations.CheckF httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.CheckForUpdatesResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.CheckForUpdatesResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -149,8 +212,10 @@ func (s *Updater) CheckForUpdates(ctx context.Context, request operations.CheckF res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -159,28 +224,65 @@ func (s *Updater) CheckForUpdates(ctx context.Context, request operations.CheckF // ApplyUpdates - Apply Updates // Note that these two parameters are effectively mutually exclusive. The `tonight` parameter takes precedence and `skip` will be ignored if `tonight` is also passed func (s *Updater) ApplyUpdates(ctx context.Context, request operations.ApplyUpdatesRequest) (*operations.ApplyUpdatesResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/updater/apply" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "applyUpdates", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "PUT", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/updater/apply") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "PUT", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.ApplyUpdatesResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -190,13 +292,6 @@ func (s *Updater) ApplyUpdates(ctx context.Context, request operations.ApplyUpda httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.ApplyUpdatesResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough @@ -205,7 +300,7 @@ func (s *Updater) ApplyUpdates(ctx context.Context, request operations.ApplyUpda case httpRes.StatusCode == 500: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.ApplyUpdatesResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -213,8 +308,10 @@ func (s *Updater) ApplyUpdates(ctx context.Context, request operations.ApplyUpda res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/sdk/video.go b/internal/sdk/video.go index bcae255..e725280 100644 --- a/internal/sdk/video.go +++ b/internal/sdk/video.go @@ -6,12 +6,13 @@ import ( "bytes" "context" "fmt" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/operations" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/models/sdkerrors" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/hooks" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/internal/utils" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/errors" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/models/operations" "io" "net/http" - "strings" + "net/url" ) // Video - API Calls that perform operations with Plex Media Server Videos @@ -28,28 +29,65 @@ func newVideo(sdkConfig sdkConfiguration) *Video { // GetTimeline - Get the timeline for a media item // Get the timeline for a media item func (s *Video) GetTimeline(ctx context.Context, request operations.GetTimelineRequest) (*operations.GetTimelineResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/:/timeline" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "getTimeline", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/:/timeline") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.GetTimelineResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -59,20 +97,13 @@ func (s *Video) GetTimeline(ctx context.Context, request operations.GetTimelineR httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.GetTimelineResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.GetTimelineResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -80,8 +111,10 @@ func (s *Video) GetTimeline(ctx context.Context, request operations.GetTimelineR res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil @@ -90,28 +123,65 @@ func (s *Video) GetTimeline(ctx context.Context, request operations.GetTimelineR // StartUniversalTranscode - Start Universal Transcode // Begin a Universal Transcode Session func (s *Video) StartUniversalTranscode(ctx context.Context, request operations.StartUniversalTranscodeRequest) (*operations.StartUniversalTranscodeResponse, error) { - baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) - url := strings.TrimSuffix(baseURL, "/") + "/video/:/transcode/universal/start.mpd" + hookCtx := hooks.HookContext{ + Context: ctx, + OperationID: "startUniversalTranscode", + OAuth2Scopes: []string{}, + SecuritySource: s.sdkConfiguration.Security, + } - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + opURL, err := url.JoinPath(baseURL, "/video/:/transcode/universal/start.mpd") + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", opURL, nil) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Accept", "application/json") - req.Header.Set("user-agent", s.sdkConfiguration.UserAgent) + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) if err := utils.PopulateQueryParams(ctx, req, request, nil); err != nil { return nil, fmt.Errorf("error populating query params: %w", err) } - client := s.sdkConfiguration.SecurityClient - - httpRes, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("error sending request: %w", err) + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err } - if httpRes == nil { - return nil, fmt.Errorf("error sending request: no response") + + req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{}, httpRes.StatusCode) { + httpRes, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } + } else { + httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.StartUniversalTranscodeResponse{ + StatusCode: httpRes.StatusCode, + ContentType: httpRes.Header.Get("Content-Type"), + RawResponse: httpRes, } rawBody, err := io.ReadAll(httpRes.Body) @@ -121,20 +191,13 @@ func (s *Video) StartUniversalTranscode(ctx context.Context, request operations. httpRes.Body.Close() httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) - contentType := httpRes.Header.Get("Content-Type") - - res := &operations.StartUniversalTranscodeResponse{ - StatusCode: httpRes.StatusCode, - ContentType: contentType, - RawResponse: httpRes, - } switch { case httpRes.StatusCode == 200: fallthrough case httpRes.StatusCode == 400: case httpRes.StatusCode == 401: switch { - case utils.MatchContentType(contentType, `application/json`): + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): var out operations.StartUniversalTranscodeResponseBody if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { return nil, err @@ -142,8 +205,10 @@ func (s *Video) StartUniversalTranscode(ctx context.Context, request operations. res.Object = &out default: - return nil, sdkerrors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", contentType), httpRes.StatusCode, string(rawBody), httpRes) + return nil, errors.NewSDKError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) } + default: + return nil, errors.NewSDKError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) } return res, nil diff --git a/internal/validators/DateValidator.go b/internal/validators/DateValidator.go index fcafac5..1e9b124 100644 --- a/internal/validators/DateValidator.go +++ b/internal/validators/DateValidator.go @@ -4,7 +4,7 @@ package validators import ( "context" - "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/pkg/types" + "github.com/LukeHagar/terraform-provider-PlexAPI/internal/sdk/types" "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/schema/validator" )