diff --git a/docs/resources/monitor.md b/docs/resources/monitor.md
new file mode 100644
index 000000000..ebc6138e9
--- /dev/null
+++ b/docs/resources/monitor.md
@@ -0,0 +1,60 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "sentry_monitor Resource - terraform-provider-sentry"
+subcategory: ""
+description: |-
+ Return a client monitor bound to a project.
+---
+
+# sentry_monitor (Resource)
+
+Return a client monitor bound to a project.
+
+
+
+
+## Schema
+
+### Required
+
+- `config` (Attributes) (see [below for nested schema](#nestedatt--config))
+- `name` (String)
+- `organization` (String) The organization of this resource.
+- `project` (String) The project of this resource.
+- `slug` (String)
+
+### Optional
+
+- `is_muted` (Boolean)
+- `owner` (String)
+- `status` (String)
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+
+
+### Nested Schema for `config`
+
+Optional:
+
+- `alert_rule_id` (Number)
+- `checkin_margin` (Number)
+- `failure_issue_threshold` (Number)
+- `max_runtime` (Number)
+- `recovery_threshold` (Number)
+- `schedule_crontab` (String)
+- `schedule_interval` (Attributes) (see [below for nested schema](#nestedatt--config--schedule_interval))
+- `timezone` (String)
+
+
+### Nested Schema for `config.schedule_interval`
+
+Optional:
+
+- `day` (Number)
+- `hour` (Number)
+- `minute` (Number)
+- `month` (Number)
+- `week` (Number)
+- `year` (Number)
diff --git a/go.mod b/go.mod
index 9f33fd474..81ce7cda7 100644
--- a/go.mod
+++ b/go.mod
@@ -21,6 +21,7 @@ require (
github.com/orange-cloudavenue/terraform-plugin-framework-superschema v1.11.0
github.com/orange-cloudavenue/terraform-plugin-framework-supertypes v1.2.0
github.com/peterhellberg/link v1.2.0
+ github.com/stretchr/testify v1.10.0
golang.org/x/sync v0.14.0
)
@@ -38,6 +39,7 @@ require (
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/getkin/kin-openapi v0.127.0 // indirect
@@ -81,6 +83,7 @@ require (
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/posener/complete v1.2.3 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/speakeasy-api/openapi-overlay v0.9.0 // indirect
diff --git a/go.sum b/go.sum
index f74d9895a..04cf1df1a 100644
--- a/go.sum
+++ b/go.sum
@@ -142,6 +142,8 @@ github.com/hashicorp/terraform-plugin-framework v1.14.1 h1:jaT1yvU/kEKEsxnbrn4ZH
github.com/hashicorp/terraform-plugin-framework v1.14.1/go.mod h1:xNUKmvTs6ldbwTuId5euAtg37dTxuyj3LHS3uj7BHQ4=
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0 h1:I/N0g/eLZ1ZkLZXUQ0oRSXa8YG/EF0CEuQP1wXdrzKw=
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0/go.mod h1:t339KhmxnaF4SzdpxmqW8HnQBHVGYazwtfxU0qCs4eE=
+github.com/hashicorp/terraform-plugin-framework-timetypes v0.5.0 h1:v3DapR8gsp3EM8fKMh6up9cJUFQ2iRaFsYLP8UJnCco=
+github.com/hashicorp/terraform-plugin-framework-timetypes v0.5.0/go.mod h1:c3PnGE9pHBDfdEVG9t1S1C9ia5LW+gkFR0CygXlM8ak=
github.com/hashicorp/terraform-plugin-framework-validators v0.17.0 h1:0uYQcqqgW3BMyyve07WJgpKorXST3zkpzvrOnf3mpbg=
github.com/hashicorp/terraform-plugin-framework-validators v0.17.0/go.mod h1:VwdfgE/5Zxm43flraNa0VjcvKQOGVrcO4X8peIri0T0=
github.com/hashicorp/terraform-plugin-go v0.26.0 h1:cuIzCv4qwigug3OS7iKhpGAbZTiypAfFQmw8aE65O2M=
@@ -241,8 +243,6 @@ github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/orange-cloudavenue/terraform-plugin-framework-superschema v1.11.0 h1:uezhiY+wp4khq2i9z2jbr26e816BQYKFolCjsZlLIzU=
github.com/orange-cloudavenue/terraform-plugin-framework-superschema v1.11.0/go.mod h1:2R4+FiimXRb6we3IP4Jy4XwkmPG17isxcjUfYEWLJ44=
-github.com/orange-cloudavenue/terraform-plugin-framework-supertypes v1.1.1 h1:5r0bXWxmY/p8YtYsS0ZQNE+fJ5BhUVSVGtVbvENad1c=
-github.com/orange-cloudavenue/terraform-plugin-framework-supertypes v1.1.1/go.mod h1:iIp5TGuiJnDvcSCYq7mWYIn4HSmjntHv+IJGMK0MsZw=
github.com/orange-cloudavenue/terraform-plugin-framework-supertypes v1.2.0 h1:tvDLBfqkKQdlcPyb3dZsZMM52mpzVpys2a9ar/38ulE=
github.com/orange-cloudavenue/terraform-plugin-framework-supertypes v1.2.0/go.mod h1:5SJrMDAHcQopy+p9Oq3anoaqclPZgTs27b0WQ3J97E0=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
@@ -343,8 +343,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
-golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
diff --git a/internal/apiclient/api.yaml b/internal/apiclient/api.yaml
index 3ed50a882..dc40620cc 100644
--- a/internal/apiclient/api.yaml
+++ b/internal/apiclient/api.yaml
@@ -857,7 +857,91 @@ paths:
description: Forbidden
"404":
description: Not Found
-
+ /0/organizations/{organization_id_or_slug}/monitors/:
+ parameters:
+ - $ref: "#/components/parameters/organization_id_or_slug"
+ post:
+ operationId: createMonitor
+ description: Create a new monitor.
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/MonitorRequest"
+ required: true
+ responses:
+ "201":
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Monitor"
+ description: ""
+ "400":
+ description: Bad Request
+ "401":
+ description: Unauthorized
+ "403":
+ description: Forbidden
+ "404":
+ description: Not Found
+ /0/organizations/{organization_id_or_slug}/monitors/{monitor_id_or_slug}/:
+ parameters:
+ - $ref: "#/components/parameters/organization_id_or_slug"
+ - $ref: "#/components/parameters/monitor_id_or_slug"
+ get:
+ operationId: getOrganizationMonitor
+ description: Retrieves details for a monitor.
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Monitor"
+ description: ""
+ "400":
+ description: Bad Request
+ "401":
+ description: Unauthorized
+ "403":
+ description: Forbidden
+ "404":
+ description: Not Found
+ put:
+ operationId: updateOrganizationMonitor
+ description: Update a monitor.
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/MonitorRequest"
+ required: true
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Monitor"
+ description: ""
+ "400":
+ description: Bad Request
+ "401":
+ description: Unauthorized
+ "403":
+ description: Forbidden
+ "404":
+ description: Not Found
+ delete:
+ operationId: deleteOrganizationMonitor
+ description: Delete a monitor.
+ responses:
+ "202":
+ description: Accepted
+ "401":
+ description: Unauthorized
+ "403":
+ description: Forbidden
+ "404":
+ description: Not Found
security:
- bearerAuth: []
components:
@@ -872,6 +956,12 @@ components:
required: true
schema:
type: string
+ monitor_id_or_slug:
+ name: monitor_id_or_slug
+ in: path
+ required: true
+ schema:
+ type: string
project_id_or_slug:
name: project_id_or_slug
in: path
@@ -903,6 +993,211 @@ components:
schema:
type: string
schemas:
+ Monitor:
+ type: object
+ properties:
+ alertRule:
+ $ref: "#/components/schemas/MonitorAlertRule"
+ id:
+ type: string
+ name:
+ type: string
+ slug:
+ type: string
+ status:
+ type: string
+ isMuted:
+ type: boolean
+ config:
+ $ref: "#/components/schemas/MonitorConfig"
+ dateCreated:
+ type: string
+ format: date-time
+ project:
+ type: object
+ properties:
+ id:
+ type: string
+ slug:
+ type: string
+ required:
+ - id
+ - slug
+ # environments:
+ # type: object
+ # properties:
+ # name:
+ # type: string
+ # required:
+ # - name
+ owner:
+ type: object
+ properties:
+ id:
+ type: string
+ type:
+ enum:
+ - user
+ - team
+ type: string
+ required:
+ - id
+ - type
+ required:
+ - config
+ - dateCreated
+ # - environments
+ - id
+ - isMuted
+ - name
+ - owner
+ - project
+ - slug
+ - status
+ MonitorConfig:
+ type: object
+ properties:
+ schedule_type:
+ $ref: "#/components/schemas/MonitorConfigScheduleType"
+ schedule:
+ anyOf:
+ - $ref: "#/components/schemas/MonitorConfigScheduleString"
+ - $ref: "#/components/schemas/MonitorConfigScheduleInterval"
+ checkin_margin:
+ type: integer
+ format: int64
+ minimum: 1
+ nullable: true
+ description: >-
+ How long (in minutes) after the expected checkin
+ time will we wait until we consider the checkin to
+ have been missed.
+ max_runtime:
+ type: integer
+ format: int64
+ maximum: 40320
+ minimum: 1
+ nullable: true
+ description: >-
+ How long (in minutes) is the checkin allowed to run
+ for in CheckInStatus.IN_PROGRESS before it is
+ considered failed.
+ timezone:
+ type: string
+ failure_issue_threshold:
+ type: integer
+ format: int64
+ maximum: 720
+ minimum: 1
+ nullable: true
+ description: >-
+ How many consecutive missed or failed check-ins in a
+ row before creating a new issue.
+ recovery_threshold:
+ type: integer
+ format: int64
+ maximum: 720
+ minimum: 1
+ nullable: true
+ description: >-
+ How many successful check-ins in a row before
+ resolving an issue.
+ alert_rule_id:
+ type: integer
+ format: int64
+ nullable: true
+ required:
+ - alert_rule_id
+ - checkin_margin
+ - failure_issue_threshold
+ - max_runtime
+ - recovery_threshold
+ - schedule
+ - schedule_type
+ - timezone
+ MonitorConfigScheduleType:
+ type: string
+ enum:
+ - crontab
+ - interval
+ MonitorConfigScheduleString:
+ type: string
+ MonitorConfigScheduleInterval:
+ type: array
+ minItems: 2
+ maxItems: 2
+ MonitorAlertRule:
+ type: object
+ properties:
+ targets:
+ type: array
+ items:
+ $ref: "#/components/schemas/MonitorAlertRuleTarget"
+ environment:
+ type: string
+ required:
+ - environment
+ - targets
+ MonitorAlertRuleTarget:
+ type: object
+ properties:
+ targetIdentifier:
+ type: integer
+ x-go-type: json.Number
+ targetType:
+ type: string
+ required:
+ - targetIdentifier
+ - targetType
+ MonitorRequest:
+ type: object
+ properties:
+ project:
+ type: string
+ description: The project slug to associate the monitor to.
+ name:
+ type: string
+ description: >-
+ Name of the monitor. Used for notifications. If not set the
+ slug will be derived from your monitor name.
+ maxLength: 128
+ config:
+ $ref: "#/components/schemas/MonitorConfig"
+ slug:
+ type: string
+ description: >-
+ Uniquely identifies your monitor within your organization.
+ Changing this slug will require updates to any instrumented
+ check-in calls.
+ maxLength: 50
+ pattern: '^(?![0-9]+$)[a-z0-9_\-]+$'
+ status:
+ enum:
+ - active
+ - disabled
+ type: string
+ default: active
+ description: >-
+ Status of the monitor. Disabled monitors will not accept
+ events and will not count towards the monitor quota.
+
+
+ * `active`
+
+ * `disabled`
+ owner:
+ type: string
+ nullable: true
+ description: >-
+ The ID of the team or user that owns the monitor. (eg.
+ user:51 or team:6)
+ is_muted:
+ type: boolean
+ description: Disable creation of monitor incidents
+ required:
+ - config
+ - name
+ - project
Organization:
type: object
required:
diff --git a/internal/apiclient/apiclient.gen.go b/internal/apiclient/apiclient.gen.go
index 59d9326ff..19aed1c14 100644
--- a/internal/apiclient/apiclient.gen.go
+++ b/internal/apiclient/apiclient.gen.go
@@ -22,6 +22,24 @@ const (
BearerAuthScopes = "bearerAuth.Scopes"
)
+// Defines values for MonitorOwnerType.
+const (
+ MonitorOwnerTypeTeam MonitorOwnerType = "team"
+ MonitorOwnerTypeUser MonitorOwnerType = "user"
+)
+
+// Defines values for MonitorConfigScheduleType.
+const (
+ Crontab MonitorConfigScheduleType = "crontab"
+ Interval MonitorConfigScheduleType = "interval"
+)
+
+// Defines values for MonitorRequestStatus.
+const (
+ MonitorRequestStatusActive MonitorRequestStatus = "active"
+ MonitorRequestStatusDisabled MonitorRequestStatus = "disabled"
+)
+
// Defines values for OrganizationIntegrationOpsgenieProviderKey.
const (
Opsgenie OrganizationIntegrationOpsgenieProviderKey = "opsgenie"
@@ -189,10 +207,111 @@ const (
// Defines values for ListProjectClientKeysParamsStatus.
const (
- Active ListProjectClientKeysParamsStatus = "active"
- Inactive ListProjectClientKeysParamsStatus = "inactive"
+ ListProjectClientKeysParamsStatusActive ListProjectClientKeysParamsStatus = "active"
+ ListProjectClientKeysParamsStatusInactive ListProjectClientKeysParamsStatus = "inactive"
)
+// Monitor defines model for Monitor.
+type Monitor struct {
+ AlertRule *MonitorAlertRule `json:"alertRule,omitempty"`
+ Config MonitorConfig `json:"config"`
+ DateCreated time.Time `json:"dateCreated"`
+ Id string `json:"id"`
+ IsMuted bool `json:"isMuted"`
+ Name string `json:"name"`
+ Owner struct {
+ Id string `json:"id"`
+ Type MonitorOwnerType `json:"type"`
+ } `json:"owner"`
+ Project struct {
+ Id string `json:"id"`
+ Slug string `json:"slug"`
+ } `json:"project"`
+ Slug string `json:"slug"`
+ Status string `json:"status"`
+}
+
+// MonitorOwnerType defines model for Monitor.Owner.Type.
+type MonitorOwnerType string
+
+// MonitorAlertRule defines model for MonitorAlertRule.
+type MonitorAlertRule struct {
+ Environment string `json:"environment"`
+ Targets []MonitorAlertRuleTarget `json:"targets"`
+}
+
+// MonitorAlertRuleTarget defines model for MonitorAlertRuleTarget.
+type MonitorAlertRuleTarget struct {
+ TargetIdentifier json.Number `json:"targetIdentifier"`
+ TargetType string `json:"targetType"`
+}
+
+// MonitorConfig defines model for MonitorConfig.
+type MonitorConfig struct {
+ AlertRuleId *int64 `json:"alert_rule_id"`
+
+ // CheckinMargin How long (in minutes) after the expected checkin time will we wait until we consider the checkin to have been missed.
+ CheckinMargin *int64 `json:"checkin_margin"`
+
+ // FailureIssueThreshold How many consecutive missed or failed check-ins in a row before creating a new issue.
+ FailureIssueThreshold *int64 `json:"failure_issue_threshold"`
+
+ // MaxRuntime How long (in minutes) is the checkin allowed to run for in CheckInStatus.IN_PROGRESS before it is considered failed.
+ MaxRuntime *int64 `json:"max_runtime"`
+
+ // RecoveryThreshold How many successful check-ins in a row before resolving an issue.
+ RecoveryThreshold *int64 `json:"recovery_threshold"`
+ Schedule MonitorConfig_Schedule `json:"schedule"`
+ ScheduleType MonitorConfigScheduleType `json:"schedule_type"`
+ Timezone string `json:"timezone"`
+}
+
+// MonitorConfig_Schedule defines model for MonitorConfig.Schedule.
+type MonitorConfig_Schedule struct {
+ union json.RawMessage
+}
+
+// MonitorConfigScheduleInterval defines model for MonitorConfigScheduleInterval.
+type MonitorConfigScheduleInterval = []interface{}
+
+// MonitorConfigScheduleString defines model for MonitorConfigScheduleString.
+type MonitorConfigScheduleString = string
+
+// MonitorConfigScheduleType defines model for MonitorConfigScheduleType.
+type MonitorConfigScheduleType string
+
+// MonitorRequest defines model for MonitorRequest.
+type MonitorRequest struct {
+ Config MonitorConfig `json:"config"`
+
+ // IsMuted Disable creation of monitor incidents
+ IsMuted *bool `json:"is_muted,omitempty"`
+
+ // Name Name of the monitor. Used for notifications. If not set the slug will be derived from your monitor name.
+ Name string `json:"name"`
+
+ // Owner The ID of the team or user that owns the monitor. (eg. user:51 or team:6)
+ Owner *string `json:"owner"`
+
+ // Project The project slug to associate the monitor to.
+ Project string `json:"project"`
+
+ // Slug Uniquely identifies your monitor within your organization. Changing this slug will require updates to any instrumented check-in calls.
+ Slug *string `json:"slug,omitempty"`
+
+ // Status Status of the monitor. Disabled monitors will not accept events and will not count towards the monitor quota.
+ //
+ // * `active`
+ // * `disabled`
+ Status *MonitorRequestStatus `json:"status,omitempty"`
+}
+
+// MonitorRequestStatus Status of the monitor. Disabled monitors will not accept events and will not count towards the monitor quota.
+//
+// * `active`
+// * `disabled`
+type MonitorRequestStatus string
+
// Organization defines model for Organization.
type Organization struct {
Features *[]string `json:"features,omitempty"`
@@ -819,6 +938,9 @@ type IntegrationId = string
// MemberId defines model for member_id.
type MemberId = string
+// MonitorIdOrSlug defines model for monitor_id_or_slug.
+type MonitorIdOrSlug = string
+
// OrganizationIdOrSlug defines model for organization_id_or_slug.
type OrganizationIdOrSlug = string
@@ -983,6 +1105,12 @@ type CreateOrganizationMemberJSONRequestBody CreateOrganizationMemberJSONBody
// UpdateOrganizationMemberJSONRequestBody defines body for UpdateOrganizationMember for application/json ContentType.
type UpdateOrganizationMemberJSONRequestBody UpdateOrganizationMemberJSONBody
+// CreateMonitorJSONRequestBody defines body for CreateMonitor for application/json ContentType.
+type CreateMonitorJSONRequestBody = MonitorRequest
+
+// UpdateOrganizationMonitorJSONRequestBody defines body for UpdateOrganizationMonitor for application/json ContentType.
+type UpdateOrganizationMonitorJSONRequestBody = MonitorRequest
+
// DisableSpikeProtectionJSONRequestBody defines body for DisableSpikeProtection for application/json ContentType.
type DisableSpikeProtectionJSONRequestBody DisableSpikeProtectionJSONBody
@@ -1010,6 +1138,68 @@ type UpdateProjectRuleJSONRequestBody UpdateProjectRuleJSONBody
// CreateOrganizationTeamProjectJSONRequestBody defines body for CreateOrganizationTeamProject for application/json ContentType.
type CreateOrganizationTeamProjectJSONRequestBody CreateOrganizationTeamProjectJSONBody
+// AsMonitorConfigScheduleString returns the union data inside the MonitorConfig_Schedule as a MonitorConfigScheduleString
+func (t MonitorConfig_Schedule) AsMonitorConfigScheduleString() (MonitorConfigScheduleString, error) {
+ var body MonitorConfigScheduleString
+ err := json.Unmarshal(t.union, &body)
+ return body, err
+}
+
+// FromMonitorConfigScheduleString overwrites any union data inside the MonitorConfig_Schedule as the provided MonitorConfigScheduleString
+func (t *MonitorConfig_Schedule) FromMonitorConfigScheduleString(v MonitorConfigScheduleString) error {
+ b, err := json.Marshal(v)
+ t.union = b
+ return err
+}
+
+// MergeMonitorConfigScheduleString performs a merge with any union data inside the MonitorConfig_Schedule, using the provided MonitorConfigScheduleString
+func (t *MonitorConfig_Schedule) MergeMonitorConfigScheduleString(v MonitorConfigScheduleString) error {
+ b, err := json.Marshal(v)
+ if err != nil {
+ return err
+ }
+
+ merged, err := runtime.JSONMerge(t.union, b)
+ t.union = merged
+ return err
+}
+
+// AsMonitorConfigScheduleInterval returns the union data inside the MonitorConfig_Schedule as a MonitorConfigScheduleInterval
+func (t MonitorConfig_Schedule) AsMonitorConfigScheduleInterval() (MonitorConfigScheduleInterval, error) {
+ var body MonitorConfigScheduleInterval
+ err := json.Unmarshal(t.union, &body)
+ return body, err
+}
+
+// FromMonitorConfigScheduleInterval overwrites any union data inside the MonitorConfig_Schedule as the provided MonitorConfigScheduleInterval
+func (t *MonitorConfig_Schedule) FromMonitorConfigScheduleInterval(v MonitorConfigScheduleInterval) error {
+ b, err := json.Marshal(v)
+ t.union = b
+ return err
+}
+
+// MergeMonitorConfigScheduleInterval performs a merge with any union data inside the MonitorConfig_Schedule, using the provided MonitorConfigScheduleInterval
+func (t *MonitorConfig_Schedule) MergeMonitorConfigScheduleInterval(v MonitorConfigScheduleInterval) error {
+ b, err := json.Marshal(v)
+ if err != nil {
+ return err
+ }
+
+ merged, err := runtime.JSONMerge(t.union, b)
+ t.union = merged
+ return err
+}
+
+func (t MonitorConfig_Schedule) MarshalJSON() ([]byte, error) {
+ b, err := t.union.MarshalJSON()
+ return b, err
+}
+
+func (t *MonitorConfig_Schedule) UnmarshalJSON(b []byte) error {
+ err := t.union.UnmarshalJSON(b)
+ return err
+}
+
// AsOrganizationIntegrationOpsgenie returns the union data inside the OrganizationIntegration as a OrganizationIntegrationOpsgenie
func (t OrganizationIntegration) AsOrganizationIntegrationOpsgenie() (OrganizationIntegrationOpsgenie, error) {
var body OrganizationIntegrationOpsgenie
@@ -2499,6 +2689,22 @@ type ClientInterface interface {
UpdateOrganizationMember(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, memberId MemberId, body UpdateOrganizationMemberJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
+ // CreateMonitorWithBody request with any body
+ CreateMonitorWithBody(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+ CreateMonitor(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, body CreateMonitorJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+ // DeleteOrganizationMonitor request
+ DeleteOrganizationMonitor(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+ // GetOrganizationMonitor request
+ GetOrganizationMonitor(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+ // UpdateOrganizationMonitorWithBody request with any body
+ UpdateOrganizationMonitorWithBody(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+ UpdateOrganizationMonitor(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, body UpdateOrganizationMonitorJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
+
// ListOrganizationProjects request
ListOrganizationProjects(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, params *ListOrganizationProjectsParams, reqEditors ...RequestEditorFn) (*http.Response, error)
@@ -2734,6 +2940,78 @@ func (c *Client) UpdateOrganizationMember(ctx context.Context, organizationIdOrS
return c.Client.Do(req)
}
+func (c *Client) CreateMonitorWithBody(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
+ req, err := NewCreateMonitorRequestWithBody(c.Server, organizationIdOrSlug, 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) CreateMonitor(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, body CreateMonitorJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
+ req, err := NewCreateMonitorRequest(c.Server, organizationIdOrSlug, 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) DeleteOrganizationMonitor(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, reqEditors ...RequestEditorFn) (*http.Response, error) {
+ req, err := NewDeleteOrganizationMonitorRequest(c.Server, organizationIdOrSlug, monitorIdOrSlug)
+ 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) GetOrganizationMonitor(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, reqEditors ...RequestEditorFn) (*http.Response, error) {
+ req, err := NewGetOrganizationMonitorRequest(c.Server, organizationIdOrSlug, monitorIdOrSlug)
+ 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) UpdateOrganizationMonitorWithBody(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
+ req, err := NewUpdateOrganizationMonitorRequestWithBody(c.Server, organizationIdOrSlug, monitorIdOrSlug, 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) UpdateOrganizationMonitor(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, body UpdateOrganizationMonitorJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
+ req, err := NewUpdateOrganizationMonitorRequest(c.Server, organizationIdOrSlug, monitorIdOrSlug, 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) ListOrganizationProjects(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, params *ListOrganizationProjectsParams, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewListOrganizationProjectsRequest(c.Server, organizationIdOrSlug, params)
if err != nil {
@@ -3549,6 +3827,189 @@ func NewUpdateOrganizationMemberRequestWithBody(server string, organizationIdOrS
return req, nil
}
+// NewCreateMonitorRequest calls the generic CreateMonitor builder with application/json body
+func NewCreateMonitorRequest(server string, organizationIdOrSlug OrganizationIdOrSlug, body CreateMonitorJSONRequestBody) (*http.Request, error) {
+ var bodyReader io.Reader
+ buf, err := json.Marshal(body)
+ if err != nil {
+ return nil, err
+ }
+ bodyReader = bytes.NewReader(buf)
+ return NewCreateMonitorRequestWithBody(server, organizationIdOrSlug, "application/json", bodyReader)
+}
+
+// NewCreateMonitorRequestWithBody generates requests for CreateMonitor with any type of body
+func NewCreateMonitorRequestWithBody(server string, organizationIdOrSlug OrganizationIdOrSlug, contentType string, body io.Reader) (*http.Request, error) {
+ var err error
+
+ var pathParam0 string
+
+ pathParam0, err = runtime.StyleParamWithLocation("simple", false, "organization_id_or_slug", runtime.ParamLocationPath, organizationIdOrSlug)
+ if err != nil {
+ return nil, err
+ }
+
+ serverURL, err := url.Parse(server)
+ if err != nil {
+ return nil, err
+ }
+
+ operationPath := fmt.Sprintf("/0/organizations/%s/monitors/", pathParam0)
+ if operationPath[0] == '/' {
+ operationPath = "." + operationPath
+ }
+
+ queryURL, err := serverURL.Parse(operationPath)
+ if err != nil {
+ return nil, err
+ }
+
+ req, err := http.NewRequest("POST", queryURL.String(), body)
+ if err != nil {
+ return nil, err
+ }
+
+ req.Header.Add("Content-Type", contentType)
+
+ return req, nil
+}
+
+// NewDeleteOrganizationMonitorRequest generates requests for DeleteOrganizationMonitor
+func NewDeleteOrganizationMonitorRequest(server string, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug) (*http.Request, error) {
+ var err error
+
+ var pathParam0 string
+
+ pathParam0, err = runtime.StyleParamWithLocation("simple", false, "organization_id_or_slug", runtime.ParamLocationPath, organizationIdOrSlug)
+ if err != nil {
+ return nil, err
+ }
+
+ var pathParam1 string
+
+ pathParam1, err = runtime.StyleParamWithLocation("simple", false, "monitor_id_or_slug", runtime.ParamLocationPath, monitorIdOrSlug)
+ if err != nil {
+ return nil, err
+ }
+
+ serverURL, err := url.Parse(server)
+ if err != nil {
+ return nil, err
+ }
+
+ operationPath := fmt.Sprintf("/0/organizations/%s/monitors/%s/", pathParam0, pathParam1)
+ if operationPath[0] == '/' {
+ operationPath = "." + operationPath
+ }
+
+ queryURL, err := serverURL.Parse(operationPath)
+ if err != nil {
+ return nil, err
+ }
+
+ req, err := http.NewRequest("DELETE", queryURL.String(), nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return req, nil
+}
+
+// NewGetOrganizationMonitorRequest generates requests for GetOrganizationMonitor
+func NewGetOrganizationMonitorRequest(server string, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug) (*http.Request, error) {
+ var err error
+
+ var pathParam0 string
+
+ pathParam0, err = runtime.StyleParamWithLocation("simple", false, "organization_id_or_slug", runtime.ParamLocationPath, organizationIdOrSlug)
+ if err != nil {
+ return nil, err
+ }
+
+ var pathParam1 string
+
+ pathParam1, err = runtime.StyleParamWithLocation("simple", false, "monitor_id_or_slug", runtime.ParamLocationPath, monitorIdOrSlug)
+ if err != nil {
+ return nil, err
+ }
+
+ serverURL, err := url.Parse(server)
+ if err != nil {
+ return nil, err
+ }
+
+ operationPath := fmt.Sprintf("/0/organizations/%s/monitors/%s/", pathParam0, pathParam1)
+ if operationPath[0] == '/' {
+ operationPath = "." + operationPath
+ }
+
+ queryURL, err := serverURL.Parse(operationPath)
+ if err != nil {
+ return nil, err
+ }
+
+ req, err := http.NewRequest("GET", queryURL.String(), nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return req, nil
+}
+
+// NewUpdateOrganizationMonitorRequest calls the generic UpdateOrganizationMonitor builder with application/json body
+func NewUpdateOrganizationMonitorRequest(server string, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, body UpdateOrganizationMonitorJSONRequestBody) (*http.Request, error) {
+ var bodyReader io.Reader
+ buf, err := json.Marshal(body)
+ if err != nil {
+ return nil, err
+ }
+ bodyReader = bytes.NewReader(buf)
+ return NewUpdateOrganizationMonitorRequestWithBody(server, organizationIdOrSlug, monitorIdOrSlug, "application/json", bodyReader)
+}
+
+// NewUpdateOrganizationMonitorRequestWithBody generates requests for UpdateOrganizationMonitor with any type of body
+func NewUpdateOrganizationMonitorRequestWithBody(server string, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, contentType string, body io.Reader) (*http.Request, error) {
+ var err error
+
+ var pathParam0 string
+
+ pathParam0, err = runtime.StyleParamWithLocation("simple", false, "organization_id_or_slug", runtime.ParamLocationPath, organizationIdOrSlug)
+ if err != nil {
+ return nil, err
+ }
+
+ var pathParam1 string
+
+ pathParam1, err = runtime.StyleParamWithLocation("simple", false, "monitor_id_or_slug", runtime.ParamLocationPath, monitorIdOrSlug)
+ if err != nil {
+ return nil, err
+ }
+
+ serverURL, err := url.Parse(server)
+ if err != nil {
+ return nil, err
+ }
+
+ operationPath := fmt.Sprintf("/0/organizations/%s/monitors/%s/", 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
+}
+
// NewListOrganizationProjectsRequest generates requests for ListOrganizationProjects
func NewListOrganizationProjectsRequest(server string, organizationIdOrSlug OrganizationIdOrSlug, params *ListOrganizationProjectsParams) (*http.Request, error) {
var err error
@@ -4676,6 +5137,22 @@ type ClientWithResponsesInterface interface {
UpdateOrganizationMemberWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, memberId MemberId, body UpdateOrganizationMemberJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateOrganizationMemberResponse, error)
+ // CreateMonitorWithBodyWithResponse request with any body
+ CreateMonitorWithBodyWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateMonitorResponse, error)
+
+ CreateMonitorWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, body CreateMonitorJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateMonitorResponse, error)
+
+ // DeleteOrganizationMonitorWithResponse request
+ DeleteOrganizationMonitorWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, reqEditors ...RequestEditorFn) (*DeleteOrganizationMonitorResponse, error)
+
+ // GetOrganizationMonitorWithResponse request
+ GetOrganizationMonitorWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, reqEditors ...RequestEditorFn) (*GetOrganizationMonitorResponse, error)
+
+ // UpdateOrganizationMonitorWithBodyWithResponse request with any body
+ UpdateOrganizationMonitorWithBodyWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateOrganizationMonitorResponse, error)
+
+ UpdateOrganizationMonitorWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, body UpdateOrganizationMonitorJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateOrganizationMonitorResponse, error)
+
// ListOrganizationProjectsWithResponse request
ListOrganizationProjectsWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, params *ListOrganizationProjectsParams, reqEditors ...RequestEditorFn) (*ListOrganizationProjectsResponse, error)
@@ -4972,6 +5449,93 @@ func (r UpdateOrganizationMemberResponse) StatusCode() int {
return 0
}
+type CreateMonitorResponse struct {
+ Body []byte
+ HTTPResponse *http.Response
+ JSON201 *Monitor
+}
+
+// Status returns HTTPResponse.Status
+func (r CreateMonitorResponse) Status() string {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.Status
+ }
+ return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r CreateMonitorResponse) StatusCode() int {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.StatusCode
+ }
+ return 0
+}
+
+type DeleteOrganizationMonitorResponse struct {
+ Body []byte
+ HTTPResponse *http.Response
+}
+
+// Status returns HTTPResponse.Status
+func (r DeleteOrganizationMonitorResponse) Status() string {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.Status
+ }
+ return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r DeleteOrganizationMonitorResponse) StatusCode() int {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.StatusCode
+ }
+ return 0
+}
+
+type GetOrganizationMonitorResponse struct {
+ Body []byte
+ HTTPResponse *http.Response
+ JSON200 *Monitor
+}
+
+// Status returns HTTPResponse.Status
+func (r GetOrganizationMonitorResponse) Status() string {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.Status
+ }
+ return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r GetOrganizationMonitorResponse) StatusCode() int {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.StatusCode
+ }
+ return 0
+}
+
+type UpdateOrganizationMonitorResponse struct {
+ Body []byte
+ HTTPResponse *http.Response
+ JSON200 *Monitor
+}
+
+// Status returns HTTPResponse.Status
+func (r UpdateOrganizationMonitorResponse) Status() string {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.Status
+ }
+ return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r UpdateOrganizationMonitorResponse) StatusCode() int {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.StatusCode
+ }
+ return 0
+}
+
type ListOrganizationProjectsResponse struct {
Body []byte
HTTPResponse *http.Response
@@ -5524,6 +6088,58 @@ func (c *ClientWithResponses) UpdateOrganizationMemberWithResponse(ctx context.C
return ParseUpdateOrganizationMemberResponse(rsp)
}
+// CreateMonitorWithBodyWithResponse request with arbitrary body returning *CreateMonitorResponse
+func (c *ClientWithResponses) CreateMonitorWithBodyWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateMonitorResponse, error) {
+ rsp, err := c.CreateMonitorWithBody(ctx, organizationIdOrSlug, contentType, body, reqEditors...)
+ if err != nil {
+ return nil, err
+ }
+ return ParseCreateMonitorResponse(rsp)
+}
+
+func (c *ClientWithResponses) CreateMonitorWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, body CreateMonitorJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateMonitorResponse, error) {
+ rsp, err := c.CreateMonitor(ctx, organizationIdOrSlug, body, reqEditors...)
+ if err != nil {
+ return nil, err
+ }
+ return ParseCreateMonitorResponse(rsp)
+}
+
+// DeleteOrganizationMonitorWithResponse request returning *DeleteOrganizationMonitorResponse
+func (c *ClientWithResponses) DeleteOrganizationMonitorWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, reqEditors ...RequestEditorFn) (*DeleteOrganizationMonitorResponse, error) {
+ rsp, err := c.DeleteOrganizationMonitor(ctx, organizationIdOrSlug, monitorIdOrSlug, reqEditors...)
+ if err != nil {
+ return nil, err
+ }
+ return ParseDeleteOrganizationMonitorResponse(rsp)
+}
+
+// GetOrganizationMonitorWithResponse request returning *GetOrganizationMonitorResponse
+func (c *ClientWithResponses) GetOrganizationMonitorWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, reqEditors ...RequestEditorFn) (*GetOrganizationMonitorResponse, error) {
+ rsp, err := c.GetOrganizationMonitor(ctx, organizationIdOrSlug, monitorIdOrSlug, reqEditors...)
+ if err != nil {
+ return nil, err
+ }
+ return ParseGetOrganizationMonitorResponse(rsp)
+}
+
+// UpdateOrganizationMonitorWithBodyWithResponse request with arbitrary body returning *UpdateOrganizationMonitorResponse
+func (c *ClientWithResponses) UpdateOrganizationMonitorWithBodyWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateOrganizationMonitorResponse, error) {
+ rsp, err := c.UpdateOrganizationMonitorWithBody(ctx, organizationIdOrSlug, monitorIdOrSlug, contentType, body, reqEditors...)
+ if err != nil {
+ return nil, err
+ }
+ return ParseUpdateOrganizationMonitorResponse(rsp)
+}
+
+func (c *ClientWithResponses) UpdateOrganizationMonitorWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, monitorIdOrSlug MonitorIdOrSlug, body UpdateOrganizationMonitorJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateOrganizationMonitorResponse, error) {
+ rsp, err := c.UpdateOrganizationMonitor(ctx, organizationIdOrSlug, monitorIdOrSlug, body, reqEditors...)
+ if err != nil {
+ return nil, err
+ }
+ return ParseUpdateOrganizationMonitorResponse(rsp)
+}
+
// ListOrganizationProjectsWithResponse request returning *ListOrganizationProjectsResponse
func (c *ClientWithResponses) ListOrganizationProjectsWithResponse(ctx context.Context, organizationIdOrSlug OrganizationIdOrSlug, params *ListOrganizationProjectsParams, reqEditors ...RequestEditorFn) (*ListOrganizationProjectsResponse, error) {
rsp, err := c.ListOrganizationProjects(ctx, organizationIdOrSlug, params, reqEditors...)
@@ -6006,6 +6622,100 @@ func ParseUpdateOrganizationMemberResponse(rsp *http.Response) (*UpdateOrganizat
return response, nil
}
+// ParseCreateMonitorResponse parses an HTTP response from a CreateMonitorWithResponse call
+func ParseCreateMonitorResponse(rsp *http.Response) (*CreateMonitorResponse, error) {
+ bodyBytes, err := io.ReadAll(rsp.Body)
+ defer func() { _ = rsp.Body.Close() }()
+ if err != nil {
+ return nil, err
+ }
+
+ response := &CreateMonitorResponse{
+ Body: bodyBytes,
+ HTTPResponse: rsp,
+ }
+
+ switch {
+ case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201:
+ var dest Monitor
+ if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+ return nil, err
+ }
+ response.JSON201 = &dest
+
+ }
+
+ return response, nil
+}
+
+// ParseDeleteOrganizationMonitorResponse parses an HTTP response from a DeleteOrganizationMonitorWithResponse call
+func ParseDeleteOrganizationMonitorResponse(rsp *http.Response) (*DeleteOrganizationMonitorResponse, error) {
+ bodyBytes, err := io.ReadAll(rsp.Body)
+ defer func() { _ = rsp.Body.Close() }()
+ if err != nil {
+ return nil, err
+ }
+
+ response := &DeleteOrganizationMonitorResponse{
+ Body: bodyBytes,
+ HTTPResponse: rsp,
+ }
+
+ return response, nil
+}
+
+// ParseGetOrganizationMonitorResponse parses an HTTP response from a GetOrganizationMonitorWithResponse call
+func ParseGetOrganizationMonitorResponse(rsp *http.Response) (*GetOrganizationMonitorResponse, error) {
+ bodyBytes, err := io.ReadAll(rsp.Body)
+ defer func() { _ = rsp.Body.Close() }()
+ if err != nil {
+ return nil, err
+ }
+
+ response := &GetOrganizationMonitorResponse{
+ Body: bodyBytes,
+ HTTPResponse: rsp,
+ }
+
+ switch {
+ case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
+ var dest Monitor
+ if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+ return nil, err
+ }
+ response.JSON200 = &dest
+
+ }
+
+ return response, nil
+}
+
+// ParseUpdateOrganizationMonitorResponse parses an HTTP response from a UpdateOrganizationMonitorWithResponse call
+func ParseUpdateOrganizationMonitorResponse(rsp *http.Response) (*UpdateOrganizationMonitorResponse, error) {
+ bodyBytes, err := io.ReadAll(rsp.Body)
+ defer func() { _ = rsp.Body.Close() }()
+ if err != nil {
+ return nil, err
+ }
+
+ response := &UpdateOrganizationMonitorResponse{
+ Body: bodyBytes,
+ HTTPResponse: rsp,
+ }
+
+ switch {
+ case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
+ var dest Monitor
+ if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+ return nil, err
+ }
+ response.JSON200 = &dest
+
+ }
+
+ return response, nil
+}
+
// ParseListOrganizationProjectsResponse parses an HTTP response from a ListOrganizationProjectsWithResponse call
func ParseListOrganizationProjectsResponse(rsp *http.Response) (*ListOrganizationProjectsResponse, error) {
bodyBytes, err := io.ReadAll(rsp.Body)
diff --git a/internal/provider/model_monitor.go b/internal/provider/model_monitor.go
new file mode 100644
index 000000000..4934f1f64
--- /dev/null
+++ b/internal/provider/model_monitor.go
@@ -0,0 +1,103 @@
+package provider
+
+import (
+ "context"
+
+ "github.com/hashicorp/terraform-plugin-framework/diag"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+ "github.com/jianyuan/terraform-provider-sentry/internal/apiclient"
+ "github.com/jianyuan/terraform-provider-sentry/internal/tfutils"
+)
+
+type MonitorResourceModel struct {
+ Id types.String `tfsdk:"id"`
+ Organization types.String `tfsdk:"organization"`
+ Project types.String `tfsdk:"project"`
+ Name types.String `tfsdk:"name"`
+ Slug types.String `tfsdk:"slug"`
+ Owner types.String `tfsdk:"owner"`
+ Config types.Object `tfsdk:"config"`
+ Status types.String `tfsdk:"status"`
+ IsMuted types.Bool `tfsdk:"is_muted"`
+}
+
+func (m MonitorResourceModel) Attributes() map[string]schema.Attribute {
+ return map[string]schema.Attribute{
+ "id": ResourceIdAttribute(),
+ "organization": ResourceOrganizationAttribute(),
+ "project": ResourceProjectAttribute(),
+ "name": schema.StringAttribute{
+ Required: true,
+ },
+ "slug": schema.StringAttribute{
+ Required: true,
+ },
+ "owner": schema.StringAttribute{
+ Optional: true,
+ },
+ "config": MonitorConfigResourceModel{}.SchemaAttribute(true),
+ "status": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ },
+ // unfortunately is_muted is not respected on creation via sentry's public api (but is for subsequent updates)
+ "is_muted": schema.BoolAttribute{
+ Computed: true,
+ },
+ }
+}
+
+func (m MonitorResourceModel) ToMonitorRequest(ctx context.Context) (apiclient.MonitorRequest, diag.Diagnostics) {
+ diags := diag.Diagnostics{}
+
+ var monitorConfigResourceModel MonitorConfigResourceModel
+ diags.Append(m.Config.As(ctx, &monitorConfigResourceModel, basetypes.ObjectAsOptions{})...)
+
+ monitorConfig, monitorConfigDiags := monitorConfigResourceModel.ToMonitorRequest(ctx, path.Root("config"))
+ diags.Append(monitorConfigDiags...)
+
+ request := apiclient.MonitorRequest{
+ Name: m.Name.ValueString(),
+ Slug: m.Slug.ValueStringPointer(),
+ Project: m.Project.ValueString(),
+ Owner: m.Owner.ValueStringPointer(),
+ Config: monitorConfig,
+ Status: (*apiclient.MonitorRequestStatus)(m.Status.ValueStringPointer()),
+ IsMuted: m.IsMuted.ValueBoolPointer(),
+ }
+
+ return request, diags
+}
+
+func (m *MonitorResourceModel) Fill(ctx context.Context, organization string, monitor apiclient.Monitor) (diags diag.Diagnostics) {
+ path := path.Empty()
+
+ var config MonitorConfigResourceModel
+ diags.Append(config.Fill(ctx, path.AtName("config"), monitor.Config)...)
+
+ m.Organization = types.StringValue(organization)
+ m.Id = types.StringValue(monitor.Id)
+ m.Name = types.StringValue(monitor.Name)
+ m.Slug = types.StringValue(monitor.Slug)
+ m.Project = types.StringValue(monitor.Project.Slug)
+ m.Owner = types.StringPointerValue(formatMonitorOwner(monitor.Owner.Type, monitor.Owner.Id))
+ m.Config = tfutils.MergeDiagnostics(types.ObjectValueFrom(ctx, config.AttributeTypes(), config))(&diags)
+
+ m.Status = types.StringValue(monitor.Status)
+ m.IsMuted = types.BoolValue(monitor.IsMuted)
+
+ return
+}
+
+func formatMonitorOwner(ownerType apiclient.MonitorOwnerType, ownerId string) *string {
+ if ownerType == "" && ownerId == "" {
+ return nil
+ }
+
+ owner := string(ownerType) + ":" + ownerId
+
+ return &owner
+}
diff --git a/internal/provider/model_monitor_config.go b/internal/provider/model_monitor_config.go
new file mode 100644
index 000000000..f9549eaba
--- /dev/null
+++ b/internal/provider/model_monitor_config.go
@@ -0,0 +1,152 @@
+package provider
+
+import (
+ "context"
+
+ "github.com/hashicorp/terraform-plugin-framework-validators/objectvalidator"
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/diag"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+ "github.com/jianyuan/terraform-provider-sentry/internal/apiclient"
+ "github.com/jianyuan/terraform-provider-sentry/internal/tfutils"
+)
+
+type MonitorConfigResourceModel struct {
+ ScheduleCrontab types.String `tfsdk:"schedule_crontab"`
+ ScheduleInterval types.Object `tfsdk:"schedule_interval"`
+ CheckinMargin types.Int64 `tfsdk:"checkin_margin"`
+ MaxRuntime types.Int64 `tfsdk:"max_runtime"`
+ Timezone types.String `tfsdk:"timezone"`
+ FailureIssueThreshold types.Int64 `tfsdk:"failure_issue_threshold"`
+ RecoveryThreshold types.Int64 `tfsdk:"recovery_threshold"`
+ AlertRuleId types.Int64 `tfsdk:"alert_rule_id"`
+}
+
+func (m MonitorConfigResourceModel) SchemaAttribute(required bool) schema.Attribute {
+ return schema.SingleNestedAttribute{
+ Required: required,
+ Attributes: m.SchemaAttributes(),
+ }
+}
+
+func (m MonitorConfigResourceModel) SchemaAttributes() map[string]schema.Attribute {
+ return map[string]schema.Attribute{
+ "schedule_crontab": schema.StringAttribute{
+ Optional: true,
+ Validators: []validator.String{
+ stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("schedule_interval")),
+ },
+ },
+ "schedule_interval": schema.SingleNestedAttribute{
+ Optional: true,
+ Attributes: MonitorConfigScheduleIntervalResourceModel{}.SchemaAttributes(),
+ Validators: []validator.Object{
+ objectvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("schedule_crontab")),
+ },
+ },
+ "checkin_margin": schema.Int64Attribute{
+ Optional: true,
+ },
+ "max_runtime": schema.Int64Attribute{
+ Optional: true,
+ },
+ "timezone": schema.StringAttribute{
+ Optional: true,
+ },
+ "failure_issue_threshold": schema.Int64Attribute{
+ Optional: true,
+ },
+ "recovery_threshold": schema.Int64Attribute{
+ Optional: true,
+ },
+ "alert_rule_id": schema.Int64Attribute{
+ Optional: true,
+ },
+ }
+}
+
+func (m *MonitorConfigResourceModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "schedule_crontab": types.StringType,
+ "schedule_interval": types.ObjectType{AttrTypes: (&MonitorConfigScheduleIntervalResourceModel{}).AttributeTypes()},
+ "checkin_margin": types.Int64Type,
+ "max_runtime": types.Int64Type,
+ "timezone": types.StringType,
+ "failure_issue_threshold": types.Int64Type,
+ "recovery_threshold": types.Int64Type,
+ "alert_rule_id": types.Int64Type,
+ }
+}
+
+func (m *MonitorConfigResourceModel) ToMonitorRequest(ctx context.Context, path path.Path) (apiclient.MonitorConfig, diag.Diagnostics) {
+ diags := diag.Diagnostics{}
+
+ var scheduleType apiclient.MonitorConfigScheduleType
+ var configSchedule apiclient.MonitorConfig_Schedule
+
+ if !m.ScheduleCrontab.IsUnknown() && !m.ScheduleCrontab.IsNull() {
+ scheduleType = apiclient.Crontab
+
+ configSchedule.FromMonitorConfigScheduleString(m.ScheduleCrontab.ValueString())
+ } else if !m.ScheduleInterval.IsUnknown() && !m.ScheduleInterval.IsNull() {
+ scheduleType = apiclient.Interval
+
+ var scheduleIntervalModel MonitorConfigScheduleIntervalResourceModel
+ diags.Append(m.ScheduleInterval.As(ctx, &scheduleIntervalModel, basetypes.ObjectAsOptions{})...)
+
+ scheduleInterval, scheduleIntervalDiags := formatMonitorConfigScheduleInterval(scheduleIntervalModel)
+ diags.Append(scheduleIntervalDiags...)
+
+ configSchedule.FromMonitorConfigScheduleInterval(scheduleInterval)
+ }
+
+ return apiclient.MonitorConfig{
+ ScheduleType: scheduleType,
+ Schedule: configSchedule,
+ Timezone: m.Timezone.ValueString(),
+ CheckinMargin: m.CheckinMargin.ValueInt64Pointer(),
+ MaxRuntime: m.MaxRuntime.ValueInt64Pointer(),
+ FailureIssueThreshold: m.FailureIssueThreshold.ValueInt64Pointer(),
+ RecoveryThreshold: m.RecoveryThreshold.ValueInt64Pointer(),
+ AlertRuleId: m.AlertRuleId.ValueInt64Pointer(),
+ }, diags
+}
+
+func (m *MonitorConfigResourceModel) Fill(ctx context.Context, path path.Path, config apiclient.MonitorConfig) (diags diag.Diagnostics) {
+ switch config.ScheduleType {
+ case apiclient.Crontab:
+ schedule, scheduleErr := config.Schedule.AsMonitorConfigScheduleString()
+ if scheduleErr != nil {
+ diags.AddAttributeError(path.AtName("schedule"), "Invalid schedule", scheduleErr.Error())
+ break
+ }
+ m.ScheduleCrontab = types.StringValue(schedule)
+ m.ScheduleInterval = types.ObjectNull((&MonitorConfigScheduleIntervalResourceModel{}).AttributeTypes())
+ case apiclient.Interval:
+ schedule, scheduleErr := config.Schedule.AsMonitorConfigScheduleInterval()
+ if scheduleErr != nil {
+ diags.AddAttributeError(path.AtName("schedule"), "Invalid schedule", scheduleErr.Error())
+ break
+ }
+ parsedSchedule := tfutils.MergeDiagnostics(parseMonitorConfigScheduleInterval(schedule))(&diags)
+ m.ScheduleCrontab = types.StringNull()
+ m.ScheduleInterval = tfutils.MergeDiagnostics(types.ObjectValueFrom(ctx, (&MonitorConfigScheduleIntervalResourceModel{}).AttributeTypes(), parsedSchedule))(&diags)
+ default:
+ diags.AddAttributeError(path.AtName("schedule"), "Invalid schedule type", string(config.ScheduleType))
+ }
+
+ m.CheckinMargin = types.Int64PointerValue(config.CheckinMargin)
+ m.MaxRuntime = types.Int64PointerValue(config.MaxRuntime)
+ m.Timezone = types.StringValue(config.Timezone)
+ m.FailureIssueThreshold = types.Int64PointerValue(config.FailureIssueThreshold)
+ m.RecoveryThreshold = types.Int64PointerValue(config.RecoveryThreshold)
+
+ m.AlertRuleId = types.Int64PointerValue(config.AlertRuleId)
+
+ return
+}
diff --git a/internal/provider/model_monitor_config_schedule_interval.go b/internal/provider/model_monitor_config_schedule_interval.go
new file mode 100644
index 000000000..14697f4e0
--- /dev/null
+++ b/internal/provider/model_monitor_config_schedule_interval.go
@@ -0,0 +1,135 @@
+package provider
+
+import (
+ "github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/diag"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/jianyuan/terraform-provider-sentry/internal/apiclient"
+)
+
+type MonitorConfigScheduleIntervalResourceModel struct {
+ Year types.Int64 `tfsdk:"year"`
+ Month types.Int64 `tfsdk:"month"`
+ Week types.Int64 `tfsdk:"week"`
+ Day types.Int64 `tfsdk:"day"`
+ Hour types.Int64 `tfsdk:"hour"`
+ Minute types.Int64 `tfsdk:"minute"`
+}
+
+func (m MonitorConfigScheduleIntervalResourceModel) SchemaAttributes() map[string]schema.Attribute {
+ attributeNames := []string{"year", "month", "week", "day", "hour", "minute"}
+
+ attributes := make(map[string]schema.Attribute, len(attributeNames))
+
+ for _, name := range attributeNames {
+ var conflictingPaths []path.Expression
+
+ for _, conflictingName := range attributeNames {
+ if conflictingName != name {
+ conflictingPaths = append(conflictingPaths, path.MatchRelative().AtParent().AtName(conflictingName))
+ }
+ }
+
+ attributes[name] = schema.Int64Attribute{
+ Optional: true,
+ Validators: []validator.Int64{
+ int64validator.AtLeast(1),
+ int64validator.ConflictsWith(conflictingPaths...),
+ },
+ }
+ }
+
+ return attributes
+}
+
+func (m *MonitorConfigScheduleIntervalResourceModel) AttributeTypes() map[string]attr.Type {
+ return map[string]attr.Type{
+ "year": types.Int64Type,
+ "month": types.Int64Type,
+ "week": types.Int64Type,
+ "day": types.Int64Type,
+ "hour": types.Int64Type,
+ "minute": types.Int64Type,
+ }
+}
+
+func parseMonitorConfigScheduleInterval(m apiclient.MonitorConfigScheduleInterval) (MonitorConfigScheduleIntervalResourceModel, diag.Diagnostics) {
+ diags := diag.Diagnostics{}
+
+ rm := MonitorConfigScheduleIntervalResourceModel{}
+
+ if len(m) != 2 {
+ diags.AddError("Invalid schedule", "Invalid schedule")
+ return rm, diags
+ }
+
+ var number int64
+ number, ok := m[0].(int64)
+ if !ok {
+ diags.AddError("Invalid schedule", "Invalid schedule")
+ return rm, diags
+ }
+
+ var unit string
+ unit, ok = m[1].(string)
+ if !ok {
+ diags.AddError("Invalid schedule", "Invalid schedule")
+ return rm, diags
+ }
+
+ switch unit {
+ case "year":
+ rm.Year = types.Int64Value(number)
+ case "month":
+ rm.Month = types.Int64Value(number)
+ case "week":
+ rm.Week = types.Int64Value(number)
+ case "day":
+ rm.Day = types.Int64Value(number)
+ case "hour":
+ rm.Hour = types.Int64Value(number)
+ case "minute":
+ rm.Minute = types.Int64Value(number)
+ default:
+ diags.AddError("Invalid schedule", "Invalid schedule")
+ }
+
+ return rm, diags
+}
+
+func formatMonitorConfigScheduleInterval(schedule MonitorConfigScheduleIntervalResourceModel) (apiclient.MonitorConfigScheduleInterval, diag.Diagnostics) {
+ diags := diag.Diagnostics{}
+
+ scheduleInterval := make(apiclient.MonitorConfigScheduleInterval, 2)
+
+ if schedule.Year.IsNull() && schedule.Month.IsNull() && schedule.Week.IsNull() &&
+ schedule.Day.IsNull() && schedule.Hour.IsNull() && schedule.Minute.IsNull() {
+ return nil, diags
+ }
+
+ if !schedule.Year.IsNull() {
+ scheduleInterval[0] = schedule.Year.ValueInt64()
+ scheduleInterval[1] = "year"
+ } else if !schedule.Month.IsNull() {
+ scheduleInterval[0] = schedule.Month.ValueInt64()
+ scheduleInterval[1] = "month"
+ } else if !schedule.Week.IsNull() {
+ scheduleInterval[0] = schedule.Week.ValueInt64()
+ scheduleInterval[1] = "week"
+ } else if !schedule.Day.IsNull() {
+ scheduleInterval[0] = schedule.Day.ValueInt64()
+ scheduleInterval[1] = "day"
+ } else if !schedule.Hour.IsNull() {
+ scheduleInterval[0] = schedule.Hour.ValueInt64()
+ scheduleInterval[1] = "hour"
+ } else if !schedule.Minute.IsNull() {
+ scheduleInterval[0] = schedule.Minute.ValueInt64()
+ scheduleInterval[1] = "minute"
+ }
+
+ return scheduleInterval, diags
+}
diff --git a/internal/provider/model_monitor_test.go b/internal/provider/model_monitor_test.go
new file mode 100644
index 000000000..43def5874
--- /dev/null
+++ b/internal/provider/model_monitor_test.go
@@ -0,0 +1,130 @@
+package provider
+
+import (
+ "context"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/jianyuan/terraform-provider-sentry/internal/apiclient"
+
+ // "github.com/jianyuan/terraform-provider-sentry/internal/provider"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestMonitorResourceModelToMonitorRequestScheduleCrontab(t *testing.T) {
+ config := MonitorConfigResourceModel{
+ ScheduleCrontab: types.StringValue("* * * * *"),
+ ScheduleInterval: types.ObjectNull((*MonitorConfigScheduleIntervalResourceModel)(nil).AttributeTypes()),
+ Timezone: types.StringValue("UTC"),
+ CheckinMargin: types.Int64Value(10),
+ MaxRuntime: types.Int64Value(20),
+ FailureIssueThreshold: types.Int64Value(5),
+ RecoveryThreshold: types.Int64Value(10),
+ AlertRuleId: types.Int64Value(123),
+ }
+
+ configObject, configObjectDiags := types.ObjectValueFrom(context.Background(), (*MonitorConfigResourceModel)(nil).AttributeTypes(), config)
+ require.Empty(t, configObjectDiags)
+
+ model := MonitorResourceModel{
+ Organization: types.StringValue("sentry-org"),
+ Id: types.StringValue("monitor_id"),
+ Project: types.StringValue("sentry-project"),
+ Name: types.StringValue("monitor name"),
+ Slug: types.StringValue("monitor-slug"),
+ Owner: types.StringValue("team:123"),
+ IsMuted: types.BoolValue(false),
+ Status: types.StringValue("active"),
+ Config: configObject,
+ }
+
+ monitorRequest, monitorRequestDiags := model.ToMonitorRequest(context.Background())
+
+ require.Empty(t, monitorRequestDiags)
+
+ assert.Equal(t, "monitor name", monitorRequest.Name)
+ assert.Equal(t, "monitor-slug", *monitorRequest.Slug)
+ assert.Equal(t, "sentry-project", monitorRequest.Project)
+ assert.Equal(t, "team:123", *monitorRequest.Owner)
+ assert.Equal(t, apiclient.MonitorRequestStatusActive, *monitorRequest.Status)
+ assert.Equal(t, false, *monitorRequest.IsMuted)
+
+ assert.Equal(t, apiclient.Crontab, monitorRequest.Config.ScheduleType)
+
+ monitorRequestConfigScheduleCrontab, monitorRequestConfigScheduleCrontabErr := monitorRequest.Config.Schedule.AsMonitorConfigScheduleString()
+ assert.NoError(t, monitorRequestConfigScheduleCrontabErr)
+ assert.Equal(t, "* * * * *", monitorRequestConfigScheduleCrontab)
+
+ _, monitorRequestConfigScheduleIntervalErr := monitorRequest.Config.Schedule.AsMonitorConfigScheduleInterval()
+ assert.Error(t, monitorRequestConfigScheduleIntervalErr)
+
+ assert.Equal(t, "UTC", monitorRequest.Config.Timezone)
+ assert.Equal(t, int64(10), *monitorRequest.Config.CheckinMargin)
+ assert.Equal(t, int64(20), *monitorRequest.Config.MaxRuntime)
+ assert.Equal(t, int64(5), *monitorRequest.Config.FailureIssueThreshold)
+ assert.Equal(t, int64(10), *monitorRequest.Config.RecoveryThreshold)
+ assert.Equal(t, int64(123), *monitorRequest.Config.AlertRuleId)
+}
+
+func TestMonitorResourceModelToMonitorRequestScheduleInterval(t *testing.T) {
+ scheduleInterval := MonitorConfigScheduleIntervalResourceModel{
+ Day: types.Int64Value(1),
+ }
+
+ scheduleIntervalObject, scheduleIntervalObjectDiags := types.ObjectValueFrom(context.Background(), (*MonitorConfigScheduleIntervalResourceModel)(nil).AttributeTypes(), scheduleInterval)
+ require.Empty(t, scheduleIntervalObjectDiags)
+
+ config := MonitorConfigResourceModel{
+ ScheduleCrontab: types.StringNull(),
+ ScheduleInterval: scheduleIntervalObject,
+ Timezone: types.StringValue("UTC"),
+ CheckinMargin: types.Int64Value(10),
+ MaxRuntime: types.Int64Value(20),
+ FailureIssueThreshold: types.Int64Value(5),
+ RecoveryThreshold: types.Int64Value(10),
+ AlertRuleId: types.Int64Value(123),
+ }
+
+ configObject, configObjectDiags := types.ObjectValueFrom(context.Background(), (*MonitorConfigResourceModel)(nil).AttributeTypes(), config)
+ require.Empty(t, configObjectDiags)
+
+ model := MonitorResourceModel{
+ Organization: types.StringValue("sentry-org"),
+ Id: types.StringValue("monitor_id"),
+ Project: types.StringValue("sentry-project"),
+ Name: types.StringValue("monitor name"),
+ Slug: types.StringValue("monitor-slug"),
+ Owner: types.StringValue("team:123"),
+ IsMuted: types.BoolValue(false),
+ Status: types.StringValue("active"),
+ Config: configObject,
+ }
+
+ monitorRequest, monitorRequestDiags := model.ToMonitorRequest(context.Background())
+
+ require.Empty(t, monitorRequestDiags)
+
+ assert.Equal(t, "monitor name", monitorRequest.Name)
+ assert.Equal(t, "monitor-slug", *monitorRequest.Slug)
+ assert.Equal(t, "sentry-project", monitorRequest.Project)
+ assert.Equal(t, "team:123", *monitorRequest.Owner)
+ assert.Equal(t, apiclient.MonitorRequestStatusActive, *monitorRequest.Status)
+ assert.Equal(t, false, *monitorRequest.IsMuted)
+
+ assert.Equal(t, apiclient.Interval, monitorRequest.Config.ScheduleType)
+
+ _, monitorRequestConfigScheduleCrontabErr := monitorRequest.Config.Schedule.AsMonitorConfigScheduleString()
+ assert.Error(t, monitorRequestConfigScheduleCrontabErr)
+
+ monitorRequestConfigScheduleInterval, monitorRequestConfigScheduleIntervalErr := monitorRequest.Config.Schedule.AsMonitorConfigScheduleInterval()
+ assert.NoError(t, monitorRequestConfigScheduleIntervalErr)
+ assert.Equal(t, []any{float64(1), "day"}, monitorRequestConfigScheduleInterval)
+
+ assert.Equal(t, "UTC", monitorRequest.Config.Timezone)
+ assert.Equal(t, int64(10), *monitorRequest.Config.CheckinMargin)
+ assert.Equal(t, int64(20), *monitorRequest.Config.MaxRuntime)
+ assert.Equal(t, int64(5), *monitorRequest.Config.FailureIssueThreshold)
+ assert.Equal(t, int64(10), *monitorRequest.Config.RecoveryThreshold)
+ assert.Equal(t, int64(123), *monitorRequest.Config.AlertRuleId)
+}
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
index a96cc8ea1..2f1214dd2 100644
--- a/internal/provider/provider.go
+++ b/internal/provider/provider.go
@@ -142,6 +142,7 @@ func (p *SentryProvider) Resources(ctx context.Context) []func() resource.Resour
NewIntegrationOpsgenie,
NewIntegrationPagerDuty,
NewIssueAlertResource,
+ NewMonitorResource,
NewNotificationActionResource,
NewOrganizationRepositoryResource,
NewProjectInboundDataFilterResource,
diff --git a/internal/provider/resource_monitor.go b/internal/provider/resource_monitor.go
new file mode 100644
index 000000000..a2c65a333
--- /dev/null
+++ b/internal/provider/resource_monitor.go
@@ -0,0 +1,193 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+
+ "github.com/jianyuan/terraform-provider-sentry/internal/tfutils"
+)
+
+var _ resource.Resource = &MonitorResource{}
+var _ resource.ResourceWithConfigure = &MonitorResource{}
+var _ resource.ResourceWithImportState = &MonitorResource{}
+
+func NewMonitorResource() resource.Resource {
+ return &MonitorResource{}
+}
+
+type MonitorResource struct {
+ baseResource
+}
+
+func (r *MonitorResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_monitor"
+}
+
+func (r *MonitorResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ MarkdownDescription: "Return a client monitor bound to a project.",
+ Attributes: MonitorResourceModel{}.Attributes(),
+ }
+}
+
+func (r *MonitorResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ var data MonitorResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ monitorRequest, monitorRequestDiags := data.ToMonitorRequest(ctx)
+ resp.Diagnostics.Append(monitorRequestDiags...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ response, err := r.apiClient.CreateMonitorWithResponse(ctx, data.Organization.ValueString(), monitorRequest)
+ if err != nil {
+ resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Create error: %s", err.Error()))
+ return
+ }
+
+ if response.JSON201 == nil {
+ resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Create error: %s", response.HTTPResponse.Status))
+ return
+ }
+
+ resp.Diagnostics.Append(data.Fill(ctx, data.Organization.ValueString(), *response.JSON201)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func (r *MonitorResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ var data MonitorResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ response, err := r.apiClient.GetOrganizationMonitorWithResponse(
+ ctx,
+ data.Organization.ValueString(),
+ data.Id.ValueString(),
+ )
+ if err != nil {
+ resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Read error: %s", err.Error()))
+ return
+ }
+
+ if response.StatusCode() == http.StatusNotFound {
+ resp.Diagnostics.AddError("Client Error", "Not found")
+ // resp.State.RemoveResource(ctx)
+ return
+ }
+
+ if response.JSON200 == nil {
+ resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Read error: %s", response.HTTPResponse.Status))
+ return
+ }
+
+ resp.Diagnostics.Append(data.Fill(ctx, data.Organization.ValueString(), *response.JSON200)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func (r *MonitorResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ var data MonitorResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ monitorRequest, monitorRequestDiags := data.ToMonitorRequest(ctx)
+ resp.Diagnostics.Append(monitorRequestDiags...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ response, err := r.apiClient.UpdateOrganizationMonitorWithResponse(
+ ctx,
+ data.Organization.ValueString(),
+ data.Id.ValueString(),
+ monitorRequest,
+ )
+
+ if err != nil {
+ resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Update error: %s", err.Error()))
+ return
+ }
+
+ if response.StatusCode() == http.StatusNotFound {
+ resp.Diagnostics.AddError("Client Error", "Not found")
+ // resp.State.RemoveResource(ctx)
+ return
+ }
+
+ if response.JSON200 == nil {
+ resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Read error: %s", response.HTTPResponse.Status))
+ return
+ }
+
+ resp.Diagnostics.Append(data.Fill(ctx, data.Organization.ValueString(), *response.JSON200)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func (r *MonitorResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ var data MonitorResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ apiResp, err := r.apiClient.DeleteOrganizationMonitorWithResponse(
+ ctx,
+ data.Organization.ValueString(),
+ data.Id.ValueString(),
+ )
+
+ if err != nil {
+ resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Delete error: %s", err.Error()))
+ return
+ }
+
+ if apiResp.StatusCode() == http.StatusNotFound {
+ resp.Diagnostics.AddWarning("Monitor not found", "Monitor may have been deleted already")
+ return
+ }
+}
+
+func (r *MonitorResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ organization, id, err := tfutils.SplitTwoPartId(req.ID, "organization", "monitor-id")
+ if err != nil {
+ resp.Diagnostics.AddError("Invalid ID", fmt.Sprintf("Error parsing ID: %s", err.Error()))
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.SetAttribute(
+ ctx, path.Root("organization"), organization,
+ )...)
+ resp.Diagnostics.Append(resp.State.SetAttribute(
+ ctx, path.Root("id"), id,
+ )...)
+}