Skip to content

Commit

Permalink
Add Healthcare beta API and associated resources and iam (hashicorp#858)
Browse files Browse the repository at this point in the history
Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
modular-magician authored and chrisst committed Jun 25, 2019
1 parent 35c2257 commit a4370ee
Show file tree
Hide file tree
Showing 29 changed files with 5,338 additions and 1 deletion.
20 changes: 20 additions & 0 deletions google-beta/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"google.golang.org/api/dns/v1"
dnsBeta "google.golang.org/api/dns/v1beta2"
file "google.golang.org/api/file/v1beta1"
healthcare "google.golang.org/api/healthcare/v1beta1"
"google.golang.org/api/iam/v1"
iamcredentials "google.golang.org/api/iamcredentials/v1"
iap "google.golang.org/api/iap/v1beta1"
Expand Down Expand Up @@ -153,6 +154,9 @@ type Config struct {
IAMBasePath string
clientIAM *iam.Service

HealthcareBasePath string
clientHealthcare *healthcare.Service

IAPBasePath string
clientIAP *iap.Service

Expand Down Expand Up @@ -534,6 +538,22 @@ func (c *Config) LoadAndValidate() error {
c.clientStorageTransfer.UserAgent = userAgent
c.clientStorageTransfer.BasePath = storageTransferClientBasePath

log.Printf("[INFO] Instantiating IAP Client...")
c.clientHealthcare, err = healthcare.NewService(context, option.WithHTTPClient(client))
if err != nil {
return err
}
c.clientHealthcare.UserAgent = userAgent

healthcareClientBasePath := removeBasePathVersion(c.HealthcareBasePath)
log.Printf("[INFO] Instantiating Google Cloud Healthcare client for path %s", healthcareClientBasePath)
c.clientHealthcare, err = healthcare.NewService(context, option.WithHTTPClient(client))
if err != nil {
return err
}
c.clientHealthcare.UserAgent = userAgent
c.clientHealthcare.BasePath = healthcareClientBasePath

return nil
}

Expand Down
235 changes: 235 additions & 0 deletions google-beta/healthcare_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
package google

import (
"fmt"
"regexp"
"strings"
)

type healthcareDatasetId struct {
Project string
Location string
Name string
}

func (s *healthcareDatasetId) datasetId() string {
return fmt.Sprintf("projects/%s/locations/%s/datasets/%s", s.Project, s.Location, s.Name)
}

func (s *healthcareDatasetId) terraformId() string {
return fmt.Sprintf("%s/%s/%s", s.Project, s.Location, s.Name)
}

func parseHealthcareDatasetId(id string, config *Config) (*healthcareDatasetId, error) {
parts := strings.Split(id, "/")

datasetIdRegex := regexp.MustCompile("^(" + ProjectRegex + ")/([a-z0-9-])+/([a-zA-Z0-9_-]{1,256})$")
datasetIdWithoutProjectRegex := regexp.MustCompile("^([a-z0-9-])+/([a-zA-Z0-9_-]{1,256})$")
datasetRelativeLinkRegex := regexp.MustCompile("^projects/(" + ProjectRegex + ")/locations/([a-z0-9-]+)/datasets/([a-zA-Z0-9_-]{1,256})$")

if datasetIdRegex.MatchString(id) {
return &healthcareDatasetId{
Project: parts[0],
Location: parts[1],
Name: parts[2],
}, nil
}

if datasetIdWithoutProjectRegex.MatchString(id) {
if config.Project == "" {
return nil, fmt.Errorf("The default project for the provider must be set when using the `{location}/{datasetName}` id format.")
}

return &healthcareDatasetId{
Project: config.Project,
Location: parts[0],
Name: parts[1],
}, nil
}

if parts := datasetRelativeLinkRegex.FindStringSubmatch(id); parts != nil {
return &healthcareDatasetId{
Project: parts[1],
Location: parts[2],
Name: parts[3],
}, nil
}
return nil, fmt.Errorf("Invalid Dataset id format, expecting `{projectId}/{locationId}/{datasetName}` or `{locationId}/{datasetName}.`")
}

type healthcareFhirStoreId struct {
DatasetId healthcareDatasetId
Name string
}

func (s *healthcareFhirStoreId) fhirStoreId() string {
return fmt.Sprintf("%s/fhirStores/%s", s.DatasetId.datasetId(), s.Name)
}

func (s *healthcareFhirStoreId) terraformId() string {
return fmt.Sprintf("%s/%s", s.DatasetId.terraformId(), s.Name)
}

func parseHealthcareFhirStoreId(id string, config *Config) (*healthcareFhirStoreId, error) {
parts := strings.Split(id, "/")

fhirStoreIdRegex := regexp.MustCompile("^(" + ProjectRegex + ")/([a-z0-9-])+/([a-zA-Z0-9_-]{1,256})/([a-zA-Z0-9_-]{1,256})$")
fhirStoreIdWithoutProjectRegex := regexp.MustCompile("^([a-z0-9-])+/([a-zA-Z0-9_-]{1,256})/([a-zA-Z0-9_-]{1,256})$")
fhirStoreRelativeLinkRegex := regexp.MustCompile("^projects/(" + ProjectRegex + ")/locations/([a-z0-9-]+)/datasets/([a-zA-Z0-9_-]{1,256})/fhirStores/([a-zA-Z0-9_-]{1,256})$")

if fhirStoreIdRegex.MatchString(id) {
return &healthcareFhirStoreId{
DatasetId: healthcareDatasetId{
Project: parts[0],
Location: parts[1],
Name: parts[2],
},
Name: parts[3],
}, nil
}

if fhirStoreIdWithoutProjectRegex.MatchString(id) {
if config.Project == "" {
return nil, fmt.Errorf("The default project for the provider must be set when using the `{location}/{datasetName}/{fhirStoreName}` id format.")
}

return &healthcareFhirStoreId{
DatasetId: healthcareDatasetId{
Project: config.Project,
Location: parts[0],
Name: parts[1],
},
Name: parts[2],
}, nil
}

if parts := fhirStoreRelativeLinkRegex.FindStringSubmatch(id); parts != nil {
return &healthcareFhirStoreId{
DatasetId: healthcareDatasetId{
Project: parts[1],
Location: parts[2],
Name: parts[3],
},
Name: parts[4],
}, nil
}
return nil, fmt.Errorf("Invalid FhirStore id format, expecting `{projectId}/{locationId}/{datasetName}/{fhirStoreName}` or `{locationId}/{datasetName}/{fhirStoreName}.`")
}

type healthcareHl7V2StoreId struct {
DatasetId healthcareDatasetId
Name string
}

func (s *healthcareHl7V2StoreId) hl7V2StoreId() string {
return fmt.Sprintf("%s/hl7V2Stores/%s", s.DatasetId.datasetId(), s.Name)
}

func (s *healthcareHl7V2StoreId) terraformId() string {
return fmt.Sprintf("%s/%s", s.DatasetId.terraformId(), s.Name)
}

func parseHealthcareHl7V2StoreId(id string, config *Config) (*healthcareHl7V2StoreId, error) {
parts := strings.Split(id, "/")

hl7V2StoreIdRegex := regexp.MustCompile("^(" + ProjectRegex + ")/([a-z0-9-])+/([a-zA-Z0-9_-]{1,256})/([a-zA-Z0-9_-]{1,256})$")
hl7V2StoreIdWithoutProjectRegex := regexp.MustCompile("^([a-z0-9-])+/([a-zA-Z0-9_-]{1,256})/([a-zA-Z0-9_-]{1,256})$")
hl7V2StoreRelativeLinkRegex := regexp.MustCompile("^projects/(" + ProjectRegex + ")/locations/([a-z0-9-]+)/datasets/([a-zA-Z0-9_-]{1,256})/hl7V2Stores/([a-zA-Z0-9_-]{1,256})$")

if hl7V2StoreIdRegex.MatchString(id) {
return &healthcareHl7V2StoreId{
DatasetId: healthcareDatasetId{
Project: parts[0],
Location: parts[1],
Name: parts[2],
},
Name: parts[3],
}, nil
}

if hl7V2StoreIdWithoutProjectRegex.MatchString(id) {
if config.Project == "" {
return nil, fmt.Errorf("The default project for the provider must be set when using the `{location}/{datasetName}/{hl7V2StoreName}` id format.")
}

return &healthcareHl7V2StoreId{
DatasetId: healthcareDatasetId{
Project: config.Project,
Location: parts[0],
Name: parts[1],
},
Name: parts[2],
}, nil
}

if parts := hl7V2StoreRelativeLinkRegex.FindStringSubmatch(id); parts != nil {
return &healthcareHl7V2StoreId{
DatasetId: healthcareDatasetId{
Project: parts[1],
Location: parts[2],
Name: parts[3],
},
Name: parts[4],
}, nil
}
return nil, fmt.Errorf("Invalid Hl7V2Store id format, expecting `{projectId}/{locationId}/{datasetName}/{hl7V2StoreName}` or `{locationId}/{datasetName}/{hl7V2StoreName}.`")
}

type healthcareDicomStoreId struct {
DatasetId healthcareDatasetId
Name string
}

func (s *healthcareDicomStoreId) dicomStoreId() string {
return fmt.Sprintf("%s/dicomStores/%s", s.DatasetId.datasetId(), s.Name)
}

func (s *healthcareDicomStoreId) terraformId() string {
return fmt.Sprintf("%s/%s", s.DatasetId.terraformId(), s.Name)
}

func parseHealthcareDicomStoreId(id string, config *Config) (*healthcareDicomStoreId, error) {
parts := strings.Split(id, "/")

dicomStoreIdRegex := regexp.MustCompile("^(" + ProjectRegex + ")/([a-z0-9-])+/([a-zA-Z0-9_-]{1,256})/([a-zA-Z0-9_-]{1,256})$")
dicomStoreIdWithoutProjectRegex := regexp.MustCompile("^([a-z0-9-])+/([a-zA-Z0-9_-]{1,256})/([a-zA-Z0-9_-]{1,256})$")
dicomStoreRelativeLinkRegex := regexp.MustCompile("^projects/(" + ProjectRegex + ")/locations/([a-z0-9-]+)/datasets/([a-zA-Z0-9_-]{1,256})/dicomStores/([a-zA-Z0-9_-]{1,256})$")

if dicomStoreIdRegex.MatchString(id) {
return &healthcareDicomStoreId{
DatasetId: healthcareDatasetId{
Project: parts[0],
Location: parts[1],
Name: parts[2],
},
Name: parts[3],
}, nil
}

if dicomStoreIdWithoutProjectRegex.MatchString(id) {
if config.Project == "" {
return nil, fmt.Errorf("The default project for the provider must be set when using the `{location}/{datasetName}/{dicomStoreName}` id format.")
}

return &healthcareDicomStoreId{
DatasetId: healthcareDatasetId{
Project: config.Project,
Location: parts[0],
Name: parts[1],
},
Name: parts[2],
}, nil
}

if parts := dicomStoreRelativeLinkRegex.FindStringSubmatch(id); parts != nil {
return &healthcareDicomStoreId{
DatasetId: healthcareDatasetId{
Project: parts[1],
Location: parts[2],
Name: parts[3],
},
Name: parts[4],
}, nil
}
return nil, fmt.Errorf("Invalid DicomStore id format, expecting `{projectId}/{locationId}/{datasetName}/{dicomStoreName}` or `{locationId}/{datasetName}/{dicomStoreName}.`")
}
113 changes: 113 additions & 0 deletions google-beta/iam_healthcare_dataset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package google

import (
"fmt"

healthcare "google.golang.org/api/healthcare/v1beta1"

"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/cloudresourcemanager/v1"
)

var IamHealthcareDatasetSchema = map[string]*schema.Schema{
"dataset_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
}

type HealthcareDatasetIamUpdater struct {
resourceId string
Config *Config
}

func NewHealthcareDatasetIamUpdater(d *schema.ResourceData, config *Config) (ResourceIamUpdater, error) {
dataset := d.Get("dataset_id").(string)
datasetId, err := parseHealthcareDatasetId(dataset, config)

if err != nil {
return nil, errwrap.Wrapf(fmt.Sprintf("Error parsing resource ID for for %s: {{err}}", dataset), err)
}

return &HealthcareDatasetIamUpdater{
resourceId: datasetId.datasetId(),
Config: config,
}, nil
}

func DatasetIdParseFunc(d *schema.ResourceData, config *Config) error {
datasetId, err := parseHealthcareDatasetId(d.Id(), config)
if err != nil {
return err
}

d.Set("dataset_id", datasetId.datasetId())
d.SetId(datasetId.datasetId())
return nil
}

func (u *HealthcareDatasetIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) {
p, err := u.Config.clientHealthcare.Projects.Locations.Datasets.GetIamPolicy(u.resourceId).Do()

if err != nil {
return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err)
}

cloudResourcePolicy, err := healthcareToResourceManagerPolicy(p)

if err != nil {
return nil, errwrap.Wrapf(fmt.Sprintf("Invalid IAM policy for %s: {{err}}", u.DescribeResource()), err)
}

return cloudResourcePolicy, nil
}

func (u *HealthcareDatasetIamUpdater) SetResourceIamPolicy(policy *cloudresourcemanager.Policy) error {
healthcarePolicy, err := resourceManagerToHealthcarePolicy(policy)

if err != nil {
return errwrap.Wrapf(fmt.Sprintf("Invalid IAM policy for %s: {{err}}", u.DescribeResource()), err)
}

_, err = u.Config.clientHealthcare.Projects.Locations.Datasets.SetIamPolicy(u.resourceId, &healthcare.SetIamPolicyRequest{
Policy: healthcarePolicy,
}).Do()

if err != nil {
return errwrap.Wrapf(fmt.Sprintf("Error setting IAM policy for %s: {{err}}", u.DescribeResource()), err)
}

return nil
}

func (u *HealthcareDatasetIamUpdater) GetResourceId() string {
return u.resourceId
}

func (u *HealthcareDatasetIamUpdater) GetMutexKey() string {
return u.resourceId
}

func (u *HealthcareDatasetIamUpdater) DescribeResource() string {
return fmt.Sprintf("Healthcare Dataset %q", u.resourceId)
}

func resourceManagerToHealthcarePolicy(p *cloudresourcemanager.Policy) (*healthcare.Policy, error) {
out := &healthcare.Policy{}
err := Convert(p, out)
if err != nil {
return nil, errwrap.Wrapf("Cannot convert a v1 policy to a healthcare policy: {{err}}", err)
}
return out, nil
}

func healthcareToResourceManagerPolicy(p *healthcare.Policy) (*cloudresourcemanager.Policy, error) {
out := &cloudresourcemanager.Policy{}
err := Convert(p, out)
if err != nil {
return nil, errwrap.Wrapf("Cannot convert a healthcare policy to a v1 policy: {{err}}", err)
}
return out, nil
}
Loading

0 comments on commit a4370ee

Please sign in to comment.