Org List and Get API Methods (#132)

* Create the organization Get handler.

* Added org listing.

* Make sure the cmp.Diff always sorts the returned organizations before comparing.

* Connect organization service to the Controller handler.
This commit is contained in:
Todd Knight authored and GitHub committed 2020-06-19 10:55:50 -07:00
1 parent 0ebb38bbe5
commit 0a0d37668d
8 files changed
+1260 -3

No files matched your search

+106
View File
@@ -16,6 +16,62 @@
"application/json"
],
"paths": {
"/v1/orgs": {
"get": {
"summary": "Lists all Organizations",
"operationId": "OrganizationService_ListOrganizations",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/controller.api.services.v1.ListOrganizationsResponse"
}
}
},
"parameters": [
{
"name": "view",
"in": "query",
"required": false,
"type": "string"
}
],
"tags": [
"controller.api.services.v1.OrganizationService"
]
}
},
"/v1/orgs/{id}": {
"get": {
"summary": "Gets a single Organization",
"operationId": "OrganizationService_GetOrganization",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/controller.api.resources.scopes.v1.Organization"
}
}
},
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "view",
"in": "query",
"required": false,
"type": "string"
}
],
"tags": [
"controller.api.services.v1.OrganizationService"
]
}
},
"/v1/orgs/{org_id}/groups": {
"get": {
"summary": "Lists all Groups",
@@ -2080,6 +2136,37 @@
},
"title": "HostSet is a collection of Hosts created and managed by a HostCatalog"
},
"controller.api.resources.scopes.v1.Organization": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The ID of the Organization\nOutput only.",
"readOnly": true
},
"name": {
"type": "string",
"title": "Optional name for identification purposes"
},
"description": {
"type": "string",
"title": "Optional user-set descripton for identification purposes"
},
"created_time": {
"type": "string",
"format": "date-time",
"description": "The time this resource was created\nOutput only.",
"readOnly": true
},
"updated_time": {
"type": "string",
"format": "date-time",
"description": "The time this resource was last updated.\nOutput only.",
"readOnly": true
}
},
"title": "Organization contains all fields related to an Organization resource"
},
"controller.api.resources.scopes.v1.Project": {
"type": "object",
"properties": {
@@ -2307,6 +2394,14 @@
}
}
},
"controller.api.services.v1.GetOrganizationResponse": {
"type": "object",
"properties": {
"item": {
"$ref": "#/definitions/controller.api.resources.scopes.v1.Organization"
}
}
},
"controller.api.services.v1.GetProjectResponse": {
"type": "object",
"properties": {
@@ -2367,6 +2462,17 @@
}
}
},
"controller.api.services.v1.ListOrganizationsResponse": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/controller.api.resources.scopes.v1.Organization"
}
}
}
},
"controller.api.services.v1.ListProjectsResponse": {
"type": "object",
"properties": {
@@ -0,0 +1,530 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.24.0
// protoc v3.12.3
// source: controller/api/services/v1/organization_service.proto
package services
import (
context "context"
proto "github.com/golang/protobuf/proto"
_ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options"
scopes "github.com/hashicorp/watchtower/internal/gen/controller/api/resources/scopes"
_ "google.golang.org/genproto/googleapis/api/annotations"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type GetOrganizationRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
View string `protobuf:"bytes,2,opt,name=view,proto3" json:"view,omitempty"`
}
func (x *GetOrganizationRequest) Reset() {
*x = GetOrganizationRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_organization_service_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetOrganizationRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetOrganizationRequest) ProtoMessage() {}
func (x *GetOrganizationRequest) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_organization_service_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetOrganizationRequest.ProtoReflect.Descriptor instead.
func (*GetOrganizationRequest) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_organization_service_proto_rawDescGZIP(), []int{0}
}
func (x *GetOrganizationRequest) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *GetOrganizationRequest) GetView() string {
if x != nil {
return x.View
}
return ""
}
type GetOrganizationResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Item *scopes.Organization `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
}
func (x *GetOrganizationResponse) Reset() {
*x = GetOrganizationResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_organization_service_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetOrganizationResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetOrganizationResponse) ProtoMessage() {}
func (x *GetOrganizationResponse) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_organization_service_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetOrganizationResponse.ProtoReflect.Descriptor instead.
func (*GetOrganizationResponse) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_organization_service_proto_rawDescGZIP(), []int{1}
}
func (x *GetOrganizationResponse) GetItem() *scopes.Organization {
if x != nil {
return x.Item
}
return nil
}
type ListOrganizationsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
View string `protobuf:"bytes,1,opt,name=view,proto3" json:"view,omitempty"`
}
func (x *ListOrganizationsRequest) Reset() {
*x = ListOrganizationsRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_organization_service_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListOrganizationsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListOrganizationsRequest) ProtoMessage() {}
func (x *ListOrganizationsRequest) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_organization_service_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListOrganizationsRequest.ProtoReflect.Descriptor instead.
func (*ListOrganizationsRequest) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_organization_service_proto_rawDescGZIP(), []int{2}
}
func (x *ListOrganizationsRequest) GetView() string {
if x != nil {
return x.View
}
return ""
}
type ListOrganizationsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Items []*scopes.Organization `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
}
func (x *ListOrganizationsResponse) Reset() {
*x = ListOrganizationsResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_organization_service_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListOrganizationsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListOrganizationsResponse) ProtoMessage() {}
func (x *ListOrganizationsResponse) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_organization_service_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListOrganizationsResponse.ProtoReflect.Descriptor instead.
func (*ListOrganizationsResponse) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_organization_service_proto_rawDescGZIP(), []int{3}
}
func (x *ListOrganizationsResponse) GetItems() []*scopes.Organization {
if x != nil {
return x.Items
}
return nil
}
var File_controller_api_services_v1_organization_service_proto protoreflect.FileDescriptor
var file_controller_api_services_v1_organization_service_proto_rawDesc = []byte{
0x0a, 0x35, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69,
0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67,
0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
0x2e, 0x76, 0x31, 0x1a, 0x2c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d,
0x73, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f,
0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e,
0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
0x35, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f,
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73,
0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3c, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x67,
0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64,
0x12, 0x12, 0x0a, 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x76, 0x69, 0x65, 0x77, 0x22, 0x5f, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x67, 0x61, 0x6e,
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x44, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e,
0x76, 0x31, 0x2e, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x2e, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x72, 0x67,
0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x12, 0x0a, 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x76, 0x69, 0x65, 0x77, 0x22, 0x63, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x72, 0x67,
0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x46, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f,
0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x32, 0xff, 0x02, 0x0a, 0x13, 0x4f,
0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x12, 0xb6, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69,
0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x67, 0x61, 0x6e,
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x3a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67,
0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x1c, 0x12,
0x1a, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x4f,
0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0xae, 0x01, 0x0a, 0x11,
0x4c, 0x69, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x73, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73,
0x92, 0x41, 0x19, 0x12, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x4f,
0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x4f, 0x5a, 0x4d,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69,
0x63, 0x6f, 0x72, 0x70, 0x2f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x2f,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_controller_api_services_v1_organization_service_proto_rawDescOnce sync.Once
file_controller_api_services_v1_organization_service_proto_rawDescData = file_controller_api_services_v1_organization_service_proto_rawDesc
)
func file_controller_api_services_v1_organization_service_proto_rawDescGZIP() []byte {
file_controller_api_services_v1_organization_service_proto_rawDescOnce.Do(func() {
file_controller_api_services_v1_organization_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_api_services_v1_organization_service_proto_rawDescData)
})
return file_controller_api_services_v1_organization_service_proto_rawDescData
}
var file_controller_api_services_v1_organization_service_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_controller_api_services_v1_organization_service_proto_goTypes = []interface{}{
(*GetOrganizationRequest)(nil), // 0: controller.api.services.v1.GetOrganizationRequest
(*GetOrganizationResponse)(nil), // 1: controller.api.services.v1.GetOrganizationResponse
(*ListOrganizationsRequest)(nil), // 2: controller.api.services.v1.ListOrganizationsRequest
(*ListOrganizationsResponse)(nil), // 3: controller.api.services.v1.ListOrganizationsResponse
(*scopes.Organization)(nil), // 4: controller.api.resources.scopes.v1.Organization
}
var file_controller_api_services_v1_organization_service_proto_depIdxs = []int32{
4, // 0: controller.api.services.v1.GetOrganizationResponse.item:type_name -> controller.api.resources.scopes.v1.Organization
4, // 1: controller.api.services.v1.ListOrganizationsResponse.items:type_name -> controller.api.resources.scopes.v1.Organization
0, // 2: controller.api.services.v1.OrganizationService.GetOrganization:input_type -> controller.api.services.v1.GetOrganizationRequest
2, // 3: controller.api.services.v1.OrganizationService.ListOrganizations:input_type -> controller.api.services.v1.ListOrganizationsRequest
1, // 4: controller.api.services.v1.OrganizationService.GetOrganization:output_type -> controller.api.services.v1.GetOrganizationResponse
3, // 5: controller.api.services.v1.OrganizationService.ListOrganizations:output_type -> controller.api.services.v1.ListOrganizationsResponse
4, // [4:6] is the sub-list for method output_type
2, // [2:4] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_controller_api_services_v1_organization_service_proto_init() }
func file_controller_api_services_v1_organization_service_proto_init() {
if File_controller_api_services_v1_organization_service_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_controller_api_services_v1_organization_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetOrganizationRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_organization_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetOrganizationResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_organization_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListOrganizationsRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_organization_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListOrganizationsResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_controller_api_services_v1_organization_service_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_controller_api_services_v1_organization_service_proto_goTypes,
DependencyIndexes: file_controller_api_services_v1_organization_service_proto_depIdxs,
MessageInfos: file_controller_api_services_v1_organization_service_proto_msgTypes,
}.Build()
File_controller_api_services_v1_organization_service_proto = out.File
file_controller_api_services_v1_organization_service_proto_rawDesc = nil
file_controller_api_services_v1_organization_service_proto_goTypes = nil
file_controller_api_services_v1_organization_service_proto_depIdxs = nil
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConnInterface
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion6
// OrganizationServiceClient is the client API for OrganizationService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type OrganizationServiceClient interface {
// GetOrganization returns a stored Organization if present. The provided request
// must include the org id for the organization being retrieved. If
// that id is missing, malformed or reference a non existing resource an error is returned.
GetOrganization(ctx context.Context, in *GetOrganizationRequest, opts ...grpc.CallOption) (*GetOrganizationResponse, error)
// ListOrganizations returns a list of stored organizations which exist in watchtower.
ListOrganizations(ctx context.Context, in *ListOrganizationsRequest, opts ...grpc.CallOption) (*ListOrganizationsResponse, error)
}
type organizationServiceClient struct {
cc grpc.ClientConnInterface
}
func NewOrganizationServiceClient(cc grpc.ClientConnInterface) OrganizationServiceClient {
return &organizationServiceClient{cc}
}
func (c *organizationServiceClient) GetOrganization(ctx context.Context, in *GetOrganizationRequest, opts ...grpc.CallOption) (*GetOrganizationResponse, error) {
out := new(GetOrganizationResponse)
err := c.cc.Invoke(ctx, "/controller.api.services.v1.OrganizationService/GetOrganization", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *organizationServiceClient) ListOrganizations(ctx context.Context, in *ListOrganizationsRequest, opts ...grpc.CallOption) (*ListOrganizationsResponse, error) {
out := new(ListOrganizationsResponse)
err := c.cc.Invoke(ctx, "/controller.api.services.v1.OrganizationService/ListOrganizations", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// OrganizationServiceServer is the server API for OrganizationService service.
type OrganizationServiceServer interface {
// GetOrganization returns a stored Organization if present. The provided request
// must include the org id for the organization being retrieved. If
// that id is missing, malformed or reference a non existing resource an error is returned.
GetOrganization(context.Context, *GetOrganizationRequest) (*GetOrganizationResponse, error)
// ListOrganizations returns a list of stored organizations which exist in watchtower.
ListOrganizations(context.Context, *ListOrganizationsRequest) (*ListOrganizationsResponse, error)
}
// UnimplementedOrganizationServiceServer can be embedded to have forward compatible implementations.
type UnimplementedOrganizationServiceServer struct {
}
func (*UnimplementedOrganizationServiceServer) GetOrganization(context.Context, *GetOrganizationRequest) (*GetOrganizationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetOrganization not implemented")
}
func (*UnimplementedOrganizationServiceServer) ListOrganizations(context.Context, *ListOrganizationsRequest) (*ListOrganizationsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListOrganizations not implemented")
}
func RegisterOrganizationServiceServer(s *grpc.Server, srv OrganizationServiceServer) {
s.RegisterService(&_OrganizationService_serviceDesc, srv)
}
func _OrganizationService_GetOrganization_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetOrganizationRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(OrganizationServiceServer).GetOrganization(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/controller.api.services.v1.OrganizationService/GetOrganization",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(OrganizationServiceServer).GetOrganization(ctx, req.(*GetOrganizationRequest))
}
return interceptor(ctx, in, info, handler)
}
func _OrganizationService_ListOrganizations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListOrganizationsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(OrganizationServiceServer).ListOrganizations(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/controller.api.services.v1.OrganizationService/ListOrganizations",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(OrganizationServiceServer).ListOrganizations(ctx, req.(*ListOrganizationsRequest))
}
return interceptor(ctx, in, info, handler)
}
var _OrganizationService_serviceDesc = grpc.ServiceDesc{
ServiceName: "controller.api.services.v1.OrganizationService",
HandlerType: (*OrganizationServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetOrganization",
Handler: _OrganizationService_GetOrganization_Handler,
},
{
MethodName: "ListOrganizations",
Handler: _OrganizationService_ListOrganizations_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "controller/api/services/v1/organization_service.proto",
}
@@ -0,0 +1,290 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: controller/api/services/v1/organization_service.proto
/*
Package services is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package services
import (
"context"
"io"
"net/http"
"github.com/golang/protobuf/descriptor"
"github.com/golang/protobuf/proto"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/status"
)
// Suppress "imported and not used" errors
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = descriptor.ForMessage
var (
filter_OrganizationService_GetOrganization_0 = &utilities.DoubleArray{Encoding: map[string]int{"id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
)
func request_OrganizationService_GetOrganization_0(ctx context.Context, marshaler runtime.Marshaler, client OrganizationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetOrganizationRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_OrganizationService_GetOrganization_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GetOrganization(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_OrganizationService_GetOrganization_0(ctx context.Context, marshaler runtime.Marshaler, server OrganizationServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetOrganizationRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_OrganizationService_GetOrganization_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GetOrganization(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_OrganizationService_ListOrganizations_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_OrganizationService_ListOrganizations_0(ctx context.Context, marshaler runtime.Marshaler, client OrganizationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ListOrganizationsRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_OrganizationService_ListOrganizations_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.ListOrganizations(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_OrganizationService_ListOrganizations_0(ctx context.Context, marshaler runtime.Marshaler, server OrganizationServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ListOrganizationsRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_OrganizationService_ListOrganizations_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.ListOrganizations(ctx, &protoReq)
return msg, metadata, err
}
// RegisterOrganizationServiceHandlerServer registers the http handlers for service OrganizationService to "mux".
// UnaryRPC :call OrganizationServiceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
func RegisterOrganizationServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server OrganizationServiceServer) error {
mux.Handle("GET", pattern_OrganizationService_GetOrganization_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_OrganizationService_GetOrganization_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_OrganizationService_GetOrganization_0(ctx, mux, outboundMarshaler, w, req, response_OrganizationService_GetOrganization_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_OrganizationService_ListOrganizations_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_OrganizationService_ListOrganizations_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_OrganizationService_ListOrganizations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterOrganizationServiceHandlerFromEndpoint is same as RegisterOrganizationServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterOrganizationServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterOrganizationServiceHandler(ctx, mux, conn)
}
// RegisterOrganizationServiceHandler registers the http handlers for service OrganizationService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterOrganizationServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterOrganizationServiceHandlerClient(ctx, mux, NewOrganizationServiceClient(conn))
}
// RegisterOrganizationServiceHandlerClient registers the http handlers for service OrganizationService
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "OrganizationServiceClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "OrganizationServiceClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "OrganizationServiceClient" to call the correct interceptors.
func RegisterOrganizationServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client OrganizationServiceClient) error {
mux.Handle("GET", pattern_OrganizationService_GetOrganization_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_OrganizationService_GetOrganization_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_OrganizationService_GetOrganization_0(ctx, mux, outboundMarshaler, w, req, response_OrganizationService_GetOrganization_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_OrganizationService_ListOrganizations_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_OrganizationService_ListOrganizations_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_OrganizationService_ListOrganizations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
type response_OrganizationService_GetOrganization_0 struct {
proto.Message
}
func (m response_OrganizationService_GetOrganization_0) XXX_ResponseBody() interface{} {
response := m.Message.(*GetOrganizationResponse)
return response.Item
}
var (
pattern_OrganizationService_GetOrganization_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "orgs", "id"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_OrganizationService_ListOrganizations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "orgs"}, "", runtime.AssumeColonVerbOpt(true)))
)
var (
forward_OrganizationService_GetOrganization_0 = runtime.ForwardResponseMessage
forward_OrganizationService_ListOrganizations_0 = runtime.ForwardResponseMessage
)
+3 -3
View File
@@ -136,10 +136,10 @@ func (r *Repository) ListProjects(ctx context.Context, withOrganizationId string
// ListOrganizations and supports the WithLimit option.
func (r *Repository) ListOrganizations(ctx context.Context, opt ...Option) ([]*Scope, error) {
var projects []*Scope
err := r.list(ctx, &projects, "type = ?", []interface{}{OrganizationScope.String()}, opt...)
var organizations []*Scope
err := r.list(ctx, &organizations, "type = ?", []interface{}{OrganizationScope.String()}, opt...)
if err != nil {
return nil, fmt.Errorf("list organizations: %w", err)
}
return projects, nil
return organizations, nil
}
@@ -0,0 +1,52 @@
syntax = "proto3";
package controller.api.services.v1;
option go_package = "github.com/hashicorp/watchtower/internal/gen/controller/api/services;services";
import "protoc-gen-swagger/options/annotations.proto";
import "google/api/annotations.proto";
import "controller/api/resources/scopes/v1/organization.proto";
service OrganizationService {
// GetOrganization returns a stored Organization if present. The provided request
// must include the org id for the organization being retrieved. If
// that id is missing, malformed or reference a non existing resource an error is returned.
rpc GetOrganization(GetOrganizationRequest) returns (GetOrganizationResponse) {
option (google.api.http) = {
get: "/v1/orgs/{id}"
response_body: "item"
};
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = {
summary: "Gets a single Organization"
};
}
// ListOrganizations returns a list of stored organizations which exist in watchtower.
rpc ListOrganizations(ListOrganizationsRequest) returns (ListOrganizationsResponse) {
option (google.api.http) = {
get: "/v1/orgs"
};
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = {
summary: "Lists all Organizations"
};
}
}
message GetOrganizationRequest {
string id = 1;
string view = 2;
}
message GetOrganizationResponse {
resources.scopes.v1.Organization item = 1;
}
message ListOrganizationsRequest {
string view = 1;
}
message ListOrganizationsResponse {
repeated resources.scopes.v1.Organization items = 1;
}
+8
View File
@@ -21,6 +21,7 @@ import (
"github.com/hashicorp/watchtower/internal/servers/controller/handlers/host_catalogs"
"github.com/hashicorp/watchtower/internal/servers/controller/handlers/host_sets"
"github.com/hashicorp/watchtower/internal/servers/controller/handlers/hosts"
"github.com/hashicorp/watchtower/internal/servers/controller/handlers/organizations"
"github.com/hashicorp/watchtower/internal/servers/controller/handlers/projects"
"github.com/hashicorp/watchtower/internal/servers/controller/handlers/users"
)
@@ -79,6 +80,13 @@ func handleGrpcGateway(c *Controller) (http.Handler, error) {
if err := services.RegisterHostServiceHandlerServer(ctx, mux, &hosts.Service{}); err != nil {
return nil, fmt.Errorf("failed to register host service handler: %w", err)
}
os, err := organizations.NewService(c.IamRepoFn)
if err != nil {
return nil, fmt.Errorf("failed to create organization handler service: %w", err)
}
if err := services.RegisterOrganizationServiceHandlerServer(ctx, mux, os); err != nil {
return nil, fmt.Errorf("failed to register organization service handler: %w", err)
}
ps, err := projects.NewService(c.IamRepoFn)
if err != nil {
return nil, fmt.Errorf("failed to create project handler service: %w", err)
@@ -0,0 +1,127 @@
package organizations
import (
"context"
"fmt"
"regexp"
"strings"
pb "github.com/hashicorp/watchtower/internal/gen/controller/api/resources/scopes"
pbs "github.com/hashicorp/watchtower/internal/gen/controller/api/services"
"github.com/hashicorp/watchtower/internal/iam"
"github.com/hashicorp/watchtower/internal/servers/controller/common"
"github.com/hashicorp/watchtower/internal/servers/controller/handlers"
"google.golang.org/protobuf/types/known/wrapperspb"
)
const orgIdFieldName = "org_id"
var (
reInvalidID = regexp.MustCompile("[^A-Za-z0-9]")
)
// Service handles request as described by the pbs.OrganizationServiceServer interface.
type Service struct {
repo common.IamRepoFactory
}
// NewService returns an organization service which handles organization related requests to watchtower.
func NewService(repo common.IamRepoFactory) (Service, error) {
if repo == nil {
return Service{}, fmt.Errorf("nil iam repository provided")
}
return Service{repo: repo}, nil
}
var _ pbs.OrganizationServiceServer = Service{}
// ListOrganizations is not yet implemented but will implement the interface pbs.OrganizationServiceServer.
func (s Service) ListOrganizations(ctx context.Context, req *pbs.ListOrganizationsRequest) (*pbs.ListOrganizationsResponse, error) {
ol, err := s.listFromRepo(ctx)
if err != nil {
return nil, err
}
return &pbs.ListOrganizationsResponse{Items: ol}, nil
}
// GetOrganizations implements the interface pbs.OrganizationServiceServer.
func (s Service) GetOrganization(ctx context.Context, req *pbs.GetOrganizationRequest) (*pbs.GetOrganizationResponse, error) {
if err := validateGetRequest(req); err != nil {
return nil, err
}
o, err := s.getFromRepo(ctx, req.GetId())
if err != nil {
return nil, err
}
return &pbs.GetOrganizationResponse{Item: o}, nil
}
func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Organization, error) {
repo, err := s.repo()
if err != nil {
return nil, err
}
p, err := repo.LookupScope(ctx, id)
if err != nil {
return nil, err
}
if p == nil {
return nil, handlers.NotFoundErrorf("Organization %q doesn't exist.", id)
}
return toProto(p), nil
}
func (s Service) listFromRepo(ctx context.Context) ([]*pb.Organization, error) {
repo, err := s.repo()
if err != nil {
return nil, err
}
ol, err := repo.ListOrganizations(ctx)
if err != nil {
return nil, err
}
var outOl []*pb.Organization
for _, o := range ol {
outOl = append(outOl, toProto(o))
}
return outOl, nil
}
func toProto(in *iam.Scope) *pb.Organization {
out := pb.Organization{
Id: in.GetPublicId(),
CreatedTime: in.GetCreateTime().GetTimestamp(),
UpdatedTime: in.GetUpdateTime().GetTimestamp(),
}
if in.GetDescription() != "" {
out.Description = &wrapperspb.StringValue{Value: in.GetDescription()}
}
if in.GetName() != "" {
out.Name = &wrapperspb.StringValue{Value: in.GetName()}
}
return &out
}
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
// requirements on the structure of the request. They verify that:
// * The path passed in is correctly formatted
// * All required parameters are set
// * There are no conflicting parameters provided
func validateGetRequest(req *pbs.GetOrganizationRequest) error {
badFields := make(map[string]string)
if !validId(req.GetId(), iam.OrganizationScope.Prefix()+"_") {
badFields["id"] = "Invalid formatted organization id."
}
if len(badFields) > 0 {
return handlers.InvalidArgumentErrorf("Improperly formatted identifier.", badFields)
}
return nil
}
func validId(id, prefix string) bool {
if !strings.HasPrefix(id, prefix) {
return false
}
id = strings.TrimPrefix(id, prefix)
return !reInvalidID.Match([]byte(id))
}
@@ -0,0 +1,144 @@
package organizations_test
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/watchtower/internal/db"
pb "github.com/hashicorp/watchtower/internal/gen/controller/api/resources/scopes"
pbs "github.com/hashicorp/watchtower/internal/gen/controller/api/services"
"github.com/hashicorp/watchtower/internal/iam"
"github.com/hashicorp/watchtower/internal/servers/controller/handlers/organizations"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/wrapperspb"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func createDefaultOrganizationAndRepo(t *testing.T) (*iam.Scope, func() (*iam.Repository, error)) {
t.Helper()
cleanup, conn, _ := db.TestSetup(t, "postgres")
t.Cleanup(func() {
if err := conn.Close(); err != nil {
t.Errorf("Error when closing gorm DB: %v", err)
}
if err := cleanup(); err != nil {
t.Errorf("Error when cleaning up TestSetup: %v", err)
}
})
rw := db.New(conn)
wrap := db.TestWrapper(t)
repoFn := func() (*iam.Repository, error) {
return iam.NewRepository(rw, rw, wrap)
}
oRes, _ := iam.TestScopes(t, conn)
oRes.Name = "default"
oRes.Description = "default"
repo, err := repoFn()
require.NoError(t, err)
oRes, _, err = repo.UpdateScope(context.Background(), oRes, []string{"Name", "Description"})
require.NoError(t, err)
return oRes, repoFn
}
func TestGet(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
org, repo := createDefaultOrganizationAndRepo(t)
toMerge := &pbs.GetOrganizationRequest{
Id: org.GetPublicId(),
}
pOrganization := &pb.Organization{
Id: org.GetPublicId(),
Name: &wrapperspb.StringValue{Value: org.GetName()},
Description: &wrapperspb.StringValue{Value: org.GetDescription()},
CreatedTime: org.CreateTime.GetTimestamp(),
UpdatedTime: org.UpdateTime.GetTimestamp(),
}
cases := []struct {
name string
req *pbs.GetOrganizationRequest
res *pbs.GetOrganizationResponse
errCode codes.Code
}{
{
name: "Get an Existing organization",
req: &pbs.GetOrganizationRequest{Id: org.GetPublicId()},
res: &pbs.GetOrganizationResponse{Item: pOrganization},
errCode: codes.OK,
},
{
name: "Get a non existing organization",
req: &pbs.GetOrganizationRequest{Id: iam.OrganizationScope.Prefix() + "_DoesntExis"},
res: nil,
errCode: codes.NotFound,
},
{
name: "Wrong id prefix",
req: &pbs.GetOrganizationRequest{Id: "j_1234567890"},
res: nil,
errCode: codes.InvalidArgument,
},
{
name: "space in id",
req: &pbs.GetOrganizationRequest{Id: iam.OrganizationScope.Prefix() + "_1 23456789"},
res: nil,
errCode: codes.InvalidArgument,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
req := proto.Clone(toMerge).(*pbs.GetOrganizationRequest)
proto.Merge(req, tc.req)
s, err := organizations.NewService(repo)
require.NoError(err, "Couldn't create new project service.")
got, gErr := s.GetOrganization(context.Background(), req)
assert.Equal(tc.errCode, status.Code(gErr), "GetOrganization(%+v) got error %v, wanted %v", req, gErr, tc.errCode)
assert.True(proto.Equal(got, tc.res), "GetOrganization(%q) got response %q, wanted %q", req, got, tc.res)
})
}
}
func TestList(t *testing.T) {
assert, require := assert.New(t), require.New(t)
cleanup, conn, _ := db.TestSetup(t, "postgres")
t.Cleanup(func() {
if err := conn.Close(); err != nil {
t.Errorf("Error when closing gorm DB: %v", err)
}
if err := cleanup(); err != nil {
t.Errorf("Error when cleaning up TestSetup: %v", err)
}
})
rw := db.New(conn)
wrap := db.TestWrapper(t)
repoFn := func() (*iam.Repository, error) {
return iam.NewRepository(rw, rw, wrap)
}
s, err := organizations.NewService(repoFn)
require.NoError(err)
ctx := context.Background()
resp, err := s.ListOrganizations(ctx, &pbs.ListOrganizationsRequest{})
assert.NoError(err)
assert.Equal(&pbs.ListOrganizationsResponse{}, resp)
var orgs []*pb.Organization
for i := 0; i < 10; i++ {
o, _ := iam.TestScopes(t, conn)
orgs = append(orgs, &pb.Organization{Id: o.PublicId, CreatedTime: o.GetCreateTime().GetTimestamp(), UpdatedTime: o.GetUpdateTime().GetTimestamp()})
}
resp, err = s.ListOrganizations(ctx, &pbs.ListOrganizationsRequest{})
assert.NoError(err)
assert.Empty(cmp.Diff(resp, &pbs.ListOrganizationsResponse{Items: orgs}, protocmp.Transform(), protocmp.SortRepeatedFields(&pbs.ListOrganizationsResponse{}, "items")))
}