Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: golang GetContainedResource(resourceType) function #33

Open
leninayya opened this issue Feb 11, 2021 · 3 comments
Open

Comments

@leninayya
Copy link

adding a function to get ContainedResource by passing a resource type.

Example GetContainedResource(Patient) to return Patient.

fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto/bundle_and_contained_resource.pb.go

@OliverCardoza
Copy link

I'm looking to do something similar:

  • create a Reference with string resource type and string id.

I think there is a whole class of problems when using the golang code which pop up as a result of Go's static typing. I'm not a Go expert but here are my ideas.

1. Large If/Select statements

This is pretty burdensome because the case will have ~150 options if you need to handle all.

switch resourceType {
case "Patient":
  DoPatientStuff(containedResource.GetPatient())
case "Encounter":
  DoEncounterStuff(containedResource.GetEncounter())
...
}

2. Marshal/Unmarshal to JSON

For example, let's say you want the ID of a ContainedResource. This option involves marsalling the proto to bytes[] and then into a new type with the subset of fields you care about. This is a bit contrived because because a type assertion with Resource would work fine here. However for other cases like creating a Reference with a given string id and string resourceType it would help to go string -> pb.

References:

Example 1

import (
  "json"
  "log"

  "github.com/google/fhir/go/jsonformat"
)

type Resource struct {
  Id string `json:"id"`
}

var resource Resource
protoMarshaller, _ := jsonformat.NewMarshaller(...)
data, _ := protoMarshaller.Marshal(containedResource)
_ = json.Unmarshal(data, &resource)
log.Printf("Contained resource id: %v", resource.Id)

Example 2

func CreateReference(resourceType, id string) *dtpb.Reference {
  unmarshaller, _ := jsonformat.NewUnmarshaller(...)
  unmarshalled, _  := unmarshaller.Unmarshal([]byte(fmt.Sprintf(`{"uri": "%v", "reference": "%v"}`, resourceType, id))
  return unmarshalled.(*dtpb.Reference)
}

3. Reflection

You can also call a method by name using reflection but this has limits and feels a bit hacky

// Get the id for a contained resource
reflect.ValueOf(&containedResource).MethodByName("GetId").Call()[0].MethodByName("GetValue").Call()[0]

@nikklassen
Copy link
Member

@OliverCardoza you can make option 3 easier by using the protoreflect library. It supports a WhichOneof, which you can use like

rpb := <resource message>.ProtoReflect()
oneof := rpb.Descriptor().Oneofs().ByName("oneof_resource")
f := rpb.Get(rpb.WhichOneof(oneof))
msg := f.Interface().(protoreflect.Message)

But in response to the original question, we can't change bundle_and_contained_resource.pb.go, it is a file created by the proto compiler.

@OliverCardoza
Copy link

OliverCardoza commented Nov 3, 2021

I went on a bit of a tangent in my prior comment but I want to avoid confusion if others find this.

h/t @nikklassen for the answer. Creating a Reference can be using something like:

func CreateReference(resourceType, resourceId string) *rpb.Reference {
  return &rpb.Reference{
    Type: &rpb.Uri{
      Value: resourceType,
    },
    Reference: &rpb.Reference_Uri{
      Uri: &rpb.String{
        Value: path.Join(resourceType, resourceId),
      },
    },
  }
}

This results in a weakly typed Reference but it can be converted to a strongly typed one via the reference.go helpers:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants