From a46121fcf55d6bbe30db16281700c5ecdcda0898 Mon Sep 17 00:00:00 2001 From: Alex Goth Date: Wed, 14 Aug 2024 16:38:20 +0200 Subject: [PATCH] feat: implement entitlement override endpoint --- api/api.gen.go | 642 +++++---- api/client/go/client.gen.go | 792 +++++++---- api/client/node/clients/subject.ts | 33 + api/client/node/schemas/openapi.ts | 130 +- .../src/openmeter/_operations/_operations.py | 1181 +++++++++++++---- .../openmeter/aio/_operations/_operations.py | 559 +++++++- api/client/web/src/client/openapi.ts | 130 +- api/openapi.yaml | 39 + e2e/e2e_test.go | 38 + internal/entitlement/connector.go | 150 ++- internal/entitlement/driver/entitlement.go | 146 +- internal/entitlement/driver/parser.go | 103 ++ internal/server/router/entitlement.go | 13 + openmeter/entitlement/driver/handlers.go | 1 + openmeter/entitlement/driver/types.go | 3 + 15 files changed, 2947 insertions(+), 1013 deletions(-) diff --git a/api/api.gen.go b/api/api.gen.go index 21dcf484a..9c3936ae9 100644 --- a/api/api.gen.go +++ b/api/api.gen.go @@ -1927,6 +1927,9 @@ type CreateEntitlementJSONRequestBody = EntitlementCreateInputs // CreateGrantJSONRequestBody defines body for CreateGrant for application/json ContentType. type CreateGrantJSONRequestBody = EntitlementGrantCreateInput +// OverrideEntitlementJSONRequestBody defines body for OverrideEntitlement for application/json ContentType. +type OverrideEntitlementJSONRequestBody = EntitlementCreateInputs + // ResetEntitlementUsageJSONRequestBody defines body for ResetEntitlementUsage for application/json ContentType. type ResetEntitlementUsageJSONRequestBody ResetEntitlementUsageJSONBody @@ -2977,6 +2980,9 @@ type ServerInterface interface { // Create a grant // (POST /api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/grants) CreateGrant(w http.ResponseWriter, r *http.Request, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey) + // Override an entitlement + // (PUT /api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/override) + OverrideEntitlement(w http.ResponseWriter, r *http.Request, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey) // Get the current value and access of an entitlement // (GET /api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/value) GetEntitlementValue(w http.ResponseWriter, r *http.Request, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, params GetEntitlementValueParams) @@ -3256,6 +3262,12 @@ func (_ Unimplemented) CreateGrant(w http.ResponseWriter, r *http.Request, subje w.WriteHeader(http.StatusNotImplemented) } +// Override an entitlement +// (PUT /api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/override) +func (_ Unimplemented) OverrideEntitlement(w http.ResponseWriter, r *http.Request, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey) { + w.WriteHeader(http.StatusNotImplemented) +} + // Get the current value and access of an entitlement // (GET /api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/value) func (_ Unimplemented) GetEntitlementValue(w http.ResponseWriter, r *http.Request, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, params GetEntitlementValueParams) { @@ -4968,6 +4980,45 @@ func (siw *ServerInterfaceWrapper) CreateGrant(w http.ResponseWriter, r *http.Re handler.ServeHTTP(w, r.WithContext(ctx)) } +// OverrideEntitlement operation middleware +func (siw *ServerInterfaceWrapper) OverrideEntitlement(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var err error + + // ------------- Path parameter "subjectIdOrKey" ------------- + var subjectIdOrKey SubjectIdOrKey + + err = runtime.BindStyledParameterWithOptions("simple", "subjectIdOrKey", chi.URLParam(r, "subjectIdOrKey"), &subjectIdOrKey, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "subjectIdOrKey", Err: err}) + return + } + + // ------------- Path parameter "entitlementIdOrFeatureKey" ------------- + var entitlementIdOrFeatureKey EntitlementIdOrFeatureKey + + err = runtime.BindStyledParameterWithOptions("simple", "entitlementIdOrFeatureKey", chi.URLParam(r, "entitlementIdOrFeatureKey"), &entitlementIdOrFeatureKey, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "entitlementIdOrFeatureKey", Err: err}) + return + } + + ctx = context.WithValue(ctx, CloudTokenAuthScopes, []string{}) + + ctx = context.WithValue(ctx, CloudCookieAuthScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.OverrideEntitlement(w, r, subjectIdOrKey, entitlementIdOrFeatureKey) + })) + + for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- { + handler = siw.HandlerMiddlewares[i](handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + // GetEntitlementValue operation middleware func (siw *ServerInterfaceWrapper) GetEntitlementValue(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -5458,6 +5509,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/grants", wrapper.CreateGrant) }) + r.Group(func(r chi.Router) { + r.Put(options.BaseURL+"/api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/override", wrapper.OverrideEntitlement) + }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/value", wrapper.GetEntitlementValue) }) @@ -5480,299 +5534,301 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "Cr96d2FRDdmP7z7+fwEAAP//lawqzyOZAQA=", + "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/client/go/client.gen.go b/api/client/go/client.gen.go index 7fa1db5fd..ffbb0dff5 100644 --- a/api/client/go/client.gen.go +++ b/api/client/go/client.gen.go @@ -1927,6 +1927,9 @@ type CreateEntitlementJSONRequestBody = EntitlementCreateInputs // CreateGrantJSONRequestBody defines body for CreateGrant for application/json ContentType. type CreateGrantJSONRequestBody = EntitlementGrantCreateInput +// OverrideEntitlementJSONRequestBody defines body for OverrideEntitlement for application/json ContentType. +type OverrideEntitlementJSONRequestBody = EntitlementCreateInputs + // ResetEntitlementUsageJSONRequestBody defines body for ResetEntitlementUsage for application/json ContentType. type ResetEntitlementUsageJSONRequestBody ResetEntitlementUsageJSONBody @@ -3078,6 +3081,11 @@ type ClientInterface interface { CreateGrant(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, body CreateGrantJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // OverrideEntitlementWithBody request with any body + OverrideEntitlementWithBody(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + OverrideEntitlement(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, body OverrideEntitlementJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetEntitlementValue request GetEntitlementValue(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, params *GetEntitlementValueParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3792,6 +3800,30 @@ func (c *Client) CreateGrant(ctx context.Context, subjectIdOrKey SubjectIdOrKey, return c.Client.Do(req) } +func (c *Client) OverrideEntitlementWithBody(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewOverrideEntitlementRequestWithBody(c.Server, subjectIdOrKey, entitlementIdOrFeatureKey, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) OverrideEntitlement(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, body OverrideEntitlementJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewOverrideEntitlementRequest(c.Server, subjectIdOrKey, entitlementIdOrFeatureKey, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) GetEntitlementValue(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, params *GetEntitlementValueParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetEntitlementValueRequest(c.Server, subjectIdOrKey, entitlementIdOrFeatureKey, params) if err != nil { @@ -6546,6 +6578,60 @@ func NewCreateGrantRequestWithBody(server string, subjectIdOrKey SubjectIdOrKey, return req, nil } +// NewOverrideEntitlementRequest calls the generic OverrideEntitlement builder with application/json body +func NewOverrideEntitlementRequest(server string, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, body OverrideEntitlementJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewOverrideEntitlementRequestWithBody(server, subjectIdOrKey, entitlementIdOrFeatureKey, "application/json", bodyReader) +} + +// NewOverrideEntitlementRequestWithBody generates requests for OverrideEntitlement with any type of body +func NewOverrideEntitlementRequestWithBody(server string, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "subjectIdOrKey", runtime.ParamLocationPath, subjectIdOrKey) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithLocation("simple", false, "entitlementIdOrFeatureKey", runtime.ParamLocationPath, entitlementIdOrFeatureKey) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/subjects/%s/entitlements/%s/override", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewGetEntitlementValueRequest generates requests for GetEntitlementValue func NewGetEntitlementValueRequest(server string, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, params *GetEntitlementValueParams) (*http.Request, error) { var err error @@ -7054,6 +7140,11 @@ type ClientWithResponsesInterface interface { CreateGrantWithResponse(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, body CreateGrantJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateGrantResponse, error) + // OverrideEntitlementWithBodyWithResponse request with any body + OverrideEntitlementWithBodyWithResponse(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*OverrideEntitlementResponse, error) + + OverrideEntitlementWithResponse(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, body OverrideEntitlementJSONRequestBody, reqEditors ...RequestEditorFn) (*OverrideEntitlementResponse, error) + // GetEntitlementValueWithResponse request GetEntitlementValueWithResponse(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, params *GetEntitlementValueParams, reqEditors ...RequestEditorFn) (*GetEntitlementValueResponse, error) @@ -8154,6 +8245,34 @@ func (r CreateGrantResponse) StatusCode() int { return 0 } +type OverrideEntitlementResponse struct { + Body []byte + HTTPResponse *http.Response + JSON201 *Entitlement + ApplicationproblemJSON400 *BadRequestProblemResponse + ApplicationproblemJSON401 *UnauthorizedProblemResponse + ApplicationproblemJSON404 *NotFoundProblemResponse + ApplicationproblemJSON409 *ConflictProblemResponse + ApplicationproblemJSON501 *NotImplementedProblemResponse + ApplicationproblemJSONDefault *UnexpectedProblemResponse +} + +// Status returns HTTPResponse.Status +func (r OverrideEntitlementResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r OverrideEntitlementResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type GetEntitlementValueResponse struct { Body []byte HTTPResponse *http.Response @@ -8791,6 +8910,23 @@ func (c *ClientWithResponses) CreateGrantWithResponse(ctx context.Context, subje return ParseCreateGrantResponse(rsp) } +// OverrideEntitlementWithBodyWithResponse request with arbitrary body returning *OverrideEntitlementResponse +func (c *ClientWithResponses) OverrideEntitlementWithBodyWithResponse(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*OverrideEntitlementResponse, error) { + rsp, err := c.OverrideEntitlementWithBody(ctx, subjectIdOrKey, entitlementIdOrFeatureKey, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseOverrideEntitlementResponse(rsp) +} + +func (c *ClientWithResponses) OverrideEntitlementWithResponse(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, body OverrideEntitlementJSONRequestBody, reqEditors ...RequestEditorFn) (*OverrideEntitlementResponse, error) { + rsp, err := c.OverrideEntitlement(ctx, subjectIdOrKey, entitlementIdOrFeatureKey, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseOverrideEntitlementResponse(rsp) +} + // GetEntitlementValueWithResponse request returning *GetEntitlementValueResponse func (c *ClientWithResponses) GetEntitlementValueWithResponse(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementIdOrFeatureKey EntitlementIdOrFeatureKey, params *GetEntitlementValueParams, reqEditors ...RequestEditorFn) (*GetEntitlementValueResponse, error) { rsp, err := c.GetEntitlementValue(ctx, subjectIdOrKey, entitlementIdOrFeatureKey, params, reqEditors...) @@ -10872,6 +11008,74 @@ func ParseCreateGrantResponse(rsp *http.Response) (*CreateGrantResponse, error) return response, nil } +// ParseOverrideEntitlementResponse parses an HTTP response from a OverrideEntitlementWithResponse call +func ParseOverrideEntitlementResponse(rsp *http.Response) (*OverrideEntitlementResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &OverrideEntitlementResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest Entitlement + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: + var dest ConflictProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON409 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 501: + var dest NotImplementedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON501 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest UnexpectedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + // ParseGetEntitlementValueResponse parses an HTTP response from a GetEntitlementValueWithResponse call func ParseGetEntitlementValueResponse(rsp *http.Response) (*GetEntitlementValueResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -11121,299 +11325,301 @@ func ParseResetEntitlementUsageResponse(rsp *http.Response) (*ResetEntitlementUs // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/+y9/3LbtrIA/CoY3jNzknNkWXaStvE3nTuK7SRuaif1jzZtnS+FSEjCMQWoBGlZzeSP", - "+xb3+e6TfINdgARJUKJk2cmX5syd21gkgcVid7G/sPshCOVkKgUTqQr2PgRTmtAJS1kCf4VjKgSLjyL9", - "R8RUmPBpyqUI9oI+yQT/M2Pk4sejA8IjJlI+5CwhQ5kQSoTUf4ZUv03MMN2gE3D97ZSm46ATCDphwZ4z", - "SSdI2J8ZT1gU7KVJxjqBCsdsQvXs7IZOprF+v7fTP/3t0cnB4avzs58fn54+f/7TN09fPHne/znoBOl8", - "qt9RacLFKPj4sRNowNKYTZhIl68DgBfE+aYB6PKodw/46+Q5o2mWsFdsXl/E+ZgRHhE5JOmYudATmcBP", - "V2xunw5xnDbrKk162zVqUEbT9PH7VF4xofxLvm61S4upDQZpWt71ahv2w+7bX387fHO8/+r42av+y7c/", - "/Pb8u7NHv/7khd5gdg34F+9JMe6d0NkooUuQXoMXPmmA1g53J7ByEcZZxA5YzFLmAfkIn5MIX9CskHCm", - "clD/zFgyL2CtDOeCGLEhzeI02BvSWLFOATKuxYA2kDJmVABsIDY105zF2ag9NjVPwqcN+CwPuwir/0jY", - "MNgL/mu7EOnb+FRt5wNoSGUSsaQO4Wv9MxnMyZCzOOpeiktxoeiI7ZE//hs++b5/tv/HpWhAJo7qxWHQ", - "P9sPtETLJsHe7+avg8Oz/eBddZc7wc2Wfm/rmiZ6YKU/OJNJCtDhl/mfOIReEsDynMcpS/bxPKmvDx9z", - "MdJrnGRxyqcx855Uqrx48+v35r9bO5dZr7f7TfXn3WbcmFdK2OEpm8ApWyHzHCM0Seg8qKzusBDQ5/Ba", - "u1W6Z4IevrJCVh71eyA6FuE6qw9VSlMeNi+28v4GFm2OobaLNQKzskbz6/fmv3YXqz8v2EXzygYW9CKR", - "2fQZnOTeiUovudPRKOJ67TR+k8gpS1LO/LOX8XTGJ4AYGBfEzkgPTgZzRWY8HRN2Q8OUTGgajitYc0H5", - "XU/z7nsupllqsFd6PJERi999P5qmW48RjbnY/BDAQ31A6KeFdIfBHHzJwX9YCD+odA5nRMTY9HX+q4PF", - "Y02lfmnrJ4yJ/aC8xvxnpHtLGdWfF1BG/uoGaOMsw7W2XJPC1yvEbn79PsxUKifFmmq/L1iUefd2S0rk", - "pL6Ss5QmKYloyrZSPmGEC3L6fJ88evToqSbOCU27lwLOcsWvWbeZIfXofsVit7f7aKu3s9XbOe/19uD/", - "fgs6AY6uicpO7tUzYB6HSStaxlCfGkRNWajP8YhQorgYxYzQ0ShhI5oyMuNxTAaMJCzNEsEiYDpGw7Hd", - "MEJFRGD1My4iOeteij/Moz8IV4SShCmWXLMo51VyTeNsATpGHoGRY+R3w4Bmue86K++lUa4OuKKDeKH2", - "Zd5oq37ZAW+nf8HgP/IJ97DOSTYZsESbQAYkkkqzNQ2wxTCQF6KdXq/nALSj/5rQGz7R2g0+nHBh/nTk", - "XMpGLClAfT0cKtYWVnXFp016F47jBdWF0wWr1wzWGzryHLT6VyIQsmWom+oR/Jhz0dYOS3riM/4XW2lP", - "yZQlxIDRBCEM2rS/a23ouawDeSiiDci5VC6TcrtrS7lfQPr4UVwVdJ1C0mX6pFkm76w3ImFp7n8opOaU", - "JVw2CEYQZc0ImRVAt7WAnHVW1n7OJ+w3KZjfrwIyVwtkDbye3i4EdvQvKRihikRsyPWquYBnR/2TPtHj", - "Ej0wOaApHVDFyINxmk73trdns1mXU0G7Mhlt64G29EDqoSaHGs71gBfn+zAhzGdxnSkWLcNRvji/YXZx", - "vu9qaUF/whIe0u0TNnv/q0yuvHSTZDG7rZdGj9Fg8Zrh78RHY2hMm8NeT1qzz8N82QBzZdx2sOdKGI88", - "wGpEMzWVQqGK/4xGp+zPjKn0TSIHMZucmqfgqZUiZQJOEzqdxgbL21N889//UXp1H1x1PGIp5VofHzOq", - "7f59HGFLm2xkTBXJBLuZsjBlkeGBy9LQN5P4MtBUldI0U8HeYy0mwfAL9jSsxABbrCxLxJ4BaEv/tDeg", - "0VZi3vrYlo/N4hFB5c1zZ/3YCfalGMY83Ay6QjMYF6NDkSZoukVAgC/f/nzcO+vtH//2w9lPu49ePD1+", - "9fb0pzffBuATohFNYVF6Y6fsDZ1bV3Qw5e8fv076V+Mfr+d8zOXT6ZOd8VPOn4tnQUGrBXVt7aBlZzYu", - "ZtGIJYTdcJWq0k48LXbCvETjhNFoXrzctCfmhdbbUUGyb1vsK3rQE5k+l5mINkvCYH6B2BzqwUu4eFzg", - "4kSm5Ll5oWn9QqZbOMgmKLKYEdd+pEHX+882jAHjmgAc8GISBxNPejtlTByVXluED3fATWHlqDzmhaBZ", - "OpYJ/2vTmJlwpVUWIhPCxTWNeUQgAlEiEgc1LiQL8JK5r20CKReVAS9y8btZfDhinSWJTEok0nPxkL93", - "aN5rxoV9dUOYqED4MR/VHIQxFSF7yVUqrQ5XP8nxdxZZTVVEZIAfEi5QQeZSoPo0LfnSzGv9FNwUfq3Q", - "9afaYWmKCi44N4ySiDput+wLAwOj0NFlNogZqAw0ei3iecXGRatLYxYHW4pZfOtjJ4CV++FPZUpjg5py", - "YNCqsBuE3Ofcq54bjb5NHLHhROk0H9E8nR/l5/PJwQ+nTx7tHn734vzZz2f7u29fPTl4HNTP0gdGM+42", - "f/XQPUtTlYJ6aE2X4qDWWqJKNV0YjjGKzl4sQxpv/3D8Og5T9ern77Z6+n87Tee3s1TrlRnILN0bxFRc", - "AVGUaNcuqK7XjrMJFVsacjqIGWE305gK1MWNrRFqAzodc0VkGGZJwjRRWzrGbeqWjIWBjOZkkqlUGyOU", - "/HD2+oRI6zOsOZXYTcqE4lKo5s02zp7ymrz7ujgE7nyCrDovQ75i2LFQ5n/3QvPOQ+DF9tf34uL0iCRs", - "yBDF6Zimhb2hXCs5bLsV7Sgs59ws4b4tshToQ+7L8/M3BF8goYwYGTHBEqql9GAOgMmEj7gg4LS0sc3W", - "xPS4JFm4SB/tBo4X5snTp44TBm2Nihsm55k6vilRY5mknSoTqGwyocm8AhcEyMro9dozVX9pDWvaktIn", - "NeVCEQq77tvr5mkXWkzLtrNCtymG4hBH+VbnItBHwk6sEVbH9eomXNBUQvh4QqdTPZU+M40jtuFMckZ6", - "Zt40AXMN3NJvjs2bCDYPW3xyhi8WAnJ+YnxpepUfO4EU7PUw2Pt98WHqAeJjp/UnORCtv7DY+fiujH/7", - "u5accbwa4Obb/YTRlB2JaQb2XftFjGnCouecxZEKPr7r1Dyc8KYqXCT6GMkUc/JaQIlYRF4+ENdZKg5Q", - "hrjzYV2Ip54Q64c8i8GS/LvykWipuwU31nmusutVfGyUA0uDr8GNle9X5MwKNW6aS9cmdh9wt+M0756W", - "SHTvQ4XUFmSSnTuaOigKhoK5sjZJZKi5eykOeTrWR3Gexaet4Hxw/Y2lyYqev5KClGe+NeYm3gPA5l9b", - "No3jCrywNUhdh9yauRT9/DNiR8uDHYsFHphbb1oZcadMK01cjPB1h6L85pRDYC8SajwDq0pP+LI01RLS", - "Ryo+Zil1hG2ZlJek32rqMB5316SGAALoShqrkFIIMXGlZMhB55zxdLwS0TbYqK59MuUJU/0mi18/Rnsp", - "ommuy2IqZAWQVZIPlsIl2E2K5MC85oQGTr+DAakCXxAlSuDD28BXgycpwbICEeuPryWPWNSEY0Bsnhfh", - "LIUqgl+SByEVQqY2/EXoMGUJkMrD7poorhzNZYpdekbXmKa2MnhDEZoYkFNJuAgTRhXLfUdyWJh8TkZP", - "TQmhE5mJBuzhMz08EiU5G8ssjtA4n0rFU35tg/jt/DqefIHCG8WGQxbqIX27eWgf4p5ixhmiQUSEinAs", - "0URMLI2Y513yJpHXsNUYdbLBzpBxc1SApqK/KKLA5MGEiyxlDzdK6QXLryBO828s0Wt6mdCbUxnH8pol", - "fWcHiyyNRopJZKyXrb8kNIWcoLRjqH425uFYs8mchFSQMb3WOx3xIXgWCsekBlMLao28mRGqczKmmjSG", - "0pzLMDIkkT2z/kzDWuaZFsAhjcMsBgFM1V7+6vu+fvX9Kbz2PTk+OnlwXF1xhxz33z6wHzyDifGLDjnm", - "ovzyw4et6LNGk2sd744LceUYXe0gnlSX8nWn72SnpwmXCU/n1fyiuli0b5ZPbGIwD9mvYz7Samf+pt4O", - "iKuwiAx5ojS23tiHkJuXi9OIhXxCYyNWVZf8ogeM5Ywl9jfCRQQRGjGyM/HJVCbgHuxeiucyIWb9HT20", - "A++Onm2iNy5LRpC7Paai8s5u91L8MmbgtddwJ4wods0SGuci95ryGBxeVk9VdJJr4phgouYqZROiWKyP", - "npIakQLlIegqzeeG1CUSUsUUmcHUZjqltcJimhzWmF2zuOMMHcZS6RH1gZgqV8NyM1zyHTjCwISeEfZy", - "Ju2MwA/oVQtpbGfkDE+biuamSguGmbTtX4AFeqdWOUI41QsAgkr6mnVO7j55sjhD7BZKU1XzdzUVowyU", - "j+LSsbXEq2ddWusYC7c0tO3nuZhxLIjNOKbMDK5dkSthwywGXphQMXfEqKYDIOgOGSZSc0MKXJtNTahs", - "QEHnDEOmIN0wj4zrtzS0MbvRZBPx1BCY6pI+vs4ViTRIE0gUw5GkyG1jnCCDWDUtBLrBjybeB5pELwMT", - "z4xjOdOvXAZkajUm44jnSmWQNgzMwRW5DAZZIlISyZm4DOxrMNDD5c65pq2qKX3FG3iFSJnUqUl9J7o1", - "zRb5I71obyMXVkVMVQoHjF8x1o/RpKDmgBvT6ZSJuhfhVurihFGVJQxW4M97P7dpg0BdeKjjdmoxj59H", - "XVLLL5cCZRGXokPMaacwWle2m+1bMAsurmUuqCtXCnR2fNviWWk7OXM/7l0P35vzibZnRJ/txdWZHKZO", - "hnk5Q72WOqvs299rs7Pk/NJKnD107LzsmgnC61d4uT4ZxzRTKYs6ZEyVkSdwctF4RudKH5V6jhIv57nx", - "nYCrCwEJ7TZrfxHkB2yasJDCbHwkZFIIlgENr5iIuuRNDNarXoKDFcKFShmN/h8M/IEMcBKDJ/K6yJAd", - "ZjVHWQlglTFQNRvY+leZARLxxDYiMUvlhIIKEM8JjaUYKR6xKkLx+Dc8T1TIBE24JDNrKyMPgRSGVA64", - "cIPiGFhTK2BoaXPl8KimsI69G1tYrUaFaISm0K+c6yQwfPdSnGtNxBkQ9Zz0nwqMBbAUzBGlIOTNR1qA", - "aBRTMqNz1GRQuYcMaxR+mqaNLMFNxaPG+Isgf74I59LYrra8VLAfjqnIANk5zlQWjgskAEonDC0W/RhP", - "MBzwMuiQy7q9pH/WWttl3Wi+DOy5QhG7VsesSrpGq6FCV29aGREHkNvt6sS5Smv1afOxtS2OhvmZ3CGV", - "OTFhwlhwzoWedTVL36mzSJweV98HYwqvGr3WZsOI9d3DdImQA48a0gt+rLliml9dsoYs4ATHKHQP/epQ", - "JiN+zUSTJKhG3WyYqhR1m+Tx4dqhvDmfuyeY7g6+1FFYDfm0O/28LvbbHZeb07u+xqnuKk5lZl9Q7SO/", - "nnDF5jaEYjTC4uKm567Bzma5pMYaDuQlhLvUspxXTDB5DR3xdhHc+0h28EB4Z8owzlVSOEbSXLpKGMhp", - "NF4t6Vs1ojE7Fl/wUyVkAE5pAvc5zVCeqjRdcq71w/xmFY2VLC6OzcZMkHDMwqs8e89AbvR3rR1wYEzH", - "krdww1q4YFujhIJ+bz7K9STDI2a9HTdbXitOc5klRM6EMd+75CWFbwZMK364n1We/nAZXLH5zmWwRy4D", - "WNXOZfBxUaKYPc1M0kTpMDO/tcznMtuxlKN+1mDVMw2M8ucpBiK0Dl1yHHrMeNUljW6WQcbjlNBEZiJq", - "8GigWVb2gIB1FmWQ/g1mcs2j0SXWb51TsDLO6wnlwtHXPbTXQf0ZaU8wrUJrulNEsBFN81uYt87VbmIT", - "D15VjUeV5pD27NSWHJeGmnPj0pNOP2Zw5LlH45gqxxfm5j8QFEv6aSIV6uu+EiRB7WJ3A4yORmj0zFuQ", - "rFFzq+SD9Ge9v+KfKQn1XGj+Iul1COuOutZKL/CgCRxT6svELIdkp9crp9STQWbiMjNN/VJDDaOziHzX", - "6xg3fm6O7vasZl3B13p02XAnoDXuThmyS5pfIqiwr3HSo1bpv0XwZM1LBK74K0jV63u6trdjFqWZV7yX", - "scwi+FCRMxOLRzEFfHiG11pK4UIbaiyVNtlKs2QgtfzGQmd7wc7uI1/BE7g++CTc6Q1pxLZ2wqds63H0", - "Tbj13e63T7bCJ7vho2++fbQTPdJngZJZAlcJtF3FQ7YF9047gTbKr1micAk73V7g3husXDDlk6qPc2cP", - "/q/b6+38VkA4TeRkmnouFyyMrPouakDizDUQAp3HkkbdBTVhGhDni7JqSMwdKH/utbnNCoLGikzI1EKL", - "mxxrI5xGwGqphFv5u73H39hb+U6xKvd+FdyrKh3Wtadgrv/IxCgdg8EushhYqlHmaqjcm9kl+7pyKQBe", - "QwkFi8EFaOnbraeDrwgHj5bOn1fWK9bfknzLsNStHUPdS+aHHb9JtYxB3zk11f4Kf34JuAqvuPjZyq9f", - "tIDO5TFviQB8aOnMFSSqJEjyML4BO1NYHKUAGTl4GUBNFYIO4K+BJRdzPFmNAaa08tmgsvRsmsgoC1lC", - "HuTmJZx9uD0Pu36DEmTLEohR9NRwxydMpXQy1WDMTAjbvS+Sb6uPXx89evS02xi9qUg2bxRkRQ7xS5oy", - "zq28QYQmDKNhRjsyHlWwt4pVltdgZO8y7R+QbtimTKEdaxlYKqmdj53gZmskt8yPyNR4YDpPtjBFAWvC", - "agwFI56Os0E3lJPtUFM4fKi2VXS1NZLb17vb8ANAWkuCqueHFTF5oxXJYV5Y0mt1Nqa9sdpQ8DKJ+RUj", - "O7tkIkU6riqbO7s+X2qUFelebSay7+NcMJGZxxweL19fnAad4KD/a9AJfjk8fBV0guPXJ+cvg07w62H/", - "tF7/sLrROUgdgwOfuuPUxyvcCJXEwSQc82t/6uVR5QKott/w7Q4Rkgg2K1t3IRVubEMri/wuc2A9GsBi", - "L4nBx0pOoLqjteb96ZdQlP+hTTg8aYiETAJgeEqshCyn+OyR/TcXWy9llqgOOQdNp0P6b47IPo1jbWCk", - "YYMLybcqz2W3+4SxTGNXTd7KK4ZpU4LwySRLwcSo11mp3EPGXNlxIrPRWKLJpGHoYMEgE76DU0NzcELx", - "tieE03Lv3D+Vx6y+gpQ469UxcNDCUKWYuGqrSHbAYLMckf8MXFBJdbpi82IKhdm3oRSKKzhpISsM0pdo", - "PB1TkUGxHRKOqYaeJejaiqga14zjYGHp5Pu619BxPW5qLsIc4+wmZYn+1GS9mDRfIVM87pDOTPiugb7B", - "4DRF97DWobrFcl5Pi8Wwot6lqYGpuuQCl2JseXxLhXIKnDNIJFTFgXw7yKTMYwMJDa9Ulxw2BpBNwgu+", - "CYHkOMaaE+ApBnyZUn1ungxAoe0hcAGIEi+EMLZTvhNexEKb3cWFNj82YdpfOPM8x0V+/cIVKOULGGaX", - "nSSKSk5LkUUK0ewczZBlyCInFnp2cayF1P7ri5PzvKxY7oIu+ABZ4D24HnyMgPWYvHcknNpdJV+9U/3q", - "yEi7pfoXhm1gsgUHsj5N6sD8iJkYhfsJdQhQgCyuE4bKnUGoqZSo0FvEU1UXn0pr7R0teR56k1ei1UpS", - "e4t1lctuvTp5s/Pr+dufTt++PD/44fGrN6ffvvmt59uU+zkYLsUtToZVxK1PF9dL9FECpBk/yxJxIGfC", - "lCw5Y6P8TnVlQxQ+KuUqk0GWCMjdI2McAJIv+gQC5/knXm9moVWTAUtnjBmLWSF7h2MqRiwijOfO3eqU", - "RaoxlAc35KFHLzn3GkqoHIpo5QIqTER3Uj7ls63rAig3MQ2V46zp3GuR5lJfl13LIGH0CvZVG6k0HFtt", - "B7e9eal7mDB/dLBnoy/VY2dBKF5j5mMjIgpmqSAi36e/KyogKcMbjNGHgsnvnWFqY4qZWEWibyXqAMf0", - "HHWZPMEscKoALzKOXuTQnLJQJlHQvARTNHhRnOaaJZj8uCDSUmGt3SefumJSqVaSuZF6L5WSarivBXEb", - "O3iUi+J4r6qulkCzEn6669zs8WFgpaYa3UXqYlFOoFJy7MejA/LgQvBrlijgowsc90d2w0M5Suh0bPJK", - "z2SCekrunE4eVjD64psnv3375En/+S/9Vy8Pd3ZPfu3t//T0+Ut9TNJUG0rBXvD//t7betp/tn9w+PzF", - "yx9eHZ+8+en07PznX97++tu7D7vffPyHB/8fmlc2oTfWl/nNo6pr052Vbv3V23r67t8P/nvvff7Hw395", - "pnvnIYAjMWIqZdE6Aba+Fkn4ufGPgkomraEGxflMD58kkUklVsTslKtE3VYIs0WfLsxWrBxL7dUqWqLP", - "A/2rVV0rx8vCvCH7bW0ql6ZsgURmvMErXpyGr3xK8I9cpdUr0245w4ItW51GtWIHtZL1tSzHfNyqKYae", - "GXPmtD0OWwCAVdDrcUkbFy/qqXcryb91t/S0RQ10owzYwufdmuT1lMvSomO/wbEOoX1RGb5bjuL7q8iX", - "kpSKKTq2LrxTfB2R7c9dqtDMZsjl9pRSMhrXoJfPgFRKvvy7Ipgqnu6TbIz35RYkYwMqtyaXvBVRW1Jp", - "nvm+ySR3dN8Vibi4uS/yON7MHb3VbuHhLfRSVQuuSJpkInTDw1rGZjF176pnImJJPOdiZHrEgUrdrihW", - "damHIpssjYJVPzrXyowXczCck8q6f3F6enhy/v7N4enR64P3Z+f90/OgE5y8/iXwKZLeiUom/aKrlpB0", - "6jMHUG+C0Fcpk9kkzZibPGM5g0IiNA2NDzvvY4HesYrq6fijg73g7OI4qOm2R7mHH+kRxjkvKXqdvEGP", - "o7z+o1tqy6N/sLXPFJg8VUUf6Mfo2nN837opna4Ye8Hx0cnF+WFdXSytZRkxpCzpO+9X/Sl1/Dt/WxrO", - "mxvW/ezkwmBrae6Fg84PjTlmkHyRymI326VWlPalydVUDFPbsaYI1AmdsAiSB99QCJFNE6agAK4Gkt2k", - "CQ1tgSu3wZMq8p5xXdrg6ZJXbK7yars2aimcACOkcLqBRf0UJIgKZcKcOGNDFt4CWqyXt43WbHHZ0lBe", - "mqusFjoEaqVeG0HC1KLbIvEezPEa61fX7qG0KqHh4VOk1ZfM8X8qzCq0pRjNyZW/ZD6WCTm7OO6Q/s8v", - "OuT46ASvax7337phO4Uy2B720EAF1mHiDphBMqWJspkIeV2q5zIhFydHP10cvq9FAztlsBGiogI1TtEl", - "z32RxAIBFoUaRnPDueoWc6RqbRtmpY5JK/QcKl2TwmaBrjR2pVxpliUZXMC0qotnYtsULjllAgtpyeLf", - "29Or0TYOBwDXDgB/3TEXx0YEZ4p5WN+oCniAwgYFncDd66AT9H9+EXT06aX/f/9tOd0Wv6x3jPUjo19C", - "7qbx8lPGkvkpU3A91oeXBJ5hCA71EujQ1PXlkP/+waccVDxbVbdRkwfqGu/67OxaKoKIUlOzMENoGGvx", - "p219fNfBXouNaV2pXDrBYrXEOvWKfoWfPUZa2XMOqciZz6obGitkQ0VIUrmxDnEbknSwsz6vYBk1nqM8", - "kTMbY2nHS58zwXRqQZulOueiSnUt19egWDtNdptyyc/rGeSIeLxBsSAXfKkyf23vIvrScYvgnYP4TVO0", - "DSxvql+sS++4uvJU7mJWOM1z5tjo6XXipPa4zdIXFBf/5fDZy9evXzVxv2fEX9hgLOXVrUt7Lxr6nX8x", - "+3IyWaVkp2cISNzy1ErAjGdv+dr8LkN+kcE0fXdLyXU3V20qYjFbDxbzZQMs63BVVGoJvKhiiHuN1MLE", - "VdExWELQqwSaGad+EdSf6nehtImXcCaieF5K+jPzNYiumaEqX9R7Gq257RMZwS2ajeG6ImtMeLKgSxdY", - "Z188ztB6TyXNNW5CaikF0K7KJiaqbrCI+zBJ3bZM8VzPq06GS2iaE4ap14z7wunOXjhaUXiBAdlAKQ19", - "J/w7WdvDuzghyrPc3XFRmafh7Fg1Y9dLr76c3RLhFb4orGWhsPfPptN2vYS3WubuJkmw6HLbkgCb/b3u", - "td7GJRtvg6XMkiPB/uhZ8gKN4zYqhdFK6iVazOD5mnzr8VTL1OLjJbTuVSs2z8OrGNDPC3v/KqKgvIoi", - "UyfZ0QgmV/o69sfbrf2Ls/PXx1svD/sHh6fBntF7W9kcfCS4GJ2xMPHV9jvDx0TBc1MDWCZVgNx8IWmt", - "xJDxaywbE0GS9HM4U/fIHwOq2DeP/yBMhDLSrExFJCdkME81h5oUpHhOpgkb8ht7v+GP2Vix8P0fXXLK", - "QjmZMKG/Vfwvtkd2H1d8hvju2Tej3Zc/ivNZ9LQ/fjm7ODp+Phr9fPb09VC+ocOT78pJYA/wo4f//Tvd", - "+qu/9Vtv6+m/t79/9+HRbmen1/MmgQVZ4mnwZ6no4vRHU1alxhsc97ksDsZpOlV729vmF7AiGo+kCkNr", - "QKq72ULTOPHKTI3umcMK3cW8WDucbsmY1WOoil/zBAgRVCygshuu8G6BAbxJFn1l3lWZl/RL1TyLZoMc", - "i+PaePrny+VVTH5JXO9PrvAw14LUGMMe7VNjfH4Lj0PVB4WbCtSQv7PizOoTZORYjN1ZRo67JfeRkVPe", - "rDyneMFxgQHxWutRU8w/v4YI2nbCU5ZwarI+oqL0RZIAOURcjC4F9Vx10zJFq5l/mKo1f5haxnkzTz0O", - "XrkgE6YgOwiksMnk8R4DvqoGK/mTRB0Rd+Za4tcsmZ8taAdr37EtYX3KeF4vZmUmA1I4KIPhYfVbWUee", - "ajar2UaGOFZe1xvzneahLF7JvDrV769hlh3mgeVmo6zsRgLIaqRQLLoVM5vrUefjhKmxjKM3BcZWV9dg", - "RFTWCgx+aAgjNjahWyFVt2j4akuXrpC86cRVFladyHLspRZLqxJEFcs/W10uj7O0XLD5sLkfWlHE1bn/", - "0MkjHsUK6tTRLij4rqn2tiG7ul/SyEH0uCiSJnw0gptkudj8w1z4+8PKqD+cFf1xKVSWTKmyl2ZZkp8X", - "f+Tr+UMLbx+Bl8mxhZvTA7tTGW2Dvh+TETdmOeoGDGrQm2JhazuE7Ln0eR1ZdyMQi8W2EndLnN6uCZkX", - "zBds5sWStkuKvvzzKeb6WVrx6BIbOo2OKtHWu3UXNm+D2QG7qhy4VhtxUFNgKnpXEfVcLzAYJIwqjDEW", - "qHpObYfCiKsp5Bbbi1TeTvulSsBnF/v7h2dnQSd43j/68fAg6ARnhycHRycvgk7wxvzr3bLg1F3EswsX", - "JALtTtlqNxrF43mhRVshZbVo6xaquDX8CtyiuETpHopt4+EctS1ptEmT2WS8YvFE73yoXcNDXqi9hvKW", - "YKjkNl/y7hJvOtZEbPYI1OFU61sPy1wDCMwGHQNmdffvFtCS8e58AjD6p3EInBrr6G7526dCb5Sv/RO8", - "8yy29tZahpIeqTHaZBXqmtMDnCdEirw+Ro5EVVdN7dUtT3EHe5u75DHRmk5cuRKHTl89M7batJrpKtfk", - "7HHc4LVGxu8EqDMf4cD6DdfcWiCQincKYFU2nUrFTPW43N4occjvee7em8PT/UPIa7YZhb2e3hTz+OTi", - "+Nnhaelpr9c6t7S1IViWg1VWLRCxavAGEALmB6IBFNq6ECeDOezxHLskCgx2wC0YWyfcKdhfLcvUgk82", - "EAwqmKYeCVqT9jViwJa0na+wulz1oBvMi7L5RymZ0LnlmoJVBnOo4wHNXipXFX6v1KpaUNDDpat64bSv", - "LLSIhSb0xix+pwf3eexfG+WudiZia7YzEr1guUth2xpvjPfyxiJVHdQ8z9u4p2MyyeKUTy0FJHD3FTpB", - "1A6YaqOUfJctNbxblFfcpsqML2W3VXKKc7yuLWny/NLmTnNhyXcEie/emnxey7atkp1HlG5zFi8wk6sK", - "+GoBDwDoa/asP4sRkLPZFMZz6wH1pjEuqQj5rKo2koTRcNzQlW+ljNqcDu4rndYy0b1k1hoDq1HKLPEq", - "NsuMprRaQGdjTu16EqRUiFSvkE6aNXxHd1lwB3ihtlIcx08WnsZ/S+7auBe2yhetjshVsofvxoq/u9Ti", - "FrP57Ht/dvHtT/1PWtz3LkIvbQhsgYTKfVZr2QDLPIcw+QYdh7iYr37DW/kNmxqN9LGYD9bo9ITNht4i", - "QDiaKf4KAxTZdaYdttZHjDGFbcGxE9QgZrfphNFwn9cLHRPRp4StsnOASIDXuz0ySWkMRV98exRKobKJ", - "pkR4D2vpVIvwxLGcQYcOrF2vkF6cshvvSgZG09qgjYuG2Zzf+Lcqf1JWJaGE5MJyJY3FIHHFgZy8x6W9", - "P+qLg0dvpr/8stvf/SX5bvL0P8O/2Mv4xdvvbib7b2cvuvMnfz4+2+r/8ufz7Js//zOkz//q/fXTn48P", - "/9r97lSJ+c+zH4bDt0/+vDm+lp7qPnUkNXmpOoQP81yH/I5K0eUd7o6qvJO9GbmsvlXRv0Bha9TR/L6l", - "kqV4J11lHEr40KLtpEMm695GXgoQrwXfb1Uhp0hBarqq7RE3Xg6FiCJUbOLmPlPerpcW9c7KWsQdkfyq", - "VUkb22BpqZTIQcwmq6Xg94n5jBywlPJYma7A5MHp833y7Xe9bx92L4XTTaXgUCejAxNFp2YkiNZO6ByK", - "4uAVs2qZEJgLSDKaOyVvoDdlfpFcmYSHx3DUapUabIeIWLU0R0KWiD0zO1wr3xvQaCupqco2rc7MXpfc", - "lTJL7GYaU4GKZXmxmnCKzm62oDpCUCachSus84xQqb+FcZ9cnB6RvNEfVh7ilZaKFsaWsGm0GSztxTKk", - "8fYPx6/jMFWvfv5uq6f/t1PvxOjN/mhKrIWrHSapNpQRqyc7Yxs56OeU6/Stsfu4VIWbi/TRLpar4pNs", - "Euw9efoUZDX+9bhBDUTCquObEjWWSVorvqWyyYQm8wpc5ppP2cz1kOqyDnzalihSsynsum+vm6ddyAzL", - "ttNvMiOO8q3uWBZqV/0BE5eseNpo9Ydq/35Pmpp5we3MJ+oNpyuKhwjH0leSURCaDHia6P3HlyDuQxWz", - "t34qs5mWP+3UY2jwck2XpnJVVo21MKvmpx2rY1fjOzMqIznd2D5PVB6V70thbym3eqmQs3ot0rHMkk++", - "Dy3Qb6uQ+v0A04RtWQ0XcaJK9efy22kF6i6zXm/3G2i1G2UxFyO4DHLQ/3WPwP8O4dJDROeX4pfDw1d7", - "zo8zxq4uBbR33Ct+ha6Ql+LXw/6p+/Kc0aTUKnKVHpGdoNatsIaD47yHW+ENprFbwAHuuChmX6AJI7R0", - "6W4qp9i8q3LNBhqQUUFHtjs4Z9hajydQ+I0KbT1QiHtzaFWlichtsecCsdKtGGzjjFOA7WuKQ5lmWZuI", - "FS3VqxfFjpbDtzh+tLMxK6IqiWgEertxCkLDC6dpxipdQZYCsCi6sxxBMVUpMUNgN0UueMqxQYQpnWwb", - "Lhb73U+7d9h+tOal9MeFfALrrLDCVumVkdfegjqaflcuxXiCTJALq9XQTHzfCsqiftluFSulNxeWMYN4", - "yjSm1oW+b8xJcoIhguWeEugFV+0e7XSvHGcDNZXQzgZaeDz5BjXmhE+ZnQ0ehpl6XyjfdZulvvy63b7b", - "ij6WFjTz4W9dr8XyhvHuBrizVPeiZcv3TbkbTJO/1q6G5S1LcSKHov20sfzKeo14KmC6dLQUb55OkF6+", - "v+Y3r6cMy49TW7bAiYf5L64twkRtjmqWDBPRVHKRdo14AvXC/GTOndJPNjTq/GaEGTAlXGzt0jRlk2na", - "ZTdjmin/syHlMdZirT5JmGnttbzVtbGcGutF/lKqRlmRl04NWiy1B/f33Tx3U3m04zblLiW35y+0qC/r", - "wLJR6wzHZZEJLpqGkf5zdGbezRO4iu6Q9YaMWaJfFo0DvsjbPlYaTa7UIq6h0aUnoGaBX3eFW+QodT2R", - "iiRyhloufqWKsEiCigWU5odPi7FR7YVSTYpf216PPDGhH62lsJvaQ6wug+OAumJIznjw1ITGsW0enOZz", - "AUVixSdVnnIg07EeVK2A7zKFIN14c33rFzGVtnZ4Oj/TQyF97Mcyi/alvOKsn2nKrUUh9QvQA3zGBoRO", - "pySEtzW4+nn+FyY/BO/fKyxBXvATnfJXDDYfBnNCQnbKAaMJS57bY1FO6Z8Zq/VaQFC8IaOgEwB+wI0I", - "gxXTj9N0mk++9rQaA62nWr7E/8zS+kS+lREuiJYaW0ZTtt3IlkLxEWIX2J37QIYeS/FAhtnEFjULTKWR", - "vERILqq6XG5HegAw94fSF1li4thpwAEIExSaamILFZR/UB4G+quZEvPFhxq9EGlSZC4zkkpT7N2ktHaw", - "7K+pRAtjYvl5NEdLduXW1tal+Fd+BCtMQdXs+H//+z/kAUD3kGCHcilADmD1/2vKY+wZIBzIYPu7/wIG", - "jXnITOzfkHt/SsMxI7vQGa5A4N729mw261J42pXJaNt8qrZ/PNo/PDk73Nrt9rrjdBI7jtWghI+gE5Ra", - "z3V7kLMyZYJOebAXPOr2uo+wqtQYdnebTvn29c52xAbZaHvC0oTjto+Y10eF4hPeJuZtEvMrU0smD+iX", - "mwUooriWyBMuIsFH45RcnO+XtxMGglg+Sx6oh3pzGGwkOlC04Jvw0scBrAv3C5S8Fyw90HCZ0UANxbwL", - "WNBurweavhSpudqfspt0expTDkEslJVlde+/yMvDH9+QnKzfm+WcNC30UvwXOf/1zWH9kxDXdimqTzAg", - "+sGYcN9fFqrwzmXwkYDq2u2t+F0HmiJ+fxloZRCG6XrycT7W2pAcDFixsR3c2cZdhTzEx72dptMnR//2", - "haBZOpYJ/4tFxlGd58QADCbVbvk47GYK3eBro0AMEyIHSArmbvQkJ4aUYhYA0EjwTn9gid/NKGukffAT", - "0jgut0TTimQSxUxBApbZjC45H3NFrJ6MR3iKlaLAFo8mXHCVJijuplkylQrk0NEQU3e4cp2wWpegV0z/", - "xkIWQdgGfCJwY3/EBcWCk4gL/bGNuPr4pNIsDwt1JBRD+I2ZbcUr2yBW39ARa+wQ5XsZ6723/ACKaLZ+", - "+/VwqFj715/zOGVJUU1jla+Kqh2rfOWgG5PZln8NHdQ9af+vobP6YI6eWKtRwVyFQgUfP5vbA5+W8llL", - "/ihr8CzzUeUi491SsWoSiDS82/8xF98LMBapqU1tHD2CyttikdwF+4CM6y2XTc9oZAx2j4T7LKUk4JCV", - "JYGVkq6AIA8Ob6Ys4aD3xQ+bZef2B+evo+hjoywF+ex2z58THjWc6Q4kz+ZH0crSqgRScGv6bXeHp9zE", - "s+q/0WbgqVZw/JYsWIng70ZFWNujoA/dpde6VqKvznPOorDT8MYUgMe9x8vHOJHpcz3rfSkQ5eDrWrxx", - "vVyjqGrK2vDgglAkgISKEWs8xK/XP76hn2XbQ+xceg4ip0csQp5KIzcbDqUYznTvkQQpxOWE4jzLBP+y", - "WSaeBOZbs7R7j3StxuEOTNMsXaWPePip+ohDf/gV16otkWIkmaXuYp/u7EY70XffbvWe0mjr8SAMt+iT", - "b6OtJ4NHT57sPn76iEW7d73Y3abFtr3hW+5YX3eQNashyAJ4i3uQjUZcjLpfuupQEV2uhMQf3n3sBFPp", - "u4iGiFYgY8FOkwkZQBUkF5lxLGf2Pjy4VA6NN8Ek4mEiqUc64vC5fDTZWc9kNF8gGMDJg3P/ezW91ZDL", - "x07TeFuwtn8vFj1fuMTpfPlypq2YaS1eina1Hv7I/Z3Gk2arZVdSYl3GcdJjoc+mIDC1HrThNZP50zgK", - "zEMjjJ7IGqeeuZyKH0IggSLXFhE9k8lfOccfe2qGZ2HIlBpmcTzPJdCXLGqPXJ+2T8Y6CqdbbmSxEyvv", - "p07uy+X03ML293Q35bdsPrHzx72Ybp0/rbKUCluxfphD8xpCk3DMr1lUatbvg9B0u+mb9/2QNtyYvnMH", - "lKHTNs6nu2WiL1x7HBbyoJ1h3aRLWsECUTnG4Vr+BMsx60NOpTTlYZf07Ywaz/Y5H+I/NWOWNs+9qYRt", - "pu0neZ2lucwgNxgKAhepOGQI/G6Sh/MbcaaUDtSpCGkcZjEW7LFAmfhkqSo9TPlPRUz7T5N82yWvRTwv", - "rtylY3J2cQyEVe9jrXFSFNDR9klOtNAMwy4mpEJI7BaPvE9imrKkY8KchlN9sh2T2p/npZ7bKdur8aUZ", - "3cmfV03qksVnKk22Z7eFnrGzaUh90JlHRdLxZ8PgT9qMcSLTI22raIa8HzGB200oKeqIr+yCs9S+/cH8", - "y7ilMcPNk5YAMtoVFfmpxlOXS0TOE3Bp2vdFB6pxlcKFxQCGCoAledohgyw1H1qL144YSSzBSOhwqFXu", - "vJFQOfYBIccJo0LBZV0tnWbUFMi1iNTw2GHzDAdIjr5i8449m4T9oAS6BRRvR7FrLjNVfmNMr22dNpNA", - "SIY80UelIBRqB9g85QFVXPmEyQF8VwiT1TTFfIt9bv7HjSdHkWb/pbqzjaa1Bit1mgM4Bc0vCt/cyV72", - "7lNe/w1CHbcQsKOEtsqawPfym0Vp0TFcYeZkXZitmj8BukqXnEsyZGmIws1MC8Vx8vu7buhxGjOq4HYX", - "vG+XZWHbtgk2r9j8dXJ08LE54PnaJgfot8y8eaq0Vrbuy9J+gVvyNa3jTtI6PnfbfAn4xvY+MBn+95nP", - "AXTZxq5G5rlDq/pztYlHlnPXFMPbH+C/S3TcnyWPtP5IcT5UGrV6y7WeSWIpRizBposd/bNWQMU/EevY", - "YYELMswSsLRttn1u00qhuqQ6Ra7DJixNJA213I7nOCIDtbaT3wKlYm4SH2Cvx1QRGieMRnMyYEwQmqYJ", - "H2TOBWScA0zthE0oF6hOD7PCsnY070GWAEQzQYaJnBQj2PaQxERlcj0aFFtKFBej2M6mz6G5zAq/gHuo", - "gG5NRX4Z0ZRakkOy0+uhnq0kGdKEfNMz8OllwvLMeB1nZQ8sebtz8FSxePiQzGQWRxZEpzzv4x4wEGRG", - "a53+WnLDPTBoJ393wEI5YYr0EGt6nm96hYpvclA0cg0C0Q7xnz9644HPVz5+DOW2VN3xCkqONL24z8qk", - "vq2a97j3dPn3+1IMYx6m9yGf9MZadl5HQFlCWKQn4jtdr1JzjN9vMNXDcZMFe8HZxXHtckXfXN9AHtCH", - "ctFHxQb9OoHxzrmBzX908V/5Af2Prq0f2aI8WJyNgr1yuSxTr9pc85vjiLaK+8y562YvprWORQJeV0l1", - "sJv0WR+kE0stlk4N+TR7kf/VX3KV419H6JT1XKzpWGcsLdx7RQdRMeSjDKmZDHnM9GCXIndu4X2ZRscq", - "7s/duFXN3vsdqXhtZuNuVLdI3xfNgJ3bbsL+Vz9xO47/v//9H2LYaWK4pcb2tZNo+wP89yh6DcGfhQrz", - "UtlwKdCesuyszcCjA4g+xdmo2dFpmXs1TakEeEt9CQEuOzpvp6F8xsRgNqORGBY4NT3b5/Nq3tHG9b4K", - "0A0KUKR51337Gbpe15dY6L9q1Kh/ci6/amMVb7TXiBle2wg5d+4u87zNq06VhdU+OecT9psU7F4cjS8M", - "q7X9Kn//1sICky9//+Dj9koiZiVxszFB0nZR2rUc7BYOqpW5NUy+qGKQXiUW2W663ZLKpRPcQooAM5wy", - "pbkcLsWwm3Q7VNdN94NxxvdQ/6Fj/mAi6hiEdQC/HY3PDuDqUviW1an8uAM/WlS/3+k429OBhN/Ozm5t", - "qJ3yUIia3eVD7fZqQ+36hnpUHmq3NBQm6XYet7ljfAEOJU2OX3LapiN+15PuNgK12GWSx9Cwxhcak80O", - "lDM76CdRXXx+mIpIKehrcYnwtt4Ki58NkdodeityUJeQi9uPY9ttwLM4/upt5dUUQfR06br3eOLGo2s8", - "qobVTBGpDUbXiuBgm/WaxNkDW1rrMwvf+aigTfjOT2pfej6rd9UOL5eaobZObt13U7d8UzR7Dj3bd0d+", - "RM9MlQZHfhejtznYJ0rc9GHLA/aJD+TPL6PzswsfFXTsQWBrLll2DG5/MP9aEgE3DiIv/TUms+FHfq5a", - "7WTMgWzpvPMS3ReftJi7VG9FMgsTGFcjgBcsvfPd730WAu1vkPJ4S6LylvC/mEbNQq6ZrvCzuyCtz/+o", - "R327zVH/eXCGrTX+xfKGoeE7Oabb1EOp2aqm1lwLS/U29VA+IzvVV56rkgV6D7bpXbnpb1NGLXe539e5", - "iRS1ss1bKo/4N7F49Zrv0twFnLYzdrG8wt2ffzDP6qcfFnS4QzN39RbFpiDFu2WnIIL+5Zm7n/sFyDr9", - "NHCaWvUw3v4A/11WrM8HQmtDyfLjinX7rjdUse+u2eHvZiwtor/25JfYrtwrqILYJrqFJohdsr8GLD7D", - "gMU9qIm5TXdfamK5yXtbLdE0Pf87KYmJ4cu7UhL1+O10ROjdf/cqop5mdQ1RL+NziIMAkpYdggDs1wjI", - "mspcgnS4gbN0+4P+zxqBD9jAFaIehnVWO1sRtnXiHQDe3zTYsRJ1tI90LN7xiva+4e3u3auk0r//7RT0", - "FalmhVDGYsKpxzFuTzuf8/H8qWMXrY5n4IC/S9TiLg7XGTZj3FbX/Aaa9fl1UhrHAxpeFUVLoF/0YE7O", - "rvkNts5O+XBO6EBmKZFFs8e8c1CNn05ZyPg1qzSHvEvv5uI+lA28UVtLd73Krs7ExCA9r2lLw5BN0X78", - "ehduESMYmkGqq1PZWm5DbC1XzlNe5wZKpQufp8rPNBvEPIznhN1MJfgYwAwy36mG2yvYOa/hDku1VbK/", - "O3JebTEvWQo9OXNHTb7mGml3vBcDKlefVvfLfFFXZe750svXCx9fL3zcm7fLtEcFWVNr4fn7O03y/g6m", - "v7/T5F29MGLaeFbvjeDXXrFsy+Y3COEWd5fBT+dtkdrobHdWo5bJ3KJRTGnool9MlxzgZugjYPdJd/X+", - "MbtPnPYx+o977B7TqsqGg69Vbq+U9+Kr8tPi7jegroQ3HxstdS172aHb4Fh2d3dVxdy9zh3HcsaivFI8", - "gFzSJN413cVsfc2xRIh+fb7USXglL/TmDuclYH5+pTHuyl1caVjd+jzY5gIKuFH0Aq9Z8+ZSHOXDqIYj", - "wimWYLqfehvy2GEqJ8dazALkVgxJaBz7Wgy5z6uiNL+8/EGj0HkXVrN4tDKHwAfugEsLJNRnzJl6lUWY", - "K58W6y4EDULiY0lMlHsSaqDXWHTtYuaGlrK0QWFdKBwNiZCERw4paoXCVH1lUQcLz+KEti5/wSbRmm6L", - "N6WFlMb7el4vP68dslh6ajvCbunt6LZ6b35BuEHTdW5M372aWNSUWPeC82d4kuVameeCc47dWxRmuxQX", - "U8WSVDkSxNSDUloWoPNbOcfT0dCtP53XN4XC+lDx1EoHG9aufQKvqtK7U5qknMbxPO+gkVfYz4vXmgYe", - "3tiJXoLd//W9u5sms3Nn0RDy0GBuXhfcNNjmVQPvV1ncjkuRCHNtxMumHhmc10g/il4nr9h8Y7XU8nYR", - "VsO8YvPmHIWCeVaLN5aBb5miYAmsnJjwJVSe/fzrui2kz87a6sALlramuBcsvTty25wZnUvMZgn5hSdH", - "aLJxNnZ9mVbq+7A8b7nWOsfRTZ7LhIRjFl5VmvhAlFOpTtGMAgw6J0ImVMpotERVPSx39L8ddd5/RZJ2", - "HVRL/fXbt2l2m4x83nmzJQLC9iU1El6vX14h/MZUkXScMEY0AlUVQ3tFLXrTftGUrjcd9bRuqD8kxpMw", - "J5F+f8IFU0C/8LA8KH6ViYgl8dztfwewQO8ojTuaci2qcw3a5RIYNbfqbZ1lmynwgHVH3Y4thVWpyy8Y", - "i5TTuIgqJUNeKOvmq4fQCWCLPMNVl/ciYkMumEFCMZJhXT07uQz2qQAuPjt7TTTx6BGQ4i+Drh76DD8v", - "jRyzFMr1Y0PBWEL/hHL96NmY2w4E0CqhPOuFwt5hXJW7fL0lvxLFUv2JugzIAz1BtUL1QwDruI4009dr", - "gm0ZGAmpYqqDrRPMoCSbYnnJLWxfiGBBg2KrJOi39MbG7EZbVRFPiZorzepdQg7RrbcHtGE9VtBbUU+4", - "0+v1esRWMlUkyhLbIg17E2iyl5EmtAqpwDb2yYhfM5Ef7XpYaGoIy5JC7x20EXogpNgy+tzDco8glrdN", - "fMXmRVuFZF7rruZ+hmK/+BC7L9heFtDXQtjJne86JXoG83JIeWwp9HHvKWFJIhNYHfarE+XOECovQm56", - "VAqZkomM+HAOLdxg+bhSwhdcqnSl7EbUnM1nLDkgtukOWZIjnybBvnR01aF0Hn+9aHnfFy1LfLROl4t2", - "6mOpbZh+43kuIz6u2lCNK5XZtmiiftK6BK8lH/Rm1AdYZFvlDrRMUMZv7ki5BiWz2s/pPlTMRmyteQHt", - "M2zcdd9aM/boWUF1Nj25/v/QP8vDDLdWmpHWrfJHyYCN6TWXSY4aUINAHXEqsZY1KeDIObAh6B6gD+kt", - "hXZlYZYkeS+zRMaxvGYJ3pjTGgEVV6BRzcY8HOe9p3N4ZjxiJKFiBAq3UcCmNE1ZIpRVHdwGVh2t/k2k", - "SlGhgxEjKf6Z5m1cUa3iIfi0jZKjVRzbne1XAwNoEyCGSvhHvcaDBmXVMuwMBivQSjmqaUWrLKcHmJFc", - "JgAd5SmbNXGHY0K/SNNXjAxYOmNMgJDDnzRcGIcUEf58M+VGwYaMfnIG1gXYJwOZjqFjOBURTWUyh8kd", - "PIDrCNdtmnBNEy4Tns5z9RjWULaNeEJApOBCWZf8KGcsIQJypcxIYz7SAtoO18Ft7JEBs/ovvKHS/BUX", - "mJzMqEtcFqS8n2YqtYEiJxofkAVLRUZjkjC9o/pNpx9fqTcaJRHl8dwdnCvC/swghOcMAKuHJsO4O+ya", - "JXMS0Tl5wEdCgjKf07s1U9AEO63+bAl+hp3hplOTSIYa84Ry6CDndEGzDei04k0SpljaJc/w8fv+MGXJ", - "+1P9I/meHB+dPDimN3bKPtBhhxz33z6wHzxjQ5kw/KJDjrkov/zwYRn9ttPzWLNlRCS0ycMDoaSDRyYT", - "msNSmFDQv1yv0C6Eq5x8uNJ2Y8JGNIlizeRyiE3lsX+fPrb4okbt6zVn2+iZffemACzSsQeazAGkjU9v", - "CJjTuA4ktrn72jF+1Tu2a7ep24ACn2fhePX38p0HNYbujQOG14Xw4ISzGxzEtjXyUCahOeX6Zeey8dyp", - "MU1Qfo+p6pvT33rl0M/DoaVnxvLOqNjucZLFKZ/GjMgUDxubFATNzGznVIKOHSm83ri8cWYoxTUTHA6D", - "hFElhepYL/ZMJldGE4EztYRBWOdi4+MFc22PnwHJn9r0qGYCcdRcUj7Biyt6DxHre6BZbMETLsjp833y", - "6NGjp3rHJzTNM6AV3hSbIQJ8hogeIfBfOmlomYFTBHtBDkGwefujpaDDbWvhnAGkfUFR1Tu59QveW1C+", - "DMKAjYwA0crPp/FnLK9AgG1+SxuesGt5paVO7jvWq3Mc9EY6dElfFaZMs1O32X/bQX3pMpjwkZYvYnQZ", - "2Meq6PebSusiha6PccUpThWZsTjuXop+DrQNjmS5+DaGy5irVCbalDIKmurg4B5ETOU0iyFhSc9rNMR+", - "CkJFpXQy7ZJfNPxG0zPmJgiWAeinoDp26v2CFVEpj2OwongEVhkaaR3XTQxrd7o0G/UVdsSGG9AG1Rrr", - "BHL20Nfd0babNhjgIEnHWl/vEJpFXC+zOW9jk17mFQV6y2QP1yf7t6lEsa7/pLEIhUuMWEPgTiLhZT3h", - "c6Co1U5SGsctytWVA+DVZO6YqhSsVJ/+yYh+jBLDNPemytjGQWdNfSJhNHot4rm9FLs0f/vdktjH36Bo", - "x6c6nrfxOGq+uH0KWr9yjy3ridBHXJbf52x2xNnTyUwFB6GaQrwzTKRShd0BhFdUeb4Uz7JERHImXppP", - "EwMNxMJTLmSm8mHlkCg2glXCsW6MIPsb2DCKgXzAagw4j/EqGseMCYQU/fYHBoLCk2eW6cabAVa8Fs2i", - "Kqwz87sPVyZczUTCw7HNPMg9PQLZi0tBbNf/uOSCR9hneqnoCNTQpgTgBUtPP4Xpl0tHA/b9C8ma7QRt", - "AXPbCR3ZqQQymrvUtYIddSlSK+tQuEGKnyJpkgl0uhslU+M1iyluNCYROAkqTn9unz0G7Qo3b4916jpI", - "tFn0GDMTbs2k+g+9cEFGm0dRKu8DQWf8L2Zhg/UjE+gVQMdL4zBHPh3Mu2Q/99CqsUyg2fmYClOvwllw", - "88KchpMLy0KYiKBpSdkJXr6+OA06wUH/1/WKdnorMtylBW/FnHGEW7HRYMePc2GI4GgEfjXklxny9gRw", - "jjZq09xKiWLde9QUEqtE+qOiGDuZ0OQKrVVlpTgmQ5XSszyqAgbgBE852Lt4+NpQUJf00+qgZigclyvy", - "F0tkRGSGI5nzEfyWMo5ZRCB25LoueVIPNXXJUUpm4IdN51MT79RSYS5C9xZP3mpzwONYiz0DDcRswENr", - "AKtMqLJBjjNQGi4s+CbwZqZERUijyRc0rY7q4hY9uWnJswyXyUHOY0Qvn4BjMEzMQUoaZ0Ik9YKUdBUU", - "KsIxBrhTfyKJ1Z/4EBIr/VEnIBGHTC9MI/JPYZWtF3Mqm1Z5CLnfYFzB2aN1MHCSYGBS4z2lV8xGoDsk", - "qhzEoDTji0Xk0CRoDjN0fJ27V8nQIOaes3rCRZYyEmWgIozlzNXj8XQDfRQcQjKx27aRY7kTTPUikmv2", - "+poleqc9DsA8Dj4bs1zzlvg+VIEyQ2BGkkwgMaADbyQ8srFvZ4P/qSw+87wMyGY9GhI4ixsnsG8Naazq", - "r9mpET9mpSYTOoDIYEq56AObtF5nSSYaFuPa/NZj4ZINveBuOvSGDFtb2JIRPWts/GLxvB4k+O9lr3GL", - "+rDkCgZXxFeVobnIm7qF+6BStSivUOSvW7Qv5RVnpZJFmnW8lX5iGUK5syyJg71gnKbTve3tnd1vu71u", - "r7uz9913333nMf1CPU3pK7W3vS2nTKBdgc/1zGaBHtMIzjxNwbGVhFyMmDL+9YhEbJCNRkWOVu5u+P1H", - "RhNBJjJh7x7U5+ZyO5Kh2h6hrrAFugiLtmGUbS0rrjmbPQTeMEaB6ULlteDqYEJon4sR3qwAxT63qG4B", - "n63u7wPQNJVuCaC5IVAq3NcarIkULOV/se2IqvFA0iQyVUq2InbNYn2ubo0yHrESgOa+f0sAnQv8ayLL", - "jlACIr8e1hKMSoLbqggqaeF+ulrA1XUYD3KCtxqh6pILxYZZDCpmwRDgZUJW6brzwQhtV18qu9p/c2S0", - "H6OEZoolCgNqkL0FSY2FQosXQXLXnCSKiag0pLoUqSTXNOESYmvY7ZU8MGVCO0TFNLzqEJaGmDi2JiWU", - "Cr96d2FRDdmP7z7+fwEAAP//lawqzyOZAQA=", + "H4sIAAAAAAAC/+y9/3LbtrIA/CoY3jNzknNkWXaStvE3nTuK7SRu6iT1jzZtnS+FSEjCMQWoBGlZzeSP", + "+xb3+e6TfINdgARJUKJk2fGX5syd21gkgcVid7G/sPsxCOVkKgUTqQr2PgZTmtAJS1kCf4VjKgSLjyL9", + "R8RUmPBpyqUI9oI+yQT/M2Pk/MejA8IjJlI+5CwhQ5kQSoTUf4ZUv03MMN2gE3D97ZSm46ATCDphwZ4z", + "SSdI2J8ZT1gU7KVJxjqBCsdsQvXs7JpOprF+v7fTP/nt0euDw1dnpz8/Pjl5/vynb56+ePK8/3PQCdL5", + "VL+j0oSLUfDpUyfQgKUxmzCRLl8HAC+I800D0OVRbx/wN8lzRtMsYa/YvL6IszEjPCJySNIxc6EnMoGf", + "LtncPh3iOG3WVZr0pmvUoIym6eMPqbxkQvmXfNVqlxZTGwzStLyr1Tbsh913v/52+PZ4/9Xxs1f9l+9+", + "+O35d6ePfv3JC73B7BrwL96TYtxbobNRQpcgvQYvfNIArR3uVmDlIoyziB2wmKXMA/IRPicRvqBZIeFM", + "5aD+mbFkXsBaGc4FMWJDmsVpsDeksWKdAmRciwFtIGXMqADYQGxqpjmNs1F7bGqehE8b8FkedhFW/5Gw", + "YbAX/Nd2IdK38anazgfQkMokYkkdwjf6ZzKYkyFncdS9EBfiXNER2yN//Dd88n3/dP+PC9GATBzVi8Og", + "f7ofaImWTYK9381fB4en+8H76i53gust/d7WFU30wEp/cCqTFKDDL/M/cQi9JIDlOY9TluzjeVJfHz7m", + "YqTXOMnilE9j5j2pVHnx5tfvzX+3di6yXm/3m+rPu824Ma+UsMNTNoFTtkLmOUZoktB5UFndYSGgz+C1", + "dqt0zwQ9fGWFrDzq90B0LMJ1Vh+qlKY8bF5s5f0NLNocQ20XawRmZY3m1+/Nf+0uVn9esIvmlQ0s6EUi", + "s+kzOMm9E5VecqejUcT12mn8NpFTlqSc+Wcv4+mUTwAxMC6InZEenAzmisx4OibsmoYpmdA0HFew5oLy", + "u57m/fdcTLPUYK/0eCIjFr//fjRNtx4jGnOx+TGAh/qA0E8L6Q6DOfiSg/+wEH5Q6RzOiIix6Zv8VweL", + "x5pK/dLWTxgT+0F5jfnPSPeWMqo/L6CM/NUN0MZphmttuSaFr1eI3fz6fZipVE6KNdV+X7Ao8+7NlpTI", + "SX0lpylNUhLRlG2lfMIIF+Tk+T559OjRU02cE5p2LwSc5YpfsW4zQ+rR/YrFbm/30VZvZ6u3c9br7cH/", + "/RZ0AhxdE5Wd3KtnwDwOk1a0jKE+NYiaslCf4xGhRHExihmho1HCRjRlZMbjmAwYSViaJYJFwHSMhmO7", + "YYSKiMDqZ1xEcta9EH+YR38QrgglCVMsuWJRzqvkisbZAnSMPAIjx8jvhgHNct93Vt5Lo1wdcEUH8ULt", + "y7zRVv2yA95M/4LBf+QT7mGd19lkwBJtAhmQSCrN1jTAFsNAXoh2er2eA9CO/mtCr/lEazf4cMKF+dOR", + "cykbsaQA9c1wqFhbWNUlnzbpXTiOF1QXThesXjNYb+nIc9DqX4lAyJahbqpH8GPORVs7LOmJT/lfbKU9", + "JVOWEANGE4QwaNP+rrWhZ7IO5KGINiDnUrlMyu2uLeV+AenjR3FV0HUKSZfpk2aZvLPeiISluf+hkJpT", + "lnDZIBhBlDUjZFYA3dYCctZZWfsZn7DfpGB+vwrIXC2QNfB6ersQ2NG/pGCEKhKxIder5gKeHfVf94ke", + "l+iByQFN6YAqRh6M03S6t709m826nAralcloWw+0pQdSDzU51HCuBzw/24cJYT6L60yxaBmO8sX5DbPz", + "s31XSwv6E5bwkG6/ZrMPv8rk0ks3SRazm3pp9BgNFq8Z/lZ8NIbGtDns9aQ1+zzMlw0wV8ZtB3uuhPHI", + "A6xGNFNTKRSq+M9odML+zJhK3yZyELPJiXkKnlopUibgNKHTaWywvD3FN//9H6VX99FVxyOWUq718TGj", + "2u7fxxG2tMlGxlSRTLDrKQtTFhkeuCgNfT2JLwJNVSlNMxXsPdZiEgy/YE/DSgywxcqyROwZgLb0T3sD", + "Gm0l5q1PbfnYLB4RVN48d9ZPnWBfimHMw82gKzSDcTE6FGmCplsEBPjy3c/HvdPe/vFvP5z+tPvoxdPj", + "V+9Ofnr7bQA+IRrRFBalN3bK3tK5dUUHU/7h8Zukfzn+8WrOx1w+nT7ZGT/l/Ll4FhS0WlDX1g5admbj", + "YhaNWELYNVepKu3E02InzEs0ThiN5sXLTXtiXmi9HRUk+7bFvqIHfS3T5zIT0WZJGMwvEJtDPXgJF48L", + "XLyWKXluXmhav5DpFg6yCYosZsS1H2nQ9f6zDWPAuCYAB7yYxMHEk95OGRNHpdcW4cMdcFNYOSqPeS5o", + "lo5lwv/aNGYmXGmVhciEcHFFYx4RiECUiMRBjQvJArxk7mubQMp5ZcDzXPxuFh+OWGdJIpMSifRcPOTv", + "HZr3mnFhX90QJioQfspHNQdhTEXIXnKVSqvD1U9y/J1FVlMVERngh4QLVJC5FKg+TUu+NPNaPwU3hV8r", + "dP2pdliaooILzg2jJKKO2y37wsDAKHR0mQ1iBioDjd6IeF6xcdHq0pjFwZZiFt/61Alg5X74U5nS2KCm", + "HBi0KuwGIfc596rnRqNvE0dsOFE6zUc0T+dH+fn8+uCHkyePdg+/e3H27OfT/d13r54cPA7qZ+kDoxl3", + "m7966J6lqUpBPbSmS3FQay1RpZouDMcYRWcvliGNt384fhOHqXr183dbPf2/nabz21mq9coMZJbuDWIq", + "LoEoSrRrF1TXa8fZhIotDTkdxIyw62lMBerixtYItQGdjrkiMgyzJGGaqC0d4zZ1S8bCQEZzMslUqo0R", + "Sn44ffOaSOszrDmV2HXKhOJSqObNNs6e8pq8+7o4BO58gqw6L0O+YtixUOZ/90Lz3kPgxfbX9+L85Igk", + "bMgQxemYpoW9oVwrOWy7Fe0oLOfcLOG+LbIU6EPuy7OztwRfIKGMGBkxwRKqpfRgDoDJhI+4IOC0tLHN", + "1sT0uCRZuEgf7QaOF+bJ06eOEwZtjYobJueZOr4pUWOZpJ0qE6hsMqHJvAIXBMjK6PXaM1V/aQ1r2pLS", + "JzXlQhEKu+7b6+ZpF1pMy7azQrcphuIQR/lW5yLQR8JOrBFWx/XqJlzQVEL4eEKnUz2VPjONI7bhTHJG", + "embeNAFzDdzSb47Nmwg2D1t8coovFgJy/tr40vQqP3UCKdibYbD3++LD1APEp07rT3IgWn9hsfPpfRn/", + "9nctOeN4NcDNt/sJoyk7EtMM7Lv2ixjThEXPOYsjFXx636l5OOFNVbhI9DGSKebktYASsYi8fCCus1Qc", + "oAxx5+O6EE89IdaPeRaDJfn35SPRUncLbqzzXGXXq/jYKAeWBl+DGyvfr8iZFWrcNJeuTew+4G7Gad49", + "LZHo3scKqS3IJDtzNHVQFAwFc2VtkshQc/dCHPJ0rI/iPItPW8H54PobS5MVPX8lBSnPfGvMTbwDgM2/", + "tmwaxyV4YWuQug65NXMp+vlnxI6WBzsWCzwwt962MuJOmFaauBjh6w5F+c0ph8BeJNR4BlaVnvBlaaol", + "pI9UfMxS6gjbMikvSb/V1GE87q5JDQEE0JU0ViGlEGLiSsmQg8454+l4JaJtsFFd+2TKE6b6TRa/foz2", + "UkTTXJfFVMgKIKskHyyFS7DrFMmBec0JDZx+BwNSBb4gSpTAhzeBrwZPUoJlBSLWH19JHrGoCceA2Dwv", + "wlkKVQS/JA9CKoRMbfiL0GHKEiCVh901UVw5mssUu/SMrjFNbWXwhiI0MSCnknARJowqlvuO5LAw+ZyM", + "npoSQicyEw3Yw2d6eCRKcjqWWRyhcT6Viqf8ygbx2/l1PPkChTeKDYcs1EP6dvPQPsQ9xYwzRIOICBXh", + "WKKJmFgaMc+75G0ir2CrMepkg50h4+aoAE1Ff1FEgcmDCRdZyh5ulNILll9BnObfWKLX9DKh1ycyjuUV", + "S/rODhZZGo0Uk8hYL1t/SWgKOUFpx1D9bMzDsWaTOQmpIGN6pXc64kPwLBSOSQ2mFtQaeTMjVOdkTDVp", + "DKU5l2FkSCJ7Zv2ZhrXMMy2AQxqHWQwCmKq9/NUPff3qhxN47XtyfPT6wXF1xR1y3H/3wH7wDCbGLzrk", + "mIvyyw8ftqLPGk2udbw7LsSVY3S1g3hSXcrXnb6VnZ4mXCY8nVfzi+pi0b5ZPrGJwTxkv475SKud+Zt6", + "OyCuwiIy5InS2HprH0JuXi5OIxbyCY2NWFVd8oseMJYzltjfCBcRRGjEyM7EJ1OZgHuweyGey4SY9Xf0", + "0A68O3q2id64LBlB7vaYiso7u90L8cuYgddew50wotgVS2ici9wrymNweFk9VdFJroljgomaq5RNiGKx", + "PnpKakQKlIegqzSfG1KXSEgVU2QGU5vplNYKi2lyWGN2xeKOM3QYS6VH1AdiqlwNy81wyXfgCAMTekbY", + "y5m0MwI/oFctpLGdkTM8bSqamyotGGbStn8BFuidWuUI4VQvAAgq6WvWObn75MniDLEbKE1Vzd/VVIwy", + "UD6KS8fWEq+edWmtYyzc0NC2n+dixrEgNuOYMjO4dkWuhA2zGHhhQsXcEaOaDoCgO2SYSM0NKXBtNjWh", + "sgEFnTMMmYJ0wzwyrt/S0MbsWpNNxFNDYKpL+vg6VyTSIE0gUQxHkiK3jXGCDGLVtBDoBj+aeB9oEr0I", + "TDwzjuVMv3IRkKnVmIwjniuVQdowMAdX5CIYZIlISSRn4iKwr8FAD5c755q2qqb0FW/gFSJlUqcm9Z3o", + "1jRb5I/0vL2NXFgVMVUpHDB+xVg/RpOCmgNuTKdTJupehBupixNGVZYwWIE/7/3Mpg0CdeGhjtupxTx+", + "HnVJLb9cCpRFXIoOMaedwmhd2W62b8EsuLiWuaCuXCnQ2fFti2el7eTM3bh3PXxvzifanhF9thdXp3KY", + "Ohnm5Qz1Wuqssm9/r83OkvNLK3H20LHzsismCK9f4eX6ZBzTTKUs6pAxVUaewMlF4xmdK31U6jlKvJzn", + "xncCrs4FJLTbrP1FkB+wacJCCrPxkZBJIVgGNLxkIuqStzFYr3oJDlYIFyplNPp/MPAHMsBJDJ7IqyJD", + "dpjVHGUlgFXGQNVsYOtfZQZIxBPbiMQslRMKKkA8JzSWYqR4xKoIxePf8DxRIRM04ZLMrK2MPARSGFI5", + "4MINimNgTa2AoaXNlcOjmsI69m5sYbUaFaIRmkK/cq6TwPDdC3GmNRFnQNRz0n8qMBbAUjBHlIKQNx9p", + "AaJRTMmMzlGTQeUeMqxR+GmaNrIENxWPGuMvgvz5IpxLY7va8lLBfjimIgNk5zhTWTgukAAonTC0WPRj", + "PMFwwIugQy7q9pL+WWttF3Wj+SKw5wpF7FodsyrpGq2GCl29bWVEHEBut6sT5yqt1afNx9a2OBrmZ3KH", + "VObEhAljwTkXetbVLH2nziJxelx9H4wpvGr0RpsNI9Z3D9MlQg48akgv+LHmiml+dckasoATHKPQPfSr", + "Q5mM+BUTTZKgGnWzYapS1G2Sx4drh/LmfO6eYLo7+FJHYTXk0+7087rYb3Zcbk7v+hqnuq04lZl9QbWP", + "/HrCJZvbEIrRCIuLm567Bjub5ZIaaziQlxDuUstyXjHB5DV0xJtFcO8i2cED4a0pwzhXSeEYSXPpKmEg", + "p9F4taRv1YjG7Fh8wU+VkAE4pQnc5zRDearSdMmZ1g/zm1U0VrK4ODYbM0HCMQsv8+w9A7nR37V2wIEx", + "HUvewg1r4YJtjRIK+r35KNeTDI+Y9XbcbHmtOM1llhA5E8Z875KXFL4ZMK344X5WefrjRXDJ5jsXwR65", + "CGBVOxfBp0WJYvY0M0kTpcPM/NYyn8tsx1KO+lmDVc80MMqfpxiI0Dp0yXHoMeNVlzS6WQYZj1NCE5mJ", + "qMGjgWZZ2QMC1lmUQfo3mMk1j0aXWL91TsHKOK8nlAtHX/fQXgf1Z6Q9wbQKrelOEcFGNM1vYd44V7uJ", + "TTx4VTUeVZpD2rNTW3JcGmrOjUtPOv2YwZHnHo1jqhxfmJv/QFAs6aeJVKiv+0qQBLWL3Q0wOhqh0TNv", + "QLJGza2SD9Kf9f6Kf6Yk1HOh+Yuk1yGsO+paK73AgyZwTKkvE7Mckp1er5xSTwaZicvMNPVLDTWMziLy", + "Xa9j3Pi5Obrbs5p1BV/r0WXDnYDWuDthyC5pfomgwr7GSY9apf8WwZM1LxG44q8gVa/v6crejlmUZl7x", + "XsYyi+BDRU5NLB7FFPDhKV5rKYULbaixVNpkK82SgdTyGwud7QU7u498BU/g+uCTcKc3pBHb2gmfsq3H", + "0Tfh1ne73z7ZCp/sho+++fbRTvRInwVKZglcJdB2FQ/ZFtw77QTaKL9iicIl7HR7gXtvsHLBlE+qPs6d", + "Pfi/bq+381sB4TSRk2nquVywMLLqu6gBiTNXQAh0HksadRfUhGlAnC/KqiExd6D8udfmNisIGisyIVML", + "LW5yrI1wGgGrpRJu5e/2Hn9jb+U7xarc+1Vwr6p0WNeegrn+IxOjdAwGu8hiYKlGmauhcm9ml+zryqUA", + "eA0lFCwGF6Clb7eeDr4iHDxaOn9eWa9Yf0vyLcNSt3YMdS+ZH3b8OtUyBn3n1FT7K/z5JeAqvOLiZyu/", + "ftECOpfHvCUC8KGlM1eQqJIgycP4BuxMYXGUAmTk4GUANVUIOoC/BpZczPFkNQaY0spng8rSs2kioyxk", + "CXmQm5dw9uH2POz6DUqQLUsgRtFTwx2fMJXSyVSDMTMhbPe+SL6tPn599OjR025j9KYi2bxRkBU5xC9p", + "yji38gYRmjCMhhntyHhUwd4qVlleg5G9y7R/QLphmzKFdqxlYKmkdj52guutkdwyPyJT44HpPNnCFAWs", + "CasxFIx4Os4G3VBOtkNN4fCh2lbR5dZIbl/tbsMPAGktCaqeH1bE5I1WJId5YUmv1dmY9sZqQ8HLJOaX", + "jOzskokU6biqbO7s+nypUVake7WZyL6Pc8FEZh5zeLx8c34SdIKD/q9BJ/jl8PBV0AmO37w+exl0gl8P", + "+yf1+ofVjc5B6hgc+NQdpz5e4UaoJA4m4Zhf+VMvjyoXQLX9hm93iJBEsFnZugupcGMbWlnkt5kD69EA", + "FntJDD5WcgLVHa0170+/hKL8D23C4UlDJGQSAMNTYiVkOcVnj+y/Pd96KbNEdcgZaDod0n97RPZpHGsD", + "Iw0bXEi+VXkuu90ljGUau2zyVl4yTJsShE8mWQomRr3OSuUeMubKjhOZjcYSTSYNQwcLBpnwHZwamoMT", + "irc9IZyWe+f+qTxm9SWkxFmvjoGDFoYqxcRVW0WyAwab5Yj8Z+CCSqrTJZsXUyjMvg2lUFzBSQtZYZC+", + "ROPpmIoMiu2QcEw19CxB11ZE1bhmHAcLSyff1b2GjutxU3MR5hhn1ylL9Kcm68Wk+QqZ4nGHdGbCdw30", + "DQanKbqHtQ7VDZbzZloshhX1Lk0NTNUl57gUY8vjWyqUU+CcQSKhKg7k20EmZR4bSGh4qbrksDGAbBJe", + "8E0IJMcx1pwATzHgy5Tqc/NkAAptD4ELQJR4IYSxnfKd8CIW2uwuLrT5qQnT/sKZZzku8usXrkApX8Aw", + "u+wkUVRyWoosUohm52iGLEMWObHQ0/NjLaT235y/PsvLiuUu6IIPkAU+gOvBxwhYj8l7R8Kp3VXy1TvV", + "r46MtFuqf2HYBiZbcCDr06QOzI+YiVG4n1CHAAXI4jphqNwZhJpKiQq9RTxVdfGptNbe0ZLnoTd5JVqt", + "JLW3WFe57Nar1293fj1799PJu5dnBz88fvX25Nu3v/V8m3I3B8OFuMHJsIq49enieok+SoA042dZIg7k", + "TJiSJadslN+prmyIwkelXGUyyBIBuXtkjANA8kWfQOA8/8TrzSy0ajJg6YwxYzErZO9wTMWIRYTx3Llb", + "nbJINYby4IY89Ogl515DCZVDEa1cQIWJ6FbKp9zbui6AchPTUDnOms69Fmku9XXZtQwSRi9hX7WRSsOx", + "1XZw25uXuocJ80cHezb6Uj12FoTiNWY+NSKiYJYKIvJ9+ruiApIyvMEYfSiY/N4ZpjammIlVJPpWog5w", + "TM9Rl8kTzAKnCvAi4+hFDs0JC2USBc1LMEWDF8VprliCyY8LIi0V1tp98rkrJpVqJZkbqXdSKamG+1oQ", + "t7GDR7kojveq6moJNCvhp7vOzR4fBlZqqtFdpC4W5QQqJcd+PDogD84Fv2KJAj46x3F/ZNc8lKOETscm", + "r/RUJqin5M7p5GEFoy++efLbt0+e9J//0n/18nBn9/Wvvf2fnj5/qY9JmmpDKdgL/t/fe1tP+8/2Dw6f", + "v3j5w6vj129/Ojk9+/mXd7/+9v7j7jef/uHB/8fmlU3otfVlfvOo6tp0Z6Vbf/W2nr7/94P/3vuQ//Hw", + "X57p3nsI4EiMmEpZtE6Ara9FEn5u/KOgkklrqEFxPtPDJ0lkUokVMTvlKlG3FcJs0ecLsxUrx1J7tYqW", + "6PNA/2pV18rxsjBvyH5bm8qlKVsgkRlv8IoXp+ErnxL8I1dp9cq0W86wYMtWp1Gt2EGtZH0tyzEft2qK", + "oWfGnDltj8MWAGAV9Hpc0sbFi3rq3Uryb90tPW1RA90oA7bwebcmeT3lsrTo2G9wrENoX1SG75aj+P4q", + "8qUkpWKKjq0L7xRfR2T7c5cqNLMZcrk5pZSMxjXo5R6QSsmXf1sEU8XTXZKN8b7cgGRsQOXG5JK3ImpL", + "Ks0z3zWZ5I7u2yIRFzd3RR7Hm7mjt9otPLyFXqpqwRVJk0yEbnhYy9gspu5d9UxELInnXIxMjzhQqdsV", + "xaou9VBkk6VRsOpHZ1qZ8WIOhnNSWffPT04OX599eHt4cvTm4MPpWf/kLOgEr9/8EvgUSe9EJZN+0VVL", + "SDr1mQOoN0Hoq5TJbJJmzE2esZxBIRGahsaHnfexQO9YRfV0/NHBXnB6fhzUdNuj3MOP9AjjnJUUvU7e", + "oMdRXv/RLbXl0T/Y2mcKTJ6qog/0Y3TtOb5v3ZROV4y94Pjo9fnZYV1dLK1lGTGkLOk771f9KXX8O39b", + "Gs6bG9b97OTcYGtp7oWDzo+NOWaQfJHKYjfbpVaU9qXJ1VQMU9uxpgjUazphESQPvqUQIpsmTEEBXA0k", + "u04TGtoCV26DJ1XkPeO6tMHTJa/YXOXVdm3UUjgBRkjhdAOL+ilIEBXKhDlxxoYsvAW0WC9vG63Z4rKl", + "obw0V1ktdAjUSr02goSpRTdF4h2Y4zXWr67dQ2lVQsPDp0irL5nj/1SYVWhLMZqTK3/JfCwTcnp+3CH9", + "n190yPHRa7yuedx/54btFMpge9hDAxVYh4k7YAbJlCbKZiLkdamey4Scvz766fzwQy0a2CmDjRAVFahx", + "ii557oskFgiwKNQwmhvOVbeYI1Vr2zArdUxaoedQ6ZoUNgt0pbEr5UqzLMngAqZVXTwT26ZwySkTWEhL", + "Fv/enl6OtnE4ALh2APjrjrk4NiI4U8zD+kZVwAMUNijoBO5eB52g//OLoKNPL/3/++/K6bb4Zb1jrB8Z", + "/RJyN42XnzKWzE+YguuxPrwk8AxDcKiXQIemri+H/PePPuWg4tmquo2aPFBXeNdnZ9dSEUSUmpqFGULD", + "WIs/bevT+w72WmxM60rl0gkWqyXWqVf0K7z3GGllzzmkImc+q25orJANFSFJ5cY6xG1I0sHO+ryCZdR4", + "jvJEzmyMpR0v3WeC6dSCNkt1zkWV6lqur0GxdprsNuWSn9UzyBHxeINiQS74UmX+yt5F9KXjFsE7B/Gb", + "pmgbWN5Uv1iX3nF15ancxaxwmufMsdHT67WT2uM2S19QXPyXw2cv37x51cT9nhF/YYOxlJc3Lu29aOj3", + "/sXsy8lklZKdniEgcctTKwEznr3la/O7DPlFBtP03S0l191ctamIxWw9WMyXDbCsw1VRqSXwoooh7jVS", + "CxNXRcdgCUGvEmhmnPpFUH+q37nSJl7CmYjieSnpz8zXILpmhqp8Ue9ptOa2T2QEt2g2huuKrDHhyYIu", + "XWCdffE4Q+s9lTTXuAmppRRAuyqbmKi6wSLuwyR12zLFcz2vOhkuoWlOGKZeM+4Lpzt74WhF4QUGZAOl", + "NPSd8O9kbQ9v44Qoz3J7x0VlnoazY9WMXS+9+nJ2S4RX+KKwloXC3j+bTtv1Et5qmbubJMGiy21LAmz2", + "97rXehuXbLwNljJLjgT7o2fJCzSOm6gURiupl2gxg+dr8q3HUy1Ti4+X0LpXrdg8D69iQD8v7P2riILy", + "KopMnWRHI5hc6evYH++29s9Pz94cb7087B8cngR7Ru9tZXPwkeBidMrCxFfb7xQfEwXPTQ1gmVQBcvOF", + "pLUSQ8avsGxMBEnSz+FM3SN/DKhi3zz+gzARykizMhWRnJDBPNUcalKQ4jmZJmzIr+39hj9mY8XCD390", + "yQkL5WTChP5W8b/YHtl9XPEZ4run34x2X/4ozmbR0/745ez86Pj5aPTz6dM3Q/mWDl9/V04Ce4AfPfzv", + "3+nWX/2t33pbT/+9/f37j492Ozu9njcJLMgST4M/S0XnJz+asio13uC4z2VxME7Tqdrb3ja/gBXReCRV", + "GFoDUt3NFprGa6/M1OieOazQXcyLtcPphoxZPYaq+DVPgBBBxQIqu+YK7xYYwJtk0VfmXZV5Sb9UzbNo", + "NsixOK6Np99fLq9i8kvien9yhYe5FqTGGPZonxrj81t4HKo+KNxUoIb8nRVnVp8hI8di7NYyctwtuYuM", + "nPJm5TnFC44LDIjXWo+aYv75NUTQthOesoRTk/URFaUvkgTIIeJidCGo56qblilazfzDVK35w9Qyzpt5", + "6nHwygWZMAXZQSCFTSaP9xjwVTVYyZ8k6oi4NdcSv2LJ/HRBO1j7jm0J61PG83oxKzMZkMJBGQwPq9/I", + "OvJUs1nNNjLEsfK63prvNA9l8Urm1Yl+fw2z7DAPLDcbZWU3EkBWI4Vi0a2Y2VyPOhsnTI1lHL0tMLa6", + "ugYjorJWYPBjQxixsQndCqm6RcNXW7p0heRNJ66ysOpElmMvtVhalSCqWP7Z6nJ5nKXlgs2Hzf3QiiKu", + "zv2HTh7xKFZQp452QcH3TbW3DdnV/ZJGDqLHRZE04aMR3CTLxeYf5sLfH1ZG/eGs6I8LobJkSpW9NMuS", + "/Lz4I1/PH1p4+wi8TI4t3Jwe2J3KaBv0/ZiMuDHLUTdgUIPeFAtb2yFkz6X7dWTdjkAsFttK3C1xersm", + "ZF4wX7CZF0vaLin68s+nmOtnacWjS2zoNDqqRFtv113YvA1mB+yqcuBabcRBTYGp6F1F1HO9wGCQMKow", + "xlig6jm1HQojrqaQW2wvUnk77ZcqAZ+e7+8fnp4GneB5/+jHw4OgE5wevj44ev0i6ARvzb/eLwtO3UY8", + "u3BBItDulK12o1E8nhVatBVSVou2bqGKW8OvwC2KS5Tuodg2Hs5R25JGmzSZTcYrFk/03ofaNTzkhdpr", + "KG8Jhkpu8yXvLvGmY03EZo9AHU61vvWwzDWAwGzQMWBWd/duAS0Zb88nAKN/HofAibGObpe/fSr0Rvna", + "P8F7z2Jrb61lKOmRGqNNVqGuOT3AeUKkyOtj5EhUddXUXt3yFHewt7lLHhOt6cSVK3Ho9NUzY6tNq5mu", + "ck3OHscNXmtk/E6AOvMRDqzfcM2tBQKpeKcAVmXTqVTMVI/L7Y0Sh/ye5+69PTzZP4S8ZptR2OvpTTGP", + "X58fPzs8KT3t9VrnlrY2BMtysMqqBSJWDd4AQsD8QDSAQlsX4mQwhz2eY5dEgcEOuAVj64Q7BfurZZla", + "8MkGgkEF09QjQWvSvkYM2JK28xVWl6sedIN5UTb/KCUTOrdcU7DKYA51PKDZS+Wqwu+VWlULCnq4dFUv", + "nPaVhRax0IRem8Xv9OA+j/1ro9zVzkRszXZGohcsdyFsW+ON8V7eWKSqg5rneRv3dEwmWZzyqaWABO6+", + "QieI2gFTbZSS77KlhveL8orbVJnxpey2Sk5xjte1JU2eX9rcaS4s+Y4g8d1bk89r2bZVsvOI0k3O4gVm", + "clUBXy3gAQB9zZ71ZzECcjabwnhmPaDeNMYlFSGfVdVGkjAajhu68q2UUZvTwV2l01omupPMWmNgNUqZ", + "JV7FZpnRlFYL6GzMqV1PgpQKkeoV0kmzhu/oLgvuAC/UVorj+MnC0/hvyV0b98JW+aLVEblK9vDtWPG3", + "l1rcYjaffe/PLr75qf9Zi/veRuilDYEtkFC5z2otG2CZ5xAm36DjEBfz1W94I79hU6ORPhbzwRqdnrDZ", + "0FsECEczxV9hgCK7zrTD1vqIMaawLTh2ghrE7CadMBru83qhYyL6nLBVdg4QCfB6t0cmKY2h6Itvj0Ip", + "VDbRlAjvYS2dahGeOJYz6NCBtesV0otTduN9ycBoWhu0cdEwm/Mb/1blT8qqJJSQXFiupLEYJK44kJMP", + "uLQPR31x8Ojt9Jdfdvu7vyTfTZ7+Z/gXexm/ePfd9WT/3exFd/7kz8enW/1f/nyeffPnf4b0+V+9v376", + "8/HhX7vfnSgx/3n2w3D47smf18dX0lPdp46kJi9Vh/BhnuuQ31EpurzD3VGVd7I3I5fVtyr6FyhsjTqa", + "37dUshRvpauMQwkfW7SddMhk3dvISwHiteD7jSrkFClITVe1PeLGy6EQUYSKTdzcZ8rb9dKi3llZi7gl", + "kl+1KmljGywtlRI5iNlktRT8PjGfkQOWUh4r0xWYPDh5vk++/a737cPuhXC6qRQc6mR0YKLo1IwE0doJ", + "nUNRHLxiVi0TAnMBSUZzp+QN9KbML5Irk/DwGI5arVKD7RARq5bmSMgSsWdmh2vlewMabSU1Vdmm1ZnZ", + "65K7UmaJXU9jKlCxLC9WE07R2c0WVEcIyoSzcIV1nhEq9bcw7pPzkyOSN/rDykO80lLRwtgSNo02g6W9", + "WIY03v7h+E0cpurVz99t9fT/duqdGL3ZH02JtXC1wyTVhjJi9WRnbCMH/Zxynb41dh+XqnBzkT7axXJV", + "fJJNgr0nT5+CrMa/HjeogUhYdXxTosYySWvFt1Q2mdBkXoHLXPMpm7keUl3WgU/bEkVqNoVd9+1187QL", + "mWHZdvpNZsRRvtUdy0Ltqj9g4pIVTxut/lDt3+9JUzMvuJ35RL3hdEXxEOFY+koyCkKTAU8Tvf/4EsR9", + "qGL21k9lNtPyp516DA1erujSVK7KqrEWZtX8tGN17Gp8Z0ZlJKcb2/1E5VH5vhT2lnKrlwo5q9ciHcss", + "+ez70AL9tgqp3w8wTdiW1XARJ6pUfy6/nVag7iLr9Xa/gVa7URZzMYLLIAf9X/cI/O8QLj1EdH4hfjk8", + "fLXn/Dhj7PJCQHvHveJX6Ap5IX497J+4L88ZTUqtIlfpEdkJat0Kazg4znu4Fd5gGrsFHOCOi2L2BZow", + "QkuX7qZyis27KtdsoAEZFXRku4Nzhq31eAKF36jQ1gOFuDeHVlWaiNwWey4QK92KwTbOOAXYvqY4lGmW", + "tYlY0VK9elHsaDl8i+NHOxuzIqqSiEagtxunIDS8cJpmrNIVZCkAi6I7yxEUU5USMwR2U+SCpxwbRJjS", + "ybbhYrHf/bR7i+1Ha15Kf1zIJ7BOCytslV4Zee0tqKPpd+VSjCfIBLmwWg3NxPetoCzql+1WsVJ6c2EZ", + "M4inTGNqXej7xpwkrzFEsNxTAr3gqt2jne6V42ygphLa2UALjyffoMac8Cmzs8HDMFMfCuW7brPUl1+3", + "23db0cfSgmY+/K3rtVjeMN7dAHeW6l60bPm+KXeDafLX2tWwvGUpTuRQtJ82ll9ZrxFPBUyXjpbizdMJ", + "0sv3V/z6zZRh+XFqyxY48TD/xbVFmKjNUc2SYSKaSi7SrhFPoF6Yn8y5U/rJhkad34wwA6aEi61dmqZs", + "Mk277HpMM+V/NqQ8xlqs1ScJM629lre6NpZTY73IX0rVKCvy0qlBi6X24P6+m+duKo923KbcpeT2/IUW", + "9WUdWDZqneG4LDLBRdMw0n+Ozsy7eQJX0R2y3pAxS/TLonHAF3nbx0qjyZVaxDU0uvQE1Czw665wixyl", + "ridSkUTOUMvFr1QRFklQsYDS/PBpMTaqvVCqSfEr2+uRJyb0o7UUdl17iNVlcBxQVwzJGQ+emtA4ts2D", + "03wuoEis+KTKUw5kOtaDqhXwXaYQpBtvrm/9IqbS1g5P56d6KKSP/Vhm0b6Ul5z1M025tSikfgF6gM/Y", + "gNDplITwtgZXP8//wuSH4MMHhSXIC36iU/6KwebDYE5IyE45YDRhyXN7LMop/TNjtV4LCIo3ZBR0AsAP", + "uBFhsGL6cZpO88nXnlZjoPVUy5f4n1lan8i3MsIF0VJjy2jKthvZUig+QewCu3MfyNBjKR7IMJvYomaB", + "qTSSlwjJRVWXy+1IDwDm/lD6IktMHDsNOABhgkJTTWyhgvIPysNAfzVTYr74UKMXIk2KzGVGUmmKvZuU", + "1g6W/TWVaGFMLD+P5mjJrtza2roQ/8qPYIUpqJod/+9//4c8AOgeEuxQLgXIAaz+f0V5jD0DhAMZbH/3", + "X8CgMQ+Zif0bcu9PaThmZBc6wxUI3Nvens1mXQpPuzIZbZtP1faPR/uHr08Pt3a7ve44ncSOYzUo4SPo", + "BKXWc90e5KxMmaBTHuwFj7q97iOsKjWG3d2mU759tbMdsUE22p6wNOG47SPm9VGh+IS3iXmbxPzS1JLJ", + "A/rlZgGKKK4l8oSLSPDROCXnZ/vl7YSBIJbPkgfqod4cBhuJDhQt+Ca89HEA68L9AiXvBUsPNFxmNFBD", + "Me8CFrTb64GmL0Vqrvan7DrdnsaUQxALZWVZ3fsv8vLwx7ckJ+sPZjmvmxZ6If6LnP369rD+SYhruxDV", + "JxgQ/WhMuO8vClV45yL4REB17fZW/K4DTRG/vwi0MgjDdD35OJ9qbUgOBqzY2A7ubOOuQh7i495O0+mT", + "o3/7XNAsHcuE/8Ui46jOc2IABpNqt3wcdj2FbvC1USCGCZEDJAVzN3qSE0NKMQsAaCR4rz+wxO9mlDXS", + "PvgJaRyXW6JpRTKJYqYgActsRpecjbkiVk/GIzzFSlFgi0cTLrhKExR30yyZSgVy6GiIqTtcuU5YrUvQ", + "S6Z/YyGLIGwDPhG4sT/igmLBScSF/thGXH18UmmWh4U6Eooh/MbMtuKVbRCrb+mINXaI8r2M9d5bfgBF", + "NFu//WY4VKz96895nLKkqKaxyldF1Y5VvnLQjclsy7+GDuqetP830Fl9MEdPrNWoYK5CoYKPn83tgU9L", + "+awlf5Q1eJb5qHKR8X6pWDUJRBre7f+Yi+8FGIvU1KY2jh5B5W2xSG6DfUDG9ZbLpmc0Mga7R8LdSykJ", + "OGRlSWClpCsgyIPD6ylLOOh98cNm2bn90fnrKPrUKEtBPrvd8+eERw1nugPJs/lRtLK0KoEU3Jh+293h", + "KTfxrPpvtBl4ohUcvyULViL4u1ER1vYo6EO36bWuleir85yzKOw0vDEF4HHv8fIxXsv0uZ71rhSIcvB1", + "Ld64Wq5RVDVlbXhwQSgSQELFiDUe4lfrH9/Qz7LtIXYmPQeR0yMWIU+lkZsNh1IMZ7r3SIIU4nJCcZ5l", + "gn/ZLBNPAvONWdq9R7pW43AHpmmWrtJHPPxcfcShP/yKa9WWSDGSzFJ3sU93dqOd6Ltvt3pPabT1eBCG", + "W/TJt9HWk8GjJ092Hz99xKLd217sbtNi297wLXesrzvImtUQZAG8xT3IRiMuRt0vXXWoiC5XQuIP7z91", + "gqn0XURDRCuQsWCnyYQMoAqSi8w4ljN7Hx5cKofGm2AS8TCR1CMdcfhcPprsrGcymi8QDODkwbn/vZre", + "asjlU6dpvC1Y278Xi54vXOJ0vnw501bMtBYvRbtaD3/k/k7jSbPVsispsS7jOOmx0GdTEJhaD9rwmsn8", + "aRwF5qERRk9kjVNPXU7FDyGQQJFri4ieyeSvnOOPPTXDszBkSg2zOJ7nEuhLFrVHrk/bJ2MdhdMtN7LY", + "iZX3Uyd35XJ6bmH7e7qb8ls2n9n5415Mt86fVllKha1YP8yheQ2hSTjmVywqNev3QWi63fTN+35IG25M", + "37oDytBpG+fT7TLRF649Dgt50M6wbtIlrWCBqBzjcC1/guWY9SGnUprysEv6dkaNZ/ucD/GfmjFLm+fe", + "VMI20/aTvM7SXGaQGwwFgYtUHDIEfjfJw/mNOFNKB+pUhDQOsxgL9ligTHyyVJUepvynIqb9p0m+7ZI3", + "Ip4XV+7SMTk9PwbCqvex1jgpCuho+yQnWmiGYRcTUiEkdotH3icxTVnSMWFOw6k+2Y5J7c/zUs/tlO3V", + "+NKM7uTPqyZ1yeIzlSbbs9tCz9jZNKQ+6MyjIun43jD4kzZjvJbpkbZVNEPejZjA7SaUFHXEV3bBWWrf", + "/mj+ZdzSmOHmSUsAGe2KivxU46nLJSLnCbg07fuiA9W4SuHCYgBDBcCSPO2QQZaaD63Fa0eMJJZgJHQ4", + "1Cp33kioHPuAkOOEUaHgsq6WTjNqCuRaRGp47LB5hgMkR1+yeceeTcJ+UALdAoq3o9gVl5kqvzGmV7ZO", + "m0kgJEOe6KNSEAq1A2ye8oAqrnzC5AC+K4TJappivsU+N//jxpOjSLP/Ut3ZRtNag5U6zQGcguYXhW9u", + "ZS97dymv/wahjhsI2FFCW2VN4Hv5zaK06BiuMHOyLsxWzZ8AXaVLziQZsjRE4WamheI4+f1dN/Q4jRlV", + "cLsL3rfLsrBt2wSbV2z+Jjk6+NQc8HxjkwP0W2bePFVaK1t3ZWm/wC35mtZxK2kd9902XwK+sb0PTIb/", + "XeZzAF22sauReW7Rqr6vNvHIcu6aYnj7I/x3iY77s+SR1h8pzodKo1ZvudYzSSzFiCXYdLGjf9YKqPgn", + "Yh07LHBBhlkClrbNts9tWilUl1SnyHXYhKWJpKGW2/EcR2Sg1nbyW6BUzE3iA+z1mCpC44TRaE4GjAlC", + "0zThg8y5gIxzgKmdsAnlAtXpYVZY1o7mPcgSgGgmyDCRk2IE2x6SmKhMrkeDYkuJ4mIU29n0OTSXWeEX", + "cA8V0K2pyC8jmlJLckh2ej3Us5UkQ5qQb3oGPr1MWJ4Zr+Os7IElb3cOnioWDx+SmcziyILolOd93AMG", + "gsxordNfSW64Bwbt5O8OWCgnTJEeYk3P802vUPFNDopGrkEg2iH+80dvPPD5ysePodyWqjteQcmRphd3", + "r0zqm6p5j3tPl3+/L8Uw5mF6F/JJb6xl53UElCWERXoivtP1KjXH+P0GUz0cN1mwF5yeH9cuV/TN9Q3k", + "AX0oF31UbNCvExjvnBvY/EcX/5Uf0P/o2vqRLcqDxdko2CuXyzL1qs01vzmOaKu4z5y7bvZiWutYJOB1", + "lVQHu0n3+iCdWGqxdGrIp9mL/K/+kqsc/zpCp6znYk3HOmNp4d4rOoiKIR9lSM1kyGOmB7sQuXML78s0", + "OlZxf27HrWr23u9IxWszG3ejukX6vmgG7Nx0E/a/+onbcfz//e//EMNOE8MtNbavnUTbH+G/R9EbCP4s", + "VJiXyoYLgfaUZWdtBh4dQPQpzkbNjk7L3KtpSiXAW+pLCHDZ0XkzDeUeE4PZjEZiWODU9Gyfz6t5SxvX", + "+ypANyhAkeZd9+09dL2uL7HQf9WoUf/kXH7VxireaK8RM7y2EXLu3F7meZtXnSoLq31yxifsNynYnTga", + "XxhWa/tV/v6NhQUmX/7+0cftlUTMSuJmY4Kk7aK0aznYLRxUK3NrmHxRxSC9Siyy3XS7JZVLJ7iBFAFm", + "OGFKczlcimHX6XaorpruB+OMH6D+Q8f8wUTUMQjrAH47Gp8dwNWF8C2rU/lxB360qP6w03G2pwMJv52d", + "3dpQO+WhEDW7y4fa7dWG2vUN9ag81G5pKEzS7Txuc8f4HBxKmhy/5LRNR/yuJ91tBGqxyySPoWGNLzQm", + "mx0op3bQz6K6+PwwFZFS0NfiEuFtvRUWPxsitVv0VuSgLiEXtx/HttuAZ3H81dvKqymC6OnSdefxxI1H", + "13hUDauZIlIbjK4VwcE26zWJswe2tNY9C9/5qKBN+M5Pal96Pqt31Q4vl5qhtk5u3XdTt3xTNHsOPdt3", + "S35Ez0yVBkd+F6O3OdhnStz0YcsD9msfyPcvo/PehY8KOvYgsDWXLDsGtz+afy2JgBsHkZf+GpPZ8CM/", + "V612MuZAtnTeeYnui09azF2qNyKZhQmMqxHAC5be+u737oVA+xukPN6QqLwl/M+nUbOQa6Yr/Ow2SOv+", + "H/Wob7c56u8HZ9ha418sbxgavpVjuk09lJqtamrNtbBUb1IP5R7Zqb7yXJUs0DuwTW/LTX+TMmq5y/2u", + "zk2kqJVt3lJ5xL+JxavXfJvmLuC0nbGL5RVu//yDeVY//bCgwy2auau3KDYFKd4vOwUR9C/P3L3vFyDr", + "9NPAaWrVw3j7I/x3WbE+HwitDSXLjyvW7bvaUMW+22aHv5uxtIj+2pNfYrtyr6AKYpvoFpogdsn+GrC4", + "hwGLO1ATc5vurtTEcpP3tlqiaXr+d1ISE8OXt6Uk6vHb6YjQu//2VUQ9zeoaol7GfYiDAJKWHYIA7NcI", + "yJrKXIJ0uIGzdPuj/s8agQ/YwBWiHoZ1VjtbEbZ14h0A3t802LESdbSPdCze8Yr2vuHt7t2ppNK//+0U", + "9BWpZoVQxmLCqccxbk479/l4/tyxi1bHM3DA3yVqcRuH6wybMW6rK34Nzfr8OimN4wENL4uiJdAvejAn", + "p1f8Gltnp3w4J3Qgs5TIotlj3jmoxk8nLGT8ilWaQ96md3NxH8oG3qitpbteZVdnYmKQnte0pWHIpmg/", + "fr0Lt4gRDM0g1dWpbC23IbaWK+cpr3MDpdKFz1PlZ5oNYh7Gc8KupxJ8DGAGme9Uw+0V7JzXcIel2irZ", + "3x05r7aYlyyFnpy5oyZfc420O96LAZWrT6v7Zb6oqzJ3fOnl64WPrxc+7szbZdqjgqyptfD8/b0meX8H", + "09/fa/KuXhgxbTyr90bwa69YtmXzG4Rwi7vL4KfztkhtdLY7q1HLZG7RKKY0dNEvpksOcDP0EbD7pLt6", + "/5jdJ077GP3HHXaPaVVlw8HXKrdXynvxVflpcfcbUFfCm4+NlrqWvezQbXAsu7u7qmLuXueOYzljUV4p", + "HkAuaRLvm+5itr7mWCJEvz5f6iS8khd6c4fzEjDvX2mM23IXVxpWtz4PtrmAAm4UvcBr1ry5EEf5MKrh", + "iHCKJZjup96GPHaYysmxFrMAuRVDEhrHvhZD7vOqKM0vL3/UKHTehdUsHq3MIfCBO+DSAgn1GXOmXmUR", + "5sqnxboLQYOQ+FQSE+WehBroNRZdu5i5oaUsbVBYFwpHQyIk4ZFDilqhMFVfWdTBwrM4oa3LX7BJtKbb", + "4m1pIaXxvp7Xy89rhyyWntqOsFt6O7qt3ptfEG7QdJ0b07evJhY1Jda94HwPT7JcK/NccM6xe4PCbBfi", + "fKpYkipHgph6UErLAnR+K+d4Ohq69afz+qZQWB8qnlrpYMPatU/gVVV6d0qTlNM4nucdNPIK+3nxWtPA", + "wxs70Uuw+7++d3fTZHbmLBpCHhrMzeuCmwbbvGrg/SqL23EpEmGujXjZ1COD8xrpR9Gb5BWbb6yWWt4u", + "wmqYl2zenKNQMM9q8cYy8C1TFCyBlRMTvoTKs/e/rttC+uysrQ68YGlrinvB0tsjt82Z0bnEbJaQX3hy", + "hCYbZ2PXl2mlvg/L85ZrrXMc3eS5TEg4ZuFlpYkPRDmV6hTNKMCgcyJkQqWMRktU1cNyR/+bUefdVyRp", + "10G11F+/fZtmt8nI/c6bLREQti+pkfB6/fIK4TemiqTjhDGiEaiqGNoratGb9oumdL3pqKd1Q/0hMZ6E", + "OYn0+xMumAL6hYflQfGrTEQsiedu/zuABXpHadzRlGtRnWvQLpfAqLlVb+ss20yBB6w76nZsKaxKXX7B", + "WKScxkVUKRnyQlk3Xz2ETgBb5BmuurwXERtywQwSipEM6+rZyUWwTwVw8enpG6KJR4+AFH8RdPXQp/h5", + "aeSYpVCuHxsKxhL6J5TrR8/G3HYggFYJ5VnPFfYO46rc5esd+ZUolupP1EVAHugJqhWqHwJYx3Wkmb5e", + "E2zLwEhIFVMdbJ1gBiXZFMtLbmH7QgQLGhRbJUG/pTc2Ztfaqop4StRcaVbvEnKIbr09oA3rsYLeinrC", + "nV6v1yO2kqkiUZbYFmnYm0CTvYw0oVVIBbaxT0b8ion8aNfDQlNDWJYUeu+gjdADIcWW0ecelnsEsbxt", + "4is2L9oqJPNadzX3MxT7xYfYfcH2soC+FsJO7nzXKdEzmJdDymNLoY97TwlLEpnA6rBfnSh3hlB5EXLT", + "o1LIlExkxIdzaOEGy8eVEr7gUqUrZTei5mw+Y8kBsU13yJIc+TwJ9qWjqw6l8/jrRcu7vmhZ4qN1uly0", + "Ux9LbcP0G89zGfFp1YZqXKnMtkUT9ZPWJXgt+aA3oz7AItsqd6BlgjJ+c0fKNSiZ1X5Od6FiNmJrzQto", + "97Bx111rzdijZwXV2fTk+v9D/ywPM9xYaUZat8ofJQM2pldcJjlqQA0CdcSpxFrWpIAj58CGoHuAPqS3", + "FNqVhVmS5L3MEhnH8ooleGNOawRUXIJGNRvzcJz3ns7hmfGIkYSKESjcRgGb0jRliVBWdXAbWHW0+jeR", + "KkWFDkaMpPhnmrdxRbWKh+DTNkqOVnFsd7ZfDQygTYAYKuEf9RoPGpRVy7AzGKxAK+WophWtspweYEZy", + "mQB0lKds1sQdjgn9Ik1fMTJg6YwxAUIOf9JwYRxSRPjz9ZQbBRsy+skpWBdgnwxkOoaO4VRENJXJHCZ3", + "8ACuI1y3acI1TbhMeDrP1WNYQ9k24gkBkYILZV3yo5yxhAjIlTIjjflIC2g7XAe3sUcGzOq/8IZK81dc", + "YHIyoy5xWZDyfpqp1AaKnGh8QBYsFRmNScL0juo3nX58pd5olESUx3N3cK4I+zODEJ4zAKwemgzj7rAr", + "lsxJROfkAR8JCcp8Tu/WTEET7KT6syX4GXaGm05NIhlqzBPKoYOc0wXNNqDTijdJmGJplzzDxx/6w5Ql", + "H070j+R7cnz0+sExvbZT9oEOO+S4/+6B/eAZG8qE4RcdcsxF+eWHD8vot52ex5otIyKhTR4eCCUdPDKZ", + "0ByWwoSC/uV6hXYhXOXkw5W2GxM2okkUayaXQ2wqj/379LHFFzVqX68520bP7Ns3BWCRjj3QZA4gbXx+", + "Q8CcxnUgsc3d147xq96xXbtN3QYUeC0QEh5h3pXvbt0bfAF7XZY1dBO19vgSbKfKPJYMydzK9JIc2kiU", + "auzPnh+a+QhmhVvWVzSlPAHnRuMQvGjFaTu8axVI2xEzrrBVvcgD6dAtzSCj8GOAYK/d/MgUG2YxNiqZ", + "jhIaaa0nkjNh/23nMeLUiEpnV0FgGu71IND4Fo1fBE7Sv1giYY6UT7xlA8xGbdQPcs8l51cnypffG/Se", + "Cm/LbffDDZPnUnq9MGX5pcbQg3fA8NInmj9ggUGYzza4H8okNLZKvxwiNPEXNaYJauFjqvrGhrOxFfTW", + "c2jMnLG8vzU27Z1kccqnMTMyskjthJaUtv81Qfe8FN6YSt7+OJTiigkOKn3CqJJCdWwsciaTS2NPgmVU", + "wiCsc7EL6QVzPUg/A5I/twOpms/J0f7UpwLIN72HiPU9sA+34AkX5OT5Pnn06NFTveMTmub3WBTe950h", + "AnzuJD1C4L862ND4CKcI9oIcgmDzXqSWIhe3rcXpAEj7giTvrdRugBgcmNAGYcBGRoBoE/bziMPldWQ8", + "CmzCruSlljp5BFCvzgmzGunQJX1VOKSaQ3PNUbgOWr0XwYSPtHwRo4vAPlZF1/ZU2kAXaKNxJbRJFZmx", + "WOuk/RxoG+LOcvFt3E9jrlKZ8JDGxszWKmoDIqZymsXU6uNGWe6nIFRUSifTLvlFw2/sdeM0BMEyAC8D", + "6LOdetd3RVSqVelQCsUj8K2hq63jBvtg7U6vfVdrtkFj9CRyRfgEMq/RROgQOpFiZA6SdMzFSHUIzSKu", + "l9mcffcZdeSWKXuuUvi3qSe0rhe8sZSQS4xYCeZW8pnKesJ9oKjVTlIaxy2KjpbTmKpXcmKqUvA1+vRP", + "RvRjlBjoKZ9RZTycQWdNfUKb9m9EPLelDZbewnm/xPj6G5Re+lzH8zYeR83lN05A61fusWX9yfqIy/Jb", + "+c3hFHs6mangIFRTyFoJE6lUYXcA4RW1+i/EsywRkZyJl+bTxEADGU0pFzJT+bBySBQbwSrhWDdGkP0N", + "bBjFQD5gTR2cx8SGjHvdhLPT3K07MBAU8RizTDdrCGDF4hYsqsI6M7/7cGWSjphIeDi2HqXcXy+QvbgU", + "gGv9QVwKpCLsM71UDOdoaFPwRqGlp5/C9MulowH77oVkzXaC5q657YThyFQCGc1d6lrBjroQqZV1KNwg", + "UVuRNMkEhk6NkqnxmsUUNxp9mE6aIRZ3abTHoOns5u2xTl0HiTaLHmNmwt3HVP+hFy7IaPMoSuVdIOiU", + "/8UsbLB+ZAK9AuhbbMKeyKeDeZfs53E2NZZJChKAClN1yFlw88KctsELi/uYvA7TWLgTvHxzfhJ0goP+", + "r+uVXvbW1blNC96KORPOtGKjwY4f58IQwdEI/GrILzPk7QngHG3UJiuX0n27d6gpJFaJ9Oe2YAR8QpNL", + "tFaVleIYRSkl2XpUBUyjEDzlYO/i4WsD+l3ST6uDmqFwXK4gDhMRmeFI5nwEv6WMYxZBDKnkuuRJPWGg", + "S45SMgM/bDqfmqwVLRXmInTvYuYNkwc8jrXYM9BAOAk8tAawyoQqG+Q4A6Xh3IJv0ifMlKgIaTT5Ul+q", + "o7q4RU9uWvIsQ0kQkPOYl5FPwDGlQcxBShpnQiT1gpR0FRQqwjGmKaX+dECrP/EhpMf7cweARBwyhcV/", + "JqtsvfhX2bTKE4H6DcYVnD1aBwMnCaaXaLyn9JLZPKIOiSoHMSjN+GKR/2HS7IcZOr7O3BAsGsTcc1ZP", + "uMhSRqIMVISxnLl6PJ5uoI+CQ0gmdts2cix3gqleRHLF3lyxRO+0xwGYZzPNxizXvCW+D7X8zBCYVyoT", + "SO/q2ICwzWByNvifyuIzz66DOwlHQwJnceME9q0hjVX9NTs14ses1NxnCSBGmVIu+sAmrddZkomGxbg2", + "v/VYuGRDL7ibDr0RE22uLGzJiJ41Nn6xeF4PEvzVNdaohXFYcgWDK+KrytBcqlPdwH1QqT2X15nzV5/b", + "l/KSs1LhOc063nptsQyhaGWWxMFeME7T6d729s7ut91et9fd2fvuu+++85h+oZ6m9JXa296WUybQrsDn", + "emazQI9pBGeepuDYSkIuRkwZ/3pEIjbIRqMi0zZ3N/z+I6OJIBOZsPcP6nNzuR3JUG2PUFfYAl2ERdsw", + "CuTrXHE2ewi8YYwC00vQa8HVwYQcAy5GeD8OFPvcoroBfLZHiw/AYzx1WwJo7nmVyq+2BmsiBUv5X2w7", + "omo8kDSJTK2prYhdsVifq1ujjEesBKCp2tISQKcMy5rIsiOUgMgv+bYEo5KmvCqCSlq4n64WcHUdxoOc", + "4K1GqLrkvEiVKhgCvEzIKl13Phih7epLxbP7b4+M9mOU0EyxRGFADXJwITW9UGjxOl/umpNEMRGVhlQX", + "IpXkiiaQV2Z7dpMHpthzh6iYhpcdwtIQ03/XpIRS+W7vLiyqBP7p/af/LwAA//9sPdQZ6Z4BAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/client/node/clients/subject.ts b/api/client/node/clients/subject.ts index 577039e9b..219b95300 100644 --- a/api/client/node/clients/subject.ts +++ b/api/client/node/clients/subject.ts @@ -118,6 +118,39 @@ export class SubjectClient extends BaseClient { }) } + /** + * Override Entitlement + * Enables zero downtime upgrades and downgrades of entitlements. + * @remarks + * Input should be either `EntitlementMeteredCreateInputs`, `EntitlementStaticCreateInputs`, or `EntitlementBooleanCreateInputs` + * @example + * // Issue 10,000,000 tokens every month + * const entitlement = await openmeter.subjects.overrideEntitlement('customer-1', 'ai_tokens', { + * type: 'metered', + * featureKey: 'ai_tokens', + * usagePeriod: { + * interval: 'MONTH', + * }, + * issueAfterReset: 10000000, + * }) + */ + public async overrideEntitlement( + subjectIdOrKey: string, + entitlementIdOrFeatureKey: string, + input: EntitlementCreateInputs, + options?: RequestOptions + ): Promise { + return await this.request({ + path: `/api/v1/subjects/${subjectIdOrKey}/entitlements/${entitlementIdOrFeatureKey}/override`, + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(input), + options, + }) + } + /** * List entitlements of a subject * @example diff --git a/api/client/node/schemas/openapi.ts b/api/client/node/schemas/openapi.ts index 44d0ac734..0d19dc1f4 100644 --- a/api/client/node/schemas/openapi.ts +++ b/api/client/node/schemas/openapi.ts @@ -227,6 +227,15 @@ export interface paths { */ delete: operations['deleteEntitlement'] } + '/api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/override': { + /** + * Override an entitlement + * @description Overriding an entitlement creates a new entitlement from the provided inputs and soft deletes the previous entitlement for the provided subject-feature pair. If the previous entitlement is already deleted or otherwise doesnt exist, the override will fail. + * + * This endpoint is useful for upgrades, downgrades, or other changes to entitlements that require a new entitlement to be created with zero downtime. + */ + put: operations['overrideEntitlement'] + } '/api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/grants': { /** * List grants for an entitlement @@ -350,6 +359,11 @@ export interface paths { * @description List all notification events. */ get: operations['listNotificationEvents'] + /** + * Create a notification event + * @description Create a new notification event. + */ + post: operations['createNotificationEvent'] } '/api/v1/notification/events/{eventId}': { /** @@ -358,6 +372,13 @@ export interface paths { */ get: operations['getNotificationEvent'] } + '/api/v1/notification/webhook/svix': { + /** + * Receive Svix operational events + * @description Callback endpoint used by Svix to notify about operational events + */ + post: operations['receiveSvixOperationalEvent'] + } } export type webhooks = Record @@ -1689,6 +1710,13 @@ export interface components { */ disabled: boolean } + /** @description Request for creating new notification event with specific type and payload. */ + NotificationEventCreateRequest: { + type: components['schemas']['NotificationEventType'] + payload: components['schemas']['NotificationEventPayload'] + /** @example 01J2KNP1YTXQRXHTDJ4KPR7PZ0 */ + ruleId: string + } /** * @description Notification event generated by the system based on the criteria defined in the corresponding * a notification rule. @@ -1701,6 +1729,7 @@ export interface components { * @example 01J2KNP1YTXQRXHTDJ4KPR7PZ0 */ id: string + type: components['schemas']['NotificationEventType'] /** * Format: date-time * @description Timestamp when the notification event was created. @@ -1738,14 +1767,16 @@ export interface components { entitlement: components['schemas']['EntitlementMetered'] feature: components['schemas']['Feature'] subject: components['schemas']['Subject'] - balance: components['schemas']['EntitlementValue'] + value: components['schemas']['EntitlementValue'] threshold: components['schemas']['NotificationRuleBalanceThresholdValue'] } } NotificationEventDeliveryStatus: { channel: components['schemas']['NotificationChannelMeta'] /** @enum {string} */ - state: 'SUCCESS' | 'FAILED' | 'SENDING' + state: 'SUCCESS' | 'FAILED' | 'SENDING' | 'PENDING' + /** @example Failed to dispatch event */ + reason?: string /** * Format: date-time * @example 2023-01-01T00:00:00Z @@ -1772,6 +1803,20 @@ export interface components { pageSize: number items: components['schemas']['NotificationEvents'] } + SvixOperationalWebhookRequest: { + /** @enum {string} */ + type: + | 'endpoint.created' + | 'endpoint.deleted' + | 'endpoint.disabled' + | 'endpoint.updated' + | 'message.attempt.exhausted' + | 'message.attempt.failing' + | 'message.attempt.recovered' + data: { + [key: string]: unknown + } + } } responses: { /** @description Conflict */ @@ -2651,6 +2696,40 @@ export interface operations { default: components['responses']['UnexpectedProblemResponse'] } } + /** + * Override an entitlement + * @description Overriding an entitlement creates a new entitlement from the provided inputs and soft deletes the previous entitlement for the provided subject-feature pair. If the previous entitlement is already deleted or otherwise doesnt exist, the override will fail. + * + * This endpoint is useful for upgrades, downgrades, or other changes to entitlements that require a new entitlement to be created with zero downtime. + */ + overrideEntitlement: { + parameters: { + path: { + subjectIdOrKey: components['parameters']['subjectIdOrKey'] + entitlementIdOrFeatureKey: components['parameters']['entitlementIdOrFeatureKey'] + } + } + /** @description The entitlement to create. */ + requestBody: { + content: { + 'application/json': components['schemas']['EntitlementCreateInputs'] + } + } + responses: { + /** @description Entitlement created. */ + 201: { + content: { + 'application/json': components['schemas']['Entitlement'] + } + } + 400: components['responses']['BadRequestProblemResponse'] + 401: components['responses']['UnauthorizedProblemResponse'] + 404: components['responses']['NotFoundProblemResponse'] + 409: components['responses']['ConflictProblemResponse'] + 501: components['responses']['NotImplementedProblemResponse'] + default: components['responses']['UnexpectedProblemResponse'] + } + } /** * List grants for an entitlement * @description List all grants issued for an entitlement. The entitlement can be defined either by its id or featureKey. @@ -3134,6 +3213,31 @@ export interface operations { default: components['responses']['UnexpectedProblemResponse'] } } + /** + * Create a notification event + * @description Create a new notification event. + */ + createNotificationEvent: { + /** @description The notification event to create. */ + requestBody: { + content: { + 'application/json': components['schemas']['NotificationEventCreateRequest'] + } + } + responses: { + /** @description Notification event created. */ + 201: { + content: { + 'application/json': components['schemas']['NotificationEvent'] + } + } + 400: components['responses']['BadRequestProblemResponse'] + 401: components['responses']['UnauthorizedProblemResponse'] + 409: components['responses']['ConflictProblemResponse'] + 501: components['responses']['NotImplementedProblemResponse'] + default: components['responses']['UnexpectedProblemResponse'] + } + } /** * Get notification event * @description Get a notification event by id. @@ -3156,4 +3260,26 @@ export interface operations { default: components['responses']['UnexpectedProblemResponse'] } } + /** + * Receive Svix operational events + * @description Callback endpoint used by Svix to notify about operational events + */ + receiveSvixOperationalEvent: { + /** @description The operational event. */ + requestBody: { + content: { + 'application/json': components['schemas']['SvixOperationalWebhookRequest'] + } + } + responses: { + /** @description Operational webhook request accepted */ + 204: { + content: never + } + 400: components['responses']['BadRequestProblemResponse'] + 401: components['responses']['UnauthorizedProblemResponse'] + 501: components['responses']['NotImplementedProblemResponse'] + default: components['responses']['UnexpectedProblemResponse'] + } + } } diff --git a/api/client/python/src/openmeter/_operations/_operations.py b/api/client/python/src/openmeter/_operations/_operations.py index c99e558ba..7ca7373a5 100644 --- a/api/client/python/src/openmeter/_operations/_operations.py +++ b/api/client/python/src/openmeter/_operations/_operations.py @@ -703,6 +703,33 @@ def build_delete_entitlement_request(subject_id_or_key: str, entitlement_id: str return HttpRequest(method="DELETE", url=_url, headers=_headers, **kwargs) +def build_override_entitlement_request( + subject_id_or_key: str, entitlement_id_or_feature_key: str, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + accept = _headers.pop("Accept", "application/json, application/problem+json") + + # Construct URL + _url = "/api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/override" + path_format_arguments = { + "subjectIdOrKey": _SERIALIZER.url("subject_id_or_key", subject_id_or_key, "str"), + "entitlementIdOrFeatureKey": _SERIALIZER.url( + "entitlement_id_or_feature_key", entitlement_id_or_feature_key, "str" + ), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct headers + if content_type is not None: + _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="PUT", url=_url, headers=_headers, **kwargs) + + def build_list_entitlement_grants_request( subject_id_or_key: str, entitlement_id_or_feature_key: str, @@ -1159,6 +1186,23 @@ def build_list_notification_events_request( return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) +def build_create_notification_event_request(**kwargs: Any) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + accept = _headers.pop("Accept", "application/json, application/problem+json") + + # Construct URL + _url = "/api/v1/notification/events" + + # Construct headers + if content_type is not None: + _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="POST", url=_url, headers=_headers, **kwargs) + + def build_get_notification_event_request(event_id: str, **kwargs: Any) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) @@ -1178,6 +1222,23 @@ def build_get_notification_event_request(event_id: str, **kwargs: Any) -> HttpRe return HttpRequest(method="GET", url=_url, headers=_headers, **kwargs) +def build_receive_svix_operational_event_request(**kwargs: Any) -> HttpRequest: # pylint: disable=name-too-long + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + accept = _headers.pop("Accept", "application/problem+json") + + # Construct URL + _url = "/api/v1/notification/webhook/svix" + + # Construct headers + if content_type is not None: + _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="POST", url=_url, headers=_headers, **kwargs) + + class ClientOperationsMixin(ClientMixinABC): # pylint: disable=too-many-public-methods @distributed_trace def list_events( @@ -4294,191 +4355,91 @@ def delete_entitlement( # pylint: disable=inconsistent-return-statements if cls: return cls(pipeline_response, None, {}) # type: ignore - @distributed_trace - def list_entitlement_grants( + @overload + def override_entitlement( self, subject_id_or_key: str, entitlement_id_or_feature_key: str, + body: JSON, *, - include_deleted: bool = False, - order_by: str = "updatedAt", + content_type: str = "application/json", **kwargs: Any - ) -> List[JSON]: - # pylint: disable=line-too-long - """List grants for an entitlement. + ) -> JSON: + """Override an entitlement. - List all grants issued for an entitlement. The entitlement can be defined either by its id or - featureKey. + Overriding an entitlement creates a new entitlement from the provided inputs and soft deletes + the previous entitlement for the provided subject-feature pair. If the previous entitlement is + already deleted or otherwise doesnt exist, the override will fail. + + This endpoint is useful for upgrades, downgrades, or other changes to entitlements that require + a new entitlement to be created with zero downtime. :param subject_id_or_key: A unique identifier for a subject. Required. :type subject_id_or_key: str :param entitlement_id_or_feature_key: The id of the entitlement or the key of the feature. Required. :type entitlement_id_or_feature_key: str - :keyword include_deleted: Include deleted entries. Default value is False. - :paramtype include_deleted: bool - :keyword order_by: Order by field. Known values are: "id", "createdAt", and "updatedAt". - Default value is "updatedAt". - :paramtype order_by: str - :return: list of JSON object - :rtype: list[JSON] + :param body: The entitlement to create. Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: Example: .. code-block:: python - # response body for status code(s): 200 - response == [ - { - "amount": 0.0, # The amount to grant. Should be a positive number. + # JSON input template you can fill out and use as your body input. + body = {} + + # response body for status code(s): 409 + response == { + "detail": "str", # A human-readable explanation specific to this occurrence + of the problem. Required. + "status": 0, # The HTTP status code generated by the origin server for this + occurrence of the problem. Required. + "title": "str", # A a short, human-readable summary of the problem type. + Required. + "type": "str", # Type contains a URI that identifies the problem type. + Required. + "extensions": { + "conflictingEntityId": "str" # The id of the conflicting entity. Required. - "createdAt": "2020-02-20 00:00:00", # The date and time the resource - was created. Required. - "effectiveAt": "2020-02-20 00:00:00", # Effective date for grants - and anchor for recurring grants. Provided value will be ceiled to metering - windowSize (minute). Required. - "entitlementId": "str", # The unique entitlement ULID that the grant - is associated with. Required. - "expiration": { - "count": 0, # The expiration period count like 12 months. - Required. - "duration": "str" # The expiration period duration like - month. Required. Known values are: "HOUR", "DAY", "WEEK", "MONTH", and - "YEAR". - }, - "id": "str", # Readonly unique ULID identifier. Required. - "updatedAt": "2020-02-20 00:00:00", # The date and time the resource - was last updated. The initial value is the same as createdAt. Required. - "deletedAt": "2020-02-20 00:00:00", # Optional. The date and time - the resource was deleted. - "expiresAt": "2020-02-20 00:00:00", # Optional. The expiration date - of the grant. - "maxRolloverAmount": 0, # Optional. Default value is 0. Grants are - rolled over at reset, after which they can have a different balance compared - to what they had before the reset. Balance after the reset is calculated as: - Balance_After_Reset = MIN(MaxRolloverAmount, MAX(Balance_Before_Reset, - MinRolloverAmount)). - "metadata": { - "str": "str" # Optional. Dictionary of :code:``. - }, - "minRolloverAmount": 0, # Optional. Default value is 0. Grants are - rolled over at reset, after which they can have a different balance compared - to what they had before the reset. Balance after the reset is calculated as: - Balance_After_Reset = MIN(MaxRolloverAmount, MAX(Balance_Before_Reset, - MinRolloverAmount)). - "nextRecurrence": "2020-02-20 00:00:00", # Optional. The next time - the grant will recurr. - "priority": 1, # Optional. Default value is 1. The priority of the - grant. Grants with higher priority are applied first. Priority is a positive - decimal numbers. With lower numbers indicating higher importance. For - example, a priority of 1 is more urgent than a priority of 2. When there are - several grants available for the same subject, the system selects the grant - with the highest priority. In cases where grants share the same priority - level, the grant closest to its expiration will be used first. In the case of - two grants have identical priorities and expiration dates, the system will - use the grant that was created first. - "recurrence": { - "anchor": "2020-02-20 00:00:00", # An arbitrary anchor to - base the recurring period on. Required. - "interval": "str" # List of pre-defined periods that can be - used for recurring & scheduling. DAY: Every day WEEK: Every - week MONTH: Every month YEAR: Every year. Required. Known values - are: "DAY", "WEEK", "MONTH", and "YEAR". - }, - "voidedAt": "2020-02-20 00:00:00" # Optional. The date and time the - grant was voided (cannot be used after that). - } - ] + }, + "instance": "str" # Optional. A URI reference that identifies the specific + occurrence of the problem. + } """ - error_map = { - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - 401: lambda response: ClientAuthenticationError(response=response), - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[List[JSON]] = kwargs.pop("cls", None) - - _request = build_list_entitlement_grants_request( - subject_id_or_key=subject_id_or_key, - entitlement_id_or_feature_key=entitlement_id_or_feature_key, - include_deleted=include_deleted, - order_by=order_by, - headers=_headers, - params=_params, - ) - _request.url = self._client.format_url(_request.url) - - _stream = False - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - response.read() # Load the body in memory and close the socket - map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore - raise HttpResponseError(response=response) - - if response.content: - deserialized = response.json() - else: - deserialized = None - - if cls: - return cls(pipeline_response, cast(List[JSON], deserialized), {}) # type: ignore - - return cast(List[JSON], deserialized) # type: ignore @overload - def create_grant( + def override_entitlement( self, subject_id_or_key: str, entitlement_id_or_feature_key: str, - body: JSON, + body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any ) -> JSON: - # pylint: disable=line-too-long - """Create a grant. - - Grants define a behavior of granting usage for a metered entitlement. They can have complicated - recurrence and rollover rules, thanks to which you can define a wide range of access patterns - with a single grant, in most cases you don't have to periodically create new grants. You can - only issue grants for active metered entitlements. - - A grant defines a given amount of usage that can be consumed for the entitlement. The grant is - in effect between its effective date and its expiration date. Specifying both is mandatory for - new grants. - - Grants have a priority setting that determines their order of use. Lower numbers have higher - priority, with 0 being the highest priority. - - Grants can have a recurrence setting intended to automate the manual reissuing of grants. For - example, a daily recurrence is equal to reissuing that same grant every day (ignoring rollover - settings). + """Override an entitlement. - Rollover settings define what happens to the remaining balance of a grant at a reset. - Balance_After_Reset = MIN(MaxRolloverAmount, MAX(Balance_Before_Reset, MinRolloverAmount)) + Overriding an entitlement creates a new entitlement from the provided inputs and soft deletes + the previous entitlement for the provided subject-feature pair. If the previous entitlement is + already deleted or otherwise doesnt exist, the override will fail. - Grants cannot be changed once created, only deleted. This is to ensure that balance is - deterministic regardless of when it is queried. + This endpoint is useful for upgrades, downgrades, or other changes to entitlements that require + a new entitlement to be created with zero downtime. :param subject_id_or_key: A unique identifier for a subject. Required. :type subject_id_or_key: str :param entitlement_id_or_feature_key: The id of the entitlement or the key of the feature. Required. :type entitlement_id_or_feature_key: str - :param body: The grant to create. Required. - :type body: JSON - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + :param body: The entitlement to create. Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str :return: JSON object @@ -4488,22 +4449,348 @@ def create_grant( Example: .. code-block:: python - # JSON input template you can fill out and use as your body input. - body = { - "amount": 0.0, # The amount to grant. Should be a positive number. Required. - "effectiveAt": "2020-02-20 00:00:00", # Effective date for grants and anchor - for recurring grants. Provided value will be ceiled to metering windowSize - (minute). Required. - "expiration": { - "count": 0, # The expiration period count like 12 months. Required. - "duration": "str" # The expiration period duration like month. - Required. Known values are: "HOUR", "DAY", "WEEK", "MONTH", and "YEAR". - }, - "maxRolloverAmount": 0, # Optional. Default value is 0. Grants are rolled - over at reset, after which they can have a different balance compared to what - they had before the reset. Balance after the reset is calculated as: - Balance_After_Reset = MIN(MaxRolloverAmount, MAX(Balance_Before_Reset, - MinRolloverAmount)). + # response body for status code(s): 409 + response == { + "detail": "str", # A human-readable explanation specific to this occurrence + of the problem. Required. + "status": 0, # The HTTP status code generated by the origin server for this + occurrence of the problem. Required. + "title": "str", # A a short, human-readable summary of the problem type. + Required. + "type": "str", # Type contains a URI that identifies the problem type. + Required. + "extensions": { + "conflictingEntityId": "str" # The id of the conflicting entity. + Required. + }, + "instance": "str" # Optional. A URI reference that identifies the specific + occurrence of the problem. + } + """ + + @distributed_trace + def override_entitlement( + self, subject_id_or_key: str, entitlement_id_or_feature_key: str, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> JSON: + """Override an entitlement. + + Overriding an entitlement creates a new entitlement from the provided inputs and soft deletes + the previous entitlement for the provided subject-feature pair. If the previous entitlement is + already deleted or otherwise doesnt exist, the override will fail. + + This endpoint is useful for upgrades, downgrades, or other changes to entitlements that require + a new entitlement to be created with zero downtime. + + :param subject_id_or_key: A unique identifier for a subject. Required. + :type subject_id_or_key: str + :param entitlement_id_or_feature_key: The id of the entitlement or the key of the feature. + Required. + :type entitlement_id_or_feature_key: str + :param body: The entitlement to create. Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = {} + + # response body for status code(s): 409 + response == { + "detail": "str", # A human-readable explanation specific to this occurrence + of the problem. Required. + "status": 0, # The HTTP status code generated by the origin server for this + occurrence of the problem. Required. + "title": "str", # A a short, human-readable summary of the problem type. + Required. + "type": "str", # Type contains a URI that identifies the problem type. + Required. + "extensions": { + "conflictingEntityId": "str" # The id of the conflicting entity. + Required. + }, + "instance": "str" # Optional. A URI reference that identifies the specific + occurrence of the problem. + } + """ + error_map = { + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 400: HttpResponseError, + 401: lambda response: ClientAuthenticationError(response=response), + 404: lambda response: ResourceNotFoundError(response=response), + 501: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_override_entitlement_request( + subject_id_or_key=subject_id_or_key, + entitlement_id_or_feature_key=entitlement_id_or_feature_key, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [201, 409]: + if _stream: + response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + if response.status_code == 201: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace + def list_entitlement_grants( + self, + subject_id_or_key: str, + entitlement_id_or_feature_key: str, + *, + include_deleted: bool = False, + order_by: str = "updatedAt", + **kwargs: Any + ) -> List[JSON]: + # pylint: disable=line-too-long + """List grants for an entitlement. + + List all grants issued for an entitlement. The entitlement can be defined either by its id or + featureKey. + + :param subject_id_or_key: A unique identifier for a subject. Required. + :type subject_id_or_key: str + :param entitlement_id_or_feature_key: The id of the entitlement or the key of the feature. + Required. + :type entitlement_id_or_feature_key: str + :keyword include_deleted: Include deleted entries. Default value is False. + :paramtype include_deleted: bool + :keyword order_by: Order by field. Known values are: "id", "createdAt", and "updatedAt". + Default value is "updatedAt". + :paramtype order_by: str + :return: list of JSON object + :rtype: list[JSON] + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == [ + { + "amount": 0.0, # The amount to grant. Should be a positive number. + Required. + "createdAt": "2020-02-20 00:00:00", # The date and time the resource + was created. Required. + "effectiveAt": "2020-02-20 00:00:00", # Effective date for grants + and anchor for recurring grants. Provided value will be ceiled to metering + windowSize (minute). Required. + "entitlementId": "str", # The unique entitlement ULID that the grant + is associated with. Required. + "expiration": { + "count": 0, # The expiration period count like 12 months. + Required. + "duration": "str" # The expiration period duration like + month. Required. Known values are: "HOUR", "DAY", "WEEK", "MONTH", and + "YEAR". + }, + "id": "str", # Readonly unique ULID identifier. Required. + "updatedAt": "2020-02-20 00:00:00", # The date and time the resource + was last updated. The initial value is the same as createdAt. Required. + "deletedAt": "2020-02-20 00:00:00", # Optional. The date and time + the resource was deleted. + "expiresAt": "2020-02-20 00:00:00", # Optional. The expiration date + of the grant. + "maxRolloverAmount": 0, # Optional. Default value is 0. Grants are + rolled over at reset, after which they can have a different balance compared + to what they had before the reset. Balance after the reset is calculated as: + Balance_After_Reset = MIN(MaxRolloverAmount, MAX(Balance_Before_Reset, + MinRolloverAmount)). + "metadata": { + "str": "str" # Optional. Dictionary of :code:``. + }, + "minRolloverAmount": 0, # Optional. Default value is 0. Grants are + rolled over at reset, after which they can have a different balance compared + to what they had before the reset. Balance after the reset is calculated as: + Balance_After_Reset = MIN(MaxRolloverAmount, MAX(Balance_Before_Reset, + MinRolloverAmount)). + "nextRecurrence": "2020-02-20 00:00:00", # Optional. The next time + the grant will recurr. + "priority": 1, # Optional. Default value is 1. The priority of the + grant. Grants with higher priority are applied first. Priority is a positive + decimal numbers. With lower numbers indicating higher importance. For + example, a priority of 1 is more urgent than a priority of 2. When there are + several grants available for the same subject, the system selects the grant + with the highest priority. In cases where grants share the same priority + level, the grant closest to its expiration will be used first. In the case of + two grants have identical priorities and expiration dates, the system will + use the grant that was created first. + "recurrence": { + "anchor": "2020-02-20 00:00:00", # An arbitrary anchor to + base the recurring period on. Required. + "interval": "str" # List of pre-defined periods that can be + used for recurring & scheduling. DAY: Every day WEEK: Every + week MONTH: Every month YEAR: Every year. Required. Known values + are: "DAY", "WEEK", "MONTH", and "YEAR". + }, + "voidedAt": "2020-02-20 00:00:00" # Optional. The date and time the + grant was voided (cannot be used after that). + } + ] + """ + error_map = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: lambda response: ClientAuthenticationError(response=response), + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[List[JSON]] = kwargs.pop("cls", None) + + _request = build_list_entitlement_grants_request( + subject_id_or_key=subject_id_or_key, + entitlement_id_or_feature_key=entitlement_id_or_feature_key, + include_deleted=include_deleted, + order_by=order_by, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(List[JSON], deserialized), {}) # type: ignore + + return cast(List[JSON], deserialized) # type: ignore + + @overload + def create_grant( + self, + subject_id_or_key: str, + entitlement_id_or_feature_key: str, + body: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + # pylint: disable=line-too-long + """Create a grant. + + Grants define a behavior of granting usage for a metered entitlement. They can have complicated + recurrence and rollover rules, thanks to which you can define a wide range of access patterns + with a single grant, in most cases you don't have to periodically create new grants. You can + only issue grants for active metered entitlements. + + A grant defines a given amount of usage that can be consumed for the entitlement. The grant is + in effect between its effective date and its expiration date. Specifying both is mandatory for + new grants. + + Grants have a priority setting that determines their order of use. Lower numbers have higher + priority, with 0 being the highest priority. + + Grants can have a recurrence setting intended to automate the manual reissuing of grants. For + example, a daily recurrence is equal to reissuing that same grant every day (ignoring rollover + settings). + + Rollover settings define what happens to the remaining balance of a grant at a reset. + Balance_After_Reset = MIN(MaxRolloverAmount, MAX(Balance_Before_Reset, MinRolloverAmount)) + + Grants cannot be changed once created, only deleted. This is to ensure that balance is + deterministic regardless of when it is queried. + + :param subject_id_or_key: A unique identifier for a subject. Required. + :type subject_id_or_key: str + :param entitlement_id_or_feature_key: The id of the entitlement or the key of the feature. + Required. + :type entitlement_id_or_feature_key: str + :param body: The grant to create. Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "amount": 0.0, # The amount to grant. Should be a positive number. Required. + "effectiveAt": "2020-02-20 00:00:00", # Effective date for grants and anchor + for recurring grants. Provided value will be ceiled to metering windowSize + (minute). Required. + "expiration": { + "count": 0, # The expiration period count like 12 months. Required. + "duration": "str" # The expiration period duration like month. + Required. Known values are: "HOUR", "DAY", "WEEK", "MONTH", and "YEAR". + }, + "maxRolloverAmount": 0, # Optional. Default value is 0. Grants are rolled + over at reset, after which they can have a different balance compared to what + they had before the reset. Balance after the reset is calculated as: + Balance_After_Reset = MIN(MaxRolloverAmount, MAX(Balance_Before_Reset, + MinRolloverAmount)). "metadata": { "str": "str" # Optional. Dictionary of :code:``. }, @@ -6332,14 +6619,308 @@ def update_notification_rule( """ @distributed_trace - def update_notification_rule(self, rule_id: str, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: - """Update a notification rule. + def update_notification_rule(self, rule_id: str, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: + """Update a notification rule. + + Update a notification rule by id. + + :param rule_id: A unique ULID identifier for a notification rule. Required. + :type rule_id: str + :param body: The notification rule to update. Is either a JSON type or a IO[bytes] type. + Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = {} + """ + error_map = { + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 401: lambda response: ClientAuthenticationError(response=response), + 404: lambda response: ResourceNotFoundError(response=response), + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_update_notification_rule_request( + rule_id=rule_id, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @distributed_trace + def list_notification_events( + self, + *, + page: int = 1, + page_size: int = 100, + order_by: str = "createdAt", + order: str = "ASC", + from_parameter: Optional[datetime.datetime] = None, + to: Optional[datetime.datetime] = None, + feature: Optional[List[str]] = None, + subject: Optional[List[str]] = None, + **kwargs: Any + ) -> JSON: + """List notification evens. + + List all notification events. + + :keyword page: Page number to return. Default value is 1. + :paramtype page: int + :keyword page_size: Number of entries to return per page. Default value is 100. + :paramtype page_size: int + :keyword order_by: Order by field. Known values are: "id" and "createdAt". Default value is + "createdAt". + :paramtype order_by: str + :keyword order: Order by field. + + Usage: ``?order=ASC``. Known values are: "ASC" and "DESC". Default value is "ASC". + :paramtype order: str + :keyword from_parameter: Start date-time in RFC 3339 format. + Inclusive. Default value is None. + :paramtype from_parameter: ~datetime.datetime + :keyword to: End date-time in RFC 3339 format. + Inclusive. Default value is None. + :paramtype to: ~datetime.datetime + :keyword feature: Filtering by multiple features. + + Usage: ``?feature=feature-1&feature=feature-2``. Default value is None. + :paramtype feature: list[str] + :keyword subject: Filtering by multiple subjects. + + Usage: ``?subject=customer-1&subject=customer-2``. Default value is None. + :paramtype subject: list[str] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 200 + response == { + "items": [ + { + "createdAt": "2020-02-20 00:00:00", # Timestamp when the + notification event was created. Required. + "deliveryStatus": [ + { + "channel": { + "id": "str", # A unique identifier + for the notification channel. Required. + "type": "str" # The type of the + notification channel. Required. "WEBHOOK" + }, + "state": "str", # Required. Known values + are: "SUCCESS", "FAILED", "SENDING", and "PENDING". + "updatedAt": "2020-02-20 00:00:00", # + Required. + "reason": "str" # Optional. The delivery + status of the notification event. Required. + } + ], + "id": "str", # A unique identifier for the notification + event. Required. + "payload": {}, + "rule": {}, + "type": "str" # The type of the notification event. + Required. "entitlements.balance.threshold" + } + ], + "page": 0, # Current page number. Required. + "pageSize": 0, # Number of rules per page. Required. + "totalCount": 0 # Total number of rules. Required. + } + """ + error_map = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 400: HttpResponseError, + 401: lambda response: ClientAuthenticationError(response=response), + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[JSON] = kwargs.pop("cls", None) + + _request = build_list_notification_events_request( + page=page, + page_size=page_size, + order_by=order_by, + order=order, + from_parameter=from_parameter, + to=to, + feature=feature, + subject=subject, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + + return cast(JSON, deserialized) # type: ignore + + @overload + def create_notification_event(self, body: JSON, *, content_type: str = "application/json", **kwargs: Any) -> JSON: + """Create a notification event. + + Create a new notification event. + + :param body: The notification event to create. Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "payload": {}, + "ruleId": "str", # Required. + "type": "str" # The type of the notification event. Required. + "entitlements.balance.threshold" + } + + # response body for status code(s): 409 + response == { + "detail": "str", # A human-readable explanation specific to this occurrence + of the problem. Required. + "status": 0, # The HTTP status code generated by the origin server for this + occurrence of the problem. Required. + "title": "str", # A a short, human-readable summary of the problem type. + Required. + "type": "str", # Type contains a URI that identifies the problem type. + Required. + "extensions": { + "conflictingEntityId": "str" # The id of the conflicting entity. + Required. + }, + "instance": "str" # Optional. A URI reference that identifies the specific + occurrence of the problem. + } + """ + + @overload + def create_notification_event( + self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> JSON: + """Create a notification event. + + Create a new notification event. + + :param body: The notification event to create. Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 409 + response == { + "detail": "str", # A human-readable explanation specific to this occurrence + of the problem. Required. + "status": 0, # The HTTP status code generated by the origin server for this + occurrence of the problem. Required. + "title": "str", # A a short, human-readable summary of the problem type. + Required. + "type": "str", # Type contains a URI that identifies the problem type. + Required. + "extensions": { + "conflictingEntityId": "str" # The id of the conflicting entity. + Required. + }, + "instance": "str" # Optional. A URI reference that identifies the specific + occurrence of the problem. + } + """ + + @distributed_trace + def create_notification_event(self, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: + """Create a notification event. - Update a notification rule by id. + Create a new notification event. - :param rule_id: A unique ULID identifier for a notification rule. Required. - :type rule_id: str - :param body: The notification rule to update. Is either a JSON type or a IO[bytes] type. + :param body: The notification event to create. Is either a JSON type or a IO[bytes] type. Required. :type body: JSON or IO[bytes] :return: JSON object @@ -6350,13 +6931,38 @@ def update_notification_rule(self, rule_id: str, body: Union[JSON, IO[bytes]], * .. code-block:: python # JSON input template you can fill out and use as your body input. - body = {} + body = { + "payload": {}, + "ruleId": "str", # Required. + "type": "str" # The type of the notification event. Required. + "entitlements.balance.threshold" + } + + # response body for status code(s): 409 + response == { + "detail": "str", # A human-readable explanation specific to this occurrence + of the problem. Required. + "status": 0, # The HTTP status code generated by the origin server for this + occurrence of the problem. Required. + "title": "str", # A a short, human-readable summary of the problem type. + Required. + "type": "str", # Type contains a URI that identifies the problem type. + Required. + "extensions": { + "conflictingEntityId": "str" # The id of the conflicting entity. + Required. + }, + "instance": "str" # Optional. A URI reference that identifies the specific + occurrence of the problem. + } """ error_map = { + 404: ResourceNotFoundError, 409: ResourceExistsError, 304: ResourceNotModifiedError, + 400: HttpResponseError, 401: lambda response: ClientAuthenticationError(response=response), - 404: lambda response: ResourceNotFoundError(response=response), + 501: HttpResponseError, } error_map.update(kwargs.pop("error_map", {}) or {}) @@ -6374,8 +6980,7 @@ def update_notification_rule(self, rule_id: str, body: Union[JSON, IO[bytes]], * else: _json = body - _request = build_update_notification_rule_request( - rule_id=rule_id, + _request = build_create_notification_event_request( content_type=content_type, json=_json, content=_content, @@ -6391,16 +6996,23 @@ def update_notification_rule(self, rule_id: str, body: Union[JSON, IO[bytes]], * response = pipeline_response.http_response - if response.status_code not in [200]: + if response.status_code not in [201, 409]: if _stream: response.read() # Load the body in memory and close the socket map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore raise HttpResponseError(response=response) - if response.content: - deserialized = response.json() - else: - deserialized = None + if response.status_code == 201: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + if response.content: + deserialized = response.json() + else: + deserialized = None if cls: return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore @@ -6408,92 +7020,22 @@ def update_notification_rule(self, rule_id: str, body: Union[JSON, IO[bytes]], * return cast(JSON, deserialized) # type: ignore @distributed_trace - def list_notification_events( - self, - *, - page: int = 1, - page_size: int = 100, - order_by: str = "createdAt", - order: str = "ASC", - from_parameter: Optional[datetime.datetime] = None, - to: Optional[datetime.datetime] = None, - feature: Optional[List[str]] = None, - subject: Optional[List[str]] = None, - **kwargs: Any - ) -> JSON: - """List notification evens. - - List all notification events. - - :keyword page: Page number to return. Default value is 1. - :paramtype page: int - :keyword page_size: Number of entries to return per page. Default value is 100. - :paramtype page_size: int - :keyword order_by: Order by field. Known values are: "id" and "createdAt". Default value is - "createdAt". - :paramtype order_by: str - :keyword order: Order by field. - - Usage: ``?order=ASC``. Known values are: "ASC" and "DESC". Default value is "ASC". - :paramtype order: str - :keyword from_parameter: Start date-time in RFC 3339 format. - Inclusive. Default value is None. - :paramtype from_parameter: ~datetime.datetime - :keyword to: End date-time in RFC 3339 format. - Inclusive. Default value is None. - :paramtype to: ~datetime.datetime - :keyword feature: Filtering by multiple features. + def get_notification_event(self, event_id: str, **kwargs: Any) -> JSON: + """Get notification event. - Usage: ``?feature=feature-1&feature=feature-2``. Default value is None. - :paramtype feature: list[str] - :keyword subject: Filtering by multiple subjects. + Get a notification event by id. - Usage: ``?subject=customer-1&subject=customer-2``. Default value is None. - :paramtype subject: list[str] + :param event_id: A unique ULID identifier for a notification event. Required. + :type event_id: str :return: JSON object :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: - - Example: - .. code-block:: python - - # response body for status code(s): 200 - response == { - "items": [ - { - "createdAt": "2020-02-20 00:00:00", # Timestamp when the - notification event was created. Required. - "deliveryStatus": [ - { - "channel": { - "id": "str", # A unique identifier - for the notification channel. Required. - "type": "str" # The type of the - notification channel. Required. "WEBHOOK" - }, - "state": "str", # Required. Known values - are: "SUCCESS", "FAILED", and "SENDING". - "updatedAt": "2020-02-20 00:00:00" # - Required. - } - ], - "id": "str", # A unique identifier for the notification - event. Required. - "payload": {}, - "rule": {} - } - ], - "page": 0, # Current page number. Required. - "pageSize": 0, # Number of rules per page. Required. - "totalCount": 0 # Total number of rules. Required. - } """ error_map = { - 404: ResourceNotFoundError, 409: ResourceExistsError, 304: ResourceNotModifiedError, - 400: HttpResponseError, 401: lambda response: ClientAuthenticationError(response=response), + 404: lambda response: ResourceNotFoundError(response=response), } error_map.update(kwargs.pop("error_map", {}) or {}) @@ -6502,15 +7044,8 @@ def list_notification_events( cls: ClsType[JSON] = kwargs.pop("cls", None) - _request = build_list_notification_events_request( - page=page, - page_size=page_size, - order_by=order_by, - order=order, - from_parameter=from_parameter, - to=to, - feature=feature, - subject=subject, + _request = build_get_notification_event_request( + event_id=event_id, headers=_headers, params=_params, ) @@ -6539,33 +7074,114 @@ def list_notification_events( return cast(JSON, deserialized) # type: ignore + @overload + def receive_svix_operational_event( # pylint: disable=inconsistent-return-statements + self, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> None: + # pylint: disable=line-too-long + """Receive Svix operational events. + + Callback endpoint used by Svix to notify about operational events. + + :param body: The operational event. Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "data": { + "str": {} # Dictionary of :code:``. Required. + }, + "type": "str" # Required. Known values are: "endpoint.created", + "endpoint.deleted", "endpoint.disabled", "endpoint.updated", + "message.attempt.exhausted", "message.attempt.failing", and + "message.attempt.recovered". + } + """ + + @overload + def receive_svix_operational_event( # pylint: disable=inconsistent-return-statements + self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> None: + """Receive Svix operational events. + + Callback endpoint used by Svix to notify about operational events. + + :param body: The operational event. Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + @distributed_trace - def get_notification_event(self, event_id: str, **kwargs: Any) -> JSON: - """Get notification event. + def receive_svix_operational_event( # pylint: disable=inconsistent-return-statements + self, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> None: + # pylint: disable=line-too-long + """Receive Svix operational events. - Get a notification event by id. + Callback endpoint used by Svix to notify about operational events. - :param event_id: A unique ULID identifier for a notification event. Required. - :type event_id: str - :return: JSON object - :rtype: JSON + :param body: The operational event. Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: None + :rtype: None :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "data": { + "str": {} # Dictionary of :code:``. Required. + }, + "type": "str" # Required. Known values are: "endpoint.created", + "endpoint.deleted", "endpoint.disabled", "endpoint.updated", + "message.attempt.exhausted", "message.attempt.failing", and + "message.attempt.recovered". + } """ error_map = { + 404: ResourceNotFoundError, 409: ResourceExistsError, 304: ResourceNotModifiedError, + 400: HttpResponseError, 401: lambda response: ClientAuthenticationError(response=response), - 404: lambda response: ResourceNotFoundError(response=response), + 501: HttpResponseError, } error_map.update(kwargs.pop("error_map", {}) or {}) - _headers = kwargs.pop("headers", {}) or {} + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - cls: ClsType[JSON] = kwargs.pop("cls", None) + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[None] = kwargs.pop("cls", None) - _request = build_get_notification_event_request( - event_id=event_id, + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_receive_svix_operational_event_request( + content_type=content_type, + json=_json, + content=_content, headers=_headers, params=_params, ) @@ -6578,18 +7194,11 @@ def get_notification_event(self, event_id: str, **kwargs: Any) -> JSON: response = pipeline_response.http_response - if response.status_code not in [200]: + if response.status_code not in [204]: if _stream: response.read() # Load the body in memory and close the socket map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore raise HttpResponseError(response=response) - if response.content: - deserialized = response.json() - else: - deserialized = None - if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore - - return cast(JSON, deserialized) # type: ignore + return cls(pipeline_response, None, {}) # type: ignore diff --git a/api/client/python/src/openmeter/aio/_operations/_operations.py b/api/client/python/src/openmeter/aio/_operations/_operations.py index e11670265..35208138e 100644 --- a/api/client/python/src/openmeter/aio/_operations/_operations.py +++ b/api/client/python/src/openmeter/aio/_operations/_operations.py @@ -28,6 +28,7 @@ build_create_grant_request, build_create_meter_request, build_create_notification_channel_request, + build_create_notification_event_request, build_create_notification_rule_request, build_create_portal_token_request, build_delete_entitlement_request, @@ -62,8 +63,10 @@ build_list_portal_tokens_request, build_list_subject_entitlements_request, build_list_subjects_request, + build_override_entitlement_request, build_query_meter_request, build_query_portal_meter_request, + build_receive_svix_operational_event_request, build_reset_entitlement_usage_request, build_update_notification_channel_request, build_update_notification_rule_request, @@ -3205,6 +3208,232 @@ async def delete_entitlement( # pylint: disable=inconsistent-return-statements if cls: return cls(pipeline_response, None, {}) # type: ignore + @overload + async def override_entitlement( + self, + subject_id_or_key: str, + entitlement_id_or_feature_key: str, + body: JSON, + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + """Override an entitlement. + + Overriding an entitlement creates a new entitlement from the provided inputs and soft deletes + the previous entitlement for the provided subject-feature pair. If the previous entitlement is + already deleted or otherwise doesnt exist, the override will fail. + + This endpoint is useful for upgrades, downgrades, or other changes to entitlements that require + a new entitlement to be created with zero downtime. + + :param subject_id_or_key: A unique identifier for a subject. Required. + :type subject_id_or_key: str + :param entitlement_id_or_feature_key: The id of the entitlement or the key of the feature. + Required. + :type entitlement_id_or_feature_key: str + :param body: The entitlement to create. Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = {} + + # response body for status code(s): 409 + response == { + "detail": "str", # A human-readable explanation specific to this occurrence + of the problem. Required. + "status": 0, # The HTTP status code generated by the origin server for this + occurrence of the problem. Required. + "title": "str", # A a short, human-readable summary of the problem type. + Required. + "type": "str", # Type contains a URI that identifies the problem type. + Required. + "extensions": { + "conflictingEntityId": "str" # The id of the conflicting entity. + Required. + }, + "instance": "str" # Optional. A URI reference that identifies the specific + occurrence of the problem. + } + """ + + @overload + async def override_entitlement( + self, + subject_id_or_key: str, + entitlement_id_or_feature_key: str, + body: IO[bytes], + *, + content_type: str = "application/json", + **kwargs: Any + ) -> JSON: + """Override an entitlement. + + Overriding an entitlement creates a new entitlement from the provided inputs and soft deletes + the previous entitlement for the provided subject-feature pair. If the previous entitlement is + already deleted or otherwise doesnt exist, the override will fail. + + This endpoint is useful for upgrades, downgrades, or other changes to entitlements that require + a new entitlement to be created with zero downtime. + + :param subject_id_or_key: A unique identifier for a subject. Required. + :type subject_id_or_key: str + :param entitlement_id_or_feature_key: The id of the entitlement or the key of the feature. + Required. + :type entitlement_id_or_feature_key: str + :param body: The entitlement to create. Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 409 + response == { + "detail": "str", # A human-readable explanation specific to this occurrence + of the problem. Required. + "status": 0, # The HTTP status code generated by the origin server for this + occurrence of the problem. Required. + "title": "str", # A a short, human-readable summary of the problem type. + Required. + "type": "str", # Type contains a URI that identifies the problem type. + Required. + "extensions": { + "conflictingEntityId": "str" # The id of the conflicting entity. + Required. + }, + "instance": "str" # Optional. A URI reference that identifies the specific + occurrence of the problem. + } + """ + + @distributed_trace_async + async def override_entitlement( + self, subject_id_or_key: str, entitlement_id_or_feature_key: str, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> JSON: + """Override an entitlement. + + Overriding an entitlement creates a new entitlement from the provided inputs and soft deletes + the previous entitlement for the provided subject-feature pair. If the previous entitlement is + already deleted or otherwise doesnt exist, the override will fail. + + This endpoint is useful for upgrades, downgrades, or other changes to entitlements that require + a new entitlement to be created with zero downtime. + + :param subject_id_or_key: A unique identifier for a subject. Required. + :type subject_id_or_key: str + :param entitlement_id_or_feature_key: The id of the entitlement or the key of the feature. + Required. + :type entitlement_id_or_feature_key: str + :param body: The entitlement to create. Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = {} + + # response body for status code(s): 409 + response == { + "detail": "str", # A human-readable explanation specific to this occurrence + of the problem. Required. + "status": 0, # The HTTP status code generated by the origin server for this + occurrence of the problem. Required. + "title": "str", # A a short, human-readable summary of the problem type. + Required. + "type": "str", # Type contains a URI that identifies the problem type. + Required. + "extensions": { + "conflictingEntityId": "str" # The id of the conflicting entity. + Required. + }, + "instance": "str" # Optional. A URI reference that identifies the specific + occurrence of the problem. + } + """ + error_map = { + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 400: HttpResponseError, + 401: lambda response: ClientAuthenticationError(response=response), + 404: lambda response: ResourceNotFoundError(response=response), + 501: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_override_entitlement_request( + subject_id_or_key=subject_id_or_key, + entitlement_id_or_feature_key=entitlement_id_or_feature_key, + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [201, 409]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + if response.status_code == 201: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + + return cast(JSON, deserialized) # type: ignore + @distributed_trace_async async def list_entitlement_grants( self, @@ -5387,15 +5616,19 @@ async def list_notification_events( notification channel. Required. "WEBHOOK" }, "state": "str", # Required. Known values - are: "SUCCESS", "FAILED", and "SENDING". - "updatedAt": "2020-02-20 00:00:00" # + are: "SUCCESS", "FAILED", "SENDING", and "PENDING". + "updatedAt": "2020-02-20 00:00:00", # Required. + "reason": "str" # Optional. The delivery + status of the notification event. Required. } ], "id": "str", # A unique identifier for the notification event. Required. "payload": {}, - "rule": {} + "rule": {}, + "type": "str" # The type of the notification event. + Required. "entitlements.balance.threshold" } ], "page": 0, # Current page number. Required. @@ -5454,6 +5687,197 @@ async def list_notification_events( return cast(JSON, deserialized) # type: ignore + @overload + async def create_notification_event( + self, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> JSON: + """Create a notification event. + + Create a new notification event. + + :param body: The notification event to create. Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "payload": {}, + "ruleId": "str", # Required. + "type": "str" # The type of the notification event. Required. + "entitlements.balance.threshold" + } + + # response body for status code(s): 409 + response == { + "detail": "str", # A human-readable explanation specific to this occurrence + of the problem. Required. + "status": 0, # The HTTP status code generated by the origin server for this + occurrence of the problem. Required. + "title": "str", # A a short, human-readable summary of the problem type. + Required. + "type": "str", # Type contains a URI that identifies the problem type. + Required. + "extensions": { + "conflictingEntityId": "str" # The id of the conflicting entity. + Required. + }, + "instance": "str" # Optional. A URI reference that identifies the specific + occurrence of the problem. + } + """ + + @overload + async def create_notification_event( + self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> JSON: + """Create a notification event. + + Create a new notification event. + + :param body: The notification event to create. Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # response body for status code(s): 409 + response == { + "detail": "str", # A human-readable explanation specific to this occurrence + of the problem. Required. + "status": 0, # The HTTP status code generated by the origin server for this + occurrence of the problem. Required. + "title": "str", # A a short, human-readable summary of the problem type. + Required. + "type": "str", # Type contains a URI that identifies the problem type. + Required. + "extensions": { + "conflictingEntityId": "str" # The id of the conflicting entity. + Required. + }, + "instance": "str" # Optional. A URI reference that identifies the specific + occurrence of the problem. + } + """ + + @distributed_trace_async + async def create_notification_event(self, body: Union[JSON, IO[bytes]], **kwargs: Any) -> JSON: + """Create a notification event. + + Create a new notification event. + + :param body: The notification event to create. Is either a JSON type or a IO[bytes] type. + Required. + :type body: JSON or IO[bytes] + :return: JSON object + :rtype: JSON + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "payload": {}, + "ruleId": "str", # Required. + "type": "str" # The type of the notification event. Required. + "entitlements.balance.threshold" + } + + # response body for status code(s): 409 + response == { + "detail": "str", # A human-readable explanation specific to this occurrence + of the problem. Required. + "status": 0, # The HTTP status code generated by the origin server for this + occurrence of the problem. Required. + "title": "str", # A a short, human-readable summary of the problem type. + Required. + "type": "str", # Type contains a URI that identifies the problem type. + Required. + "extensions": { + "conflictingEntityId": "str" # The id of the conflicting entity. + Required. + }, + "instance": "str" # Optional. A URI reference that identifies the specific + occurrence of the problem. + } + """ + error_map = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 400: HttpResponseError, + 401: lambda response: ClientAuthenticationError(response=response), + 501: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[JSON] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_create_notification_event_request( + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [201, 409]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + if response.status_code == 201: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if response.status_code == 409: + if response.content: + deserialized = response.json() + else: + deserialized = None + + if cls: + return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + + return cast(JSON, deserialized) # type: ignore + @distributed_trace_async async def get_notification_event(self, event_id: str, **kwargs: Any) -> JSON: """Get notification event. @@ -5508,3 +5932,132 @@ async def get_notification_event(self, event_id: str, **kwargs: Any) -> JSON: return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore return cast(JSON, deserialized) # type: ignore + + @overload + async def receive_svix_operational_event( # pylint: disable=inconsistent-return-statements + self, body: JSON, *, content_type: str = "application/json", **kwargs: Any + ) -> None: + # pylint: disable=line-too-long + """Receive Svix operational events. + + Callback endpoint used by Svix to notify about operational events. + + :param body: The operational event. Required. + :type body: JSON + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "data": { + "str": {} # Dictionary of :code:``. Required. + }, + "type": "str" # Required. Known values are: "endpoint.created", + "endpoint.deleted", "endpoint.disabled", "endpoint.updated", + "message.attempt.exhausted", "message.attempt.failing", and + "message.attempt.recovered". + } + """ + + @overload + async def receive_svix_operational_event( # pylint: disable=inconsistent-return-statements + self, body: IO[bytes], *, content_type: str = "application/json", **kwargs: Any + ) -> None: + """Receive Svix operational events. + + Callback endpoint used by Svix to notify about operational events. + + :param body: The operational event. Required. + :type body: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def receive_svix_operational_event( # pylint: disable=inconsistent-return-statements + self, body: Union[JSON, IO[bytes]], **kwargs: Any + ) -> None: + # pylint: disable=line-too-long + """Receive Svix operational events. + + Callback endpoint used by Svix to notify about operational events. + + :param body: The operational event. Is either a JSON type or a IO[bytes] type. Required. + :type body: JSON or IO[bytes] + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + + Example: + .. code-block:: python + + # JSON input template you can fill out and use as your body input. + body = { + "data": { + "str": {} # Dictionary of :code:``. Required. + }, + "type": "str" # Required. Known values are: "endpoint.created", + "endpoint.deleted", "endpoint.disabled", "endpoint.updated", + "message.attempt.exhausted", "message.attempt.failing", and + "message.attempt.recovered". + } + """ + error_map = { + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + 400: HttpResponseError, + 401: lambda response: ClientAuthenticationError(response=response), + 501: HttpResponseError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[None] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _json = None + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _json = body + + _request = build_receive_svix_operational_event_request( + content_type=content_type, + json=_json, + content=_content, + headers=_headers, + params=_params, + ) + _request.url = self._client.format_url(_request.url) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + if _stream: + await response.read() # Load the body in memory and close the socket + map_error(status_code=response.status_code, response=response, error_map=error_map) # type: ignore + raise HttpResponseError(response=response) + + if cls: + return cls(pipeline_response, None, {}) # type: ignore diff --git a/api/client/web/src/client/openapi.ts b/api/client/web/src/client/openapi.ts index 44d0ac734..0d19dc1f4 100644 --- a/api/client/web/src/client/openapi.ts +++ b/api/client/web/src/client/openapi.ts @@ -227,6 +227,15 @@ export interface paths { */ delete: operations['deleteEntitlement'] } + '/api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/override': { + /** + * Override an entitlement + * @description Overriding an entitlement creates a new entitlement from the provided inputs and soft deletes the previous entitlement for the provided subject-feature pair. If the previous entitlement is already deleted or otherwise doesnt exist, the override will fail. + * + * This endpoint is useful for upgrades, downgrades, or other changes to entitlements that require a new entitlement to be created with zero downtime. + */ + put: operations['overrideEntitlement'] + } '/api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/grants': { /** * List grants for an entitlement @@ -350,6 +359,11 @@ export interface paths { * @description List all notification events. */ get: operations['listNotificationEvents'] + /** + * Create a notification event + * @description Create a new notification event. + */ + post: operations['createNotificationEvent'] } '/api/v1/notification/events/{eventId}': { /** @@ -358,6 +372,13 @@ export interface paths { */ get: operations['getNotificationEvent'] } + '/api/v1/notification/webhook/svix': { + /** + * Receive Svix operational events + * @description Callback endpoint used by Svix to notify about operational events + */ + post: operations['receiveSvixOperationalEvent'] + } } export type webhooks = Record @@ -1689,6 +1710,13 @@ export interface components { */ disabled: boolean } + /** @description Request for creating new notification event with specific type and payload. */ + NotificationEventCreateRequest: { + type: components['schemas']['NotificationEventType'] + payload: components['schemas']['NotificationEventPayload'] + /** @example 01J2KNP1YTXQRXHTDJ4KPR7PZ0 */ + ruleId: string + } /** * @description Notification event generated by the system based on the criteria defined in the corresponding * a notification rule. @@ -1701,6 +1729,7 @@ export interface components { * @example 01J2KNP1YTXQRXHTDJ4KPR7PZ0 */ id: string + type: components['schemas']['NotificationEventType'] /** * Format: date-time * @description Timestamp when the notification event was created. @@ -1738,14 +1767,16 @@ export interface components { entitlement: components['schemas']['EntitlementMetered'] feature: components['schemas']['Feature'] subject: components['schemas']['Subject'] - balance: components['schemas']['EntitlementValue'] + value: components['schemas']['EntitlementValue'] threshold: components['schemas']['NotificationRuleBalanceThresholdValue'] } } NotificationEventDeliveryStatus: { channel: components['schemas']['NotificationChannelMeta'] /** @enum {string} */ - state: 'SUCCESS' | 'FAILED' | 'SENDING' + state: 'SUCCESS' | 'FAILED' | 'SENDING' | 'PENDING' + /** @example Failed to dispatch event */ + reason?: string /** * Format: date-time * @example 2023-01-01T00:00:00Z @@ -1772,6 +1803,20 @@ export interface components { pageSize: number items: components['schemas']['NotificationEvents'] } + SvixOperationalWebhookRequest: { + /** @enum {string} */ + type: + | 'endpoint.created' + | 'endpoint.deleted' + | 'endpoint.disabled' + | 'endpoint.updated' + | 'message.attempt.exhausted' + | 'message.attempt.failing' + | 'message.attempt.recovered' + data: { + [key: string]: unknown + } + } } responses: { /** @description Conflict */ @@ -2651,6 +2696,40 @@ export interface operations { default: components['responses']['UnexpectedProblemResponse'] } } + /** + * Override an entitlement + * @description Overriding an entitlement creates a new entitlement from the provided inputs and soft deletes the previous entitlement for the provided subject-feature pair. If the previous entitlement is already deleted or otherwise doesnt exist, the override will fail. + * + * This endpoint is useful for upgrades, downgrades, or other changes to entitlements that require a new entitlement to be created with zero downtime. + */ + overrideEntitlement: { + parameters: { + path: { + subjectIdOrKey: components['parameters']['subjectIdOrKey'] + entitlementIdOrFeatureKey: components['parameters']['entitlementIdOrFeatureKey'] + } + } + /** @description The entitlement to create. */ + requestBody: { + content: { + 'application/json': components['schemas']['EntitlementCreateInputs'] + } + } + responses: { + /** @description Entitlement created. */ + 201: { + content: { + 'application/json': components['schemas']['Entitlement'] + } + } + 400: components['responses']['BadRequestProblemResponse'] + 401: components['responses']['UnauthorizedProblemResponse'] + 404: components['responses']['NotFoundProblemResponse'] + 409: components['responses']['ConflictProblemResponse'] + 501: components['responses']['NotImplementedProblemResponse'] + default: components['responses']['UnexpectedProblemResponse'] + } + } /** * List grants for an entitlement * @description List all grants issued for an entitlement. The entitlement can be defined either by its id or featureKey. @@ -3134,6 +3213,31 @@ export interface operations { default: components['responses']['UnexpectedProblemResponse'] } } + /** + * Create a notification event + * @description Create a new notification event. + */ + createNotificationEvent: { + /** @description The notification event to create. */ + requestBody: { + content: { + 'application/json': components['schemas']['NotificationEventCreateRequest'] + } + } + responses: { + /** @description Notification event created. */ + 201: { + content: { + 'application/json': components['schemas']['NotificationEvent'] + } + } + 400: components['responses']['BadRequestProblemResponse'] + 401: components['responses']['UnauthorizedProblemResponse'] + 409: components['responses']['ConflictProblemResponse'] + 501: components['responses']['NotImplementedProblemResponse'] + default: components['responses']['UnexpectedProblemResponse'] + } + } /** * Get notification event * @description Get a notification event by id. @@ -3156,4 +3260,26 @@ export interface operations { default: components['responses']['UnexpectedProblemResponse'] } } + /** + * Receive Svix operational events + * @description Callback endpoint used by Svix to notify about operational events + */ + receiveSvixOperationalEvent: { + /** @description The operational event. */ + requestBody: { + content: { + 'application/json': components['schemas']['SvixOperationalWebhookRequest'] + } + } + responses: { + /** @description Operational webhook request accepted */ + 204: { + content: never + } + 400: components['responses']['BadRequestProblemResponse'] + 401: components['responses']['UnauthorizedProblemResponse'] + 501: components['responses']['NotImplementedProblemResponse'] + default: components['responses']['UnexpectedProblemResponse'] + } + } } diff --git a/api/openapi.yaml b/api/openapi.yaml index 4de91cbe8..0ea95a7a6 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -1017,6 +1017,45 @@ paths: $ref: "#/components/responses/NotFoundProblemResponse" default: $ref: "#/components/responses/UnexpectedProblemResponse" + /api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/override: + put: + operationId: overrideEntitlement + summary: Override an entitlement + description: | + Overriding an entitlement creates a new entitlement from the provided inputs and soft deletes the previous entitlement for the provided subject-feature pair. If the previous entitlement is already deleted or otherwise doesnt exist, the override will fail. + + This endpoint is useful for upgrades, downgrades, or other changes to entitlements that require a new entitlement to be created with zero downtime. + tags: + - Entitlements (Experimental) + parameters: + - $ref: "#/components/parameters/subjectIdOrKey" + - $ref: "#/components/parameters/entitlementIdOrFeatureKey" + requestBody: + description: The entitlement to create. + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EntitlementCreateInputs" + responses: + "201": + description: Entitlement created. + content: + application/json: + schema: + $ref: "#/components/schemas/Entitlement" + "400": + $ref: "#/components/responses/BadRequestProblemResponse" + "401": + $ref: "#/components/responses/UnauthorizedProblemResponse" + "404": + $ref: "#/components/responses/NotFoundProblemResponse" + "409": + $ref: "#/components/responses/ConflictProblemResponse" + "501": + $ref: "#/components/responses/NotImplementedProblemResponse" + default: + $ref: "#/components/responses/UnexpectedProblemResponse" /api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/grants: get: operationId: listEntitlementGrants diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index c8820004e..885db7239 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -853,6 +853,44 @@ func TestCredit(t *testing.T) { require.Len(t, *grantListResp.JSON200, 1) }) + t.Run("Override previous entitlement", func(t *testing.T) { + meteredEntitlement := api.EntitlementMeteredCreateInputs{ + Type: "metered", + FeatureId: featureId, + UsagePeriod: api.RecurringPeriodCreateInput{ + Anchor: convert.ToPointer(time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC)), + Interval: "MONTH", + }, + } + body := &api.CreateEntitlementJSONRequestBody{} + err := body.FromEntitlementMeteredCreateInputs(meteredEntitlement) + require.NoError(t, err) + + subject := "test-override" + + // create an entitlement + resp, err := client.CreateEntitlementWithResponse(context.Background(), subject, *body) + require.NoError(t, err) + require.Equal(t, http.StatusCreated, resp.StatusCode(), "Invalid status code [response_body=%s]", string(resp.Body)) + + metered, err := resp.JSON201.AsEntitlementMetered() + require.NoError(t, err) + + entId := metered.Id + + // Override entitlement + resp2, err := client.OverrideEntitlementWithResponse(context.Background(), subject, *entId, *body) + + require.NoError(t, err) + require.Equal(t, http.StatusCreated, resp.StatusCode(), "Invalid status code [response_body=%s]", string(resp.Body)) + + metered, err = resp2.JSON201.AsEntitlementMetered() + require.NoError(t, err) + + require.Equal(t, metered.SubjectKey, subject) + require.NotEqual(t, metered.Id, entId) + }) + t.Run("List entitlements", func(t *testing.T) { // should return 2 entitlements for subject for feature resp, err := client.ListEntitlementsWithResponse(context.Background(), &api.ListEntitlementsParams{ diff --git a/internal/entitlement/connector.go b/internal/entitlement/connector.go index cdfb32469..489b5e737 100644 --- a/internal/entitlement/connector.go +++ b/internal/entitlement/connector.go @@ -56,6 +56,7 @@ type ListEntitlementsParams struct { type Connector interface { CreateEntitlement(ctx context.Context, input CreateEntitlementInputs) (*Entitlement, error) + OverrideEntitlement(ctx context.Context, subject string, entitlementIdOrFeatureKey string, input CreateEntitlementInputs) (*Entitlement, error) GetEntitlement(ctx context.Context, namespace string, id string) (*Entitlement, error) DeleteEntitlement(ctx context.Context, namespace string, id string) error @@ -98,45 +99,45 @@ func NewEntitlementConnector( } func (c *entitlementConnector) CreateEntitlement(ctx context.Context, input CreateEntitlementInputs) (*Entitlement, error) { - // ID has priority over key - idOrFeatureKey := input.FeatureID - if idOrFeatureKey == nil { - idOrFeatureKey = input.FeatureKey - } - if idOrFeatureKey == nil { - return nil, &models.GenericUserError{Message: "Feature ID or Key is required"} - } + doInTx := func(ctx context.Context, tx *entutils.TxDriver) (*Entitlement, error) { + // ID has priority over key + featureIdOrKey := input.FeatureID + if featureIdOrKey == nil { + featureIdOrKey = input.FeatureKey + } + if featureIdOrKey == nil { + return nil, &models.GenericUserError{Message: "Feature ID or Key is required"} + } - feature, err := c.featureConnector.GetFeature(ctx, input.Namespace, *idOrFeatureKey, productcatalog.IncludeArchivedFeatureFalse) - if err != nil || feature == nil { - return nil, &productcatalog.FeatureNotFoundError{ID: *idOrFeatureKey} - } + feature, err := c.featureConnector.GetFeature(ctx, input.Namespace, *featureIdOrKey, productcatalog.IncludeArchivedFeatureFalse) + if err != nil || feature == nil { + return nil, &productcatalog.FeatureNotFoundError{ID: *featureIdOrKey} + } - // fill featureId and featureKey - input.FeatureID = &feature.ID - input.FeatureKey = &feature.Key + // fill featureId and featureKey + input.FeatureID = &feature.ID + input.FeatureKey = &feature.Key - currentEntitlements, err := c.entitlementRepo.GetEntitlementsOfSubject(ctx, input.Namespace, models.SubjectKey(input.SubjectKey)) - if err != nil { - return nil, err - } - for _, ent := range currentEntitlements { - // you can only have a single entitlemnet per feature key - if ent.FeatureKey == feature.Key || ent.FeatureID == feature.ID { - return nil, &AlreadyExistsError{EntitlementID: ent.ID, FeatureID: feature.ID, SubjectKey: input.SubjectKey} + currentEntitlements, err := c.entitlementRepo.WithTx(ctx, tx).GetEntitlementsOfSubject(ctx, input.Namespace, models.SubjectKey(input.SubjectKey)) + if err != nil { + return nil, err + } + for _, ent := range currentEntitlements { + // you can only have a single entitlemnet per feature key + if ent.FeatureKey == feature.Key || ent.FeatureID == feature.ID { + return nil, &AlreadyExistsError{EntitlementID: ent.ID, FeatureID: feature.ID, SubjectKey: input.SubjectKey} + } } - } - connector, err := c.getTypeConnector(input) - if err != nil { - return nil, err - } - repoInputs, err := connector.BeforeCreate(input, *feature) - if err != nil { - return nil, err - } + connector, err := c.getTypeConnector(input) + if err != nil { + return nil, err + } + repoInputs, err := connector.BeforeCreate(input, *feature) + if err != nil { + return nil, err + } - ent, err := entutils.StartAndRunTx(ctx, c.entitlementRepo, func(ctx context.Context, tx *entutils.TxDriver) (*Entitlement, error) { txCtx := entutils.NewTxContext(ctx, tx) ent, err := c.entitlementRepo.WithTx(txCtx, tx).CreateEntitlement(txCtx, *repoInputs) @@ -159,10 +160,68 @@ func (c *entitlementConnector) CreateEntitlement(ctx context.Context, input Crea return nil, err } - return ent, nil - }) + return ent, err + } - return ent, err + if ctxTx, err := entutils.GetTxDriver(ctx); err == nil { + // we're already in a tx + return doInTx(ctx, ctxTx) + } else { + return entutils.StartAndRunTx(ctx, c.entitlementRepo, doInTx) + } +} + +func (c *entitlementConnector) OverrideEntitlement(ctx context.Context, subject string, entitlementIdOrFeatureKey string, input CreateEntitlementInputs) (*Entitlement, error) { + // Validate input + if subject != input.SubjectKey { + return nil, &models.GenericUserError{Message: "Subject key in path and body do not match"} + } + + oldEnt, err := c.entitlementRepo.GetEntitlementOfSubject(ctx, input.Namespace, input.SubjectKey, entitlementIdOrFeatureKey) + if err != nil { + return nil, err + } + + if oldEnt == nil { + return nil, fmt.Errorf("inconsistency error, entitlement not found: %s", entitlementIdOrFeatureKey) + } + + if oldEnt.DeletedAt != nil { + return nil, fmt.Errorf("inconsistency error, entitlement already deleted: %s", oldEnt.ID) + } + + // ID has priority over key + featureIdOrKey := input.FeatureID + if featureIdOrKey == nil { + featureIdOrKey = input.FeatureKey + } + if featureIdOrKey == nil { + return nil, &models.GenericUserError{Message: "Feature ID or Key is required"} + } + + feature, err := c.featureConnector.GetFeature(ctx, input.Namespace, *featureIdOrKey, productcatalog.IncludeArchivedFeatureFalse) + if err != nil || feature == nil { + return nil, &productcatalog.FeatureNotFoundError{ID: *featureIdOrKey} + } + + if feature.ID != oldEnt.FeatureID { + return nil, &models.GenericUserError{Message: "Feature in path and body do not match"} + } + + // Do the override in TX + return entutils.StartAndRunTx(ctx, c.entitlementRepo, func(ctx context.Context, tx *entutils.TxDriver) (*Entitlement, error) { + ctx = entutils.NewTxContext(ctx, tx) + + // Delete previous entitlement + // FIXME: we publish an event during this even if we fail later + err := c.DeleteEntitlement(ctx, input.Namespace, oldEnt.ID) + if err != nil { + return nil, err + } + + // Create new entitlement + return c.CreateEntitlement(ctx, input) + }) } func (c *entitlementConnector) GetEntitlement(ctx context.Context, namespace string, id string) (*Entitlement, error) { @@ -170,15 +229,13 @@ func (c *entitlementConnector) GetEntitlement(ctx context.Context, namespace str } func (c *entitlementConnector) DeleteEntitlement(ctx context.Context, namespace string, id string) error { - _, err := entutils.StartAndRunTx(ctx, c.entitlementRepo, func(ctx context.Context, tx *entutils.TxDriver) (*Entitlement, error) { - txCtx := entutils.NewTxContext(ctx, tx) - - ent, err := c.entitlementRepo.WithTx(txCtx, tx).GetEntitlement(txCtx, models.NamespacedID{Namespace: namespace, ID: id}) + doInTx := func(ctx context.Context, tx *entutils.TxDriver) (*Entitlement, error) { + ent, err := c.entitlementRepo.WithTx(ctx, tx).GetEntitlement(ctx, models.NamespacedID{Namespace: namespace, ID: id}) if err != nil { return nil, err } - err = c.entitlementRepo.WithTx(txCtx, tx).DeleteEntitlement(ctx, models.NamespacedID{Namespace: namespace, ID: id}) + err = c.entitlementRepo.WithTx(ctx, tx).DeleteEntitlement(ctx, models.NamespacedID{Namespace: namespace, ID: id}) if err != nil { return nil, err } @@ -194,9 +251,16 @@ func (c *entitlementConnector) DeleteEntitlement(ctx context.Context, namespace } return ent, nil - }) + } - return err + if ctxTx, err := entutils.GetTxDriver(ctx); err == nil { + // we're already in a tx + _, err := doInTx(ctx, ctxTx) + return err + } else { + _, err := entutils.StartAndRunTx(ctx, c.entitlementRepo, doInTx) + return err + } } func (c *entitlementConnector) GetEntitlementsOfSubject(ctx context.Context, namespace string, subjectKey models.SubjectKey) ([]Entitlement, error) { diff --git a/internal/entitlement/driver/entitlement.go b/internal/entitlement/driver/entitlement.go index a7e3fc17d..6dc67988c 100644 --- a/internal/entitlement/driver/entitlement.go +++ b/internal/entitlement/driver/entitlement.go @@ -17,13 +17,13 @@ import ( "github.com/openmeterio/openmeter/pkg/framework/transport/httptransport" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/pagination" - "github.com/openmeterio/openmeter/pkg/recurrence" "github.com/openmeterio/openmeter/pkg/slicesx" "github.com/openmeterio/openmeter/pkg/strcase" ) type EntitlementHandler interface { CreateEntitlement() CreateEntitlementHandler + OverrideEntitlement() OverrideEntitlementHandler GetEntitlement() GetEntitlementHandler GetEntitlementById() GetEntitlementByIdHandler DeleteEntitlement() DeleteEntitlementHandler @@ -72,111 +72,75 @@ func (h *entitlementHandler) CreateEntitlement() CreateEntitlementHandler { return request, err } - value, err := inp.ValueByDiscriminator() + return ParseAPICreateInput(inp, ns, subjectIdOrKey) + }, + func(ctx context.Context, request CreateEntitlementHandlerRequest) (CreateEntitlementHandlerResponse, error) { + res, err := h.connector.CreateEntitlement(ctx, request) if err != nil { + return nil, err + } + return Parser.ToAPIGeneric(res) + }, + commonhttp.JSONResponseEncoderWithStatus[CreateEntitlementHandlerResponse](http.StatusCreated), + httptransport.AppendOptions( + h.options, + httptransport.WithOperationName("createEntitlement"), + httptransport.WithErrorEncoder(getErrorEncoder()), + )..., + ) +} + +type ( + OverrideEntitlementHandlerRequest struct { + Inputs entitlement.CreateEntitlementInputs + SubjectIdOrKey string + EntitlementIdOrFeatureKey string + } + OverrideEntitlementHandlerResponse = *api.Entitlement + OverrideEntitlementHandlerParams struct { + SubjectIdOrKey string + EntitlementIdOrFeatureKey string + } +) + +type OverrideEntitlementHandler httptransport.HandlerWithArgs[OverrideEntitlementHandlerRequest, OverrideEntitlementHandlerResponse, OverrideEntitlementHandlerParams] + +func (h *entitlementHandler) OverrideEntitlement() OverrideEntitlementHandler { + return httptransport.NewHandlerWithArgs[OverrideEntitlementHandlerRequest, OverrideEntitlementHandlerResponse, OverrideEntitlementHandlerParams]( + func(ctx context.Context, r *http.Request, params OverrideEntitlementHandlerParams) (OverrideEntitlementHandlerRequest, error) { + inp := &api.EntitlementCreateInputs{} + request := OverrideEntitlementHandlerRequest{} + if err := commonhttp.JSONRequestBodyDecoder(r, &inp); err != nil { return request, err } - switch v := value.(type) { - case api.EntitlementMeteredCreateInputs: - request = entitlement.CreateEntitlementInputs{ - Namespace: ns, - FeatureID: v.FeatureId, - FeatureKey: v.FeatureKey, - SubjectKey: subjectIdOrKey, - EntitlementType: entitlement.EntitlementTypeMetered, - IsSoftLimit: v.IsSoftLimit, - IssueAfterReset: v.IssueAfterReset, - IssueAfterResetPriority: convert.SafeDeRef(v.IssueAfterResetPriority, func(i int) *uint8 { - return convert.ToPointer(uint8(i)) - }), - UsagePeriod: &entitlement.UsagePeriod{ - Anchor: defaultx.WithDefault(v.UsagePeriod.Anchor, clock.Now()), // TODO: shouldn't we truncate this? - Interval: recurrence.RecurrenceInterval(v.UsagePeriod.Interval), - }, - PreserveOverageAtReset: v.PreserveOverageAtReset, - } - if v.Metadata != nil { - request.Metadata = *v.Metadata - } - if v.MeasureUsageFrom != nil { - measureUsageFrom := &entitlement.MeasureUsageFromInput{} - apiTime, err := v.MeasureUsageFrom.AsMeasureUsageFromTime() - if err == nil { - err := measureUsageFrom.FromTime(apiTime) - if err != nil { - return request, err - } - } else { - apiEnum, err := v.MeasureUsageFrom.AsMeasureUsageFromEnum() - if err != nil { - return request, err - } - - // sanity check - if request.UsagePeriod == nil { - return request, errors.New("usage period is required for enum measure usage from") - } - - err = measureUsageFrom.FromEnum(entitlement.MeasureUsageFromEnum(apiEnum), *request.UsagePeriod, clock.Now()) - if err != nil { - return request, err - } - } - request.MeasureUsageFrom = measureUsageFrom - } - case api.EntitlementStaticCreateInputs: - request = entitlement.CreateEntitlementInputs{ - Namespace: ns, - FeatureID: v.FeatureId, - FeatureKey: v.FeatureKey, - SubjectKey: subjectIdOrKey, - EntitlementType: entitlement.EntitlementTypeStatic, - Config: []byte(v.Config), - } - if v.UsagePeriod != nil { - request.UsagePeriod = &entitlement.UsagePeriod{ - Anchor: defaultx.WithDefault(v.UsagePeriod.Anchor, clock.Now()), // TODO: shouldn't we truncate this? - Interval: recurrence.RecurrenceInterval(v.UsagePeriod.Interval), - } - } - if v.Metadata != nil { - request.Metadata = *v.Metadata - } - case api.EntitlementBooleanCreateInputs: - request = entitlement.CreateEntitlementInputs{ - Namespace: ns, - FeatureID: v.FeatureId, - FeatureKey: v.FeatureKey, - SubjectKey: subjectIdOrKey, - EntitlementType: entitlement.EntitlementTypeBoolean, - } - if v.UsagePeriod != nil { - request.UsagePeriod = &entitlement.UsagePeriod{ - Anchor: defaultx.WithDefault(v.UsagePeriod.Anchor, clock.Now()), // TODO: shouldn't we truncate this? - Interval: recurrence.RecurrenceInterval(v.UsagePeriod.Interval), - } - } - if v.Metadata != nil { - request.Metadata = *v.Metadata - } - default: - return request, errors.New("unknown entitlement type") + ns, err := h.resolveNamespace(ctx) + if err != nil { + return request, err + } + + eInp, err := ParseAPICreateInput(inp, ns, params.SubjectIdOrKey) + if err != nil { + return request, err } + request.Inputs = eInp + request.SubjectIdOrKey = params.SubjectIdOrKey + request.EntitlementIdOrFeatureKey = params.EntitlementIdOrFeatureKey + return request, nil }, - func(ctx context.Context, request CreateEntitlementHandlerRequest) (CreateEntitlementHandlerResponse, error) { - res, err := h.connector.CreateEntitlement(ctx, request) + func(ctx context.Context, request OverrideEntitlementHandlerRequest) (OverrideEntitlementHandlerResponse, error) { + res, err := h.connector.OverrideEntitlement(ctx, request.SubjectIdOrKey, request.EntitlementIdOrFeatureKey, request.Inputs) if err != nil { return nil, err } return Parser.ToAPIGeneric(res) }, - commonhttp.JSONResponseEncoderWithStatus[CreateEntitlementHandlerResponse](http.StatusCreated), + commonhttp.JSONResponseEncoderWithStatus[OverrideEntitlementHandlerResponse](http.StatusCreated), httptransport.AppendOptions( h.options, - httptransport.WithOperationName("createEntitlement"), + httptransport.WithOperationName("overrideEntitlement"), httptransport.WithErrorEncoder(getErrorEncoder()), )..., ) diff --git a/internal/entitlement/driver/parser.go b/internal/entitlement/driver/parser.go index a973b155a..5ae360015 100644 --- a/internal/entitlement/driver/parser.go +++ b/internal/entitlement/driver/parser.go @@ -9,7 +9,9 @@ import ( booleanentitlement "github.com/openmeterio/openmeter/internal/entitlement/boolean" meteredentitlement "github.com/openmeterio/openmeter/internal/entitlement/metered" staticentitlement "github.com/openmeterio/openmeter/internal/entitlement/static" + "github.com/openmeterio/openmeter/pkg/clock" "github.com/openmeterio/openmeter/pkg/convert" + "github.com/openmeterio/openmeter/pkg/defaultx" "github.com/openmeterio/openmeter/pkg/recurrence" ) @@ -183,3 +185,104 @@ func mapPeriod(u *recurrence.Period) *api.Period { To: u.To, } } + +func ParseAPICreateInput(inp *api.EntitlementCreateInputs, ns string, subjectIdOrKey string) (entitlement.CreateEntitlementInputs, error) { + request := entitlement.CreateEntitlementInputs{} + if inp == nil { + return request, errors.New("input is nil") + } + + value, err := inp.ValueByDiscriminator() + if err != nil { + return request, err + } + + switch v := value.(type) { + case api.EntitlementMeteredCreateInputs: + request = entitlement.CreateEntitlementInputs{ + Namespace: ns, + FeatureID: v.FeatureId, + FeatureKey: v.FeatureKey, + SubjectKey: subjectIdOrKey, + EntitlementType: entitlement.EntitlementTypeMetered, + IsSoftLimit: v.IsSoftLimit, + IssueAfterReset: v.IssueAfterReset, + IssueAfterResetPriority: convert.SafeDeRef(v.IssueAfterResetPriority, func(i int) *uint8 { + return convert.ToPointer(uint8(i)) + }), + UsagePeriod: &entitlement.UsagePeriod{ + Anchor: defaultx.WithDefault(v.UsagePeriod.Anchor, clock.Now()), // TODO: shouldn't we truncate this? + Interval: recurrence.RecurrenceInterval(v.UsagePeriod.Interval), + }, + PreserveOverageAtReset: v.PreserveOverageAtReset, + } + if v.Metadata != nil { + request.Metadata = *v.Metadata + } + if v.MeasureUsageFrom != nil { + measureUsageFrom := &entitlement.MeasureUsageFromInput{} + apiTime, err := v.MeasureUsageFrom.AsMeasureUsageFromTime() + if err == nil { + err := measureUsageFrom.FromTime(apiTime) + if err != nil { + return request, err + } + } else { + apiEnum, err := v.MeasureUsageFrom.AsMeasureUsageFromEnum() + if err != nil { + return request, err + } + + // sanity check + if request.UsagePeriod == nil { + return request, errors.New("usage period is required for enum measure usage from") + } + + err = measureUsageFrom.FromEnum(entitlement.MeasureUsageFromEnum(apiEnum), *request.UsagePeriod, clock.Now()) + if err != nil { + return request, err + } + } + request.MeasureUsageFrom = measureUsageFrom + } + case api.EntitlementStaticCreateInputs: + request = entitlement.CreateEntitlementInputs{ + Namespace: ns, + FeatureID: v.FeatureId, + FeatureKey: v.FeatureKey, + SubjectKey: subjectIdOrKey, + EntitlementType: entitlement.EntitlementTypeStatic, + Config: []byte(v.Config), + } + if v.UsagePeriod != nil { + request.UsagePeriod = &entitlement.UsagePeriod{ + Anchor: defaultx.WithDefault(v.UsagePeriod.Anchor, clock.Now()), // TODO: shouldn't we truncate this? + Interval: recurrence.RecurrenceInterval(v.UsagePeriod.Interval), + } + } + if v.Metadata != nil { + request.Metadata = *v.Metadata + } + case api.EntitlementBooleanCreateInputs: + request = entitlement.CreateEntitlementInputs{ + Namespace: ns, + FeatureID: v.FeatureId, + FeatureKey: v.FeatureKey, + SubjectKey: subjectIdOrKey, + EntitlementType: entitlement.EntitlementTypeBoolean, + } + if v.UsagePeriod != nil { + request.UsagePeriod = &entitlement.UsagePeriod{ + Anchor: defaultx.WithDefault(v.UsagePeriod.Anchor, clock.Now()), // TODO: shouldn't we truncate this? + Interval: recurrence.RecurrenceInterval(v.UsagePeriod.Interval), + } + } + if v.Metadata != nil { + request.Metadata = *v.Metadata + } + default: + return request, errors.New("unknown entitlement type") + } + + return request, nil +} diff --git a/internal/server/router/entitlement.go b/internal/server/router/entitlement.go index b55f27151..2be68abfc 100644 --- a/internal/server/router/entitlement.go +++ b/internal/server/router/entitlement.go @@ -142,3 +142,16 @@ func (a *Router) GetEntitlementById(w http.ResponseWriter, r *http.Request, enti EntitlementId: entitlementId, }).ServeHTTP(w, r) } + +// Override an entitlement +// (PUT /api/v1/subjects/{subjectIdOrKey}/entitlements/{entitlementIdOrFeatureKey}/override) +func (a *Router) OverrideEntitlement(w http.ResponseWriter, r *http.Request, subjectIdOrKey api.SubjectIdOrKey, entitlementIdOrFeatureKey api.EntitlementIdOrFeatureKey) { + if !a.config.EntitlementsEnabled { + unimplemented.OverrideEntitlement(w, r, subjectIdOrKey, entitlementIdOrFeatureKey) + return + } + a.entitlementHandler.OverrideEntitlement().With(entitlementdriver.OverrideEntitlementHandlerParams{ + SubjectIdOrKey: subjectIdOrKey, + EntitlementIdOrFeatureKey: entitlementIdOrFeatureKey, + }).ServeHTTP(w, r) +} diff --git a/openmeter/entitlement/driver/handlers.go b/openmeter/entitlement/driver/handlers.go index f4848edc7..deb2cd394 100644 --- a/openmeter/entitlement/driver/handlers.go +++ b/openmeter/entitlement/driver/handlers.go @@ -26,6 +26,7 @@ type ( GetEntitlementHandler = httpdriver.GetEntitlementHandler GetEntitlementByIdHandler = httpdriver.GetEntitlementByIdHandler DeleteEntitlementHandler = httpdriver.DeleteEntitlementHandler + OverrideEntitlementHandler = httpdriver.OverrideEntitlementHandler ) func NewEntitlementHandler( diff --git a/openmeter/entitlement/driver/types.go b/openmeter/entitlement/driver/types.go index 27e2ce6cf..04a461943 100644 --- a/openmeter/entitlement/driver/types.go +++ b/openmeter/entitlement/driver/types.go @@ -15,6 +15,7 @@ type ( GetEntitlementHandlerRequest = httpdriver.GetEntitlementHandlerRequest GetEntitlementByIdHandlerRequest = httpdriver.GetEntitlementByIdHandlerRequest DeleteEntitlementHandlerRequest = httpdriver.DeleteEntitlementHandlerRequest + OverrideEntitlementHandlerRequest = httpdriver.OverrideEntitlementHandlerRequest ) // responses @@ -30,6 +31,7 @@ type ( GetEntitlementHandlerResponse = httpdriver.GetEntitlementHandlerResponse GetEntitlementByIdHandlerResponse = httpdriver.GetEntitlementByIdHandlerResponse DeleteEntitlementHandlerResponse = httpdriver.DeleteEntitlementHandlerResponse + OverrideEntitlementHandlerResponse = httpdriver.OverrideEntitlementHandlerResponse ) // params @@ -45,4 +47,5 @@ type ( GetEntitlementHandlerParams = httpdriver.GetEntitlementHandlerParams GetEntitlementByIdHandlerParams = httpdriver.GetEntitlementByIdHandlerParams DeleteEntitlementHandlerParams = httpdriver.DeleteEntitlementHandlerParams + OverrideEntitlementHandlerParams = httpdriver.OverrideEntitlementHandlerParams )