Skip to content

Commit 58f8e1b

Browse files
authored
Add Search data source (#153)
1 parent 69bdc2d commit 58f8e1b

File tree

11 files changed

+1757
-11
lines changed

11 files changed

+1757
-11
lines changed

docs/data-sources/port_search.md

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "port_search Data Source - terraform-provider-port-labs"
4+
subcategory: ""
5+
description: |-
6+
Search Data Source
7+
The search data source allows you to search for entities in Port.
8+
See the Port documentation https://docs.getport.io/search-and-query/ for more information about the search capabilities in Port.
9+
Example Usage
10+
Search for all entities in a specific blueprint:
11+
```hcl
12+
data "portsearch" "allservice" {
13+
query = jsonencode({
14+
"combinator" : "and", "rules" : [
15+
{ "operator" : "=", "property" : "$blueprint", "value" : "Service" },
16+
]
17+
})
18+
}
19+
```
20+
Search for entity with specific identifier in a specific blueprint to create another resource based on the values of the entity:
21+
```hcl
22+
data "portsearch" "adsservice" {
23+
query = jsonencode({
24+
"combinator" : "and", "rules" : [
25+
{ "operator" : "=", "property" : "$blueprint", "value" : "Service" },
26+
{ "operator" : "=", "property" : "$identifier", "value" : "Ads" },
27+
]
28+
})
29+
}
30+
```
31+
Scorecards automation example
32+
In this example we are creating a jira task for each service that its Ownership Scorecard hasn't reached Gold level :
33+
```hcl
34+
data "portsearch" "allservices" {
35+
query = jsonencode({
36+
"combinator" : "and", "rules" : [
37+
{ "operator" : "=", "property" : "$blueprint", "value" : "microservice" },
38+
]
39+
})
40+
}
41+
locals {
42+
// Count the number of services that are not owned by a team with a Gold level
43+
microserviceownershipwithoutgoldlevel = length([
44+
for entity in data.portsearch.allservices.entities : entity.scorecards["ownership"].level
45+
if entity.scorecards["ownership"].level != "Gold"
46+
])
47+
}
48+
// create jira issue per service that is not owned by a team with a Gold level
49+
resource "jiraissue" "microserviceownershipwithoutgoldlevel" {
50+
count = local.microserviceownershipwithoutgoldlevel
51+
issuetype = "Task"
52+
project_key = "PORT"
53+
summary = "Service ${data.portsearch.backendservices.entities[count.index].title} hasn't reached Gold level in Ownership Scorecard"
54+
description = "Service https://app.getport.io/${port_blueprint.microservice.identifier}Entity/${data.port_search.backend_services.entities[count.index].identifier} is not owned by a team with a Gold level, please assign a team with a Gold level to the service"
55+
}
56+
```
57+
---
58+
59+
# port_search (Data Source)
60+
61+
# Search Data Source
62+
63+
The search data source allows you to search for entities in Port.
64+
65+
See the [Port documentation](https://docs.getport.io/search-and-query/) for more information about the search capabilities in Port.
66+
67+
## Example Usage
68+
69+
### Search for all entities in a specific blueprint:
70+
71+
```hcl
72+
73+
data "port_search" "all_service" {
74+
query = jsonencode({
75+
"combinator" : "and", "rules" : [
76+
{ "operator" : "=", "property" : "$blueprint", "value" : "Service" },
77+
]
78+
})
79+
}
80+
81+
82+
```
83+
84+
### Search for entity with specific identifier in a specific blueprint to create another resource based on the values of the entity:
85+
86+
87+
```hcl
88+
89+
data "port_search" "ads_service" {
90+
query = jsonencode({
91+
"combinator" : "and", "rules" : [
92+
{ "operator" : "=", "property" : "$blueprint", "value" : "Service" },
93+
{ "operator" : "=", "property" : "$identifier", "value" : "Ads" },
94+
]
95+
})
96+
}
97+
98+
99+
```
100+
101+
### Scorecards automation example
102+
In this example we are creating a jira task for each service that its Ownership Scorecard hasn't reached Gold level :
103+
104+
```hcl
105+
106+
data "port_search" "all_services" {
107+
query = jsonencode({
108+
"combinator" : "and", "rules" : [
109+
{ "operator" : "=", "property" : "$blueprint", "value" : "microservice" },
110+
]
111+
})
112+
}
113+
114+
locals {
115+
// Count the number of services that are not owned by a team with a Gold level
116+
microservice_ownership_without_gold_level = length([
117+
for entity in data.port_search.all_services.entities : entity.scorecards["ownership"].level
118+
if entity.scorecards["ownership"].level != "Gold"
119+
])
120+
}
121+
122+
// create jira issue per service that is not owned by a team with a Gold level
123+
resource "jira_issue" "microservice_ownership_without_gold_level" {
124+
count = local.microservice_ownership_without_gold_level
125+
issue_type = "Task"
126+
127+
project_key = "PORT"
128+
129+
summary = "Service ${data.port_search.backend_services.entities[count.index].title} hasn't reached Gold level in Ownership Scorecard"
130+
description = "[Service](https://app.getport.io/${port_blueprint.microservice.identifier}Entity/${data.port_search.backend_services.entities[count.index].identifier}) is not owned by a team with a Gold level, please assign a team with a Gold level to the service"
131+
}
132+
133+
134+
```
135+
136+
137+
138+
<!-- schema generated by tfplugindocs -->
139+
## Schema
140+
141+
### Required
142+
143+
- `query` (String) The search query
144+
145+
### Optional
146+
147+
- `attach_title_to_relation` (Boolean) Attach title to relation
148+
- `exclude` (List of String) Properties to exclude from the results
149+
- `exclude_calculated_properties` (Boolean) Exclude calculated properties
150+
- `include` (List of String) Properties to include in the results
151+
152+
### Read-Only
153+
154+
- `entities` (Attributes List) A list of entities matching the search query (see [below for nested schema](#nestedatt--entities))
155+
- `id` (String) The ID of this resource.
156+
- `matching_blueprints` (List of String) The matching blueprints for the search query
157+
158+
<a id="nestedatt--entities"></a>
159+
### Nested Schema for `entities`
160+
161+
Optional:
162+
163+
- `icon` (String) The icon of the entity
164+
- `properties` (Attributes) The properties of the entity (see [below for nested schema](#nestedatt--entities--properties))
165+
- `relations` (Attributes) The relations of the entity (see [below for nested schema](#nestedatt--entities--relations))
166+
- `run_id` (String) The runID of the action run that created the entity
167+
- `scorecards` (Map of Object) The scorecards of the entity (see [below for nested schema](#nestedatt--entities--scorecards))
168+
- `teams` (List of String) The teams the entity belongs to
169+
- `title` (String) The title of the entity
170+
171+
Read-Only:
172+
173+
- `blueprint` (String) The blueprint identifier the entity relates to
174+
- `created_at` (String) The creation date of the entity
175+
- `created_by` (String) The creator of the entity
176+
- `identifier` (String) The identifier of the entity
177+
- `updated_at` (String) The last update date of the entity
178+
- `updated_by` (String) The last updater of the entity
179+
180+
<a id="nestedatt--entities--properties"></a>
181+
### Nested Schema for `entities.properties`
182+
183+
Optional:
184+
185+
- `array_props` (Attributes) The array properties of the entity (see [below for nested schema](#nestedatt--entities--properties--array_props))
186+
- `boolean_props` (Map of Boolean) The bool properties of the entity
187+
- `number_props` (Map of Number) The number properties of the entity
188+
- `object_props` (Map of String) The object properties of the entity
189+
- `string_props` (Map of String) The string properties of the entity
190+
191+
<a id="nestedatt--entities--properties--array_props"></a>
192+
### Nested Schema for `entities.properties.array_props`
193+
194+
Optional:
195+
196+
- `boolean_items` (Map of List of Boolean)
197+
- `number_items` (Map of List of Number)
198+
- `object_items` (Map of List of String)
199+
- `string_items` (Map of List of String)
200+
201+
202+
203+
<a id="nestedatt--entities--relations"></a>
204+
### Nested Schema for `entities.relations`
205+
206+
Optional:
207+
208+
- `many_relations` (Map of List of String) The many relation of the entity
209+
- `single_relations` (Map of String) The single relation of the entity
210+
211+
212+
<a id="nestedatt--entities--scorecards"></a>
213+
### Nested Schema for `entities.scorecards`
214+
215+
Read-Only:
216+
217+
- `level` (String)
218+
- `rules` (List of Object) (see [below for nested schema](#nestedobjatt--entities--scorecards--rules))
219+
220+
<a id="nestedobjatt--entities--scorecards--rules"></a>
221+
### Nested Schema for `entities.scorecards.rules`
222+
223+
Read-Only:
224+
225+
- `identifier` (String)
226+
- `level` (String)
227+
- `status` (String)

internal/cli/models.go

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,27 @@ type (
1919
ExpiresIn int64 `json:"expiresIn"`
2020
TokenType string `json:"tokenType"`
2121
}
22+
23+
ScorecardRulesModel struct {
24+
Identifier string `tfsdk:"identifier"`
25+
Status string `tfsdk:"status"`
26+
Level string `tfsdk:"level"`
27+
}
28+
29+
ScorecardModel struct {
30+
Rules []ScorecardRulesModel `tfsdk:"rules"`
31+
Level string `tfsdk:"level"`
32+
}
33+
2234
Entity struct {
2335
Meta
24-
Identifier string `json:"identifier,omitempty"`
25-
Title string `json:"title"`
26-
Blueprint string `json:"blueprint"`
27-
Team []string `json:"team,omitempty"`
28-
Properties map[string]any `json:"properties"`
29-
Relations map[string]any `json:"relations"`
36+
Identifier string `json:"identifier,omitempty"`
37+
Title string `json:"title"`
38+
Blueprint string `json:"blueprint"`
39+
Team []string `json:"team,omitempty"`
40+
Properties map[string]any `json:"properties"`
41+
Relations map[string]any `json:"relations"`
42+
Scorecards map[string]ScorecardModel `json:"scorecards,omitempty"`
3043
// TODO: add the rest of the fields.
3144
}
3245

@@ -385,6 +398,14 @@ type (
385398
FailureCount int `json:"failureCount,omitempty"`
386399
SuccessCount int `json:"successCount,omitempty"`
387400
}
401+
402+
SearchRequestQuery struct {
403+
Query *map[string]any `json:"query"`
404+
ExcludeCalculatedProperties *bool `json:"exclude_calculated_properties,omitempty"`
405+
Include []string `json:"include,omitempty"`
406+
Exclude []string `json:"exclude,omitempty"`
407+
AttachTitleToRelation *bool `json:"attach_title_to_relation,omitempty"`
408+
}
388409
)
389410

390411
type PortBody struct {
@@ -402,6 +423,22 @@ type PortBody struct {
402423
Migration Migration `json:"migration"`
403424
}
404425

426+
type SearchEntityResult struct {
427+
Meta
428+
Identifier string `json:"identifier,omitempty"`
429+
Title string `json:"title,omitempty"`
430+
Icon *string `json:"icon,omitempty"`
431+
Team []string `json:"team,omitempty"`
432+
Properties map[string]any `json:"properties,omitempty"`
433+
Relations map[string]any `json:"relations,omitempty"`
434+
}
435+
436+
type SearchResult struct {
437+
OK bool `json:"ok"`
438+
MatchingBlueprints []string `json:"matchingBlueprints"`
439+
Entities []Entity `json:"entities"`
440+
}
441+
405442
type PortPagePermissionsBody struct {
406443
OK bool `json:"ok"`
407444
PagePermissions PagePermissions `json:"permissions"`

internal/cli/search.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package cli
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"strings"
8+
)
9+
10+
func (c *PortClient) Search(ctx context.Context, searchRequest *SearchRequestQuery) (*SearchResult, error) {
11+
url := "v1/entities/search"
12+
13+
req := c.Client.R().
14+
SetContext(ctx).
15+
SetBody(*searchRequest.Query).
16+
SetHeader("Accept", "application/json")
17+
18+
if searchRequest.ExcludeCalculatedProperties != nil {
19+
req.SetQueryParam("exclude_calculated_properties", fmt.Sprintf("%v", &searchRequest.ExcludeCalculatedProperties))
20+
}
21+
22+
if searchRequest.Include != nil && len(searchRequest.Include) > 0 {
23+
req.SetQueryParam("include", strings.Join(searchRequest.Include, ","))
24+
}
25+
26+
if searchRequest.Exclude != nil && len(searchRequest.Exclude) > 0 {
27+
req.SetQueryParam("exclude", strings.Join(searchRequest.Exclude, ","))
28+
}
29+
30+
if searchRequest.AttachTitleToRelation != nil {
31+
req.SetQueryParam("attach_title_to_relation", fmt.Sprintf("%v", &searchRequest.AttachTitleToRelation))
32+
}
33+
34+
resp, err := req.Post(url)
35+
36+
if err != nil {
37+
return nil, err
38+
}
39+
var searchResult SearchResult
40+
err = json.Unmarshal(resp.Body(), &searchResult)
41+
if err != nil {
42+
return nil, err
43+
}
44+
if !searchResult.OK {
45+
return nil, fmt.Errorf("failed to search, got: %s", resp.Body())
46+
}
47+
return &searchResult, nil
48+
}

port/integration/schema.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,6 @@ func IntegrationSchema() map[string]schema.Attribute {
5151
}
5252
}
5353

54-
func StaticString(s string) {
55-
panic("unimplemented")
56-
}
57-
5854
func (r *IntegrationResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
5955
resp.Schema = schema.Schema{
6056
MarkdownDescription: IntegrationResourceMarkdownDescription,

0 commit comments

Comments
 (0)