diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3cde5ca..8cf306f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -69,6 +69,25 @@ jobs: - "1.5.*" - "1.6.*" - "1.7.*" + - "1.8.*" + - "1.9.*" + - "1.10.*" + is-pr: + - ${{ github.event_name == 'pull_request' }} + # Only run the latest version of Terraform on pull requests + exclude: + - terraform: "1.4.*" + is-pr: true + - terraform: "1.5.*" + is-pr: true + - terraform: "1.6.*" + is-pr: true + - terraform: "1.7.*" + is-pr: true + - terraform: "1.8.*" + is-pr: true + - terraform: "1.9.*" + is-pr: true steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 diff --git a/README.md b/README.md index 6110f9f..d82a7a7 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ Unofficial Terraform provider for OpenAI. If you find this provider useful, please consider supporting me through GitHub Sponsorship or Ko-Fi to help with its development. -[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/L3L71DQEL) +[![Github-sponsors](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#EA4AAA)](https://github.com/sponsors/jianyuan) +[![Ko-Fi](https://img.shields.io/badge/Ko--fi-F16061?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/L3L71DQEL) ## Requirements diff --git a/docs/index.md b/docs/index.md index 77b2556..0ff0423 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,12 +4,20 @@ page_title: "openai Provider" subcategory: "" description: |- The OpenAI provider enables you to configure resources and data sources for your OpenAI organization. It utilizes the official Administration API https://platform.openai.com/docs/api-reference/administration to interact with the OpenAI platform. + If you find this provider useful, please consider supporting me through GitHub Sponsorship or Ko-Fi to help with its development. + Github-sponsors https://github.com/sponsors/jianyuan + Ko-Fi https://ko-fi.com/L3L71DQEL --- # openai Provider The OpenAI provider enables you to configure resources and data sources for your OpenAI organization. It utilizes the official [Administration API](https://platform.openai.com/docs/api-reference/administration) to interact with the OpenAI platform. +If you find this provider useful, please consider supporting me through GitHub Sponsorship or Ko-Fi to help with its development. + +[![Github-sponsors](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#EA4AAA)](https://github.com/sponsors/jianyuan) +[![Ko-Fi](https://img.shields.io/badge/Ko--fi-F16061?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/L3L71DQEL) + ## Example Usage ```terraform diff --git a/internal/apiclient/api-patches.yaml b/internal/apiclient/api-patches.yaml index d6d89cc..ccba712 100644 --- a/internal/apiclient/api-patches.yaml +++ b/internal/apiclient/api-patches.yaml @@ -2,36 +2,76 @@ #@overlay/match by=overlay.all --- +#@ def rename_property(m, old, new): +#@ m = dict(m) +#@ if old in m: +#@ m[new] = m.pop(old) +#@ end +#@ return m +#@ end + +#@ def fix_property(p): +#@ p = dict(p) +#@ if "type" in p and p["type"] == "integer": +#@ p["format"] = "int64" +#@ end +#@ return p +#@ end + +#@ def fix_properties(m): +#@ m = dict(m) +#@ return {k: fix_property(dict(v)) for k, v in m.items()} +#@ end + paths: /organization/admin_api_keys: post: requestBody: content: application/json: - #@overlay/replace schema: - type: object required: - - name - type properties: - name: - type: string + #@overlay/match missing_ok=True type: type: string /organization/invites: post: - #@overlay/replace via=lambda left, right: {"201": left["200"]} + #@overlay/replace via=lambda left, right: rename_property(left, "200", "201") responses: /organization/projects: post: - #@overlay/replace via=lambda left, right: {"201": left["200"]} + #@overlay/replace via=lambda left, right: rename_property(left, "200", "201") responses: /organization/projects/{project_id}/users: post: - #@overlay/replace via=lambda left, right: {"201": left["200"]} + #@overlay/replace via=lambda left, right: rename_property(left, "200", "201") responses: /organization/projects/{project_id}/service_accounts: post: - #@overlay/replace via=lambda left, right: {"201": left["200"]} + #@overlay/replace via=lambda left, right: rename_property(left, "200", "201") responses: +components: + schemas: + Invite: + #@overlay/replace via=lambda left, right: fix_properties(dict(left)) + properties: + Project: + #@overlay/replace via=lambda left, right: fix_properties(dict(left)) + properties: + ProjectRateLimitUpdateRequest: + #@overlay/replace via=lambda left, right: fix_properties(dict(left)) + properties: + ProjectRateLimit: + #@overlay/replace via=lambda left, right: fix_properties(dict(left)) + properties: + ProjectServiceAccount: + #@overlay/replace via=lambda left, right: fix_properties(dict(left)) + properties: + ProjectServiceAccountCreateResponse: + #@overlay/replace via=lambda left, right: fix_properties(dict(left)) + properties: + User: + #@overlay/replace via=lambda left, right: fix_properties(dict(left)) + properties: diff --git a/internal/apiclient/api.yaml b/internal/apiclient/api.yaml index 8422171..26c8b06 100644 --- a/internal/apiclient/api.yaml +++ b/internal/apiclient/api.yaml @@ -3859,6 +3859,7 @@ paths: properties: name: type: string + example: New Admin Key type: type: string responses: @@ -5103,6 +5104,12 @@ paths: schema: $ref: '#/components/schemas/ProjectServiceAccountCreateRequest' responses: + "400": + description: Error response when project is archived. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' "201": description: Project service account created successfully. content: @@ -5320,6 +5327,12 @@ paths: schema: $ref: '#/components/schemas/ProjectUserCreateRequest' responses: + "400": + description: Error response for various conditions. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' "201": description: User added to project successfully. content: @@ -16142,12 +16155,15 @@ components: invited_at: type: integer description: The Unix timestamp (in seconds) of when the invite was sent. + format: int64 expires_at: type: integer description: The Unix timestamp (in seconds) of when the invite expires. + format: int64 accepted_at: type: integer description: The Unix timestamp (in seconds) of when the invite was accepted. + format: int64 projects: type: array description: The projects that were granted membership upon acceptance of the invite. @@ -17570,10 +17586,12 @@ components: created_at: type: integer description: The Unix timestamp (in seconds) of when the project was created. + format: int64 archived_at: type: integer nullable: true description: The Unix timestamp (in seconds) of when the project was archived or `null`. + format: int64 status: type: string enum: @@ -17746,21 +17764,27 @@ components: max_requests_per_1_minute: type: integer description: The maximum requests per minute. + format: int64 max_tokens_per_1_minute: type: integer description: The maximum tokens per minute. + format: int64 max_images_per_1_minute: type: integer description: The maximum images per minute. Only present for relevant models. + format: int64 max_audio_megabytes_per_1_minute: type: integer description: The maximum audio megabytes per minute. Only present for relevant models. + format: int64 max_requests_per_1_day: type: integer description: The maximum requests per day. Only present for relevant models. + format: int64 batch_1_day_max_input_tokens: type: integer description: The maximum batch input tokens per day. Only present for relevant models. + format: int64 required: - object - id @@ -17807,21 +17831,27 @@ components: max_requests_per_1_minute: type: integer description: The maximum requests per minute. + format: int64 max_tokens_per_1_minute: type: integer description: The maximum tokens per minute. + format: int64 max_images_per_1_minute: type: integer description: The maximum images per minute. Only relevant for certain models. + format: int64 max_audio_megabytes_per_1_minute: type: integer description: The maximum audio megabytes per minute. Only relevant for certain models. + format: int64 max_requests_per_1_day: type: integer description: The maximum requests per day. Only relevant for certain models. + format: int64 batch_1_day_max_input_tokens: type: integer description: The maximum batch input tokens per day. Only relevant for certain models. + format: int64 ProjectServiceAccount: type: object description: Represents an individual service account in a project. @@ -17846,6 +17876,7 @@ components: created_at: type: integer description: The Unix timestamp (in seconds) of when the service account was created + format: int64 required: - object - id @@ -17910,6 +17941,7 @@ components: description: Service accounts can only have one role of type `member` created_at: type: integer + format: int64 api_key: $ref: '#/components/schemas/ProjectServiceAccountApiKey' required: @@ -22313,6 +22345,7 @@ components: added_at: type: integer description: The Unix timestamp (in seconds) of when the user was added. + format: int64 required: - object - id diff --git a/internal/apiclient/apiclient.gen.go b/internal/apiclient/apiclient.gen.go index ac4a2d2..32d8625 100644 --- a/internal/apiclient/apiclient.gen.go +++ b/internal/apiclient/apiclient.gen.go @@ -5147,19 +5147,19 @@ type ImagesResponse struct { // Invite Represents an individual `invite` to the organization. type Invite struct { // AcceptedAt The Unix timestamp (in seconds) of when the invite was accepted. - AcceptedAt *int `json:"accepted_at,omitempty"` + AcceptedAt *int64 `json:"accepted_at,omitempty"` // Email The email address of the individual to whom the invite was sent Email string `json:"email"` // ExpiresAt The Unix timestamp (in seconds) of when the invite expires. - ExpiresAt int `json:"expires_at"` + ExpiresAt int64 `json:"expires_at"` // Id The identifier, which can be referenced in API endpoints Id string `json:"id"` // InvitedAt The Unix timestamp (in seconds) of when the invite was sent. - InvitedAt int `json:"invited_at"` + InvitedAt int64 `json:"invited_at"` // Object The object type, which is always `organization.invite` Object InviteObject `json:"object"` @@ -5768,10 +5768,10 @@ type PredictionContentType string // Project Represents an individual project. type Project struct { // ArchivedAt The Unix timestamp (in seconds) of when the project was archived or `null`. - ArchivedAt *int `json:"archived_at"` + ArchivedAt *int64 `json:"archived_at"` // CreatedAt The Unix timestamp (in seconds) of when the project was created. - CreatedAt int `json:"created_at"` + CreatedAt int64 `json:"created_at"` // Id The identifier, which can be referenced in API endpoints Id string `json:"id"` @@ -5869,25 +5869,25 @@ type ProjectListResponseObject string // ProjectRateLimit Represents a project rate limit config. type ProjectRateLimit struct { // Batch1DayMaxInputTokens The maximum batch input tokens per day. Only present for relevant models. - Batch1DayMaxInputTokens *int `json:"batch_1_day_max_input_tokens,omitempty"` + Batch1DayMaxInputTokens *int64 `json:"batch_1_day_max_input_tokens,omitempty"` // Id The identifier, which can be referenced in API endpoints. Id string `json:"id"` // MaxAudioMegabytesPer1Minute The maximum audio megabytes per minute. Only present for relevant models. - MaxAudioMegabytesPer1Minute *int `json:"max_audio_megabytes_per_1_minute,omitempty"` + MaxAudioMegabytesPer1Minute *int64 `json:"max_audio_megabytes_per_1_minute,omitempty"` // MaxImagesPer1Minute The maximum images per minute. Only present for relevant models. - MaxImagesPer1Minute *int `json:"max_images_per_1_minute,omitempty"` + MaxImagesPer1Minute *int64 `json:"max_images_per_1_minute,omitempty"` // MaxRequestsPer1Day The maximum requests per day. Only present for relevant models. - MaxRequestsPer1Day *int `json:"max_requests_per_1_day,omitempty"` + MaxRequestsPer1Day *int64 `json:"max_requests_per_1_day,omitempty"` // MaxRequestsPer1Minute The maximum requests per minute. - MaxRequestsPer1Minute int `json:"max_requests_per_1_minute"` + MaxRequestsPer1Minute int64 `json:"max_requests_per_1_minute"` // MaxTokensPer1Minute The maximum tokens per minute. - MaxTokensPer1Minute int `json:"max_tokens_per_1_minute"` + MaxTokensPer1Minute int64 `json:"max_tokens_per_1_minute"` // Model The model this rate limit applies to. Model string `json:"model"` @@ -5914,28 +5914,28 @@ type ProjectRateLimitListResponseObject string // ProjectRateLimitUpdateRequest defines model for ProjectRateLimitUpdateRequest. type ProjectRateLimitUpdateRequest struct { // Batch1DayMaxInputTokens The maximum batch input tokens per day. Only relevant for certain models. - Batch1DayMaxInputTokens *int `json:"batch_1_day_max_input_tokens,omitempty"` + Batch1DayMaxInputTokens *int64 `json:"batch_1_day_max_input_tokens,omitempty"` // MaxAudioMegabytesPer1Minute The maximum audio megabytes per minute. Only relevant for certain models. - MaxAudioMegabytesPer1Minute *int `json:"max_audio_megabytes_per_1_minute,omitempty"` + MaxAudioMegabytesPer1Minute *int64 `json:"max_audio_megabytes_per_1_minute,omitempty"` // MaxImagesPer1Minute The maximum images per minute. Only relevant for certain models. - MaxImagesPer1Minute *int `json:"max_images_per_1_minute,omitempty"` + MaxImagesPer1Minute *int64 `json:"max_images_per_1_minute,omitempty"` // MaxRequestsPer1Day The maximum requests per day. Only relevant for certain models. - MaxRequestsPer1Day *int `json:"max_requests_per_1_day,omitempty"` + MaxRequestsPer1Day *int64 `json:"max_requests_per_1_day,omitempty"` // MaxRequestsPer1Minute The maximum requests per minute. - MaxRequestsPer1Minute *int `json:"max_requests_per_1_minute,omitempty"` + MaxRequestsPer1Minute *int64 `json:"max_requests_per_1_minute,omitempty"` // MaxTokensPer1Minute The maximum tokens per minute. - MaxTokensPer1Minute *int `json:"max_tokens_per_1_minute,omitempty"` + MaxTokensPer1Minute *int64 `json:"max_tokens_per_1_minute,omitempty"` } // ProjectServiceAccount Represents an individual service account in a project. type ProjectServiceAccount struct { // CreatedAt The Unix timestamp (in seconds) of when the service account was created - CreatedAt int `json:"created_at"` + CreatedAt int64 `json:"created_at"` // Id The identifier, which can be referenced in API endpoints Id string `json:"id"` @@ -5979,7 +5979,7 @@ type ProjectServiceAccountCreateRequest struct { // ProjectServiceAccountCreateResponse defines model for ProjectServiceAccountCreateResponse. type ProjectServiceAccountCreateResponse struct { ApiKey ProjectServiceAccountApiKey `json:"api_key"` - CreatedAt int `json:"created_at"` + CreatedAt int64 `json:"created_at"` Id string `json:"id"` Name string `json:"name"` Object ProjectServiceAccountCreateResponseObject `json:"object"` @@ -7245,7 +7245,7 @@ type UsageVectorStoresResultObject string // User Represents an individual `user` within an organization. type User struct { // AddedAt The Unix timestamp (in seconds) of when the user was added. - AddedAt int `json:"added_at"` + AddedAt int64 `json:"added_at"` // Email The email address of the user Email string `json:"email"` @@ -24460,6 +24460,7 @@ type CreateProjectServiceAccountResp struct { Body []byte HTTPResponse *http.Response JSON201 *ProjectServiceAccountCreateResponse + JSON400 *ErrorResponse } // Status returns HTTPResponse.Status @@ -24549,6 +24550,7 @@ type CreateProjectUserResp struct { Body []byte HTTPResponse *http.Response JSON201 *ProjectUser + JSON400 *ErrorResponse } // Status returns HTTPResponse.Status @@ -28404,6 +28406,13 @@ func ParseCreateProjectServiceAccountResp(rsp *http.Response) (*CreateProjectSer } response.JSON201 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + } return response, nil @@ -28515,6 +28524,13 @@ func ParseCreateProjectUserResp(rsp *http.Response) (*CreateProjectUserResp, err } response.JSON201 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + } return response, nil diff --git a/internal/provider/data_source_invite.go b/internal/provider/data_source_invite.go index fd0624d..f31c34d 100644 --- a/internal/provider/data_source_invite.go +++ b/internal/provider/data_source_invite.go @@ -64,7 +64,6 @@ func (d *InviteDataSource) Read(ctx context.Context, req datasource.ReadRequest, var data InviteModel resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -76,20 +75,13 @@ func (d *InviteDataSource) Read(ctx context.Context, req datasource.ReadRequest, if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if httpResp.JSON200 == nil { - resp.Diagnostics.AddError("Client Error", "Unable to read, got empty response") - return - } - - if err := data.Fill(*httpResp.JSON200); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON200)...) + if resp.Diagnostics.HasError() { return } diff --git a/internal/provider/data_source_invites.go b/internal/provider/data_source_invites.go index 59c2644..e473b75 100644 --- a/internal/provider/data_source_invites.go +++ b/internal/provider/data_source_invites.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/jianyuan/go-utils/ptr" "github.com/jianyuan/terraform-provider-openai/internal/apiclient" ) @@ -15,14 +16,15 @@ type InvitesDataSourceModel struct { Invites []InviteModel `tfsdk:"invites"` } -func (m *InvitesDataSourceModel) Fill(invites []apiclient.Invite) error { +func (m *InvitesDataSourceModel) Fill(ctx context.Context, invites []apiclient.Invite) (diags diag.Diagnostics) { m.Invites = make([]InviteModel, len(invites)) for i, invite := range invites { - if err := m.Invites[i].Fill(invite); err != nil { - return err + diags.Append(m.Invites[i].Fill(ctx, invite)...) + if diags.HasError() { + return } } - return nil + return } var _ datasource.DataSource = &InvitesDataSource{} @@ -88,7 +90,6 @@ func (d *InvitesDataSource) Read(ctx context.Context, req datasource.ReadRequest var data InvitesDataSourceModel resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -106,9 +107,7 @@ func (d *InvitesDataSource) Read(ctx context.Context, req datasource.ReadRequest if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } @@ -122,8 +121,8 @@ func (d *InvitesDataSource) Read(ctx context.Context, req datasource.ReadRequest params.After = httpResp.JSON200.LastId } - if err := data.Fill(invites); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, invites)...) + if resp.Diagnostics.HasError() { return } diff --git a/internal/provider/data_source_project.go b/internal/provider/data_source_project.go index 7608e49..d5f517f 100644 --- a/internal/provider/data_source_project.go +++ b/internal/provider/data_source_project.go @@ -56,7 +56,6 @@ func (d *ProjectDataSource) Read(ctx context.Context, req datasource.ReadRequest var data ProjectModel resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -68,15 +67,13 @@ func (d *ProjectDataSource) Read(ctx context.Context, req datasource.ReadRequest if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("API Error", fmt.Sprintf("Unable to read, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if err := data.Fill(*httpResp.JSON200); err != nil { - resp.Diagnostics.AddError("API Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON200)...) + if resp.Diagnostics.HasError() { return } diff --git a/internal/provider/data_source_project_rate_limits.go b/internal/provider/data_source_project_rate_limits.go index 1571017..9137687 100644 --- a/internal/provider/data_source_project_rate_limits.go +++ b/internal/provider/data_source_project_rate_limits.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/jianyuan/go-utils/ptr" "github.com/jianyuan/terraform-provider-openai/internal/apiclient" @@ -23,32 +24,16 @@ type ProjectRateLimitModel struct { Batch1DayMaxInputTokens types.Int64 `tfsdk:"batch_1_day_max_input_tokens"` } -func (m *ProjectRateLimitModel) Fill(rl apiclient.ProjectRateLimit) error { +func (m *ProjectRateLimitModel) Fill(ctx context.Context, rl apiclient.ProjectRateLimit) (diags diag.Diagnostics) { m.Id = types.StringValue(rl.Id) m.Model = types.StringValue(rl.Model) - m.MaxRequestsPer1Minute = types.Int64Value(int64(rl.MaxRequestsPer1Minute)) - m.MaxTokensPer1Minute = types.Int64Value(int64(rl.MaxTokensPer1Minute)) - if rl.MaxImagesPer1Minute == nil { - m.MaxImagesPer1Minute = types.Int64Null() - } else { - m.MaxImagesPer1Minute = types.Int64Value(int64(*rl.MaxImagesPer1Minute)) - } - if rl.MaxAudioMegabytesPer1Minute == nil { - m.MaxAudioMegabytesPer1Minute = types.Int64Null() - } else { - m.MaxAudioMegabytesPer1Minute = types.Int64Value(int64(*rl.MaxAudioMegabytesPer1Minute)) - } - if rl.MaxRequestsPer1Day == nil { - m.MaxRequestsPer1Day = types.Int64Null() - } else { - m.MaxRequestsPer1Day = types.Int64Value(int64(*rl.MaxRequestsPer1Day)) - } - if rl.Batch1DayMaxInputTokens == nil { - m.Batch1DayMaxInputTokens = types.Int64Null() - } else { - m.Batch1DayMaxInputTokens = types.Int64Value(int64(*rl.Batch1DayMaxInputTokens)) - } - return nil + m.MaxRequestsPer1Minute = types.Int64Value(rl.MaxRequestsPer1Minute) + m.MaxTokensPer1Minute = types.Int64Value(rl.MaxTokensPer1Minute) + m.MaxImagesPer1Minute = types.Int64PointerValue(rl.MaxImagesPer1Minute) + m.MaxAudioMegabytesPer1Minute = types.Int64PointerValue(rl.MaxAudioMegabytesPer1Minute) + m.MaxRequestsPer1Day = types.Int64PointerValue(rl.MaxRequestsPer1Day) + m.Batch1DayMaxInputTokens = types.Int64PointerValue(rl.Batch1DayMaxInputTokens) + return } type ProjectRateLimitsDataSourceModel struct { @@ -56,14 +41,15 @@ type ProjectRateLimitsDataSourceModel struct { RateLimits []ProjectRateLimitModel `tfsdk:"rate_limits"` } -func (m *ProjectRateLimitsDataSourceModel) Fill(rateLimits []apiclient.ProjectRateLimit) error { +func (m *ProjectRateLimitsDataSourceModel) Fill(ctx context.Context, rateLimits []apiclient.ProjectRateLimit) (diags diag.Diagnostics) { m.RateLimits = make([]ProjectRateLimitModel, len(rateLimits)) for i, rl := range rateLimits { - if err := m.RateLimits[i].Fill(rl); err != nil { - return err + diags.Append(m.RateLimits[i].Fill(ctx, rl)...) + if diags.HasError() { + return } } - return nil + return } var _ datasource.DataSource = &ProjectRateLimitsDataSource{} @@ -137,7 +123,6 @@ func (d *ProjectRateLimitsDataSource) Read(ctx context.Context, req datasource.R var data ProjectRateLimitsDataSourceModel resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -156,9 +141,7 @@ func (d *ProjectRateLimitsDataSource) Read(ctx context.Context, req datasource.R if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } @@ -172,8 +155,8 @@ func (d *ProjectRateLimitsDataSource) Read(ctx context.Context, req datasource.R params.After = &httpResp.JSON200.LastId } - if err := data.Fill(rateLimits); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, rateLimits)...) + if resp.Diagnostics.HasError() { return } diff --git a/internal/provider/data_source_projects.go b/internal/provider/data_source_projects.go index b1ca3be..99e4502 100644 --- a/internal/provider/data_source_projects.go +++ b/internal/provider/data_source_projects.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/jianyuan/go-utils/ptr" "github.com/jianyuan/terraform-provider-openai/internal/apiclient" @@ -27,14 +28,15 @@ type ProjectsDataSourceModel struct { Projects []ProjectModel `tfsdk:"projects"` } -func (m *ProjectsDataSourceModel) Fill(projects []apiclient.Project) error { +func (m *ProjectsDataSourceModel) Fill(ctx context.Context, projects []apiclient.Project) (diags diag.Diagnostics) { m.Projects = make([]ProjectModel, len(projects)) for i, project := range projects { - if err := m.Projects[i].Fill(project); err != nil { - return err + diags.Append(m.Projects[i].Fill(ctx, project)...) + if diags.HasError() { + return } } - return nil + return } func (d *ProjectsDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { @@ -86,7 +88,6 @@ func (d *ProjectsDataSource) Read(ctx context.Context, req datasource.ReadReques var data ProjectsDataSourceModel resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -105,9 +106,7 @@ func (d *ProjectsDataSource) Read(ctx context.Context, req datasource.ReadReques if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("API Error", fmt.Sprintf("Unable to read, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } @@ -121,8 +120,8 @@ func (d *ProjectsDataSource) Read(ctx context.Context, req datasource.ReadReques params.After = &httpResp.JSON200.LastId } - if err := data.Fill(projects); err != nil { - resp.Diagnostics.AddError("API Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, projects)...) + if resp.Diagnostics.HasError() { return } diff --git a/internal/provider/data_source_user.go b/internal/provider/data_source_user.go index 6677de0..6bf00fc 100644 --- a/internal/provider/data_source_user.go +++ b/internal/provider/data_source_user.go @@ -56,7 +56,6 @@ func (d *UserDataSource) Read(ctx context.Context, req datasource.ReadRequest, r var data UserModel resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -68,20 +67,13 @@ func (d *UserDataSource) Read(ctx context.Context, req datasource.ReadRequest, r if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if httpResp.JSON200 == nil { - resp.Diagnostics.AddError("Client Error", "Unable to read, got empty response") - return - } - - if err := data.Fill(*httpResp.JSON200); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON200)...) + if resp.Diagnostics.HasError() { return } diff --git a/internal/provider/data_source_users.go b/internal/provider/data_source_users.go index 3f90c70..2c53d4b 100644 --- a/internal/provider/data_source_users.go +++ b/internal/provider/data_source_users.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/jianyuan/go-utils/ptr" "github.com/jianyuan/terraform-provider-openai/internal/apiclient" ) @@ -15,14 +16,15 @@ type UsersDataSourceModel struct { Users []UserModel `tfsdk:"users"` } -func (m *UsersDataSourceModel) Fill(users []apiclient.User) error { +func (m *UsersDataSourceModel) Fill(ctx context.Context, users []apiclient.User) (diags diag.Diagnostics) { m.Users = make([]UserModel, len(users)) for i, u := range users { - if err := m.Users[i].Fill(u); err != nil { - return err + diags.Append(m.Users[i].Fill(ctx, u)...) + if diags.HasError() { + return } } - return nil + return } var _ datasource.DataSource = &UsersDataSource{} @@ -80,7 +82,6 @@ func (d *UsersDataSource) Read(ctx context.Context, req datasource.ReadRequest, var data UsersDataSourceModel resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -98,9 +99,7 @@ func (d *UsersDataSource) Read(ctx context.Context, req datasource.ReadRequest, if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } @@ -114,8 +113,8 @@ func (d *UsersDataSource) Read(ctx context.Context, req datasource.ReadRequest, params.After = &httpResp.JSON200.LastId } - if err := data.Fill(users); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, users)...) + if resp.Diagnostics.HasError() { return } diff --git a/internal/provider/model_invite.go b/internal/provider/model_invite.go index 780baeb..91d1bc5 100644 --- a/internal/provider/model_invite.go +++ b/internal/provider/model_invite.go @@ -1,6 +1,9 @@ package provider import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/jianyuan/terraform-provider-openai/internal/apiclient" ) @@ -15,17 +18,13 @@ type InviteModel struct { AcceptedAt types.Int64 `tfsdk:"accepted_at"` } -func (m *InviteModel) Fill(i apiclient.Invite) error { +func (m *InviteModel) Fill(ctx context.Context, i apiclient.Invite) (diags diag.Diagnostics) { m.Id = types.StringValue(i.Id) m.Email = types.StringValue(i.Email) m.Role = types.StringValue(string(i.Role)) m.Status = types.StringValue(string(i.Status)) - m.InvitedAt = types.Int64Value(int64(i.InvitedAt)) - m.ExpiresAt = types.Int64Value(int64(i.ExpiresAt)) - if i.AcceptedAt == nil { - m.AcceptedAt = types.Int64Null() - } else { - m.AcceptedAt = types.Int64Value(int64(*i.AcceptedAt)) - } - return nil + m.InvitedAt = types.Int64Value(i.InvitedAt) + m.ExpiresAt = types.Int64Value(i.ExpiresAt) + m.AcceptedAt = types.Int64PointerValue(i.AcceptedAt) + return } diff --git a/internal/provider/model_project.go b/internal/provider/model_project.go index 1d21e4b..980f585 100644 --- a/internal/provider/model_project.go +++ b/internal/provider/model_project.go @@ -1,6 +1,9 @@ package provider import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/jianyuan/terraform-provider-openai/internal/apiclient" ) @@ -13,16 +16,11 @@ type ProjectModel struct { ArchivedAt types.Int64 `tfsdk:"archived_at"` } -func (m *ProjectModel) Fill(p apiclient.Project) error { +func (m *ProjectModel) Fill(ctx context.Context, p apiclient.Project) (diags diag.Diagnostics) { m.Id = types.StringValue(p.Id) m.Name = types.StringValue(p.Name) m.Status = types.StringValue(string(p.Status)) - m.CreatedAt = types.Int64Value(int64(p.CreatedAt)) - if p.ArchivedAt == nil { - m.ArchivedAt = types.Int64Null() - } else { - m.ArchivedAt = types.Int64Value(int64(*p.ArchivedAt)) - - } - return nil + m.CreatedAt = types.Int64Value(p.CreatedAt) + m.ArchivedAt = types.Int64PointerValue(p.ArchivedAt) + return } diff --git a/internal/provider/model_user.go b/internal/provider/model_user.go index ffbc7ad..63468c3 100644 --- a/internal/provider/model_user.go +++ b/internal/provider/model_user.go @@ -1,6 +1,9 @@ package provider import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/jianyuan/terraform-provider-openai/internal/apiclient" ) @@ -13,11 +16,11 @@ type UserModel struct { AddedAt types.Int64 `tfsdk:"added_at"` } -func (m *UserModel) Fill(u apiclient.User) error { +func (m *UserModel) Fill(ctx context.Context, u apiclient.User) (diags diag.Diagnostics) { m.Id = types.StringValue(u.Id) m.Email = types.StringValue(u.Email) m.Name = types.StringValue(u.Name) m.Role = types.StringValue(string(u.Role)) - m.AddedAt = types.Int64Value(int64(u.AddedAt)) - return nil + m.AddedAt = types.Int64Value(u.AddedAt) + return } diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 5512d0b..76109cf 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -41,7 +41,7 @@ func (p *OpenAIProvider) Metadata(ctx context.Context, req provider.MetadataRequ func (p *OpenAIProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ - MarkdownDescription: "The OpenAI provider enables you to configure resources and data sources for your OpenAI organization. It utilizes the official [Administration API](https://platform.openai.com/docs/api-reference/administration) to interact with the OpenAI platform.", + MarkdownDescription: "The OpenAI provider enables you to configure resources and data sources for your OpenAI organization. It utilizes the official [Administration API](https://platform.openai.com/docs/api-reference/administration) to interact with the OpenAI platform.\n\nIf you find this provider useful, please consider supporting me through GitHub Sponsorship or Ko-Fi to help with its development.\n\n[![Github-sponsors](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#EA4AAA)](https://github.com/sponsors/jianyuan)\n[![Ko-Fi](https://img.shields.io/badge/Ko--fi-F16061?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/L3L71DQEL)", Attributes: map[string]schema.Attribute{ "base_url": schema.StringAttribute{ MarkdownDescription: "Base URL for the OpenAI API. Defaults to `https://api.openai.com`.", diff --git a/internal/provider/resource_admin_api_key.go b/internal/provider/resource_admin_api_key.go index 5b9aff3..a295d52 100644 --- a/internal/provider/resource_admin_api_key.go +++ b/internal/provider/resource_admin_api_key.go @@ -111,7 +111,7 @@ func (r *AdminApiKeyResource) Create(ctx context.Context, req resource.CreateReq resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got error: %s", err)) return } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got status code %d", httpResp.StatusCode())) + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } diff --git a/internal/provider/resource_invite.go b/internal/provider/resource_invite.go index 30687a5..15a1f35 100644 --- a/internal/provider/resource_invite.go +++ b/internal/provider/resource_invite.go @@ -83,7 +83,6 @@ func (r *InviteResource) Create(ctx context.Context, req resource.CreateRequest, var data InviteModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -111,8 +110,8 @@ func (r *InviteResource) Create(ctx context.Context, req resource.CreateRequest, return } - if err := data.Fill(*httpResp.JSON201); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON201)...) + if resp.Diagnostics.HasError() { return } @@ -123,7 +122,6 @@ func (r *InviteResource) Read(ctx context.Context, req resource.ReadRequest, res var data InviteModel resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -136,20 +134,13 @@ func (r *InviteResource) Read(ctx context.Context, req resource.ReadRequest, res if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if httpResp.JSON200 == nil { - resp.Diagnostics.AddError("Client Error", "Unable to read, got empty response body") - return - } - - if err := data.Fill(*httpResp.JSON200); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON200)...) + if resp.Diagnostics.HasError() { return } @@ -164,7 +155,6 @@ func (r *InviteResource) Delete(ctx context.Context, req resource.DeleteRequest, var data InviteModel resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -177,9 +167,7 @@ func (r *InviteResource) Delete(ctx context.Context, req resource.DeleteRequest, if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete, got status code %d: %s", httpResp.StatusCode(), httpResp.Body)) return } diff --git a/internal/provider/resource_project.go b/internal/provider/resource_project.go index a9fca61..b405412 100644 --- a/internal/provider/resource_project.go +++ b/internal/provider/resource_project.go @@ -64,7 +64,6 @@ func (r *ProjectResource) Create(ctx context.Context, req resource.CreateRequest var data ProjectModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -79,15 +78,13 @@ func (r *ProjectResource) Create(ctx context.Context, req resource.CreateRequest if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusCreated { + } else if httpResp.StatusCode() != http.StatusCreated || httpResp.JSON201 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if err := data.Fill(*httpResp.JSON201); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON201)...) + if resp.Diagnostics.HasError() { return } @@ -98,7 +95,6 @@ func (r *ProjectResource) Read(ctx context.Context, req resource.ReadRequest, re var data ProjectModel resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -111,15 +107,13 @@ func (r *ProjectResource) Read(ctx context.Context, req resource.ReadRequest, re if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if err := data.Fill(*httpResp.JSON200); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON200)...) + if resp.Diagnostics.HasError() { return } @@ -130,7 +124,6 @@ func (r *ProjectResource) Update(ctx context.Context, req resource.UpdateRequest var data ProjectModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -146,15 +139,13 @@ func (r *ProjectResource) Update(ctx context.Context, req resource.UpdateRequest if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if err := data.Fill(*httpResp.JSON200); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON200)...) + if resp.Diagnostics.HasError() { return } @@ -165,7 +156,6 @@ func (r *ProjectResource) Delete(ctx context.Context, req resource.DeleteRequest var data ProjectModel resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -178,9 +168,7 @@ func (r *ProjectResource) Delete(ctx context.Context, req resource.DeleteRequest if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } diff --git a/internal/provider/resource_project_rate_limit.go b/internal/provider/resource_project_rate_limit.go index 45ec6e1..6548a14 100644 --- a/internal/provider/resource_project_rate_limit.go +++ b/internal/provider/resource_project_rate_limit.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" @@ -25,39 +26,15 @@ type ProjectRateLimitResourceModel struct { Batch1DayMaxInputTokens types.Int64 `tfsdk:"batch_1_day_max_input_tokens"` } -func (m *ProjectRateLimitResourceModel) Fill(rl apiclient.ProjectRateLimit) error { +func (m *ProjectRateLimitResourceModel) Fill(ctx context.Context, rl apiclient.ProjectRateLimit) (diags diag.Diagnostics) { m.Model = types.StringValue(rl.Model) - m.MaxRequestsPer1Minute = types.Int64Value(int64(rl.MaxRequestsPer1Minute)) - m.MaxTokensPer1Minute = types.Int64Value(int64(rl.MaxTokensPer1Minute)) - if !m.MaxImagesPer1Minute.IsNull() { - if rl.MaxImagesPer1Minute == nil { - m.MaxImagesPer1Minute = types.Int64Null() - } else { - m.MaxImagesPer1Minute = types.Int64Value(int64(*rl.MaxImagesPer1Minute)) - } - } - if !m.MaxAudioMegabytesPer1Minute.IsNull() { - if rl.MaxAudioMegabytesPer1Minute == nil { - m.MaxAudioMegabytesPer1Minute = types.Int64Null() - } else { - m.MaxAudioMegabytesPer1Minute = types.Int64Value(int64(*rl.MaxAudioMegabytesPer1Minute)) - } - } - if !m.MaxRequestsPer1Day.IsNull() { - if rl.MaxRequestsPer1Day == nil { - m.MaxRequestsPer1Day = types.Int64Null() - } else { - m.MaxRequestsPer1Day = types.Int64Value(int64(*rl.MaxRequestsPer1Day)) - } - } - if !m.Batch1DayMaxInputTokens.IsNull() { - if rl.Batch1DayMaxInputTokens == nil { - m.Batch1DayMaxInputTokens = types.Int64Null() - } else { - m.Batch1DayMaxInputTokens = types.Int64Value(int64(*rl.Batch1DayMaxInputTokens)) - } - } - return nil + m.MaxRequestsPer1Minute = types.Int64Value(rl.MaxRequestsPer1Minute) + m.MaxTokensPer1Minute = types.Int64Value(rl.MaxTokensPer1Minute) + m.MaxImagesPer1Minute = types.Int64PointerValue(rl.MaxImagesPer1Minute) + m.MaxAudioMegabytesPer1Minute = types.Int64PointerValue(rl.MaxAudioMegabytesPer1Minute) + m.MaxRequestsPer1Day = types.Int64PointerValue(rl.MaxRequestsPer1Day) + m.Batch1DayMaxInputTokens = types.Int64PointerValue(rl.Batch1DayMaxInputTokens) + return } var _ resource.Resource = &ProjectRateLimitResource{} @@ -96,26 +73,32 @@ func (r *ProjectRateLimitResource) Schema(ctx context.Context, req resource.Sche "max_requests_per_1_minute": schema.Int64Attribute{ MarkdownDescription: "The maximum requests per minute.", Optional: true, + Computed: true, }, "max_tokens_per_1_minute": schema.Int64Attribute{ MarkdownDescription: "The maximum tokens per minute.", Optional: true, + Computed: true, }, "max_images_per_1_minute": schema.Int64Attribute{ MarkdownDescription: "The maximum images per minute. Only present for relevant models.", Optional: true, + Computed: true, }, "max_audio_megabytes_per_1_minute": schema.Int64Attribute{ MarkdownDescription: "The maximum audio megabytes per minute. Only present for relevant models.", Optional: true, + Computed: true, }, "max_requests_per_1_day": schema.Int64Attribute{ MarkdownDescription: "The maximum requests per day. Only present for relevant models.", Optional: true, + Computed: true, }, "batch_1_day_max_input_tokens": schema.Int64Attribute{ MarkdownDescription: "The maximum batch input tokens per day. Only present for relevant models.", Optional: true, + Computed: true, }, }, } @@ -125,29 +108,28 @@ func (r *ProjectRateLimitResource) Create(ctx context.Context, req resource.Crea var data ProjectRateLimitResourceModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } body := apiclient.ProjectRateLimitUpdateRequest{} - if !data.MaxRequestsPer1Minute.IsNull() { - body.MaxRequestsPer1Minute = ptr.Ptr(int(data.MaxRequestsPer1Minute.ValueInt64())) + if !data.MaxRequestsPer1Minute.IsUnknown() { + body.MaxRequestsPer1Minute = data.MaxRequestsPer1Minute.ValueInt64Pointer() } - if !data.MaxTokensPer1Minute.IsNull() { - body.MaxTokensPer1Minute = ptr.Ptr(int(data.MaxTokensPer1Minute.ValueInt64())) + if !data.MaxTokensPer1Minute.IsUnknown() { + body.MaxTokensPer1Minute = data.MaxTokensPer1Minute.ValueInt64Pointer() } - if !data.MaxImagesPer1Minute.IsNull() { - body.MaxImagesPer1Minute = ptr.Ptr(int(data.MaxImagesPer1Minute.ValueInt64())) + if !data.MaxImagesPer1Minute.IsUnknown() { + body.MaxImagesPer1Minute = data.MaxImagesPer1Minute.ValueInt64Pointer() } - if !data.MaxAudioMegabytesPer1Minute.IsNull() { - body.MaxAudioMegabytesPer1Minute = ptr.Ptr(int(data.MaxAudioMegabytesPer1Minute.ValueInt64())) + if !data.MaxAudioMegabytesPer1Minute.IsUnknown() { + body.MaxAudioMegabytesPer1Minute = data.MaxAudioMegabytesPer1Minute.ValueInt64Pointer() } - if !data.MaxRequestsPer1Day.IsNull() { - body.MaxRequestsPer1Day = ptr.Ptr(int(data.MaxRequestsPer1Day.ValueInt64())) + if !data.MaxRequestsPer1Day.IsUnknown() { + body.MaxRequestsPer1Day = data.MaxRequestsPer1Day.ValueInt64Pointer() } - if !data.Batch1DayMaxInputTokens.IsNull() { - body.Batch1DayMaxInputTokens = ptr.Ptr(int(data.Batch1DayMaxInputTokens.ValueInt64())) + if !data.Batch1DayMaxInputTokens.IsUnknown() { + body.Batch1DayMaxInputTokens = data.Batch1DayMaxInputTokens.ValueInt64Pointer() } httpResp, err := r.client.UpdateProjectRateLimitsWithResponse( @@ -160,20 +142,13 @@ func (r *ProjectRateLimitResource) Create(ctx context.Context, req resource.Crea if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if httpResp.JSON200 == nil { - resp.Diagnostics.AddError("Client Error", "Unable to create, got empty response") - return - } - - if err := data.Fill(*httpResp.JSON200); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON200)...) + if resp.Diagnostics.HasError() { return } @@ -184,7 +159,6 @@ func (r *ProjectRateLimitResource) Read(ctx context.Context, req resource.ReadRe var data ProjectRateLimitResourceModel resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -204,25 +178,18 @@ out: if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if httpResp.JSON200 == nil { - resp.Diagnostics.AddError("Client Error", "Unable to read, got empty response") - return - } - for _, rl := range httpResp.JSON200.Data { if rl.Model != data.Model.ValueString() { continue } - if err := data.Fill(rl); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, rl)...) + if resp.Diagnostics.HasError() { return } @@ -237,29 +204,28 @@ func (r *ProjectRateLimitResource) Update(ctx context.Context, req resource.Upda var data ProjectRateLimitResourceModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } body := apiclient.ProjectRateLimitUpdateRequest{} - if !data.MaxRequestsPer1Minute.IsNull() { - body.MaxRequestsPer1Minute = ptr.Ptr(int(data.MaxRequestsPer1Minute.ValueInt64())) + if !data.MaxRequestsPer1Minute.IsUnknown() { + body.MaxRequestsPer1Minute = data.MaxRequestsPer1Minute.ValueInt64Pointer() } - if !data.MaxTokensPer1Minute.IsNull() { - body.MaxTokensPer1Minute = ptr.Ptr(int(data.MaxTokensPer1Minute.ValueInt64())) + if !data.MaxTokensPer1Minute.IsUnknown() { + body.MaxTokensPer1Minute = data.MaxTokensPer1Minute.ValueInt64Pointer() } - if !data.MaxImagesPer1Minute.IsNull() { - body.MaxImagesPer1Minute = ptr.Ptr(int(data.MaxImagesPer1Minute.ValueInt64())) + if !data.MaxImagesPer1Minute.IsUnknown() { + body.MaxImagesPer1Minute = data.MaxImagesPer1Minute.ValueInt64Pointer() } - if !data.MaxAudioMegabytesPer1Minute.IsNull() { - body.MaxAudioMegabytesPer1Minute = ptr.Ptr(int(data.MaxAudioMegabytesPer1Minute.ValueInt64())) + if !data.MaxAudioMegabytesPer1Minute.IsUnknown() { + body.MaxAudioMegabytesPer1Minute = data.MaxAudioMegabytesPer1Minute.ValueInt64Pointer() } - if !data.MaxRequestsPer1Day.IsNull() { - body.MaxRequestsPer1Day = ptr.Ptr(int(data.MaxRequestsPer1Day.ValueInt64())) + if !data.MaxRequestsPer1Day.IsUnknown() { + body.MaxRequestsPer1Day = data.MaxRequestsPer1Day.ValueInt64Pointer() } - if !data.Batch1DayMaxInputTokens.IsNull() { - body.Batch1DayMaxInputTokens = ptr.Ptr(int(data.Batch1DayMaxInputTokens.ValueInt64())) + if !data.Batch1DayMaxInputTokens.IsUnknown() { + body.Batch1DayMaxInputTokens = data.Batch1DayMaxInputTokens.ValueInt64Pointer() } httpResp, err := r.client.UpdateProjectRateLimitsWithResponse( @@ -272,20 +238,13 @@ func (r *ProjectRateLimitResource) Update(ctx context.Context, req resource.Upda if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if httpResp.JSON200 == nil { - resp.Diagnostics.AddError("Client Error", "Unable to update, got empty response") - return - } - - if err := data.Fill(*httpResp.JSON200); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON200)...) + if resp.Diagnostics.HasError() { return } diff --git a/internal/provider/resource_project_rate_limit_test.go b/internal/provider/resource_project_rate_limit_test.go index 6cfb14d..332369e 100644 --- a/internal/provider/resource_project_rate_limit_test.go +++ b/internal/provider/resource_project_rate_limit_test.go @@ -27,6 +27,10 @@ func TestAccProjectRateLimitResource(t *testing.T) { statecheck.ExpectKnownValue(rn, tfjsonpath.New("model"), knownvalue.StringExact("text-embedding-3-small")), statecheck.ExpectKnownValue(rn, tfjsonpath.New("max_requests_per_1_minute"), knownvalue.Int64Exact(3)), statecheck.ExpectKnownValue(rn, tfjsonpath.New("max_tokens_per_1_minute"), knownvalue.Int64Exact(3)), + statecheck.ExpectKnownValue(rn, tfjsonpath.New("max_images_per_1_minute"), knownvalue.Null()), + statecheck.ExpectKnownValue(rn, tfjsonpath.New("max_audio_megabytes_per_1_minute"), knownvalue.Null()), + statecheck.ExpectKnownValue(rn, tfjsonpath.New("max_requests_per_1_day"), knownvalue.NotNull()), + statecheck.ExpectKnownValue(rn, tfjsonpath.New("batch_1_day_max_input_tokens"), knownvalue.Null()), }, }, { @@ -36,6 +40,10 @@ func TestAccProjectRateLimitResource(t *testing.T) { statecheck.ExpectKnownValue(rn, tfjsonpath.New("model"), knownvalue.StringExact("text-embedding-3-small")), statecheck.ExpectKnownValue(rn, tfjsonpath.New("max_requests_per_1_minute"), knownvalue.Int64Exact(2)), statecheck.ExpectKnownValue(rn, tfjsonpath.New("max_tokens_per_1_minute"), knownvalue.Int64Exact(2)), + statecheck.ExpectKnownValue(rn, tfjsonpath.New("max_images_per_1_minute"), knownvalue.Null()), + statecheck.ExpectKnownValue(rn, tfjsonpath.New("max_audio_megabytes_per_1_minute"), knownvalue.Null()), + statecheck.ExpectKnownValue(rn, tfjsonpath.New("max_requests_per_1_day"), knownvalue.NotNull()), + statecheck.ExpectKnownValue(rn, tfjsonpath.New("batch_1_day_max_input_tokens"), knownvalue.Null()), }, }, }, diff --git a/internal/provider/resource_project_service_account.go b/internal/provider/resource_project_service_account.go index bd02387..bcb0bdf 100644 --- a/internal/provider/resource_project_service_account.go +++ b/internal/provider/resource_project_service_account.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" @@ -24,22 +25,22 @@ type ProjectServiceAccountResourceModel struct { ApiKey types.String `tfsdk:"api_key"` } -func (m *ProjectServiceAccountResourceModel) Fill(sa apiclient.ProjectServiceAccount) error { +func (m *ProjectServiceAccountResourceModel) Fill(ctx context.Context, sa apiclient.ProjectServiceAccount) (diags diag.Diagnostics) { m.Id = types.StringValue(sa.Id) m.Name = types.StringValue(sa.Name) m.Role = types.StringValue(string(sa.Role)) - m.CreatedAt = types.Int64Value(int64(sa.CreatedAt)) - return nil + m.CreatedAt = types.Int64Value(sa.CreatedAt) + return } -func (m *ProjectServiceAccountResourceModel) FillFromCreate(sa apiclient.ProjectServiceAccountCreateResponse) error { +func (m *ProjectServiceAccountResourceModel) FillFromCreate(ctx context.Context, sa apiclient.ProjectServiceAccountCreateResponse) (diags diag.Diagnostics) { m.Id = types.StringValue(sa.Id) m.Name = types.StringValue(sa.Name) m.Role = types.StringValue(string(sa.Role)) - m.CreatedAt = types.Int64Value(int64(sa.CreatedAt)) + m.CreatedAt = types.Int64Value(sa.CreatedAt) m.ApiKeyId = types.StringValue(sa.ApiKey.Id) m.ApiKey = types.StringValue(sa.ApiKey.Value) - return nil + return } var _ resource.Resource = &ProjectServiceAccountResource{} @@ -116,7 +117,6 @@ func (r *ProjectServiceAccountResource) Create(ctx context.Context, req resource var data ProjectServiceAccountResourceModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -132,15 +132,13 @@ func (r *ProjectServiceAccountResource) Create(ctx context.Context, req resource if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusCreated { + } else if httpResp.StatusCode() != http.StatusCreated || httpResp.JSON201 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if err := data.FillFromCreate(*httpResp.JSON201); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.FillFromCreate(ctx, *httpResp.JSON201)...) + if resp.Diagnostics.HasError() { return } @@ -151,7 +149,6 @@ func (r *ProjectServiceAccountResource) Read(ctx context.Context, req resource.R var data ProjectServiceAccountResourceModel resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -165,15 +162,13 @@ func (r *ProjectServiceAccountResource) Read(ctx context.Context, req resource.R if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got status code %d", httpResp.StatusCode())) + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if err := data.Fill(*httpResp.JSON200); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON200)...) + if resp.Diagnostics.HasError() { return } @@ -188,7 +183,6 @@ func (r *ProjectServiceAccountResource) Delete(ctx context.Context, req resource var data ProjectServiceAccountResourceModel resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -202,9 +196,7 @@ func (r *ProjectServiceAccountResource) Delete(ctx context.Context, req resource if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } diff --git a/internal/provider/resource_project_user.go b/internal/provider/resource_project_user.go index 8347428..891fb61 100644 --- a/internal/provider/resource_project_user.go +++ b/internal/provider/resource_project_user.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -22,10 +23,10 @@ type ProjectUserResourceModel struct { Role types.String `tfsdk:"role"` } -func (m *ProjectUserResourceModel) Fill(u apiclient.ProjectUser) error { +func (m *ProjectUserResourceModel) Fill(ctx context.Context, u apiclient.ProjectUser) (diags diag.Diagnostics) { m.UserId = types.StringValue(u.Id) m.Role = types.StringValue(string(u.Role)) - return nil + return } var _ resource.Resource = &ProjectUserResource{} @@ -74,7 +75,6 @@ func (r *ProjectUserResource) Create(ctx context.Context, req resource.CreateReq var data ProjectUserResourceModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -91,15 +91,13 @@ func (r *ProjectUserResource) Create(ctx context.Context, req resource.CreateReq if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusCreated { + } else if httpResp.StatusCode() != http.StatusCreated || httpResp.JSON201 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if err := data.Fill(*httpResp.JSON201); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON201)...) + if resp.Diagnostics.HasError() { return } @@ -110,7 +108,6 @@ func (r *ProjectUserResource) Read(ctx context.Context, req resource.ReadRequest var data ProjectUserResourceModel resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -124,20 +121,13 @@ func (r *ProjectUserResource) Read(ctx context.Context, req resource.ReadRequest if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if httpResp.JSON200 == nil { - resp.Diagnostics.AddError("Client Error", "Unable to read, got empty response") - return - } - - if err := data.Fill(*httpResp.JSON200); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON200)...) + if resp.Diagnostics.HasError() { return } @@ -148,7 +138,6 @@ func (r *ProjectUserResource) Update(ctx context.Context, req resource.UpdateReq var data ProjectUserResourceModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -165,20 +154,13 @@ func (r *ProjectUserResource) Update(ctx context.Context, req resource.UpdateReq if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if httpResp.JSON200 == nil { - resp.Diagnostics.AddError("Client Error", "Unable to read, got empty response") - return - } - - if err := data.Fill(*httpResp.JSON200); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON200)...) + if resp.Diagnostics.HasError() { return } @@ -189,7 +171,6 @@ func (r *ProjectUserResource) Delete(ctx context.Context, req resource.DeleteReq var data ProjectUserResourceModel resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -203,9 +184,7 @@ func (r *ProjectUserResource) Delete(ctx context.Context, req resource.DeleteReq if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } diff --git a/internal/provider/resource_user_role.go b/internal/provider/resource_user_role.go index e986110..aaee810 100644 --- a/internal/provider/resource_user_role.go +++ b/internal/provider/resource_user_role.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -18,10 +19,10 @@ type UserRoleResourceModel struct { Role types.String `tfsdk:"role"` } -func (m *UserRoleResourceModel) Fill(u apiclient.User) error { +func (m *UserRoleResourceModel) Fill(ctx context.Context, u apiclient.User) (diags diag.Diagnostics) { m.UserId = types.StringValue(u.Id) m.Role = types.StringValue(string(u.Role)) - return nil + return } func NewUserRoleResource() resource.Resource { @@ -60,7 +61,6 @@ func (r *UserRoleResource) Create(ctx context.Context, req resource.CreateReques var data UserRoleResourceModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -76,15 +76,13 @@ func (r *UserRoleResource) Create(ctx context.Context, req resource.CreateReques if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if err := data.Fill(*httpResp.JSON200); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON200)...) + if resp.Diagnostics.HasError() { return } @@ -95,7 +93,6 @@ func (r *UserRoleResource) Read(ctx context.Context, req resource.ReadRequest, r var data UserRoleResourceModel resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -108,15 +105,13 @@ func (r *UserRoleResource) Read(ctx context.Context, req resource.ReadRequest, r if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if err := data.Fill(*httpResp.JSON200); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON200)...) + if resp.Diagnostics.HasError() { return } @@ -127,7 +122,6 @@ func (r *UserRoleResource) Update(ctx context.Context, req resource.UpdateReques var data UserRoleResourceModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } @@ -143,15 +137,13 @@ func (r *UserRoleResource) Update(ctx context.Context, req resource.UpdateReques if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update, got error: %s", err)) return - } - - if httpResp.StatusCode() != http.StatusOK { + } else if httpResp.StatusCode() != http.StatusOK || httpResp.JSON200 == nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update, got status code %d: %s", httpResp.StatusCode(), string(httpResp.Body))) return } - if err := data.Fill(*httpResp.JSON200); err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to fill data: %s", err)) + resp.Diagnostics.Append(data.Fill(ctx, *httpResp.JSON200)...) + if resp.Diagnostics.HasError() { return }