From 843569b0246fd2ab8cab58e5f4c71f2bcb56b0bf Mon Sep 17 00:00:00 2001 From: Ryan Willis Date: Fri, 17 Jan 2025 08:04:29 -0700 Subject: [PATCH] [APP-16093] add smart classes to tf provider (#229) --- .gitignore | 1 + README.md | 2 +- docs/resources/smart_class.md | 50 + docs/resources/smart_class_query.md | 36 + docs/resources/smart_class_tag.md | 52 ++ .../jupiterone_smart_class/resource.tf | 18 + .../jupiterone_smart_class_query/resource.tf | 5 + .../jupiterone_smart_class_tag/resource.tf | 20 + .../TestResourcePermission_Basic.yaml | 82 +- .../cassettes/TestSmartClassQuery_Basic.yaml | 471 ++++++++++ .../cassettes/TestSmartClassTag_Basic.yaml | 471 ++++++++++ .../cassettes/TestSmartClass_Basic.yaml | 237 +++++ .../cassettes/TestSmartClass_BasicImport.yaml | 159 ++++ jupiterone/internal/client/client.go | 5 +- jupiterone/internal/client/generated.go | 866 ++++++++++++++++++ jupiterone/internal/client/genqlient.yaml | 1 + .../internal/client/smartClasses.graphql | 91 ++ jupiterone/provider.go | 3 + jupiterone/resource_resource_group.go | 6 +- jupiterone/resource_smart_class.go | 174 ++++ jupiterone/resource_smart_class_query.go | 181 ++++ jupiterone/resource_smart_class_query_test.go | 109 +++ jupiterone/resource_smart_class_tag.go | 206 +++++ jupiterone/resource_smart_class_tag_test.go | 124 +++ jupiterone/resource_smart_class_test.go | 174 ++++ scripts/get_current_schema.bash | 2 +- 26 files changed, 3536 insertions(+), 10 deletions(-) create mode 100644 docs/resources/smart_class.md create mode 100644 docs/resources/smart_class_query.md create mode 100644 docs/resources/smart_class_tag.md create mode 100644 examples/resources/jupiterone_smart_class/resource.tf create mode 100644 examples/resources/jupiterone_smart_class_query/resource.tf create mode 100644 examples/resources/jupiterone_smart_class_tag/resource.tf create mode 100644 jupiterone/cassettes/TestSmartClassQuery_Basic.yaml create mode 100644 jupiterone/cassettes/TestSmartClassTag_Basic.yaml create mode 100644 jupiterone/cassettes/TestSmartClass_Basic.yaml create mode 100644 jupiterone/cassettes/TestSmartClass_BasicImport.yaml create mode 100644 jupiterone/internal/client/smartClasses.graphql create mode 100644 jupiterone/resource_smart_class.go create mode 100644 jupiterone/resource_smart_class_query.go create mode 100644 jupiterone/resource_smart_class_query_test.go create mode 100644 jupiterone/resource_smart_class_tag.go create mode 100644 jupiterone/resource_smart_class_tag_test.go create mode 100644 jupiterone/resource_smart_class_test.go diff --git a/.gitignore b/.gitignore index 11434969..07664737 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ website/node_modules .terraform/ *.log *.bak +.env *~ .*.swp .idea diff --git a/README.md b/README.md index f9e230e6..65f8f053 100644 --- a/README.md +++ b/README.md @@ -321,7 +321,7 @@ If you create a new one, be sure to add the file to the [genqlient.yaml](jupiter You should always generate the gql client from the production api so that you do not include any in-progress work from dev. Set these environment variables before running the next commands. ```shell -export JUPITERONE_ACCOUNT=:your_account_id +export JUPITERONE_ACCOUNT_ID=:your_account_id export JUPITERONE_API_KEY=:your_api_key export JUPITERONE_REGION=us ``` diff --git a/docs/resources/smart_class.md b/docs/resources/smart_class.md new file mode 100644 index 00000000..1985db78 --- /dev/null +++ b/docs/resources/smart_class.md @@ -0,0 +1,50 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "jupiterone_smart_class Resource - terraform-provider-jupiterone" +subcategory: "" +description: |- + JupiterOne Smart Class +--- + +# jupiterone_smart_class (Resource) + +JupiterOne Smart Class + +## Example Usage + +```terraform +resource "jupiterone_smart_class" "example" { + tag_name = "Example" + description = "Example smart class" +} + +resource "jupiterone_smart_class_query" "query1" { + smart_class_id = jupiterone_smart_class.example.id + query = "Find User" + description = "Example query" +} + +resource "jupiterone_smart_class_tag" "tag2" { + smart_class_id = jupiterone_smart_class.example.id + name = "user" + type = "boolean" + value = "true" +} +``` + + +## Schema + +### Required + +- `tag_name` (String) The tag name of the smart class. + +### Optional + +- `description` (String) The description of the smart class. + +### Read-Only + +- `id` (String) The ID of this resource. + + diff --git a/docs/resources/smart_class_query.md b/docs/resources/smart_class_query.md new file mode 100644 index 00000000..46aef4e4 --- /dev/null +++ b/docs/resources/smart_class_query.md @@ -0,0 +1,36 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "jupiterone_smart_class_query Resource - terraform-provider-jupiterone" +subcategory: "" +description: |- + A smart class query is a J1QL query that finds entities to associate with a smart class +--- + +# jupiterone_smart_class_query (Resource) + +A smart class query is a J1QL query that finds entities to associate with a smart class + +## Example Usage + +```terraform +resource "jupiterone_smart_class_query" "query1" { + smart_class_id = jupiterone_smart_class.example.id + query = "Find User with active=true" + description = "Find all active users" +} +``` + + +## Schema + +### Required + +- `description` (String) A description of the smart class query +- `query` (String) The J1QL query to find entities for the smart class +- `smart_class_id` (String) The ID of the smart class to associate the query with + +### Read-Only + +- `id` (String) The ID of this resource. + + diff --git a/docs/resources/smart_class_tag.md b/docs/resources/smart_class_tag.md new file mode 100644 index 00000000..d147fd70 --- /dev/null +++ b/docs/resources/smart_class_tag.md @@ -0,0 +1,52 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "jupiterone_smart_class_tag Resource - terraform-provider-jupiterone" +subcategory: "" +description: |- + A smart class tag is another tag applied to entities found by a smart class query +--- + +# jupiterone_smart_class_tag (Resource) + +A smart class tag is another tag applied to entities found by a smart class query + +## Example Usage + +```terraform +resource "jupiterone_smart_class_tag" "tag1" { + smart_class_id = jupiterone_smart_class.example.id + name = "person" + type = "boolean" + value = "true" +} + +resource "jupiterone_smart_class_tag" "tag2" { + smart_class_id = jupiterone_smart_class.example.id + name = "worth" + type = "number" + value = "50000" +} + +resource "jupiterone_smart_class_tag" "tag3" { + smart_class_id = jupiterone_smart_class.example.id + name = "label" + type = "string" + value = "example" +} +``` + + +## Schema + +### Required + +- `name` (String) The name (key) of the tag +- `smart_class_id` (String) The ID of the smart class to associate the tag with +- `type` (String) The type of the tag, one of 'string', 'boolean', or 'number' +- `value` (String) The value of the tag as a string + +### Read-Only + +- `id` (String) The ID of this resource. + + diff --git a/examples/resources/jupiterone_smart_class/resource.tf b/examples/resources/jupiterone_smart_class/resource.tf new file mode 100644 index 00000000..1991d199 --- /dev/null +++ b/examples/resources/jupiterone_smart_class/resource.tf @@ -0,0 +1,18 @@ +resource "jupiterone_smart_class" "example" { + tag_name = "Example" + description = "Example smart class" +} + +resource "jupiterone_smart_class_query" "query1" { + smart_class_id = jupiterone_smart_class.example.id + query = "Find User" + description = "Example query" +} + +resource "jupiterone_smart_class_tag" "tag2" { + smart_class_id = jupiterone_smart_class.example.id + name = "user" + type = "boolean" + value = "true" +} + diff --git a/examples/resources/jupiterone_smart_class_query/resource.tf b/examples/resources/jupiterone_smart_class_query/resource.tf new file mode 100644 index 00000000..2fa817e8 --- /dev/null +++ b/examples/resources/jupiterone_smart_class_query/resource.tf @@ -0,0 +1,5 @@ +resource "jupiterone_smart_class_query" "query1" { + smart_class_id = jupiterone_smart_class.example.id + query = "Find User with active=true" + description = "Find all active users" +} diff --git a/examples/resources/jupiterone_smart_class_tag/resource.tf b/examples/resources/jupiterone_smart_class_tag/resource.tf new file mode 100644 index 00000000..4495412f --- /dev/null +++ b/examples/resources/jupiterone_smart_class_tag/resource.tf @@ -0,0 +1,20 @@ +resource "jupiterone_smart_class_tag" "tag1" { + smart_class_id = jupiterone_smart_class.example.id + name = "person" + type = "boolean" + value = "true" +} + +resource "jupiterone_smart_class_tag" "tag2" { + smart_class_id = jupiterone_smart_class.example.id + name = "worth" + type = "number" + value = "50000" +} + +resource "jupiterone_smart_class_tag" "tag3" { + smart_class_id = jupiterone_smart_class.example.id + name = "label" + type = "string" + value = "example" +} diff --git a/jupiterone/cassettes/TestResourcePermission_Basic.yaml b/jupiterone/cassettes/TestResourcePermission_Basic.yaml index 558c5397..01517882 100644 --- a/jupiterone/cassettes/TestResourcePermission_Basic.yaml +++ b/jupiterone/cassettes/TestResourcePermission_Basic.yaml @@ -78,8 +78,86 @@ interactions: - "0" status: 200 OK code: 200 - duration: 664.85025ms + duration: 533.467416ms - id: 1 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 544 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nquery GetResourcePermissions ($filter: GetResourcePermissionsFilter!, $cursor: String, $limit: Int) {\n\tgetResourcePermissions(filter: $filter, cursor: $cursor, limit: $limit) {\n\t\tcanCreate\n\t\tcanDelete\n\t\tcanRead\n\t\tcanUpdate\n\t\tresourceArea\n\t\tresourceId\n\t\tresourceType\n\t\tsubjectId\n\t\tsubjectType\n\t}\n}\n","variables":{"filter":{"subjectType":"group","subjectId":"example-group-id","resourceArea":"rule","resourceType":"*","resourceId":"*"},"cursor":"","limit":10},"operationName":"GetResourcePermissions"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 217 + uncompressed: false + body: | + {"data":{"getResourcePermissions":[{"canCreate":true,"canDelete":true,"canRead":true,"canUpdate":true,"resourceArea":"rule","resourceId":"*","resourceType":"*","subjectId":"example-group-id","subjectType":"group"}]}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "217" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 450.648917ms + - id: 2 request: proto: HTTP/1.1 proto_major: 1 @@ -156,4 +234,4 @@ interactions: - "0" status: 200 OK code: 200 - duration: 234.689667ms + duration: 482.037916ms diff --git a/jupiterone/cassettes/TestSmartClassQuery_Basic.yaml b/jupiterone/cassettes/TestSmartClassQuery_Basic.yaml new file mode 100644 index 00000000..ad370894 --- /dev/null +++ b/jupiterone/cassettes/TestSmartClassQuery_Basic.yaml @@ -0,0 +1,471 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 268 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nmutation CreateSmartClass ($input: CreateSmartClassInput!) {\n\tcreateSmartClass(input: $input) {\n\t\tid\n\t\ttagName\n\t\tdescription\n\t}\n}\n","variables":{"input":{"tagName":"TfProviderTagTest","description":"xyz"}},"operationName":"CreateSmartClass"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 126 + uncompressed: false + body: | + {"data":{"createSmartClass":{"id":"db268834-80f4-42b7-953d-ffde1b8770f9","tagName":"TfProviderTagTest","description":"xyz"}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "126" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 1.087211667s + - id: 1 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 355 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nmutation CreateSmartClassQuery ($input: CreateSmartClassQueryInput!) {\n\tcreateSmartClassQuery(input: $input) {\n\t\tid\n\t\tquery\n\t\tsmartClassId\n\t\tdescription\n\t}\n}\n","variables":{"input":{"smartClassId":"db268834-80f4-42b7-953d-ffde1b8770f9","description":"find users","query":"FIND User"}},"operationName":"CreateSmartClassQuery"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 182 + uncompressed: false + body: | + {"data":{"createSmartClassQuery":{"id":"f656bdf2-da8a-4f89-9ec5-c4565aed641b","query":"FIND User","smartClassId":"db268834-80f4-42b7-953d-ffde1b8770f9","description":"find users"}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "182" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 814.919833ms + - id: 2 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 305 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nquery GetSmartClass ($smartClassId: ID!) {\n\tsmartClass(id: $smartClassId) {\n\t\tid\n\t\ttagName\n\t\tdescription\n\t\ttags {\n\t\t\tid\n\t\t\tname\n\t\t\ttype\n\t\t\tvalue\n\t\t}\n\t}\n}\n","variables":{"smartClassId":"db268834-80f4-42b7-953d-ffde1b8770f9"},"operationName":"GetSmartClass"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 130 + uncompressed: false + body: | + {"data":{"smartClass":{"id":"db268834-80f4-42b7-953d-ffde1b8770f9","tagName":"TfProviderTagTest","description":"xyz","tags":[]}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "130" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 381.960917ms + - id: 3 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 285 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nquery GetSmartClassQuery ($smartClassQueryId: ID!) {\n\tsmartClassQuery(id: $smartClassQueryId) {\n\t\tid\n\t\tquery\n\t\tsmartClassId\n\t\tdescription\n\t}\n}\n","variables":{"smartClassQueryId":"f656bdf2-da8a-4f89-9ec5-c4565aed641b"},"operationName":"GetSmartClassQuery"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 176 + uncompressed: false + body: | + {"data":{"smartClassQuery":{"id":"f656bdf2-da8a-4f89-9ec5-c4565aed641b","query":"FIND User","smartClassId":"db268834-80f4-42b7-953d-ffde1b8770f9","description":"find users"}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "176" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 374.910708ms + - id: 4 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 259 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nmutation DeleteSmartClassQuery ($smartClassQueryId: ID!) {\n\tdeleteSmartClassQuery(id: $smartClassQueryId) {\n\t\tsuccess\n\t}\n}\n","variables":{"smartClassQueryId":"f656bdf2-da8a-4f89-9ec5-c4565aed641b"},"operationName":"DeleteSmartClassQuery"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 52 + uncompressed: false + body: | + {"data":{"deleteSmartClassQuery":{"success":true}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "52" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 654.072959ms + - id: 5 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 229 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nmutation DeleteSmartClass ($smartClassId: ID!) {\n\tdeleteSmartClass(id: $smartClassId) {\n\t\tsuccess\n\t}\n}\n","variables":{"smartClassId":"db268834-80f4-42b7-953d-ffde1b8770f9"},"operationName":"DeleteSmartClass"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 47 + uncompressed: false + body: | + {"data":{"deleteSmartClass":{"success":true}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "47" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 692.91325ms diff --git a/jupiterone/cassettes/TestSmartClassTag_Basic.yaml b/jupiterone/cassettes/TestSmartClassTag_Basic.yaml new file mode 100644 index 00000000..4fb28c18 --- /dev/null +++ b/jupiterone/cassettes/TestSmartClassTag_Basic.yaml @@ -0,0 +1,471 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 268 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nmutation CreateSmartClass ($input: CreateSmartClassInput!) {\n\tcreateSmartClass(input: $input) {\n\t\tid\n\t\ttagName\n\t\tdescription\n\t}\n}\n","variables":{"input":{"tagName":"TfProviderTagTest","description":"xyz"}},"operationName":"CreateSmartClass"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 126 + uncompressed: false + body: | + {"data":{"createSmartClass":{"id":"b9f97b9b-13f3-47f4-9d5d-8e32abfdf9e7","tagName":"TfProviderTagTest","description":"xyz"}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "126" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 988.278416ms + - id: 1 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 334 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nmutation CreateSmartClassTag ($input: CreateSmartClassTagInput!) {\n\tcreateSmartClassTag(input: $input) {\n\t\tid\n\t\tname\n\t\ttype\n\t\tvalue\n\t}\n}\n","variables":{"input":{"smartClassId":"b9f97b9b-13f3-47f4-9d5d-8e32abfdf9e7","name":"tagname","type":"boolean","value":"true"}},"operationName":"CreateSmartClassTag"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 128 + uncompressed: false + body: | + {"data":{"createSmartClassTag":{"id":"b7458939-c5ca-4a33-8d5b-749dd4665a95","name":"tagname","type":"boolean","value":"true"}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "128" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 711.427875ms + - id: 2 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 305 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nquery GetSmartClass ($smartClassId: ID!) {\n\tsmartClass(id: $smartClassId) {\n\t\tid\n\t\ttagName\n\t\tdescription\n\t\ttags {\n\t\t\tid\n\t\t\tname\n\t\t\ttype\n\t\t\tvalue\n\t\t}\n\t}\n}\n","variables":{"smartClassId":"b9f97b9b-13f3-47f4-9d5d-8e32abfdf9e7"},"operationName":"GetSmartClass"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 224 + uncompressed: false + body: | + {"data":{"smartClass":{"id":"b9f97b9b-13f3-47f4-9d5d-8e32abfdf9e7","tagName":"TfProviderTagTest","description":"xyz","tags":[{"id":"b7458939-c5ca-4a33-8d5b-749dd4665a95","name":"tagname","type":"boolean","value":"true"}]}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "224" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 360.229667ms + - id: 3 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 305 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nquery GetSmartClass ($smartClassId: ID!) {\n\tsmartClass(id: $smartClassId) {\n\t\tid\n\t\ttagName\n\t\tdescription\n\t\ttags {\n\t\t\tid\n\t\t\tname\n\t\t\ttype\n\t\t\tvalue\n\t\t}\n\t}\n}\n","variables":{"smartClassId":"b9f97b9b-13f3-47f4-9d5d-8e32abfdf9e7"},"operationName":"GetSmartClass"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 224 + uncompressed: false + body: | + {"data":{"smartClass":{"id":"b9f97b9b-13f3-47f4-9d5d-8e32abfdf9e7","tagName":"TfProviderTagTest","description":"xyz","tags":[{"id":"b7458939-c5ca-4a33-8d5b-749dd4665a95","name":"tagname","type":"boolean","value":"true"}]}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "224" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 367.938792ms + - id: 4 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 247 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nmutation DeleteSmartClassTag ($smartClassTagId: ID!) {\n\tdeleteSmartClassTag(id: $smartClassTagId) {\n\t\tsuccess\n\t}\n}\n","variables":{"smartClassTagId":"b7458939-c5ca-4a33-8d5b-749dd4665a95"},"operationName":"DeleteSmartClassTag"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 50 + uncompressed: false + body: | + {"data":{"deleteSmartClassTag":{"success":true}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "50" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 1.356674708s + - id: 5 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 229 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nmutation DeleteSmartClass ($smartClassId: ID!) {\n\tdeleteSmartClass(id: $smartClassId) {\n\t\tsuccess\n\t}\n}\n","variables":{"smartClassId":"b9f97b9b-13f3-47f4-9d5d-8e32abfdf9e7"},"operationName":"DeleteSmartClass"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 47 + uncompressed: false + body: | + {"data":{"deleteSmartClass":{"success":true}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "47" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 696.425417ms diff --git a/jupiterone/cassettes/TestSmartClass_Basic.yaml b/jupiterone/cassettes/TestSmartClass_Basic.yaml new file mode 100644 index 00000000..2e793c09 --- /dev/null +++ b/jupiterone/cassettes/TestSmartClass_Basic.yaml @@ -0,0 +1,237 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 311 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nmutation CreateSmartClass ($input: CreateSmartClassInput!) {\n\tcreateSmartClass(input: $input) {\n\t\tid\n\t\ttagName\n\t\tdescription\n\t}\n}\n","variables":{"input":{"tagName":"TfProviderTestSmartClass","description":"description of TfProviderTestSmartClass"}},"operationName":"CreateSmartClass"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 169 + uncompressed: false + body: | + {"data":{"createSmartClass":{"id":"ec6c835c-d669-41f9-9cb7-790eb29b7856","tagName":"TfProviderTestSmartClass","description":"description of TfProviderTestSmartClass"}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "169" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 1.307006708s + - id: 1 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 305 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nquery GetSmartClass ($smartClassId: ID!) {\n\tsmartClass(id: $smartClassId) {\n\t\tid\n\t\ttagName\n\t\tdescription\n\t\ttags {\n\t\t\tid\n\t\t\tname\n\t\t\ttype\n\t\t\tvalue\n\t\t}\n\t}\n}\n","variables":{"smartClassId":"ec6c835c-d669-41f9-9cb7-790eb29b7856"},"operationName":"GetSmartClass"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 173 + uncompressed: false + body: | + {"data":{"smartClass":{"id":"ec6c835c-d669-41f9-9cb7-790eb29b7856","tagName":"TfProviderTestSmartClass","description":"description of TfProviderTestSmartClass","tags":[]}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "173" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 512.516875ms + - id: 2 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 229 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nmutation DeleteSmartClass ($smartClassId: ID!) {\n\tdeleteSmartClass(id: $smartClassId) {\n\t\tsuccess\n\t}\n}\n","variables":{"smartClassId":"ec6c835c-d669-41f9-9cb7-790eb29b7856"},"operationName":"DeleteSmartClass"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 47 + uncompressed: false + body: | + {"data":{"deleteSmartClass":{"success":true}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "47" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 724.513458ms diff --git a/jupiterone/cassettes/TestSmartClass_BasicImport.yaml b/jupiterone/cassettes/TestSmartClass_BasicImport.yaml new file mode 100644 index 00000000..d295ba74 --- /dev/null +++ b/jupiterone/cassettes/TestSmartClass_BasicImport.yaml @@ -0,0 +1,159 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 303 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nmutation CreateSmartClass ($input: CreateSmartClassInput!) {\n\tcreateSmartClass(input: $input) {\n\t\tid\n\t\ttagName\n\t\tdescription\n\t}\n}\n","variables":{"input":{"tagName":"TfProviderTestImport","description":"description of TfProviderTestImport"}},"operationName":"CreateSmartClass"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 161 + uncompressed: false + body: | + {"data":{"createSmartClass":{"id":"d1e89b60-a19b-47ca-b706-f61f4d71a972","tagName":"TfProviderTestImport","description":"description of TfProviderTestImport"}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "161" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 1.108235791s + - id: 1 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 305 + transfer_encoding: [] + trailer: {} + host: graphql.dev.jupiterone.io + remote_addr: "" + request_uri: "" + body: '{"query":"\nquery GetSmartClass ($smartClassId: ID!) {\n\tsmartClass(id: $smartClassId) {\n\t\tid\n\t\ttagName\n\t\tdescription\n\t\ttags {\n\t\t\tid\n\t\t\tname\n\t\t\ttype\n\t\t\tvalue\n\t\t}\n\t}\n}\n","variables":{"smartClassId":"d1e89b60-a19b-47ca-b706-f61f4d71a972"},"operationName":"GetSmartClass"}' + form: {} + headers: + Cache-Control: + - no-cache + Content-Type: + - application/json + url: https://graphql.dev.jupiterone.io/ + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 165 + uncompressed: false + body: | + {"data":{"smartClass":{"id":"d1e89b60-a19b-47ca-b706-f61f4d71a972","tagName":"TfProviderTestImport","description":"description of TfProviderTestImport","tags":[]}}} + headers: + Access-Control-Allow-Credentials: + - "true" + Content-Length: + - "165" + Content-Security-Policy: + - 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self'' https: data:;form-action ''self'';frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src ''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests' + Content-Type: + - application/json + Cross-Origin-Embedder-Policy: + - require-corp + Cross-Origin-Opener-Policy: + - same-origin + Cross-Origin-Resource-Policy: + - same-origin + Expect-Ct: + - max-age=0 + Origin-Agent-Cluster: + - ?1 + Ratelimit-Limit: + - "1000" + Ratelimit-Remaining: + - "999" + Ratelimit-Requested: + - "1" + Ratelimit-Reset: + - "1" + Referrer-Policy: + - no-referrer + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Vary: + - Origin + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - "off" + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Xss-Protection: + - "0" + status: 200 OK + code: 200 + duration: 372.458458ms diff --git a/jupiterone/internal/client/client.go b/jupiterone/internal/client/client.go index 702366ea..fd43175c 100644 --- a/jupiterone/internal/client/client.go +++ b/jupiterone/internal/client/client.go @@ -85,7 +85,10 @@ func (rt *RetryTransport) RoundTrip(req *http.Request) (*http.Response, error) { // Setting the body for the request req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) - resp, _ = rt.Transport.RoundTrip(req) + resp, err = rt.Transport.RoundTrip(req) + if err != nil { + return nil, err + } if resp.StatusCode != http.StatusTooManyRequests { updateLastNon429Response() diff --git a/jupiterone/internal/client/generated.go b/jupiterone/internal/client/generated.go index e0bc13e3..72ae9391 100644 --- a/jupiterone/internal/client/generated.go +++ b/jupiterone/internal/client/generated.go @@ -1177,6 +1177,137 @@ func (v *CreateResourceGroupResponse) GetCreateResourceGroup() CreateResourceGro return v.CreateResourceGroup } +// CreateSmartClassCreateSmartClass includes the requested fields of the GraphQL type SmartClass. +type CreateSmartClassCreateSmartClass struct { + Id string `json:"id"` + TagName string `json:"tagName"` + Description string `json:"description"` +} + +// GetId returns CreateSmartClassCreateSmartClass.Id, and is useful for accessing the field via an interface. +func (v *CreateSmartClassCreateSmartClass) GetId() string { return v.Id } + +// GetTagName returns CreateSmartClassCreateSmartClass.TagName, and is useful for accessing the field via an interface. +func (v *CreateSmartClassCreateSmartClass) GetTagName() string { return v.TagName } + +// GetDescription returns CreateSmartClassCreateSmartClass.Description, and is useful for accessing the field via an interface. +func (v *CreateSmartClassCreateSmartClass) GetDescription() string { return v.Description } + +type CreateSmartClassInput struct { + TagName string `json:"tagName"` + Description string `json:"description"` +} + +// GetTagName returns CreateSmartClassInput.TagName, and is useful for accessing the field via an interface. +func (v *CreateSmartClassInput) GetTagName() string { return v.TagName } + +// GetDescription returns CreateSmartClassInput.Description, and is useful for accessing the field via an interface. +func (v *CreateSmartClassInput) GetDescription() string { return v.Description } + +// CreateSmartClassQueryCreateSmartClassQuery includes the requested fields of the GraphQL type SmartClassQuery. +type CreateSmartClassQueryCreateSmartClassQuery struct { + Id string `json:"id"` + Query string `json:"query"` + SmartClassId string `json:"smartClassId"` + Description string `json:"description"` +} + +// GetId returns CreateSmartClassQueryCreateSmartClassQuery.Id, and is useful for accessing the field via an interface. +func (v *CreateSmartClassQueryCreateSmartClassQuery) GetId() string { return v.Id } + +// GetQuery returns CreateSmartClassQueryCreateSmartClassQuery.Query, and is useful for accessing the field via an interface. +func (v *CreateSmartClassQueryCreateSmartClassQuery) GetQuery() string { return v.Query } + +// GetSmartClassId returns CreateSmartClassQueryCreateSmartClassQuery.SmartClassId, and is useful for accessing the field via an interface. +func (v *CreateSmartClassQueryCreateSmartClassQuery) GetSmartClassId() string { return v.SmartClassId } + +// GetDescription returns CreateSmartClassQueryCreateSmartClassQuery.Description, and is useful for accessing the field via an interface. +func (v *CreateSmartClassQueryCreateSmartClassQuery) GetDescription() string { return v.Description } + +type CreateSmartClassQueryInput struct { + SmartClassId string `json:"smartClassId"` + Description string `json:"description"` + Query string `json:"query"` +} + +// GetSmartClassId returns CreateSmartClassQueryInput.SmartClassId, and is useful for accessing the field via an interface. +func (v *CreateSmartClassQueryInput) GetSmartClassId() string { return v.SmartClassId } + +// GetDescription returns CreateSmartClassQueryInput.Description, and is useful for accessing the field via an interface. +func (v *CreateSmartClassQueryInput) GetDescription() string { return v.Description } + +// GetQuery returns CreateSmartClassQueryInput.Query, and is useful for accessing the field via an interface. +func (v *CreateSmartClassQueryInput) GetQuery() string { return v.Query } + +// CreateSmartClassQueryResponse is returned by CreateSmartClassQuery on success. +type CreateSmartClassQueryResponse struct { + CreateSmartClassQuery CreateSmartClassQueryCreateSmartClassQuery `json:"createSmartClassQuery"` +} + +// GetCreateSmartClassQuery returns CreateSmartClassQueryResponse.CreateSmartClassQuery, and is useful for accessing the field via an interface. +func (v *CreateSmartClassQueryResponse) GetCreateSmartClassQuery() CreateSmartClassQueryCreateSmartClassQuery { + return v.CreateSmartClassQuery +} + +// CreateSmartClassResponse is returned by CreateSmartClass on success. +type CreateSmartClassResponse struct { + CreateSmartClass CreateSmartClassCreateSmartClass `json:"createSmartClass"` +} + +// GetCreateSmartClass returns CreateSmartClassResponse.CreateSmartClass, and is useful for accessing the field via an interface. +func (v *CreateSmartClassResponse) GetCreateSmartClass() CreateSmartClassCreateSmartClass { + return v.CreateSmartClass +} + +// CreateSmartClassTagCreateSmartClassTag includes the requested fields of the GraphQL type SmartClassTag. +type CreateSmartClassTagCreateSmartClassTag struct { + Id string `json:"id"` + Name string `json:"name"` + Type SmartClassTagType `json:"type"` + Value string `json:"value"` +} + +// GetId returns CreateSmartClassTagCreateSmartClassTag.Id, and is useful for accessing the field via an interface. +func (v *CreateSmartClassTagCreateSmartClassTag) GetId() string { return v.Id } + +// GetName returns CreateSmartClassTagCreateSmartClassTag.Name, and is useful for accessing the field via an interface. +func (v *CreateSmartClassTagCreateSmartClassTag) GetName() string { return v.Name } + +// GetType returns CreateSmartClassTagCreateSmartClassTag.Type, and is useful for accessing the field via an interface. +func (v *CreateSmartClassTagCreateSmartClassTag) GetType() SmartClassTagType { return v.Type } + +// GetValue returns CreateSmartClassTagCreateSmartClassTag.Value, and is useful for accessing the field via an interface. +func (v *CreateSmartClassTagCreateSmartClassTag) GetValue() string { return v.Value } + +type CreateSmartClassTagInput struct { + SmartClassId string `json:"smartClassId"` + Name string `json:"name"` + Type SmartClassTagType `json:"type"` + Value string `json:"value"` +} + +// GetSmartClassId returns CreateSmartClassTagInput.SmartClassId, and is useful for accessing the field via an interface. +func (v *CreateSmartClassTagInput) GetSmartClassId() string { return v.SmartClassId } + +// GetName returns CreateSmartClassTagInput.Name, and is useful for accessing the field via an interface. +func (v *CreateSmartClassTagInput) GetName() string { return v.Name } + +// GetType returns CreateSmartClassTagInput.Type, and is useful for accessing the field via an interface. +func (v *CreateSmartClassTagInput) GetType() SmartClassTagType { return v.Type } + +// GetValue returns CreateSmartClassTagInput.Value, and is useful for accessing the field via an interface. +func (v *CreateSmartClassTagInput) GetValue() string { return v.Value } + +// CreateSmartClassTagResponse is returned by CreateSmartClassTag on success. +type CreateSmartClassTagResponse struct { + CreateSmartClassTag CreateSmartClassTagCreateSmartClassTag `json:"createSmartClassTag"` +} + +// GetCreateSmartClassTag returns CreateSmartClassTagResponse.CreateSmartClassTag, and is useful for accessing the field via an interface. +func (v *CreateSmartClassTagResponse) GetCreateSmartClassTag() CreateSmartClassTagCreateSmartClassTag { + return v.CreateSmartClassTag +} + // CreateUserGroupCreateIamGroupV1Group includes the requested fields of the GraphQL type V1Group. type CreateUserGroupCreateIamGroupV1Group struct { Id string `json:"id"` @@ -1531,6 +1662,62 @@ func (v *DeleteRuleInstanceResponse) GetDeleteRuleInstance() DeleteRuleInstanceD return v.DeleteRuleInstance } +// DeleteSmartClassDeleteSmartClassDeleteResponse includes the requested fields of the GraphQL type DeleteResponse. +type DeleteSmartClassDeleteSmartClassDeleteResponse struct { + Success bool `json:"success"` +} + +// GetSuccess returns DeleteSmartClassDeleteSmartClassDeleteResponse.Success, and is useful for accessing the field via an interface. +func (v *DeleteSmartClassDeleteSmartClassDeleteResponse) GetSuccess() bool { return v.Success } + +// DeleteSmartClassQueryDeleteSmartClassQueryDeleteResponse includes the requested fields of the GraphQL type DeleteResponse. +type DeleteSmartClassQueryDeleteSmartClassQueryDeleteResponse struct { + Success bool `json:"success"` +} + +// GetSuccess returns DeleteSmartClassQueryDeleteSmartClassQueryDeleteResponse.Success, and is useful for accessing the field via an interface. +func (v *DeleteSmartClassQueryDeleteSmartClassQueryDeleteResponse) GetSuccess() bool { + return v.Success +} + +// DeleteSmartClassQueryResponse is returned by DeleteSmartClassQuery on success. +type DeleteSmartClassQueryResponse struct { + DeleteSmartClassQuery DeleteSmartClassQueryDeleteSmartClassQueryDeleteResponse `json:"deleteSmartClassQuery"` +} + +// GetDeleteSmartClassQuery returns DeleteSmartClassQueryResponse.DeleteSmartClassQuery, and is useful for accessing the field via an interface. +func (v *DeleteSmartClassQueryResponse) GetDeleteSmartClassQuery() DeleteSmartClassQueryDeleteSmartClassQueryDeleteResponse { + return v.DeleteSmartClassQuery +} + +// DeleteSmartClassResponse is returned by DeleteSmartClass on success. +type DeleteSmartClassResponse struct { + DeleteSmartClass DeleteSmartClassDeleteSmartClassDeleteResponse `json:"deleteSmartClass"` +} + +// GetDeleteSmartClass returns DeleteSmartClassResponse.DeleteSmartClass, and is useful for accessing the field via an interface. +func (v *DeleteSmartClassResponse) GetDeleteSmartClass() DeleteSmartClassDeleteSmartClassDeleteResponse { + return v.DeleteSmartClass +} + +// DeleteSmartClassTagDeleteSmartClassTagDeleteResponse includes the requested fields of the GraphQL type DeleteResponse. +type DeleteSmartClassTagDeleteSmartClassTagDeleteResponse struct { + Success bool `json:"success"` +} + +// GetSuccess returns DeleteSmartClassTagDeleteSmartClassTagDeleteResponse.Success, and is useful for accessing the field via an interface. +func (v *DeleteSmartClassTagDeleteSmartClassTagDeleteResponse) GetSuccess() bool { return v.Success } + +// DeleteSmartClassTagResponse is returned by DeleteSmartClassTag on success. +type DeleteSmartClassTagResponse struct { + DeleteSmartClassTag DeleteSmartClassTagDeleteSmartClassTagDeleteResponse `json:"deleteSmartClassTag"` +} + +// GetDeleteSmartClassTag returns DeleteSmartClassTagResponse.DeleteSmartClassTag, and is useful for accessing the field via an interface. +func (v *DeleteSmartClassTagResponse) GetDeleteSmartClassTag() DeleteSmartClassTagDeleteSmartClassTagDeleteResponse { + return v.DeleteSmartClassTag +} + // DeleteUserGroupDeleteIamGroupSuccessStatusResult includes the requested fields of the GraphQL type SuccessStatusResult. type DeleteUserGroupDeleteIamGroupSuccessStatusResult struct { Success bool `json:"success"` @@ -2488,6 +2675,84 @@ func (v *GetResourcePermissionsResponse) GetGetResourcePermissions() []GetResour return v.GetResourcePermissions } +// GetSmartClassQueryResponse is returned by GetSmartClassQuery on success. +type GetSmartClassQueryResponse struct { + SmartClassQuery GetSmartClassQuerySmartClassQuery `json:"smartClassQuery"` +} + +// GetSmartClassQuery returns GetSmartClassQueryResponse.SmartClassQuery, and is useful for accessing the field via an interface. +func (v *GetSmartClassQueryResponse) GetSmartClassQuery() GetSmartClassQuerySmartClassQuery { + return v.SmartClassQuery +} + +// GetSmartClassQuerySmartClassQuery includes the requested fields of the GraphQL type SmartClassQuery. +type GetSmartClassQuerySmartClassQuery struct { + Id string `json:"id"` + Query string `json:"query"` + SmartClassId string `json:"smartClassId"` + Description string `json:"description"` +} + +// GetId returns GetSmartClassQuerySmartClassQuery.Id, and is useful for accessing the field via an interface. +func (v *GetSmartClassQuerySmartClassQuery) GetId() string { return v.Id } + +// GetQuery returns GetSmartClassQuerySmartClassQuery.Query, and is useful for accessing the field via an interface. +func (v *GetSmartClassQuerySmartClassQuery) GetQuery() string { return v.Query } + +// GetSmartClassId returns GetSmartClassQuerySmartClassQuery.SmartClassId, and is useful for accessing the field via an interface. +func (v *GetSmartClassQuerySmartClassQuery) GetSmartClassId() string { return v.SmartClassId } + +// GetDescription returns GetSmartClassQuerySmartClassQuery.Description, and is useful for accessing the field via an interface. +func (v *GetSmartClassQuerySmartClassQuery) GetDescription() string { return v.Description } + +// GetSmartClassResponse is returned by GetSmartClass on success. +type GetSmartClassResponse struct { + SmartClass GetSmartClassSmartClass `json:"smartClass"` +} + +// GetSmartClass returns GetSmartClassResponse.SmartClass, and is useful for accessing the field via an interface. +func (v *GetSmartClassResponse) GetSmartClass() GetSmartClassSmartClass { return v.SmartClass } + +// GetSmartClassSmartClass includes the requested fields of the GraphQL type SmartClass. +type GetSmartClassSmartClass struct { + Id string `json:"id"` + TagName string `json:"tagName"` + Description string `json:"description"` + Tags []GetSmartClassSmartClassTagsSmartClassTag `json:"tags"` +} + +// GetId returns GetSmartClassSmartClass.Id, and is useful for accessing the field via an interface. +func (v *GetSmartClassSmartClass) GetId() string { return v.Id } + +// GetTagName returns GetSmartClassSmartClass.TagName, and is useful for accessing the field via an interface. +func (v *GetSmartClassSmartClass) GetTagName() string { return v.TagName } + +// GetDescription returns GetSmartClassSmartClass.Description, and is useful for accessing the field via an interface. +func (v *GetSmartClassSmartClass) GetDescription() string { return v.Description } + +// GetTags returns GetSmartClassSmartClass.Tags, and is useful for accessing the field via an interface. +func (v *GetSmartClassSmartClass) GetTags() []GetSmartClassSmartClassTagsSmartClassTag { return v.Tags } + +// GetSmartClassSmartClassTagsSmartClassTag includes the requested fields of the GraphQL type SmartClassTag. +type GetSmartClassSmartClassTagsSmartClassTag struct { + Id string `json:"id"` + Name string `json:"name"` + Type SmartClassTagType `json:"type"` + Value string `json:"value"` +} + +// GetId returns GetSmartClassSmartClassTagsSmartClassTag.Id, and is useful for accessing the field via an interface. +func (v *GetSmartClassSmartClassTagsSmartClassTag) GetId() string { return v.Id } + +// GetName returns GetSmartClassSmartClassTagsSmartClassTag.Name, and is useful for accessing the field via an interface. +func (v *GetSmartClassSmartClassTagsSmartClassTag) GetName() string { return v.Name } + +// GetType returns GetSmartClassSmartClassTagsSmartClassTag.Type, and is useful for accessing the field via an interface. +func (v *GetSmartClassSmartClassTagsSmartClassTag) GetType() SmartClassTagType { return v.Type } + +// GetValue returns GetSmartClassSmartClassTagsSmartClassTag.Value, and is useful for accessing the field via an interface. +func (v *GetSmartClassSmartClassTagsSmartClassTag) GetValue() string { return v.Value } + // GetUserGroupIamGetGroupIamGroup includes the requested fields of the GraphQL type IamGroup. type GetUserGroupIamGetGroupIamGroup struct { Id string `json:"id"` @@ -2890,6 +3155,51 @@ func (v *PatchInsightsDashboardInput) GetPublishedToGroupIds() []string { return // GetResourceGroupId returns PatchInsightsDashboardInput.ResourceGroupId, and is useful for accessing the field via an interface. func (v *PatchInsightsDashboardInput) GetResourceGroupId() string { return v.ResourceGroupId } +type PatchSmartClassInput struct { + Id string `json:"id"` + Description string `json:"description"` +} + +// GetId returns PatchSmartClassInput.Id, and is useful for accessing the field via an interface. +func (v *PatchSmartClassInput) GetId() string { return v.Id } + +// GetDescription returns PatchSmartClassInput.Description, and is useful for accessing the field via an interface. +func (v *PatchSmartClassInput) GetDescription() string { return v.Description } + +type PatchSmartClassQueryInput struct { + Id string `json:"id"` + Description string `json:"description"` + Query string `json:"query"` +} + +// GetId returns PatchSmartClassQueryInput.Id, and is useful for accessing the field via an interface. +func (v *PatchSmartClassQueryInput) GetId() string { return v.Id } + +// GetDescription returns PatchSmartClassQueryInput.Description, and is useful for accessing the field via an interface. +func (v *PatchSmartClassQueryInput) GetDescription() string { return v.Description } + +// GetQuery returns PatchSmartClassQueryInput.Query, and is useful for accessing the field via an interface. +func (v *PatchSmartClassQueryInput) GetQuery() string { return v.Query } + +type PatchSmartClassTagInput struct { + Id string `json:"id"` + Name string `json:"name"` + Type SmartClassTagType `json:"type"` + Value string `json:"value"` +} + +// GetId returns PatchSmartClassTagInput.Id, and is useful for accessing the field via an interface. +func (v *PatchSmartClassTagInput) GetId() string { return v.Id } + +// GetName returns PatchSmartClassTagInput.Name, and is useful for accessing the field via an interface. +func (v *PatchSmartClassTagInput) GetName() string { return v.Name } + +// GetType returns PatchSmartClassTagInput.Type, and is useful for accessing the field via an interface. +func (v *PatchSmartClassTagInput) GetType() SmartClassTagType { return v.Type } + +// GetValue returns PatchSmartClassTagInput.Value, and is useful for accessing the field via an interface. +func (v *PatchSmartClassTagInput) GetValue() string { return v.Value } + type QueryResultsAre string const ( @@ -3255,6 +3565,14 @@ func (v *SetResourcePermissionSetResourcePermissionIamResourcePermission) GetSub return v.SubjectType } +type SmartClassTagType string + +const ( + SmartClassTagTypeString SmartClassTagType = "string" + SmartClassTagTypeBoolean SmartClassTagType = "boolean" + SmartClassTagTypeNumber SmartClassTagType = "number" +) + type UpdateComplianceFrameworkFields struct { Name string `json:"name"` WebLink string `json:"webLink"` @@ -4013,6 +4331,88 @@ func (v *UpdateResourceGroupUpdateResourceGroupIamResourceGroup) __premarshalJSO return &retval, nil } +// UpdateSmartClassPatchSmartClass includes the requested fields of the GraphQL type SmartClass. +type UpdateSmartClassPatchSmartClass struct { + Id string `json:"id"` + Description string `json:"description"` +} + +// GetId returns UpdateSmartClassPatchSmartClass.Id, and is useful for accessing the field via an interface. +func (v *UpdateSmartClassPatchSmartClass) GetId() string { return v.Id } + +// GetDescription returns UpdateSmartClassPatchSmartClass.Description, and is useful for accessing the field via an interface. +func (v *UpdateSmartClassPatchSmartClass) GetDescription() string { return v.Description } + +// UpdateSmartClassQueryPatchSmartClassQuery includes the requested fields of the GraphQL type SmartClassQuery. +type UpdateSmartClassQueryPatchSmartClassQuery struct { + Id string `json:"id"` + Query string `json:"query"` + SmartClassId string `json:"smartClassId"` + Description string `json:"description"` +} + +// GetId returns UpdateSmartClassQueryPatchSmartClassQuery.Id, and is useful for accessing the field via an interface. +func (v *UpdateSmartClassQueryPatchSmartClassQuery) GetId() string { return v.Id } + +// GetQuery returns UpdateSmartClassQueryPatchSmartClassQuery.Query, and is useful for accessing the field via an interface. +func (v *UpdateSmartClassQueryPatchSmartClassQuery) GetQuery() string { return v.Query } + +// GetSmartClassId returns UpdateSmartClassQueryPatchSmartClassQuery.SmartClassId, and is useful for accessing the field via an interface. +func (v *UpdateSmartClassQueryPatchSmartClassQuery) GetSmartClassId() string { return v.SmartClassId } + +// GetDescription returns UpdateSmartClassQueryPatchSmartClassQuery.Description, and is useful for accessing the field via an interface. +func (v *UpdateSmartClassQueryPatchSmartClassQuery) GetDescription() string { return v.Description } + +// UpdateSmartClassQueryResponse is returned by UpdateSmartClassQuery on success. +type UpdateSmartClassQueryResponse struct { + PatchSmartClassQuery UpdateSmartClassQueryPatchSmartClassQuery `json:"patchSmartClassQuery"` +} + +// GetPatchSmartClassQuery returns UpdateSmartClassQueryResponse.PatchSmartClassQuery, and is useful for accessing the field via an interface. +func (v *UpdateSmartClassQueryResponse) GetPatchSmartClassQuery() UpdateSmartClassQueryPatchSmartClassQuery { + return v.PatchSmartClassQuery +} + +// UpdateSmartClassResponse is returned by UpdateSmartClass on success. +type UpdateSmartClassResponse struct { + PatchSmartClass UpdateSmartClassPatchSmartClass `json:"patchSmartClass"` +} + +// GetPatchSmartClass returns UpdateSmartClassResponse.PatchSmartClass, and is useful for accessing the field via an interface. +func (v *UpdateSmartClassResponse) GetPatchSmartClass() UpdateSmartClassPatchSmartClass { + return v.PatchSmartClass +} + +// UpdateSmartClassTagPatchSmartClassTag includes the requested fields of the GraphQL type SmartClassTag. +type UpdateSmartClassTagPatchSmartClassTag struct { + Id string `json:"id"` + Name string `json:"name"` + Type SmartClassTagType `json:"type"` + Value string `json:"value"` +} + +// GetId returns UpdateSmartClassTagPatchSmartClassTag.Id, and is useful for accessing the field via an interface. +func (v *UpdateSmartClassTagPatchSmartClassTag) GetId() string { return v.Id } + +// GetName returns UpdateSmartClassTagPatchSmartClassTag.Name, and is useful for accessing the field via an interface. +func (v *UpdateSmartClassTagPatchSmartClassTag) GetName() string { return v.Name } + +// GetType returns UpdateSmartClassTagPatchSmartClassTag.Type, and is useful for accessing the field via an interface. +func (v *UpdateSmartClassTagPatchSmartClassTag) GetType() SmartClassTagType { return v.Type } + +// GetValue returns UpdateSmartClassTagPatchSmartClassTag.Value, and is useful for accessing the field via an interface. +func (v *UpdateSmartClassTagPatchSmartClassTag) GetValue() string { return v.Value } + +// UpdateSmartClassTagResponse is returned by UpdateSmartClassTag on success. +type UpdateSmartClassTagResponse struct { + PatchSmartClassTag UpdateSmartClassTagPatchSmartClassTag `json:"patchSmartClassTag"` +} + +// GetPatchSmartClassTag returns UpdateSmartClassTagResponse.PatchSmartClassTag, and is useful for accessing the field via an interface. +func (v *UpdateSmartClassTagResponse) GetPatchSmartClassTag() UpdateSmartClassTagPatchSmartClassTag { + return v.PatchSmartClassTag +} + // UpdateUserGroupResponse is returned by UpdateUserGroup on success. type UpdateUserGroupResponse struct { UpdateIamGroup UpdateUserGroupUpdateIamGroupV1Group `json:"updateIamGroup"` @@ -4212,6 +4612,30 @@ type __CreateResourceGroupInput struct { // GetInput returns __CreateResourceGroupInput.Input, and is useful for accessing the field via an interface. func (v *__CreateResourceGroupInput) GetInput() CreateIamResourceGroupInput { return v.Input } +// __CreateSmartClassInput is used internally by genqlient +type __CreateSmartClassInput struct { + Input CreateSmartClassInput `json:"input"` +} + +// GetInput returns __CreateSmartClassInput.Input, and is useful for accessing the field via an interface. +func (v *__CreateSmartClassInput) GetInput() CreateSmartClassInput { return v.Input } + +// __CreateSmartClassQueryInput is used internally by genqlient +type __CreateSmartClassQueryInput struct { + Input CreateSmartClassQueryInput `json:"input"` +} + +// GetInput returns __CreateSmartClassQueryInput.Input, and is useful for accessing the field via an interface. +func (v *__CreateSmartClassQueryInput) GetInput() CreateSmartClassQueryInput { return v.Input } + +// __CreateSmartClassTagInput is used internally by genqlient +type __CreateSmartClassTagInput struct { + Input CreateSmartClassTagInput `json:"input"` +} + +// GetInput returns __CreateSmartClassTagInput.Input, and is useful for accessing the field via an interface. +func (v *__CreateSmartClassTagInput) GetInput() CreateSmartClassTagInput { return v.Input } + // __CreateUserGroupInput is used internally by genqlient type __CreateUserGroupInput struct { Name string `json:"name"` @@ -4340,6 +4764,30 @@ type __DeleteRuleInstanceInput struct { // GetId returns __DeleteRuleInstanceInput.Id, and is useful for accessing the field via an interface. func (v *__DeleteRuleInstanceInput) GetId() string { return v.Id } +// __DeleteSmartClassInput is used internally by genqlient +type __DeleteSmartClassInput struct { + SmartClassId string `json:"smartClassId"` +} + +// GetSmartClassId returns __DeleteSmartClassInput.SmartClassId, and is useful for accessing the field via an interface. +func (v *__DeleteSmartClassInput) GetSmartClassId() string { return v.SmartClassId } + +// __DeleteSmartClassQueryInput is used internally by genqlient +type __DeleteSmartClassQueryInput struct { + SmartClassQueryId string `json:"smartClassQueryId"` +} + +// GetSmartClassQueryId returns __DeleteSmartClassQueryInput.SmartClassQueryId, and is useful for accessing the field via an interface. +func (v *__DeleteSmartClassQueryInput) GetSmartClassQueryId() string { return v.SmartClassQueryId } + +// __DeleteSmartClassTagInput is used internally by genqlient +type __DeleteSmartClassTagInput struct { + SmartClassTagId string `json:"smartClassTagId"` +} + +// GetSmartClassTagId returns __DeleteSmartClassTagInput.SmartClassTagId, and is useful for accessing the field via an interface. +func (v *__DeleteSmartClassTagInput) GetSmartClassTagId() string { return v.SmartClassTagId } + // __DeleteUserGroupInput is used internally by genqlient type __DeleteUserGroupInput struct { Name string `json:"name"` @@ -4456,6 +4904,22 @@ func (v *__GetResourcePermissionsInput) GetCursor() string { return v.Cursor } // GetLimit returns __GetResourcePermissionsInput.Limit, and is useful for accessing the field via an interface. func (v *__GetResourcePermissionsInput) GetLimit() int { return v.Limit } +// __GetSmartClassInput is used internally by genqlient +type __GetSmartClassInput struct { + SmartClassId string `json:"smartClassId"` +} + +// GetSmartClassId returns __GetSmartClassInput.SmartClassId, and is useful for accessing the field via an interface. +func (v *__GetSmartClassInput) GetSmartClassId() string { return v.SmartClassId } + +// __GetSmartClassQueryInput is used internally by genqlient +type __GetSmartClassQueryInput struct { + SmartClassQueryId string `json:"smartClassQueryId"` +} + +// GetSmartClassQueryId returns __GetSmartClassQueryInput.SmartClassQueryId, and is useful for accessing the field via an interface. +func (v *__GetSmartClassQueryInput) GetSmartClassQueryId() string { return v.SmartClassQueryId } + // __GetUserGroupInput is used internally by genqlient type __GetUserGroupInput struct { Id string `json:"id"` @@ -4634,6 +5098,30 @@ type __UpdateResourceGroupInput struct { // GetInput returns __UpdateResourceGroupInput.Input, and is useful for accessing the field via an interface. func (v *__UpdateResourceGroupInput) GetInput() UpdateIamResourceGroupInput { return v.Input } +// __UpdateSmartClassInput is used internally by genqlient +type __UpdateSmartClassInput struct { + Input PatchSmartClassInput `json:"input"` +} + +// GetInput returns __UpdateSmartClassInput.Input, and is useful for accessing the field via an interface. +func (v *__UpdateSmartClassInput) GetInput() PatchSmartClassInput { return v.Input } + +// __UpdateSmartClassQueryInput is used internally by genqlient +type __UpdateSmartClassQueryInput struct { + Input PatchSmartClassQueryInput `json:"input"` +} + +// GetInput returns __UpdateSmartClassQueryInput.Input, and is useful for accessing the field via an interface. +func (v *__UpdateSmartClassQueryInput) GetInput() PatchSmartClassQueryInput { return v.Input } + +// __UpdateSmartClassTagInput is used internally by genqlient +type __UpdateSmartClassTagInput struct { + Input PatchSmartClassTagInput `json:"input"` +} + +// GetInput returns __UpdateSmartClassTagInput.Input, and is useful for accessing the field via an interface. +func (v *__UpdateSmartClassTagInput) GetInput() PatchSmartClassTagInput { return v.Input } + // __UpdateUserGroupInput is used internally by genqlient type __UpdateUserGroupInput struct { Id string `json:"id"` @@ -5081,6 +5569,110 @@ fragment ResourceGroup on IamResourceGroup { return &data, err } +func CreateSmartClass( + ctx context.Context, + client graphql.Client, + input CreateSmartClassInput, +) (*CreateSmartClassResponse, error) { + req := &graphql.Request{ + OpName: "CreateSmartClass", + Query: ` +mutation CreateSmartClass ($input: CreateSmartClassInput!) { + createSmartClass(input: $input) { + id + tagName + description + } +} +`, + Variables: &__CreateSmartClassInput{ + Input: input, + }, + } + var err error + + var data CreateSmartClassResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + ctx, + req, + resp, + ) + + return &data, err +} + +func CreateSmartClassQuery( + ctx context.Context, + client graphql.Client, + input CreateSmartClassQueryInput, +) (*CreateSmartClassQueryResponse, error) { + req := &graphql.Request{ + OpName: "CreateSmartClassQuery", + Query: ` +mutation CreateSmartClassQuery ($input: CreateSmartClassQueryInput!) { + createSmartClassQuery(input: $input) { + id + query + smartClassId + description + } +} +`, + Variables: &__CreateSmartClassQueryInput{ + Input: input, + }, + } + var err error + + var data CreateSmartClassQueryResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + ctx, + req, + resp, + ) + + return &data, err +} + +func CreateSmartClassTag( + ctx context.Context, + client graphql.Client, + input CreateSmartClassTagInput, +) (*CreateSmartClassTagResponse, error) { + req := &graphql.Request{ + OpName: "CreateSmartClassTag", + Query: ` +mutation CreateSmartClassTag ($input: CreateSmartClassTagInput!) { + createSmartClassTag(input: $input) { + id + name + type + value + } +} +`, + Variables: &__CreateSmartClassTagInput{ + Input: input, + }, + } + var err error + + var data CreateSmartClassTagResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + ctx, + req, + resp, + ) + + return &data, err +} + func CreateUserGroup( ctx context.Context, client graphql.Client, @@ -5546,6 +6138,102 @@ mutation DeleteRuleInstance ($id: ID!) { return &data, err } +func DeleteSmartClass( + ctx context.Context, + client graphql.Client, + smartClassId string, +) (*DeleteSmartClassResponse, error) { + req := &graphql.Request{ + OpName: "DeleteSmartClass", + Query: ` +mutation DeleteSmartClass ($smartClassId: ID!) { + deleteSmartClass(id: $smartClassId) { + success + } +} +`, + Variables: &__DeleteSmartClassInput{ + SmartClassId: smartClassId, + }, + } + var err error + + var data DeleteSmartClassResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + ctx, + req, + resp, + ) + + return &data, err +} + +func DeleteSmartClassQuery( + ctx context.Context, + client graphql.Client, + smartClassQueryId string, +) (*DeleteSmartClassQueryResponse, error) { + req := &graphql.Request{ + OpName: "DeleteSmartClassQuery", + Query: ` +mutation DeleteSmartClassQuery ($smartClassQueryId: ID!) { + deleteSmartClassQuery(id: $smartClassQueryId) { + success + } +} +`, + Variables: &__DeleteSmartClassQueryInput{ + SmartClassQueryId: smartClassQueryId, + }, + } + var err error + + var data DeleteSmartClassQueryResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + ctx, + req, + resp, + ) + + return &data, err +} + +func DeleteSmartClassTag( + ctx context.Context, + client graphql.Client, + smartClassTagId string, +) (*DeleteSmartClassTagResponse, error) { + req := &graphql.Request{ + OpName: "DeleteSmartClassTag", + Query: ` +mutation DeleteSmartClassTag ($smartClassTagId: ID!) { + deleteSmartClassTag(id: $smartClassTagId) { + success + } +} +`, + Variables: &__DeleteSmartClassTagInput{ + SmartClassTagId: smartClassTagId, + }, + } + var err error + + var data DeleteSmartClassTagResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + ctx, + req, + resp, + ) + + return &data, err +} + func DeleteUserGroup( ctx context.Context, client graphql.Client, @@ -6161,6 +6849,81 @@ query GetResourcePermissions ($filter: GetResourcePermissionsFilter!, $cursor: S return &data, err } +func GetSmartClass( + ctx context.Context, + client graphql.Client, + smartClassId string, +) (*GetSmartClassResponse, error) { + req := &graphql.Request{ + OpName: "GetSmartClass", + Query: ` +query GetSmartClass ($smartClassId: ID!) { + smartClass(id: $smartClassId) { + id + tagName + description + tags { + id + name + type + value + } + } +} +`, + Variables: &__GetSmartClassInput{ + SmartClassId: smartClassId, + }, + } + var err error + + var data GetSmartClassResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + ctx, + req, + resp, + ) + + return &data, err +} + +func GetSmartClassQuery( + ctx context.Context, + client graphql.Client, + smartClassQueryId string, +) (*GetSmartClassQueryResponse, error) { + req := &graphql.Request{ + OpName: "GetSmartClassQuery", + Query: ` +query GetSmartClassQuery ($smartClassQueryId: ID!) { + smartClassQuery(id: $smartClassQueryId) { + id + query + smartClassId + description + } +} +`, + Variables: &__GetSmartClassQueryInput{ + SmartClassQueryId: smartClassQueryId, + }, + } + var err error + + var data GetSmartClassQueryResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + ctx, + req, + resp, + ) + + return &data, err +} + func GetUserGroup( ctx context.Context, client graphql.Client, @@ -6826,6 +7589,109 @@ fragment ResourceGroup on IamResourceGroup { return &data, err } +func UpdateSmartClass( + ctx context.Context, + client graphql.Client, + input PatchSmartClassInput, +) (*UpdateSmartClassResponse, error) { + req := &graphql.Request{ + OpName: "UpdateSmartClass", + Query: ` +mutation UpdateSmartClass ($input: PatchSmartClassInput!) { + patchSmartClass(input: $input) { + id + description + } +} +`, + Variables: &__UpdateSmartClassInput{ + Input: input, + }, + } + var err error + + var data UpdateSmartClassResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + ctx, + req, + resp, + ) + + return &data, err +} + +func UpdateSmartClassQuery( + ctx context.Context, + client graphql.Client, + input PatchSmartClassQueryInput, +) (*UpdateSmartClassQueryResponse, error) { + req := &graphql.Request{ + OpName: "UpdateSmartClassQuery", + Query: ` +mutation UpdateSmartClassQuery ($input: PatchSmartClassQueryInput!) { + patchSmartClassQuery(input: $input) { + id + query + smartClassId + description + } +} +`, + Variables: &__UpdateSmartClassQueryInput{ + Input: input, + }, + } + var err error + + var data UpdateSmartClassQueryResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + ctx, + req, + resp, + ) + + return &data, err +} + +func UpdateSmartClassTag( + ctx context.Context, + client graphql.Client, + input PatchSmartClassTagInput, +) (*UpdateSmartClassTagResponse, error) { + req := &graphql.Request{ + OpName: "UpdateSmartClassTag", + Query: ` +mutation UpdateSmartClassTag ($input: PatchSmartClassTagInput!) { + patchSmartClassTag(input: $input) { + id + name + type + value + } +} +`, + Variables: &__UpdateSmartClassTagInput{ + Input: input, + }, + } + var err error + + var data UpdateSmartClassTagResponse + resp := &graphql.Response{Data: &data} + + err = client.MakeRequest( + ctx, + req, + resp, + ) + + return &data, err +} + func UpdateUserGroup( ctx context.Context, client graphql.Client, diff --git a/jupiterone/internal/client/genqlient.yaml b/jupiterone/internal/client/genqlient.yaml index e604eb1e..5cc11638 100644 --- a/jupiterone/internal/client/genqlient.yaml +++ b/jupiterone/internal/client/genqlient.yaml @@ -13,6 +13,7 @@ operations: - integration.graphql - resourcePermissions.graphql - resourceGroups.graphql + - smartClasses.graphql generated: generated.go bindings: diff --git a/jupiterone/internal/client/smartClasses.graphql b/jupiterone/internal/client/smartClasses.graphql new file mode 100644 index 00000000..1ac03886 --- /dev/null +++ b/jupiterone/internal/client/smartClasses.graphql @@ -0,0 +1,91 @@ +query GetSmartClass($smartClassId: ID!) { + smartClass(id: $smartClassId) { + id + tagName + description + tags { + id + name + type + value + } + } +} + +mutation CreateSmartClass($input: CreateSmartClassInput!) { + createSmartClass(input: $input) { + id + tagName + description + } +} + +mutation DeleteSmartClass($smartClassId: ID!) { + deleteSmartClass(id: $smartClassId) { + success + } +} + +mutation UpdateSmartClass($input: PatchSmartClassInput!) { + patchSmartClass(input: $input) { + id + description + } +} + +query GetSmartClassQuery($smartClassQueryId: ID!) { + smartClassQuery(id: $smartClassQueryId) { + id + query + smartClassId + description + } +} + +mutation CreateSmartClassQuery($input: CreateSmartClassQueryInput!) { + createSmartClassQuery(input: $input) { + id + query + smartClassId + description + } +} + +mutation DeleteSmartClassQuery($smartClassQueryId: ID!) { + deleteSmartClassQuery(id: $smartClassQueryId) { + success + } +} + +mutation UpdateSmartClassQuery($input: PatchSmartClassQueryInput!) { + patchSmartClassQuery(input: $input) { + id + query + smartClassId + description + } +} + +mutation CreateSmartClassTag($input: CreateSmartClassTagInput!) { + createSmartClassTag(input: $input) { + id + name + type + value + } +} + +mutation DeleteSmartClassTag($smartClassTagId: ID!) { + deleteSmartClassTag(id: $smartClassTagId) { + success + } +} + +mutation UpdateSmartClassTag($input: PatchSmartClassTagInput!) { + patchSmartClassTag(input: $input) { + id + name + type + value + } +} \ No newline at end of file diff --git a/jupiterone/provider.go b/jupiterone/provider.go index 612cc807..9b23c1ab 100644 --- a/jupiterone/provider.go +++ b/jupiterone/provider.go @@ -146,6 +146,9 @@ func (*JupiterOneProvider) Resources(context.Context) []func() resource.Resource NewIntegrationResource, NewResourcePermissionResource, NewResourceGroupResource, + NewSmartClassResource, + NewSmartClassQueryResource, + NewSmartClassTagResource, } } diff --git a/jupiterone/resource_resource_group.go b/jupiterone/resource_resource_group.go index bc8de128..d437ecba 100644 --- a/jupiterone/resource_resource_group.go +++ b/jupiterone/resource_resource_group.go @@ -164,14 +164,10 @@ func (r *ResourceGroupResource) Read(ctx context.Context, req resource.ReadReque if strings.Contains(err.Error(), "Item not found") { resp.State.RemoveResource(ctx) } else { - resp.Diagnostics.AddError("failed to get user group", err.Error()) + resp.Diagnostics.AddError("failed to get resource group", err.Error()) } return } - if err != nil { - resp.Diagnostics.AddError("failed to get resource group", err.Error()) - return - } data.Name = types.StringValue(resourceGroup.ResourceGroup.Name) diff --git a/jupiterone/resource_smart_class.go b/jupiterone/resource_smart_class.go new file mode 100644 index 00000000..9bf27629 --- /dev/null +++ b/jupiterone/resource_smart_class.go @@ -0,0 +1,174 @@ +package jupiterone + +import ( + "context" + "fmt" + "strings" + + "github.com/Khan/genqlient/graphql" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/jupiterone/terraform-provider-jupiterone/jupiterone/internal/client" +) + +type SmartClassResource struct { + version string + qlient graphql.Client +} + +func NewSmartClassResource() resource.Resource { + return &SmartClassResource{} +} + +type SmartClassRule struct { + EvaluationStep types.String `json:"evaluation_step,omitempty" tfsdk:"evaluation_step"` + LastEvaluationEndOn types.Int64 `json:"last_evaluation_end_on,omitempty" tfsdk:"last_evaluation_end_on"` +} + +type SmartClass struct { + Id types.String `json:"id,omitempty" tfsdk:"id"` + Description types.String `json:"description,omitempty" tfsdk:"description"` + TagName types.String `json:"tag_name,omitempty" tfsdk:"tag_name"` +} + +func (r *SmartClassResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data SmartClass + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + mutationResult, err := client.CreateSmartClass(ctx, r.qlient, client.CreateSmartClassInput{ + TagName: data.TagName.ValueString(), + Description: data.Description.ValueString(), + }) + + if err != nil { + resp.Diagnostics.AddError("Failed to create smart class", err.Error()) + return + } + + data.Id = types.StringValue(mutationResult.CreateSmartClass.Id) + + tflog.Trace(ctx, "Created smart class", map[string]interface{}{"id": data.Id}) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SmartClassResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data *SmartClass + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + smartClass, err := client.GetSmartClass(ctx, r.qlient, data.Id.ValueString()) + if err != nil { + if strings.Contains(err.Error(), "not found") { + resp.State.RemoveResource(ctx) + } else { + resp.Diagnostics.AddError("failed to get smart class", err.Error()) + } + return + } + + data.Id = types.StringValue(smartClass.SmartClass.Id) + data.TagName = types.StringValue(smartClass.SmartClass.TagName) + data.Description = types.StringValue(smartClass.SmartClass.Description) +} + +func (r *SmartClassResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data *SmartClass + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + if _, err := client.UpdateSmartClass(ctx, r.qlient, client.PatchSmartClassInput{ + Id: data.Id.ValueString(), + Description: data.Description.ValueString(), + }); err != nil { + resp.Diagnostics.AddError("Failed to update smart class", err.Error()) + return + } + + tflog.Trace(ctx, "Updated smart class", map[string]interface{}{"id": data.Id}) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SmartClassResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data *SmartClass + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + if _, err := client.DeleteSmartClass(ctx, r.qlient, data.Id.ValueString()); err != nil { + resp.Diagnostics.AddError("Failed to delete smart class", err.Error()) + } +} + +func (r *SmartClassResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *SmartClassResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_smart_class" +} + +func (r *SmartClassResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + p, ok := req.ProviderData.(*JupiterOneProvider) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected JupiterOneProvider, got %T. Please report this issue to the provider developers.", req.ProviderData), + ) + } + + r.version = p.version + r.qlient = p.Qlient + +} + +func (r *SmartClassResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "JupiterOne Smart Class", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "tag_name": schema.StringAttribute{ + Required: true, + Description: "The name of the smart class and the name of the tag that will be added to each entity returned from this class's queries. Must start with a capital letter, be alphanumeric, and contain no spaces.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "description": schema.StringAttribute{ + Optional: true, + Description: "The description of the smart class.", + }, + }, + } + +} diff --git a/jupiterone/resource_smart_class_query.go b/jupiterone/resource_smart_class_query.go new file mode 100644 index 00000000..c464bc28 --- /dev/null +++ b/jupiterone/resource_smart_class_query.go @@ -0,0 +1,181 @@ +package jupiterone + +import ( + "context" + "fmt" + "strings" + + "github.com/Khan/genqlient/graphql" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/jupiterone/terraform-provider-jupiterone/jupiterone/internal/client" +) + +type SmartClassQuery struct { + Id types.String `json:"id,omitempty" tfsdk:"id"` + Query types.String `json:"query,omitempty" tfsdk:"query"` + SmartClassId types.String `json:"smart_class_id,omitempty" tfsdk:"smart_class_id"` + Description types.String `json:"description,omitempty" tfsdk:"description"` +} + +func NewSmartClassQueryResource() resource.Resource { + return &SmartClassQueryResource{} +} + +type SmartClassQueryResource struct { + version string + qlient graphql.Client +} + +func (r *SmartClassQueryResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_smart_class_query" +} + +func (r *SmartClassQueryResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "A smart class query is a J1QL query that finds entities to associate with a smart class", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "query": schema.StringAttribute{ + Required: true, + Description: "The J1QL query to find entities for the smart class", + }, + "smart_class_id": schema.StringAttribute{ + Required: true, + Description: "The ID of the smart class to associate the query with", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "description": schema.StringAttribute{ + Required: true, + Description: "A description of the smart class query", + }, + }, + } +} + +func (r *SmartClassQueryResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + p, ok := req.ProviderData.(*JupiterOneProvider) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected JupiterOneProvider, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.version = p.version + r.qlient = p.Qlient +} + +func (r *SmartClassQueryResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *SmartClassQueryResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data SmartClassQuery + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + mutationResult, err := client.CreateSmartClassQuery(ctx, r.qlient, client.CreateSmartClassQueryInput{ + Query: data.Query.ValueString(), + SmartClassId: data.SmartClassId.ValueString(), + Description: data.Description.ValueString(), + }) + + if err != nil { + resp.Diagnostics.AddError("Failed to create smart class query", err.Error()) + return + } + + data.Id = types.StringValue(mutationResult.CreateSmartClassQuery.Id) + + tflog.Trace(ctx, "Created smart class query", map[string]interface{}{"id": data.Id}) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SmartClassQueryResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data *SmartClassQuery + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + smartClassQuery, err := client.GetSmartClassQuery(ctx, r.qlient, data.Id.ValueString()) + if err != nil { + if strings.Contains(err.Error(), "not found") { + resp.State.RemoveResource(ctx) + } else { + resp.Diagnostics.AddError("Failed to get smart class query", err.Error()) + } + return + } + + data.Id = types.StringValue(smartClassQuery.SmartClassQuery.Id) + data.Query = types.StringValue(smartClassQuery.SmartClassQuery.Query) + data.SmartClassId = types.StringValue(smartClassQuery.SmartClassQuery.SmartClassId) + data.Description = types.StringValue(smartClassQuery.SmartClassQuery.Description) +} + +func (r *SmartClassQueryResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data SmartClassQuery + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + _, err := client.UpdateSmartClassQuery(ctx, r.qlient, client.PatchSmartClassQueryInput{ + Id: data.Id.ValueString(), + Query: data.Query.ValueString(), + Description: data.Description.ValueString(), + }) + + if err != nil { + resp.Diagnostics.AddError("Failed to update smart class query", err.Error()) + return + } + + tflog.Trace(ctx, "Updated smart class query", map[string]interface{}{"id": data.Id}) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SmartClassQueryResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data *SmartClassQuery + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + if _, err := client.DeleteSmartClassQuery(ctx, r.qlient, data.Id.ValueString()); err != nil { + resp.Diagnostics.AddError("Failed to delete smart class query", err.Error()) + return + } + + resp.State.RemoveResource(ctx) +} diff --git a/jupiterone/resource_smart_class_query_test.go b/jupiterone/resource_smart_class_query_test.go new file mode 100644 index 00000000..848d405e --- /dev/null +++ b/jupiterone/resource_smart_class_query_test.go @@ -0,0 +1,109 @@ +package jupiterone + +import ( + "context" + "fmt" + "testing" + + "github.com/Khan/genqlient/graphql" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/jupiterone/terraform-provider-jupiterone/jupiterone/internal/client" +) + +func TestSmartClassQuery_Basic(t *testing.T) { + ctx := context.TODO() + + recordingClient, directClient, cleanup := setupTestClients(ctx, t) + defer cleanup(t) + + resourceName := "jupiterone_smart_class_query.test" + smartClassResourceName := "jupiterone_smart_class.test" + _query := "FIND User" + _description := "find users" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(recordingClient), + CheckDestroy: testAccCheckSmartClassQueryDestroy(ctx, directClient), + Steps: []resource.TestStep{ + { + Config: testSmartClassQueryBasicConfig(_query, _description), + Check: resource.ComposeTestCheckFunc( + testAccCheckSmartClassQueryExists(ctx, directClient), + resource.TestCheckResourceAttrSet(resourceName, "id"), + resource.TestCheckResourceAttrPair(resourceName, "smart_class_id", smartClassResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "query", _query), + resource.TestCheckResourceAttr(resourceName, "description", _description), + ), + }, + }, + }) +} + +func testAccCheckSmartClassQueryExists(ctx context.Context, qlient graphql.Client) resource.TestCheckFunc { + return func(s *terraform.State) error { + if err := smartClassQueryExistsHelper(ctx, s, qlient); err != nil { + return err + } + return nil + } +} + +func smartClassQueryExistsHelper(ctx context.Context, s *terraform.State, qlient graphql.Client) error { + if qlient == nil { + return nil + } + + for _, r := range s.RootModule().Resources { + if r.Type == "jupiterone_smart_class_query" { + id := r.Primary.ID + _, err := client.GetSmartClassQuery(ctx, qlient, id) + if err != nil { + return err + } + } + } + + return nil +} + +func testAccCheckSmartClassQueryDestroy(ctx context.Context, qlient graphql.Client) func(*terraform.State) error { + return func(s *terraform.State) error { + if err := smartClassQueryDestroyHelper(ctx, s, qlient); err != nil { + return err + } + return nil + } +} + +func smartClassQueryDestroyHelper(ctx context.Context, s *terraform.State, qlient graphql.Client) error { + if qlient == nil { + return nil + } + + err := smartClassQueryExistsHelper(ctx, s, qlient) + + if err == nil { + return fmt.Errorf("Smart class query still exists") + } + + return nil +} + +func testSmartClassQueryBasicConfig(_query string, _description string) string { + return fmt.Sprintf(` + provider "jupiterone" {} + + resource "jupiterone_smart_class" "test" { + tag_name = "TfProviderTagTest" + description = "xyz" + } + + resource "jupiterone_smart_class_query" "test" { + smart_class_id = jupiterone_smart_class.test.id + query = %q + description = %q + } + `, _query, _description) +} diff --git a/jupiterone/resource_smart_class_tag.go b/jupiterone/resource_smart_class_tag.go new file mode 100644 index 00000000..ae108220 --- /dev/null +++ b/jupiterone/resource_smart_class_tag.go @@ -0,0 +1,206 @@ +package jupiterone + +import ( + "context" + "fmt" + "strings" + + "github.com/Khan/genqlient/graphql" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/jupiterone/terraform-provider-jupiterone/jupiterone/internal/client" +) + +type SmartClassTag struct { + Id types.String `json:"id,omitempty" tfsdk:"id"` + SmartClassId types.String `json:"smart_class_id,omitempty" tfsdk:"smart_class_id"` + Name types.String `json:"name,omitempty" tfsdk:"name"` + Type types.String `json:"type,omitempty" tfsdk:"type"` + Value types.String `json:"value,omitempty" tfsdk:"value"` +} + +func NewSmartClassTagResource() resource.Resource { + return &SmartClassTagResource{} +} + +type SmartClassTagResource struct { + version string + qlient graphql.Client +} + +func (r *SmartClassTagResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_smart_class_tag" +} + +func (r *SmartClassTagResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "A smart class tag is another tag applied to entities found by a smart class query", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "smart_class_id": schema.StringAttribute{ + Required: true, + Description: "The ID of the smart class to associate the tag with", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "name": schema.StringAttribute{ + Required: true, + Description: "The name (key) of the tag", + }, + "type": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.OneOf("string", "boolean", "number"), + }, + Description: "The type of the tag, one of 'string', 'boolean', or 'number'", + }, + "value": schema.StringAttribute{ + Required: true, + Description: "The value of the tag as a string", + }, + }, + } +} + +func (r *SmartClassTagResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + p, ok := req.ProviderData.(*JupiterOneProvider) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected JupiterOneProvider, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.version = p.version + r.qlient = p.Qlient +} + +func (r *SmartClassTagResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *SmartClassTagResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data SmartClassTag + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + mutationResult, err := client.CreateSmartClassTag(ctx, r.qlient, client.CreateSmartClassTagInput{ + SmartClassId: data.SmartClassId.ValueString(), + Type: client.SmartClassTagType(data.Type.ValueString()), + Name: data.Name.ValueString(), + Value: data.Value.ValueString(), + }) + + if err != nil { + resp.Diagnostics.AddError("Failed to create smart class Tag", err.Error()) + return + } + + data.Id = types.StringValue(mutationResult.CreateSmartClassTag.Id) + + tflog.Trace(ctx, "Created smart class Tag", map[string]interface{}{"id": data.Id}) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SmartClassTagResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data *SmartClassTag + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + smartClass, err := client.GetSmartClass(ctx, r.qlient, data.SmartClassId.ValueString()) + + if err != nil { + resp.Diagnostics.AddError("Failed to read smart class Tag", err.Error()) + return + } + + var smartClassTag client.GetSmartClassSmartClassTagsSmartClassTag + found := false + for _, tag := range smartClass.SmartClass.Tags { + if strings.EqualFold(tag.Id, data.Id.ValueString()) { + smartClassTag = tag + found = true + break + } + } + + if !found { + resp.Diagnostics.AddError("Failed to read smart class Tag", "Tag not found on smart class") + return + } + + data.Id = types.StringValue(smartClassTag.Id) + data.Type = types.StringValue(string(smartClassTag.Type)) + data.Name = types.StringValue(smartClassTag.Name) + data.Value = types.StringValue(smartClassTag.Value) + data.SmartClassId = types.StringValue(data.SmartClassId.ValueString()) +} + +func (r *SmartClassTagResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data SmartClassTag + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + _, err := client.UpdateSmartClassTag(ctx, r.qlient, client.PatchSmartClassTagInput{ + Id: data.Id.ValueString(), + Type: client.SmartClassTagType(data.Type.ValueString()), + Name: data.Name.ValueString(), + Value: data.Value.ValueString(), + }) + + if err != nil { + resp.Diagnostics.AddError("Failed to update smart class Tag", err.Error()) + return + } + + tflog.Trace(ctx, "Updated smart class Tag", map[string]interface{}{"id": data.Id}) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SmartClassTagResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data *SmartClassTag + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + if _, err := client.DeleteSmartClassTag(ctx, r.qlient, data.Id.ValueString()); err != nil { + resp.Diagnostics.AddError("Failed to delete smart class Tag", err.Error()) + return + } + + resp.State.RemoveResource(ctx) +} diff --git a/jupiterone/resource_smart_class_tag_test.go b/jupiterone/resource_smart_class_tag_test.go new file mode 100644 index 00000000..616115e6 --- /dev/null +++ b/jupiterone/resource_smart_class_tag_test.go @@ -0,0 +1,124 @@ +package jupiterone + +import ( + "context" + "fmt" + "testing" + + "github.com/Khan/genqlient/graphql" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/jupiterone/terraform-provider-jupiterone/jupiterone/internal/client" +) + +func TestSmartClassTag_Basic(t *testing.T) { + ctx := context.TODO() + + recordingClient, directClient, cleanup := setupTestClients(ctx, t) + defer cleanup(t) + + resourceName := "jupiterone_smart_class_tag.test" + smartClassResourceName := "jupiterone_smart_class.test" + _name := "tagname" + _type := "boolean" + _value := "true" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(recordingClient), + CheckDestroy: testAccCheckSmartClassTagDestroy(ctx, directClient), + Steps: []resource.TestStep{ + { + Config: testSmartClassTagBasicConfig(_name, _type, _value), + Check: resource.ComposeTestCheckFunc( + testAccCheckSmartClassTagExists(ctx, directClient), + resource.TestCheckResourceAttrSet(resourceName, "id"), + resource.TestCheckResourceAttrPair(resourceName, "smart_class_id", smartClassResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "name", _name), + resource.TestCheckResourceAttr(resourceName, "type", _type), + resource.TestCheckResourceAttr(resourceName, "value", _value), + ), + }, + }, + }) +} + +func testAccCheckSmartClassTagExists(ctx context.Context, qlient graphql.Client) resource.TestCheckFunc { + return func(s *terraform.State) error { + if err := smartClassTagExistsHelper(ctx, s, qlient); err != nil { + return err + } + return nil + } +} + +func smartClassTagExistsHelper(ctx context.Context, s *terraform.State, qlient graphql.Client) error { + if qlient == nil { + return nil + } + + var smartClassTag client.GetSmartClassSmartClassTagsSmartClassTag + for _, r := range s.RootModule().Resources { + if r.Type == "jupiterone_smart_class" { + id := r.Primary.ID + smartClassResponse, err := client.GetSmartClass(ctx, qlient, id) + if err != nil { + return err + } + + for _, tag := range smartClassResponse.SmartClass.Tags { + if tag.Name == "tagname" { + smartClassTag = tag + break + } + } + } + } + + if smartClassTag.Name != "tagname" { + return fmt.Errorf("Smart class tag does not exist") + } + + return nil +} + +func testAccCheckSmartClassTagDestroy(ctx context.Context, qlient graphql.Client) func(*terraform.State) error { + return func(s *terraform.State) error { + if err := smartClassTagDestroyHelper(ctx, s, qlient); err != nil { + return err + } + return nil + } +} + +func smartClassTagDestroyHelper(ctx context.Context, s *terraform.State, qlient graphql.Client) error { + if qlient == nil { + return nil + } + + err := smartClassTagExistsHelper(ctx, s, qlient) + + if err == nil { + return fmt.Errorf("Smart class tag still exists") + } + + return nil +} + +func testSmartClassTagBasicConfig(_name string, _type string, _value string) string { + return fmt.Sprintf(` + provider "jupiterone" {} + + resource "jupiterone_smart_class" "test" { + tag_name = "TfProviderTagTest" + description = "xyz" + } + + resource "jupiterone_smart_class_tag" "test" { + smart_class_id = jupiterone_smart_class.test.id + name = %q + type = %q + value = %q + } + `, _name, _type, _value) +} diff --git a/jupiterone/resource_smart_class_test.go b/jupiterone/resource_smart_class_test.go new file mode 100644 index 00000000..dc30bf07 --- /dev/null +++ b/jupiterone/resource_smart_class_test.go @@ -0,0 +1,174 @@ +package jupiterone + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + "github.com/Khan/genqlient/graphql" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/jupiterone/terraform-provider-jupiterone/jupiterone/internal/client" +) + +func TestSmartClass_Basic(t *testing.T) { + ctx := context.TODO() + + recordingClient, directClient, cleanup := setupTestClients(ctx, t) + defer cleanup(t) + + resourceName := "jupiterone_smart_class.test" + smartClassTagName := "TfProviderTestSmartClass" + smartClassDescription := "description of " + smartClassTagName + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(recordingClient), + CheckDestroy: testAccCheckSmartClassDestroy(ctx, directClient), + Steps: []resource.TestStep{ + { + Config: testSmartClassBasicConfig(smartClassTagName, smartClassDescription), + Check: resource.ComposeTestCheckFunc( + testAccCheckSmartClassExists(ctx, directClient), + resource.TestCheckResourceAttrSet(resourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "tag_name", smartClassTagName), + resource.TestCheckResourceAttr(resourceName, "description", smartClassDescription), + ), + }, + }, + }) +} + +func TestSmartClass_BasicImport(t *testing.T) { + ctx := context.TODO() + + recordingClient, directClient, cleanup := setupTestClients(ctx, t) + defer cleanup(t) + + resourceName := "jupiterone_smart_class.test" + smartClassTagName := "TfProviderTestImport" + smartClassDescription := "description of " + smartClassTagName + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(recordingClient), + Steps: []resource.TestStep{ + { + ImportState: true, + ResourceName: resourceName, + ImportStateId: createTestSmartClass(ctx, t, recordingClient, smartClassTagName, smartClassDescription), + Config: testSmartClassBasicConfig(smartClassTagName, smartClassDescription), + Check: resource.ComposeTestCheckFunc( + testAccCheckSmartClassExists(ctx, directClient), + resource.TestCheckResourceAttrSet(resourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "tag_name", smartClassTagName), + resource.TestCheckResourceAttr(resourceName, "description", smartClassDescription), + ), + }, + }, + }) +} + +func createTestSmartClass(ctx context.Context, t *testing.T, qlient graphql.Client, tagName string, description string) string { + r, err := client.CreateSmartClass(ctx, qlient, client.CreateSmartClassInput{ + TagName: tagName, + Description: description, + }) + if err != nil { + t.Log("error creating smart class import test", err) + t.FailNow() + } + + return r.CreateSmartClass.Id +} + +func testAccCheckSmartClassExists(ctx context.Context, qlient graphql.Client) resource.TestCheckFunc { + return func(s *terraform.State) error { + if err := smartClassExistsHelper(ctx, s, qlient); err != nil { + return err + } + return nil + } +} + +func smartClassExistsHelper(ctx context.Context, s *terraform.State, qlient graphql.Client) error { + if qlient == nil { + return nil + } + + duration := 10 * time.Second + for _, r := range s.RootModule().Resources { + err := retry.RetryContext(ctx, duration, func() *retry.RetryError { + id := r.Primary.ID + _, err := client.GetSmartClass(ctx, qlient, id) + + if err == nil { + return nil + } + + if strings.Contains(err.Error(), "Smart class string does not exist") { + return retry.RetryableError(fmt.Errorf("Smart class does not exist (id=%q)", id)) + } + + return retry.NonRetryableError(err) + }) + + if err != nil { + return err + } + } + + return nil +} + +func testAccCheckSmartClassDestroy(ctx context.Context, qlient graphql.Client) func(*terraform.State) error { + return func(s *terraform.State) error { + if err := smartClassDestroyHelper(ctx, s, qlient); err != nil { + return err + } + return nil + } +} + +func smartClassDestroyHelper(ctx context.Context, s *terraform.State, qlient graphql.Client) error { + if qlient == nil { + return nil + } + + duration := 10 * time.Second + for _, r := range s.RootModule().Resources { + err := retry.RetryContext(ctx, duration, func() *retry.RetryError { + id := r.Primary.ID + _, err := client.GetSmartClass(ctx, qlient, id) + + if err == nil { + return retry.RetryableError(fmt.Errorf("Smart class still exists (id=%q)", id)) + } + + if strings.Contains(err.Error(), "does not exist") { + return nil + } + + return retry.NonRetryableError(err) + }) + + if err != nil { + return err + } + } + return nil +} + +func testSmartClassBasicConfig(tagName string, description string) string { + return fmt.Sprintf(` + provider "jupiterone" {} + + resource "jupiterone_smart_class" "test" { + tag_name = %q + description = %q + } + `, tagName, description) +} diff --git a/scripts/get_current_schema.bash b/scripts/get_current_schema.bash index fb9907a6..4aad0b88 100755 --- a/scripts/get_current_schema.bash +++ b/scripts/get_current_schema.bash @@ -20,7 +20,7 @@ if [ -f "introspection_result.json" ]; then echo "Using already downloaded results, delete introspection_result.json to force a fetch" else curl --fail --location --request POST "${JUPITERONE_URL}" \ - --header "LifeOmic-Account: ${JUPITERONE_ACCOUNT}" \ + --header "LifeOmic-Account: ${JUPITERONE_ACCOUNT_ID}" \ --header "Authorization: Bearer ${JUPITERONE_API_KEY}" \ --header 'Content-Type: application/json' \ --output introspection_result.json \