diff --git a/tools/data-api/internal/endpoints/routing.go b/tools/data-api/internal/endpoints/routing.go index 290868bfcc0..d676d48ea94 100644 --- a/tools/data-api/internal/endpoints/routing.go +++ b/tools/data-api/internal/endpoints/routing.go @@ -1,6 +1,8 @@ package endpoints import ( + "log" + "github.com/go-chi/chi/v5" "github.com/hashicorp/pandora/tools/data-api/internal/endpoints/infrastructure" "github.com/hashicorp/pandora/tools/data-api/internal/endpoints/v1" @@ -16,7 +18,11 @@ func Router(directory string, serviceNames *[]string) func(chi.Router) { UriPrefix: "/v1/microsoft-graph/beta", UsesCommonTypes: true, } - serviceRepo := repositories.NewServicesRepository(directory, opts.ServiceType, serviceNames) + serviceRepo, err := repositories.NewServicesRepository(directory, opts.ServiceType, serviceNames) + if err != nil { + // TODO logging + log.Fatalf("Error: %+v", err) + } v1.Router(r, opts, serviceRepo) }) router.Route("/v1/microsoft-graph/stable-v1", func(r chi.Router) { @@ -25,7 +31,11 @@ func Router(directory string, serviceNames *[]string) func(chi.Router) { UriPrefix: "/v1/microsoft-graph/stable-v1", UsesCommonTypes: true, } - serviceRepo := repositories.NewServicesRepository(directory, opts.ServiceType, serviceNames) + serviceRepo, err := repositories.NewServicesRepository(directory, opts.ServiceType, serviceNames) + if err != nil { + // TODO logging + log.Fatalf("Error: %+v", err) + } v1.Router(r, opts, serviceRepo) }) router.Route("/v1/resource-manager", func(r chi.Router) { @@ -34,7 +44,11 @@ func Router(directory string, serviceNames *[]string) func(chi.Router) { UriPrefix: "/v1/resource-manager", UsesCommonTypes: false, } - serviceRepo := repositories.NewServicesRepository(directory, opts.ServiceType, serviceNames) + serviceRepo, err := repositories.NewServicesRepository(directory, opts.ServiceType, serviceNames) + if err != nil { + // TODO logging + log.Fatalf("Error: %+v", err) + } v1.Router(r, opts, serviceRepo) }) router.Get("/", HomePage(router)) diff --git a/tools/data-api/internal/repositories/discovery.go b/tools/data-api/internal/repositories/discovery.go new file mode 100644 index 00000000000..9a68534c800 --- /dev/null +++ b/tools/data-api/internal/repositories/discovery.go @@ -0,0 +1,118 @@ +package repositories + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "path" + + "github.com/hashicorp/pandora/tools/sdk/dataapimodels" +) + +func (s *ServicesRepositoryImpl) discoverServiceTypeDirectories() (*[]string, error) { + // discoverServiceTypeDirectories finds all directories under the root directory that contain api definitions for a given + // service type by checking for a metadata.json and comparing the Data Source value defined within it to the Data Source + // value we're expecting for the Services Repository + dirs, err := listSubDirectories(s.rootDirectory) + if err != nil { + return nil, fmt.Errorf("listing directories under %q: %+v", s.rootDirectory, err) + } + + serviceTypeDirectories := make([]string, 0) + + for _, d := range *dirs { + serviceTypeDir := path.Join(s.rootDirectory, d) + + // check whether directory contains a metadata.json + var metadata dataapimodels.MetaData + contents, err := loadJson(path.Join(serviceTypeDir, "metadata.json")) + if err != nil { + var pathError *os.PathError + if errors.As(err, &pathError) { + // this folder has no metadata.json, so we skip it + continue + } + return nil, fmt.Errorf("loading metadata.json: %+v", err) + } + + if err := json.Unmarshal(*contents, &metadata); err != nil { + return nil, fmt.Errorf("unmarshaling metadata.json: %+v", err) + } + + if metadata.DataSource != s.expectedDataSource { + // this folder contains definitions not belonging to this service type, so we skip it + continue + } + serviceTypeDirectories = append(serviceTypeDirectories, serviceTypeDir) + } + + return &serviceTypeDirectories, nil +} + +func (s *ServicesRepositoryImpl) discoverSubsetOfServices() error { + // discoverSubsetOfServices populates the serviceNamesToDirectory attribute of the ServicesRepositoryImpl. + // This function is called if we're spinning up the data API for a subset of services and avoids iterating over + // all available services. + dirs, err := s.discoverServiceTypeDirectories() + if err != nil { + return fmt.Errorf("discovering service type directories for service type %q: %+v", s.serviceType, err) + } + + services := make(map[string]string, 0) + for _, d := range *dirs { + for _, service := range *s.serviceNames { + serviceDir := path.Join(d, service) + if _, err := os.Stat(serviceDir); os.IsNotExist(err) { + // we continue here since the service we're looking for could exist in another source directory e.g. under handwritten definitions + continue + } + if _, ok := services[service]; ok { + return fmt.Errorf("duplicate definitions for service %q", service) + } + services[service] = serviceDir + } + } + + // this checks if all services have been found if we're running the data API for a subset + for _, service := range *s.serviceNames { + if _, ok := services[service]; !ok { + return fmt.Errorf("service %q was not found", service) + } + } + + s.serviceNamesToDirectory = &services + + return nil +} + +func (s *ServicesRepositoryImpl) discoverAllServices() error { + // discoverAllServices populates the serviceNamesToDirectory attribute of the ServicesRepositoryImpl. + // It iterates through all available services to build a complete list of available services for a given + // service type and checks if there are duplicate definitions for a service. + dirs, err := s.discoverServiceTypeDirectories() + if err != nil { + return fmt.Errorf("discovering service type directories for service type %q: %+v", s.serviceType, err) + } + + allServices := make(map[string]string, 0) + for _, d := range *dirs { + files, err := os.ReadDir(d) + if err != nil { + return fmt.Errorf("getting all services: %+v", err) + } + + for _, f := range files { + if f.IsDir() { + if _, ok := allServices[f.Name()]; ok { + return fmt.Errorf("duplicate definitions for service %q", f.Name()) + } + allServices[f.Name()] = path.Join(d, f.Name()) + } + } + } + + s.serviceNamesToDirectory = &allServices + + return nil +} diff --git a/tools/data-api/internal/repositories/mappings.go b/tools/data-api/internal/repositories/mappings.go new file mode 100644 index 00000000000..03546713fed --- /dev/null +++ b/tools/data-api/internal/repositories/mappings.go @@ -0,0 +1,155 @@ +package repositories + +import ( + "fmt" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/pandora/tools/sdk/dataapimodels" +) + +func mapObjectDefinition(input *dataapimodels.ObjectDefinition) (*ObjectDefinition, error) { + if input == nil { + return nil, nil + } + + objectDefinitionType, err := mapObjectDefinitionType(input.Type) + if err != nil { + return nil, err + } + + output := ObjectDefinition{ + ReferenceName: input.ReferenceName, + Type: pointer.From(objectDefinitionType), + } + + if input.NestedItem != nil { + nestedItem, err := mapObjectDefinition(input.NestedItem) + if err != nil { + return nil, fmt.Errorf("mapping Nested Item for Object Definition: %+v", err) + } + output.NestedItem = nestedItem + } + + return &output, nil +} + +func mapOptionObjectDefinition(input *dataapimodels.OptionObjectDefinition, constants map[string]ConstantDetails, apiModels map[string]ModelDetails) (*OptionObjectDefinition, error) { + optionObjectType, err := mapOptionObjectDefinitionType(input.Type) + if err != nil { + return nil, err + } + + output := OptionObjectDefinition{ + ReferenceName: input.ReferenceName, + Type: pointer.From(optionObjectType), + } + + if input.NestedItem != nil { + nestedItem, err := mapOptionObjectDefinition(input.NestedItem, constants, apiModels) + if err != nil { + return nil, fmt.Errorf("mapping Nested Item for Option Object Definition: %+v", err) + } + output.NestedItem = nestedItem + } + + if err := validateOptionObjectDefinition(output, constants, apiModels); err != nil { + return nil, fmt.Errorf("validating mapped Option Object Definition: %+v", err) + } + + return &output, nil +} + +func mapDateFormatType(input dataapimodels.DateFormat) (*DateFormat, error) { + mappings := map[dataapimodels.DateFormat]DateFormat{ + dataapimodels.RFC3339DateFormat: RFC3339DateFormat, + } + if v, ok := mappings[input]; ok { + return &v, nil + } + + return nil, fmt.Errorf("unmapped Date Format Type %q", string(input)) +} + +func mapObjectDefinitionType(input dataapimodels.ObjectDefinitionType) (*ObjectDefinitionType, error) { + mappings := map[dataapimodels.ObjectDefinitionType]ObjectDefinitionType{ + dataapimodels.BooleanObjectDefinitionType: BooleanObjectDefinitionType, + dataapimodels.DateTimeObjectDefinitionType: DateTimeObjectDefinitionType, + dataapimodels.IntegerObjectDefinitionType: IntegerObjectDefinitionType, + dataapimodels.FloatObjectDefinitionType: FloatObjectDefinitionType, + dataapimodels.RawFileObjectDefinitionType: RawFileObjectDefinitionType, + dataapimodels.RawObjectObjectDefinitionType: RawObjectObjectDefinitionType, + dataapimodels.ReferenceObjectDefinitionType: ReferenceObjectDefinitionType, + dataapimodels.StringObjectDefinitionType: StringObjectDefinitionType, + dataapimodels.CsvObjectDefinitionType: CsvObjectDefinitionType, + dataapimodels.DictionaryObjectDefinitionType: DictionaryObjectDefinitionType, + dataapimodels.ListObjectDefinitionType: ListObjectDefinitionType, + + dataapimodels.EdgeZoneObjectDefinitionType: EdgeZoneObjectDefinitionType, + dataapimodels.LocationObjectDefinitionType: LocationObjectDefinitionType, + dataapimodels.TagsObjectDefinitionType: TagsObjectDefinitionType, + dataapimodels.SystemAssignedIdentityObjectDefinitionType: SystemAssignedIdentityObjectDefinitionType, + dataapimodels.SystemAndUserAssignedIdentityListObjectDefinitionType: SystemAndUserAssignedIdentityListObjectDefinitionType, + dataapimodels.SystemAndUserAssignedIdentityMapObjectDefinitionType: SystemAndUserAssignedIdentityMapObjectDefinitionType, + dataapimodels.LegacySystemAndUserAssignedIdentityListObjectDefinitionType: LegacySystemAndUserAssignedIdentityListObjectDefinitionType, + dataapimodels.LegacySystemAndUserAssignedIdentityMapObjectDefinitionType: LegacySystemAndUserAssignedIdentityMapObjectDefinitionType, + dataapimodels.SystemOrUserAssignedIdentityListObjectDefinitionType: SystemOrUserAssignedIdentityListObjectDefinitionType, + dataapimodels.SystemOrUserAssignedIdentityMapObjectDefinitionType: SystemOrUserAssignedIdentityMapObjectDefinitionType, + dataapimodels.UserAssignedIdentityListObjectDefinitionType: UserAssignedIdentityListObjectDefinitionType, + dataapimodels.UserAssignedIdentityMapObjectDefinitionType: UserAssignedIdentityMapObjectDefinitionType, + dataapimodels.SystemDataObjectDefinitionType: SystemDataObjectDefinitionType, + dataapimodels.ZoneObjectDefinitionType: ZoneObjectDefinitionType, + dataapimodels.ZonesObjectDefinitionType: ZonesObjectDefinitionType, + } + if v, ok := mappings[input]; ok { + return &v, nil + } + + return nil, fmt.Errorf("unmapped Object Definition Type %q", string(input)) +} + +func mapOptionObjectDefinitionType(input dataapimodels.OptionObjectDefinitionType) (*OptionObjectDefinitionType, error) { + mappings := map[dataapimodels.OptionObjectDefinitionType]OptionObjectDefinitionType{ + dataapimodels.BooleanOptionObjectDefinitionType: BooleanOptionObjectDefinition, + dataapimodels.IntegerOptionObjectDefinitionType: IntegerOptionObjectDefinition, + dataapimodels.FloatOptionObjectDefinitionType: FloatOptionObjectDefinitionType, + dataapimodels.StringOptionObjectDefinitionType: StringOptionObjectDefinitionType, + dataapimodels.CsvOptionObjectDefinitionType: CsvOptionObjectDefinitionType, + dataapimodels.ListOptionObjectDefinitionType: ListOptionObjectDefinitionType, + dataapimodels.ReferenceOptionObjectDefinitionType: ReferenceOptionObjectDefinitionType, + } + if v, ok := mappings[input]; ok { + return &v, nil + } + + return nil, fmt.Errorf("unmapped Options Object Definition Type %q", string(input)) +} + +func mapConstantFieldType(input dataapimodels.ConstantType) (*ConstantType, error) { + mappings := map[dataapimodels.ConstantType]ConstantType{ + dataapimodels.FloatConstant: FloatConstant, + dataapimodels.IntegerConstant: IntegerConstant, + dataapimodels.StringConstant: StringConstant, + } + if v, ok := mappings[input]; ok { + return &v, nil + } + + return nil, fmt.Errorf("unmapped Constant Type %q", string(input)) +} + +func mapResourceIdSegmentType(input dataapimodels.ResourceIdSegmentType) (*ResourceIdSegmentType, error) { + mappings := map[dataapimodels.ResourceIdSegmentType]ResourceIdSegmentType{ + dataapimodels.ConstantResourceIdSegmentType: ConstantResourceIdSegmentType, + dataapimodels.ResourceGroupResourceIdSegmentType: ResourceGroupResourceIdSegmentType, + dataapimodels.ResourceProviderResourceIdSegmentType: ResourceProviderResourceIdSegmentType, + dataapimodels.ScopeResourceIdSegmentType: ScopeResourceIdSegmentType, + dataapimodels.StaticResourceIdSegmentType: StaticResourceIdSegmentType, + dataapimodels.SubscriptionIdResourceIdSegmentType: SubscriptionIdResourceIdSegmentType, + dataapimodels.UserSpecifiedResourceIdSegmentType: UserSpecifiedResourceIdSegmentType, + } + if v, ok := mappings[input]; ok { + return &v, nil + } + + return nil, fmt.Errorf("unmapped Resource Id Segment Type %q", string(input)) +} diff --git a/tools/data-api/internal/repositories/models.go b/tools/data-api/internal/repositories/models.go index 9952c69e6da..610b0d82c35 100644 --- a/tools/data-api/internal/repositories/models.go +++ b/tools/data-api/internal/repositories/models.go @@ -75,7 +75,7 @@ type ServiceDetails struct { Name string ApiVersions map[string]*ServiceApiVersionDetails Generate bool - ResourceProvider string + ResourceProvider *string TerraformPackageName *string TerraformDetails TerraformDetails } diff --git a/tools/data-api/internal/repositories/services.go b/tools/data-api/internal/repositories/services.go index c4098fd922f..a2031f3ccec 100644 --- a/tools/data-api/internal/repositories/services.go +++ b/tools/data-api/internal/repositories/services.go @@ -22,16 +22,26 @@ type ServicesRepository interface { var _ ServicesRepository = &ServicesRepositoryImpl{} type ServicesRepositoryImpl struct { - // directory is where the api definitions for all services exist - this is where the server will read from - directory string + // expectedDataSource specifies the Data Source of the API definitions that we should load + expectedDataSource dataapimodels.DataSource + + // rootDirectory is the directory containing the API definitions for all service types + rootDirectory string // Service, Version and Resource definitions loaded and unmarshalled from the JSON API definitions services *map[string]ServiceDetails + // serviceNamesToDirectory is a map of all the services belonging to a serviceType mapped to the directory containing its + // definitions + serviceNamesToDirectory *map[string]string + // serviceNames is a list containing the names of services which should be loaded // this allows the loading/parsing of a subset of services for faster iterations during development serviceNames *[]string + // serviceType specifies the type of definitions to be loaded, e.g. for resource manager, graph etc. + serviceType ServiceType + sync.Mutex } @@ -47,15 +57,38 @@ type ApiDefinition struct { fileName string } -func NewServicesRepository(directory string, serviceType ServiceType, serviceNames *[]string) ServicesRepository { - // TODO we should implement some validation in here ensure that there aren't multiple API definitions for a Service - // e.g. definitions for `Containers` under `handwritten` and under `resource-manager` - // we probably want to save a map of map[ServiceName]FilePath for each serviceType to reduce the overhead of - // iterating through the directories multiple times. - return &ServicesRepositoryImpl{ - directory: path.Join(directory, string(serviceType)), - serviceNames: serviceNames, +func NewServicesRepository(directory string, serviceType ServiceType, serviceNames *[]string) (*ServicesRepositoryImpl, error) { + // NewServicesRepository initialises a service repository for a given service type (e.g. resource-manager/graph etc.) + // beginning in the root directory for all api definitions, it auto discovers subdirectories with a metadata.json and collects + // all service definitions for the specified service type, building a complete list of services as well as their directory paths + // to load from + + var expectedDataSource dataapimodels.DataSource + if serviceType == ResourceManagerServiceType { + expectedDataSource = dataapimodels.AzureResourceManagerDataSource + } + if serviceType == MicrosoftGraphV1StableServiceType { + expectedDataSource = dataapimodels.MicrosoftGraphDataSource + } + + repo := &ServicesRepositoryImpl{ + expectedDataSource: expectedDataSource, + rootDirectory: directory, + serviceNames: serviceNames, + serviceType: serviceType, } + + if repo.serviceNames != nil { + if err := repo.discoverSubsetOfServices(); err != nil { + return nil, fmt.Errorf("discovering subset of services for %q: %+v", string(repo.serviceType), err) + } + } else { + if err := repo.discoverAllServices(); err != nil { + return nil, fmt.Errorf("discovering all services for %q: %+v", string(repo.serviceType), err) + } + } + + return repo, nil } func (s *ServicesRepositoryImpl) ClearCache() error { @@ -69,33 +102,54 @@ func (s *ServicesRepositoryImpl) ClearCache() error { func (s *ServicesRepositoryImpl) GetAll(serviceType ServiceType) (*[]ServiceDetails, error) { // GetAll calls GetByName for all the service names passed to the serve command, or for all the services available in the api definitions directory - serviceDetails := make([]ServiceDetails, 0) - // TODO add locking around this when reloading data/clearing cache so that it's only done once - if s.services == nil { - var err error - services := s.serviceNames - if services == nil { - // this means we haven't passed any specific services to the serve command, so we get whatever is available in the api definitions directory - services, err = listSubDirectories(s.directory) - if err != nil { - return nil, fmt.Errorf("retrieving list of services for %s: %+v", serviceType, err) - } - } + if s.serviceNamesToDirectory == nil { + return nil, fmt.Errorf("no services to load") + } + servicesToLoadSorted := make([]string, 0) + + for service := range *s.serviceNamesToDirectory { + servicesToLoadSorted = append(servicesToLoadSorted, service) + } - if services != nil { - servicesMap := make(map[string]ServiceDetails) - for _, service := range *services { - // loads the service definition locations - serviceDetail, err := s.GetByName(service, serviceType) + sort.Strings(servicesToLoadSorted) + + serviceDetails := make([]ServiceDetails, 0) + + if s.services != nil { + s.Lock() + for _, serviceToLoad := range servicesToLoadSorted { + serviceInCache, ok := (*s.services)[serviceToLoad] + if !ok { + serviceDetail, err := s.GetByName(serviceToLoad, s.serviceType) if err != nil { - return nil, fmt.Errorf("retrieving service details for %s: %+v", service, err) + return nil, fmt.Errorf("retrieving service details for %s: %+v", serviceToLoad, err) } + // add it to the cache + (*s.services)[serviceToLoad] = *serviceDetail serviceDetails = append(serviceDetails, *serviceDetail) - servicesMap[serviceDetail.Name] = *serviceDetail + } else { + serviceDetails = append(serviceDetails, serviceInCache) } - s.services = &servicesMap } + s.Unlock() + return &serviceDetails, nil + } + + if s.services == nil { + s.Lock() + servicesMap := make(map[string]ServiceDetails) + for _, service := range servicesToLoadSorted { + serviceDetail, err := s.GetByName(service, s.serviceType) + if err != nil { + return nil, fmt.Errorf("retrieving service details for %s: %+v", service, err) + } + serviceDetails = append(serviceDetails, *serviceDetail) + servicesMap[serviceDetail.Name] = *serviceDetail + } + s.services = &servicesMap + s.Unlock() + return &serviceDetails, nil } @@ -107,11 +161,16 @@ func (s *ServicesRepositoryImpl) GetAll(serviceType ServiceType) (*[]ServiceDeta } func (s *ServicesRepositoryImpl) GetByName(serviceName string, serviceType ServiceType) (*ServiceDetails, error) { - // GetByName builds the ServiceDetails for a singular service by calling processing functions to build the - // structs for the ServiceApiVersionDetails and ServiceApiVersionResourceDetails - serviceDirectory := fmt.Sprintf("%s/%s", s.directory, serviceName) - if _, err := os.Stat(serviceDirectory); os.IsNotExist(err) { - return nil, fmt.Errorf("service %q does not exist: %+v", serviceName, err) + // GetByName loads the ServiceDetails for a service from cache if available or builds the ServiceDetails for a singular + // service by calling processing functions to build the structs for the ServiceApiVersionDetails and ServiceApiVersionResourceDetails + + if s.services != nil { + s.Lock() + service, ok := (*s.services)[serviceName] + s.Unlock() + if ok { + return &service, nil + } } serviceDetails, err := s.ProcessServiceDefinitions(serviceName) @@ -121,8 +180,7 @@ func (s *ServicesRepositoryImpl) GetByName(serviceName string, serviceType Servi if s.services != nil { s.Lock() - services := *s.services - services[serviceName] = *serviceDetails + (*s.services)[serviceName] = *serviceDetails s.Unlock() } @@ -130,7 +188,8 @@ func (s *ServicesRepositoryImpl) GetByName(serviceName string, serviceType Servi } func (s *ServicesRepositoryImpl) ProcessServiceDefinitions(serviceName string) (*ServiceDetails, error) { - versions, err := listSubDirectories(fmt.Sprintf("%s/%s", s.directory, serviceName)) + servicePath := (*s.serviceNamesToDirectory)[serviceName] + versions, err := listSubDirectories(servicePath) if err != nil { return nil, fmt.Errorf("retrieving versions: %+v", err) } @@ -143,7 +202,7 @@ func (s *ServicesRepositoryImpl) ProcessServiceDefinitions(serviceName string) ( if version == "Terraform" { continue } - resources, err := listSubDirectories(fmt.Sprintf("%s/%s/%s", s.directory, serviceName, version)) + resources, err := listSubDirectories(path.Join(servicePath, version)) if err != nil { return nil, fmt.Errorf("retrieving resources for %s: %+v", version, err) } @@ -160,11 +219,21 @@ func (s *ServicesRepositoryImpl) ProcessServiceDefinitions(serviceName string) ( return nil, err } + var serviceDefinition dataapimodels.ServiceDefinition + + contents, err := loadJson(path.Join(servicePath, "ServiceDefinition.json")) + if err != nil { + return nil, fmt.Errorf("processing service definition for %q: %+v", serviceName, err) + } + + if err = json.Unmarshal(*contents, &serviceDefinition); err != nil { + return nil, fmt.Errorf("unmarshaling service definition for %q: %+v", serviceName, err) + } + serviceDetails := &ServiceDetails{ - Name: serviceName, - // TODO RP name needs to be stored in the api definitions - // ResourceProvider: fmt.Sprintf("Microsoft.%s", serviceName), - ApiVersions: versionDefinitions, + Name: serviceName, + ResourceProvider: serviceDefinition.ResourceProvider, + ApiVersions: versionDefinitions, } if terraformDetails != nil { @@ -204,7 +273,7 @@ func (s *ServicesRepositoryImpl) ProcessResourceDefinitions(serviceName string, operations := make(map[string]ResourceOperations) resourceIds := make(map[string]ResourceIdDefinition) - resourcePath := path.Join(s.directory, serviceName, version, resource) + resourcePath := path.Join((*s.serviceNamesToDirectory)[serviceName], version, resource) files, err := os.ReadDir(resourcePath) if err != nil { return nil, fmt.Errorf("retrieving definitions under %s: %+v", resourcePath, err) @@ -460,7 +529,7 @@ func (s *ServicesRepositoryImpl) ProcessTerraformDefinitions(serviceName string) DataSources: make(map[string]TerraformDataSourceDetails), } - terraformDefinitionsPath := path.Join(s.directory, serviceName, "Terraform") + terraformDefinitionsPath := path.Join((*s.serviceNamesToDirectory)[serviceName], "Terraform") files, err := os.ReadDir(terraformDefinitionsPath) if err != nil { if strings.Contains(err.Error(), "no such file or directory") { @@ -916,150 +985,3 @@ func parseResourceIdFromFilePath(filePath string, constants map[string]ConstantD ConstantNames: constantNames, }, nil } - -func mapObjectDefinition(input *dataapimodels.ObjectDefinition) (*ObjectDefinition, error) { - if input == nil { - return nil, nil - } - - objectDefinitionType, err := mapObjectDefinitionType(input.Type) - if err != nil { - return nil, err - } - - output := ObjectDefinition{ - ReferenceName: input.ReferenceName, - Type: pointer.From(objectDefinitionType), - } - - if input.NestedItem != nil { - nestedItem, err := mapObjectDefinition(input.NestedItem) - if err != nil { - return nil, fmt.Errorf("mapping Nested Item for Object Definition: %+v", err) - } - output.NestedItem = nestedItem - } - - return &output, nil -} - -func mapOptionObjectDefinition(input *dataapimodels.OptionObjectDefinition, constants map[string]ConstantDetails, apiModels map[string]ModelDetails) (*OptionObjectDefinition, error) { - optionObjectType, err := mapOptionObjectDefinitionType(input.Type) - if err != nil { - return nil, err - } - - output := OptionObjectDefinition{ - ReferenceName: input.ReferenceName, - Type: pointer.From(optionObjectType), - } - - if input.NestedItem != nil { - nestedItem, err := mapOptionObjectDefinition(input.NestedItem, constants, apiModels) - if err != nil { - return nil, fmt.Errorf("mapping Nested Item for Option Object Definition: %+v", err) - } - output.NestedItem = nestedItem - } - - if err := validateOptionObjectDefinition(output, constants, apiModels); err != nil { - return nil, fmt.Errorf("validating mapped Option Object Definition: %+v", err) - } - - return &output, nil -} - -func mapDateFormatType(input dataapimodels.DateFormat) (*DateFormat, error) { - mappings := map[dataapimodels.DateFormat]DateFormat{ - dataapimodels.RFC3339DateFormat: RFC3339DateFormat, - } - if v, ok := mappings[input]; ok { - return &v, nil - } - - return nil, fmt.Errorf("unmapped Date Format Type %q", string(input)) -} - -func mapObjectDefinitionType(input dataapimodels.ObjectDefinitionType) (*ObjectDefinitionType, error) { - mappings := map[dataapimodels.ObjectDefinitionType]ObjectDefinitionType{ - dataapimodels.BooleanObjectDefinitionType: BooleanObjectDefinitionType, - dataapimodels.DateTimeObjectDefinitionType: DateTimeObjectDefinitionType, - dataapimodels.IntegerObjectDefinitionType: IntegerObjectDefinitionType, - dataapimodels.FloatObjectDefinitionType: FloatObjectDefinitionType, - dataapimodels.RawFileObjectDefinitionType: RawFileObjectDefinitionType, - dataapimodels.RawObjectObjectDefinitionType: RawObjectObjectDefinitionType, - dataapimodels.ReferenceObjectDefinitionType: ReferenceObjectDefinitionType, - dataapimodels.StringObjectDefinitionType: StringObjectDefinitionType, - dataapimodels.CsvObjectDefinitionType: CsvObjectDefinitionType, - dataapimodels.DictionaryObjectDefinitionType: DictionaryObjectDefinitionType, - dataapimodels.ListObjectDefinitionType: ListObjectDefinitionType, - - dataapimodels.EdgeZoneObjectDefinitionType: EdgeZoneObjectDefinitionType, - dataapimodels.LocationObjectDefinitionType: LocationObjectDefinitionType, - dataapimodels.TagsObjectDefinitionType: TagsObjectDefinitionType, - dataapimodels.SystemAssignedIdentityObjectDefinitionType: SystemAssignedIdentityObjectDefinitionType, - dataapimodels.SystemAndUserAssignedIdentityListObjectDefinitionType: SystemAndUserAssignedIdentityListObjectDefinitionType, - dataapimodels.SystemAndUserAssignedIdentityMapObjectDefinitionType: SystemAndUserAssignedIdentityMapObjectDefinitionType, - dataapimodels.LegacySystemAndUserAssignedIdentityListObjectDefinitionType: LegacySystemAndUserAssignedIdentityListObjectDefinitionType, - dataapimodels.LegacySystemAndUserAssignedIdentityMapObjectDefinitionType: LegacySystemAndUserAssignedIdentityMapObjectDefinitionType, - dataapimodels.SystemOrUserAssignedIdentityListObjectDefinitionType: SystemOrUserAssignedIdentityListObjectDefinitionType, - dataapimodels.SystemOrUserAssignedIdentityMapObjectDefinitionType: SystemOrUserAssignedIdentityMapObjectDefinitionType, - dataapimodels.UserAssignedIdentityListObjectDefinitionType: UserAssignedIdentityListObjectDefinitionType, - dataapimodels.UserAssignedIdentityMapObjectDefinitionType: UserAssignedIdentityMapObjectDefinitionType, - dataapimodels.SystemDataObjectDefinitionType: SystemDataObjectDefinitionType, - dataapimodels.ZoneObjectDefinitionType: ZoneObjectDefinitionType, - dataapimodels.ZonesObjectDefinitionType: ZonesObjectDefinitionType, - } - if v, ok := mappings[input]; ok { - return &v, nil - } - - return nil, fmt.Errorf("unmapped Object Definition Type %q", string(input)) -} - -func mapOptionObjectDefinitionType(input dataapimodels.OptionObjectDefinitionType) (*OptionObjectDefinitionType, error) { - mappings := map[dataapimodels.OptionObjectDefinitionType]OptionObjectDefinitionType{ - dataapimodels.BooleanOptionObjectDefinitionType: BooleanOptionObjectDefinition, - dataapimodels.IntegerOptionObjectDefinitionType: IntegerOptionObjectDefinition, - dataapimodels.FloatOptionObjectDefinitionType: FloatOptionObjectDefinitionType, - dataapimodels.StringOptionObjectDefinitionType: StringOptionObjectDefinitionType, - dataapimodels.CsvOptionObjectDefinitionType: CsvOptionObjectDefinitionType, - dataapimodels.ListOptionObjectDefinitionType: ListOptionObjectDefinitionType, - dataapimodels.ReferenceOptionObjectDefinitionType: ReferenceOptionObjectDefinitionType, - } - if v, ok := mappings[input]; ok { - return &v, nil - } - - return nil, fmt.Errorf("unmapped Options Object Definition Type %q", string(input)) -} - -func mapConstantFieldType(input dataapimodels.ConstantType) (*ConstantType, error) { - mappings := map[dataapimodels.ConstantType]ConstantType{ - dataapimodels.FloatConstant: FloatConstant, - dataapimodels.IntegerConstant: IntegerConstant, - dataapimodels.StringConstant: StringConstant, - } - if v, ok := mappings[input]; ok { - return &v, nil - } - - return nil, fmt.Errorf("unmapped Constant Type %q", string(input)) -} - -func mapResourceIdSegmentType(input dataapimodels.ResourceIdSegmentType) (*ResourceIdSegmentType, error) { - mappings := map[dataapimodels.ResourceIdSegmentType]ResourceIdSegmentType{ - dataapimodels.ConstantResourceIdSegmentType: ConstantResourceIdSegmentType, - dataapimodels.ResourceGroupResourceIdSegmentType: ResourceGroupResourceIdSegmentType, - dataapimodels.ResourceProviderResourceIdSegmentType: ResourceProviderResourceIdSegmentType, - dataapimodels.ScopeResourceIdSegmentType: ScopeResourceIdSegmentType, - dataapimodels.StaticResourceIdSegmentType: StaticResourceIdSegmentType, - dataapimodels.SubscriptionIdResourceIdSegmentType: SubscriptionIdResourceIdSegmentType, - dataapimodels.UserSpecifiedResourceIdSegmentType: UserSpecifiedResourceIdSegmentType, - } - if v, ok := mappings[input]; ok { - return &v, nil - } - - return nil, fmt.Errorf("unmapped Resource Id Segment Type %q", string(input)) -} diff --git a/tools/data-api/models/service_detail.go b/tools/data-api/models/service_detail.go index ead3c0d98c2..f35f570e5d0 100644 --- a/tools/data-api/models/service_detail.go +++ b/tools/data-api/models/service_detail.go @@ -2,7 +2,7 @@ package models type ServiceDetails struct { // ResourceProvider is the Resource Provider this service represents - ResourceProvider string `json:"resourceProvider,omitempty"` + ResourceProvider *string `json:"resourceProvider,omitempty"` // TerraformPackageName is the name of the Service Package within // the Terraform Provider associated with this service.