Repository: apigee/registry
Pull request #874 (2022-12-14 01:58:44)

Add tests for additional artifact YAML I/O.
This adds tests for seven more artifact types: ApiSpecExtensions, DisplaySettings, Lifecycle, Manifest, ReferenceLists, StyleGuides, and TaxonomyLists. Test configurations now allow a `parent` to be specified that can be any valid artifact parent - this must match the `parent` field in the artifact YAML. 

codecov[bot]: # [Codecov](https://codecov.io/gh/apigee/registry/pull/874?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) Report
> Merging [#874](https://codecov.io/gh/apigee/registry/pull/874?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) (4b9883e) into [main](https://codecov.io/gh/apigee/registry/commit/647db579cae977faeca392241bfe5c1ddbf02c9f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) (647db57) will **increase** coverage by `0.05%`.
> The diff coverage is `n/a`.

> :exclamation: Current head 4b9883e differs from pull request most recent head 0c9e125. Consider uploading reports for the commit 0c9e125 to get more accurate results

```diff
@@            Coverage Diff             @@
##             main     #874      +/-   ##
==========================================
+ Coverage   53.62%   53.68%   +0.05%     
==========================================
  Files         106      106              
  Lines        9864     9864              
==========================================
+ Hits         5290     5295       +5     
+ Misses       4031     4026       -5     
  Partials      543      543              
```


| [Impacted Files](https://codecov.io/gh/apigee/registry/pull/874?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) | Coverage Δ | |
|---|---|---|
| [cmd/registry/patch/artifact.go](https://codecov.io/gh/apigee/registry/pull/874/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee#diff-Y21kL3JlZ2lzdHJ5L3BhdGNoL2FydGlmYWN0Lmdv) | `63.42% <0.00%> (+2.31%)` | :arrow_up: |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee)


Patch:
diff --git a/cmd/registry/core/print.go b/cmd/registry/core/print.go
index 248f3e9..43bb709 100644
--- a/cmd/registry/core/print.go
+++ b/cmd/registry/core/print.go
@@ -138,6 +138,8 @@ func GetArtifactMessageContents(artifact *rpc.Artifact) (proto.Message, error) {
 		return unmarshal(artifact.GetContents(), &rpc.Lint{})
 	case "google.cloud.apigeeregistry.applications.v1alpha1.LintStats":
 		return unmarshal(artifact.GetContents(), &rpc.LintStats{})
+	case "google.cloud.apigeeregistry.v1.apihub.ApiSpecExtensionList":
+		return unmarshal(artifact.GetContents(), &rpc.ApiSpecExtensionList{})
 	case "google.cloud.apigeeregistry.v1.apihub.DisplaySettings":
 		return unmarshal(artifact.GetContents(), &rpc.DisplaySettings{})
 	case "google.cloud.apigeeregistry.v1.apihub.Lifecycle":


Test patch:
diff --git a/cmd/registry/patch/patch_test.go b/cmd/registry/patch/patch_test.go
index ac1bf34..5fca23a 100644
--- a/cmd/registry/patch/patch_test.go
+++ b/cmd/registry/patch/patch_test.go
@@ -25,15 +25,17 @@ func TestMain(m *testing.M) {
 	grpctest.TestMain(m, registry.Config{})
 }
 
-func TestSpecArtifactPatches(t *testing.T) {
+func TestArtifactPatches(t *testing.T) {
 	tests := []struct {
-		artifactName string
-		yamlFileName string
-		message      proto.Message
+		artifactID string
+		parent     string
+		yamlFile   string
+		message    proto.Message
 	}{
 		{
-			artifactName: "complexity",
-			yamlFileName: "testdata/artifacts/complexity.yaml",
+			artifactID: "complexity",
+			parent:     "apis/a/versions/v/specs/s",
+			yamlFile:   "testdata/artifacts/complexity.yaml",
 			message: &metrics.Complexity{
 				PathCount:           76,
 				GetCount:            25,
@@ -45,8 +47,243 @@ func TestSpecArtifactPatches(t *testing.T) {
 			},
 		},
 		{
-			artifactName: "vocabulary",
-			yamlFileName: "testdata/artifacts/vocabulary.yaml",
+			artifactID: "display-settings",
+			yamlFile:   "testdata/artifacts/displaysettings.yaml",
+			message: &rpc.DisplaySettings{
+				Id:              "display-settings", // deprecated field
+				Kind:            "DisplaySettings",  // deprecated field
+				Description:     "Defines display settings",
+				Organization:    "Sample",
+				ApiGuideEnabled: true,
+				ApiScoreEnabled: true,
+			},
+		},
+		{
+			artifactID: "extensions",
+			yamlFile:   "testdata/artifacts/extensions.yaml",
+			message: &rpc.ApiSpecExtensionList{
+				Id:          "extensions",           // deprecated field
+				Kind:        "ApiSpecExtensionList", // deprecated field
+				DisplayName: "Sample Extensions",
+				Description: "Extensions connect external tools to registry applications",
+				Extensions: []*rpc.ApiSpecExtensionList_ApiSpecExtension{
+					{
+						Id:          "sample",
+						DisplayName: "Sample",
+						Description: "A sample extension",
+						Filter:      "mime_type.contains('openapi')",
+						UriPattern:  "https://example.com",
+					},
+				},
+			},
+		},
+		{
+			artifactID: "lifecycle",
+			yamlFile:   "testdata/artifacts/lifecycle.yaml",
+			message: &rpc.Lifecycle{
+				Id:          "lifecycle", // deprecated field
+				Kind:        "Lifecycle", // deprecated field
+				DisplayName: "Lifecycle",
+				Description: "A series of stages that an API typically moves through in its lifetime",
+				Stages: []*rpc.Lifecycle_Stage{
+					{
+						Id:           "concept",
+						DisplayName:  "Concept",
+						Description:  "Description of the business case and user needs for why an API should exist",
+						Url:          "https://google.com",
+						DisplayOrder: 0,
+					},
+					{
+						Id:           "design",
+						DisplayName:  "Design",
+						Description:  "Definition of the interface details and proposal of the API contract",
+						Url:          "https://google.com",
+						DisplayOrder: 1,
+					},
+					{
+						Id:           "develop",
+						DisplayName:  "Develop",
+						Description:  "Implementation of the service and its API",
+						Url:          "https://google.com",
+						DisplayOrder: 2,
+					},
+				},
+			},
+		},
+		{
+			artifactID: "manifest",
+			yamlFile:   "testdata/artifacts/manifest.yaml",
+			message: &rpc.Manifest{
+				Id:          "manifest", // deprecated field
+				Kind:        "Manifest", // deprecated field
+				DisplayName: "Sample Manifest",
+				Description: "A sample manifest",
+				GeneratedResources: []*rpc.GeneratedResource{
+					{
+						Pattern: "apis/-/versions/-/specs/-/artifacts/lint-spectral",
+						Filter:  "invalid-filter",
+						Receipt: false,
+						Dependencies: []*rpc.Dependency{
+							{
+								Pattern: "$resource.spec",
+								Filter:  "mime_type.contains('openapi')",
+							},
+						},
+						Action:  "registry compute lint $resource.spec --linter spectral",
+						Refresh: nil,
+					},
+				},
+			},
+		},
+		{
+			artifactID: "references",
+			parent:     "apis/a",
+			yamlFile:   "testdata/artifacts/references.yaml",
+			message: &rpc.ReferenceList{
+				Id:          "references",    // deprecated field
+				Kind:        "ReferenceList", // deprecated field
+				DisplayName: "Related References",
+				Description: "References related to this API",
+				References: []*rpc.ReferenceList_Reference{
+					{
+						Id:          "github",
+						DisplayName: "GitHub Repo",
+						Category:    "apihub-source-code",
+						Resource:    "invalid-resource",
+						Uri:         "https://github.com/apigee/registry",
+					},
+					{
+						Id:          "docs",
+						DisplayName: "GitHub Documentation",
+						Category:    "apihub-other",
+						Resource:    "invalid-resource",
+						Uri:         "https://apigee.github.io/registry/",
+					},
+				},
+			},
+		},
+		{
+			artifactID: "styleguide",
+			yamlFile:   "testdata/artifacts/styleguide.yaml",
+			message: &rpc.StyleGuide{
+				Id:          "styleguide", // deprecated field
+				Kind:        "StyleGuide", // deprecated field
+				DisplayName: "Sample Style Guide",
+				MimeTypes: []string{
+					"application/x.openapi+gzip;version=2",
+				},
+				Guidelines: []*rpc.Guideline{
+					{
+						Id:          "refproperties",
+						DisplayName: "Govern Ref Properties",
+						Description: "This guideline governs properties for ref fields on specs.",
+						Rules: []*rpc.Rule{
+							{
+								Id:             "norefsiblings",
+								DisplayName:    "No Ref Siblings",
+								Description:    "An object exposing a $ref property cannot be further extended with additional properties.",
+								Linter:         "spectral",
+								LinterRulename: "no-$ref-siblings",
+								Severity:       rpc.Rule_ERROR,
+								DocUri:         "https://meta.stoplight.io/docs/spectral/4dec24461f3af-open-api-rules#no-ref-siblings",
+							},
+						},
+						State: rpc.Guideline_ACTIVE,
+					},
+				},
+				Linters: []*rpc.Linter{
+					{
+						Name: "spectral",
+						Uri:  "https://github.com/stoplightio/spectral",
+					},
+				},
+			},
+		},
+		{
+			artifactID: "taxonomies",
+			yamlFile:   "testdata/artifacts/taxonomies.yaml",
+			message: &rpc.TaxonomyList{
+				Id:          "taxonomies",   // deprecated field
+				Kind:        "TaxonomyList", // deprecated field
+				DisplayName: "TaxonomyList",
+				Description: "A list of taxonomies that can be used to classify resources in the registry",
+				Taxonomies: []*rpc.TaxonomyList_Taxonomy{
+					{
+						Id:              "target-users",
+						DisplayName:     "Target users",
+						Description:     "The intended users (consumers) of an API",
+						AdminApplied:    false,
+						SingleSelection: false,
+						SearchExcluded:  false,
+						SystemManaged:   true,
+						DisplayOrder:    0,
+						Elements: []*rpc.TaxonomyList_Taxonomy_Element{
+							{
+								Id:          "team",
+								DisplayName: "Team",
+								Description: "Intended for exclusive use by the producing team",
+							},
+							{
+								Id:          "internal",
+								DisplayName: "Internal",
+								Description: "Available to internal teams",
+							},
+							{
+								Id:          "partner",
+								DisplayName: "Partner",
+								Description: "Available to select partners",
+							},
+							{
+								Id:          "public",
+								DisplayName: "Public",
+								Description: "Published for discovery by the general public",
+							},
+						},
+					},
+					{
+						Id:              "style",
+						DisplayName:     "Style (primary)",
+						Description:     "The primary architectural style of the API",
+						AdminApplied:    false,
+						SingleSelection: true,
+						SearchExcluded:  false,
+						SystemManaged:   true,
+						DisplayOrder:    1,
+						Elements: []*rpc.TaxonomyList_Taxonomy_Element{
+							{
+								Id:          "openapi",
+								DisplayName: "OpenAPI",
+								Description: "https://spec.openapis.org/oas/latest.html",
+							},
+							{
+								Id:          "grpc",
+								DisplayName: "gRPC",
+								Description: "https://grpc.io",
+							},
+							{
+								Id:          "graphql",
+								DisplayName: "GraphQL",
+								Description: "https://graphql.org",
+							},
+							{
+								Id:          "asyncapi",
+								DisplayName: "AsyncAPI",
+								Description: "https://www.asyncapi.com",
+							},
+							{
+								Id:          "soap",
+								DisplayName: "SOAP",
+								Description: "https://en.wikipedia.org/wiki/Web_Services_Description_Language",
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			artifactID: "vocabulary",
+			parent:     "apis/a/versions/v/specs/s",
+			yamlFile:   "testdata/artifacts/vocabulary.yaml",
 			message: &metrics.Vocabulary{
 				Name:       "sample-name",
 				Schemas:    []*metrics.WordCount{{Word: "sample-schema", Count: 1}},
@@ -78,8 +315,8 @@ func TestSpecArtifactPatches(t *testing.T) {
 		t.Fatalf("Setup/Seeding: Failed to seed registry: %s", err)
 	}
 	for _, test := range tests {
-		t.Run(test.artifactName, func(t *testing.T) {
-			b, err := os.ReadFile(test.yamlFileName)
+		t.Run(test.artifactID, func(t *testing.T) {
+			b, err := os.ReadFile(test.yamlFile)
 			if err != nil {
 				t.Fatalf("%s", err)
 			}
@@ -87,7 +324,13 @@ func TestSpecArtifactPatches(t *testing.T) {
 			if err != nil {
 				t.Fatalf("%s", err)
 			}
-			artifactName, err := names.ParseArtifact("projects/patch-project-test/locations/global/apis/a/versions/v/specs/s/artifacts/" + test.artifactName)
+			var collection string
+			if test.parent != "" {
+				collection = "projects/patch-project-test/locations/global/" + test.parent + "/artifacts/"
+			} else {
+				collection = "projects/patch-project-test/locations/global/artifacts/"
+			}
+			artifactName, err := names.ParseArtifact(collection + test.artifactID)
 			if err != nil {
 				t.Fatalf("%s", err)
 			}
@@ -105,12 +348,11 @@ func TestSpecArtifactPatches(t *testing.T) {
 					if err != nil {
 						t.Fatalf("%s", err)
 					}
-					wantedParent := "apis/a/versions/v/specs/s"
-					if header.Metadata.Parent != wantedParent {
-						t.Errorf("Incorrect export parent. Wanted %s, got %s", wantedParent, header.Metadata.Parent)
+					if header.Metadata.Parent != test.parent {
+						t.Errorf("Incorrect export parent. Wanted %s, got %s", test.parent, header.Metadata.Parent)
 					}
-					if header.Metadata.Name != test.artifactName {
-						t.Errorf("Incorrect export name. Wanted %s, got %s", test.artifactName, header.Metadata.Name)
+					if header.Metadata.Name != test.artifactID {
+						t.Errorf("Incorrect export name. Wanted %s, got %s", test.artifactID, header.Metadata.Name)
 					}
 					if !cmp.Equal(b, out, opts) {
 						t.Errorf("GetDiff returned unexpected diff (-want +got):\n%s", cmp.Diff(b, out, opts))
diff --git a/cmd/registry/patch/testdata/artifacts/displaysettings.yaml b/cmd/registry/patch/testdata/artifacts/displaysettings.yaml
new file mode 100644
index 0000000..bc3101e
--- /dev/null
+++ b/cmd/registry/patch/testdata/artifacts/displaysettings.yaml
@@ -0,0 +1,9 @@
+apiVersion: apigeeregistry/v1
+kind: DisplaySettings
+metadata:
+  name: display-settings
+data:
+  description: Defines display settings
+  organization: Sample
+  apiGuideEnabled: true
+  apiScoreEnabled: true
diff --git a/cmd/registry/patch/testdata/artifacts/extensions.yaml b/cmd/registry/patch/testdata/artifacts/extensions.yaml
new file mode 100644
index 0000000..f5ef47c
--- /dev/null
+++ b/cmd/registry/patch/testdata/artifacts/extensions.yaml
@@ -0,0 +1,13 @@
+apiVersion: apigeeregistry/v1
+kind: ApiSpecExtensionList
+metadata:
+  name: extensions
+data:
+  displayName: Sample Extensions
+  description: Extensions connect external tools to registry applications
+  extensions:
+    - id: sample
+      displayName: Sample
+      description: A sample extension
+      filter: mime_type.contains('openapi')
+      uriPattern: https://example.com
diff --git a/cmd/registry/patch/testdata/artifacts/lifecycle.yaml b/cmd/registry/patch/testdata/artifacts/lifecycle.yaml
new file mode 100644
index 0000000..057b91c
--- /dev/null
+++ b/cmd/registry/patch/testdata/artifacts/lifecycle.yaml
@@ -0,0 +1,23 @@
+apiVersion: apigeeregistry/v1
+kind: Lifecycle
+metadata:
+  name: lifecycle
+data:
+  displayName: Lifecycle
+  description: A series of stages that an API typically moves through in its lifetime
+  stages:
+    - id: concept
+      displayName: Concept
+      description: Description of the business case and user needs for why an API should exist
+      url: https://google.com
+      displayOrder: 0
+    - id: design
+      displayName: Design
+      description: Definition of the interface details and proposal of the API contract
+      url: https://google.com
+      displayOrder: 1
+    - id: develop
+      displayName: Develop
+      description: Implementation of the service and its API
+      url: https://google.com
+      displayOrder: 2
diff --git a/cmd/registry/patch/testdata/artifacts/manifest.yaml b/cmd/registry/patch/testdata/artifacts/manifest.yaml
new file mode 100644
index 0000000..2409af2
--- /dev/null
+++ b/cmd/registry/patch/testdata/artifacts/manifest.yaml
@@ -0,0 +1,16 @@
+apiVersion: apigeeregistry/v1
+kind: Manifest
+metadata:
+  name: manifest
+data:
+  displayName: Sample Manifest
+  description: A sample manifest
+  generatedResources:
+    - pattern: apis/-/versions/-/specs/-/artifacts/lint-spectral
+      filter: invalid-filter
+      receipt: false
+      dependencies:
+        - pattern: $resource.spec
+          filter: mime_type.contains('openapi')
+      action: registry compute lint $resource.spec --linter spectral
+      refresh: null
diff --git a/cmd/registry/patch/testdata/artifacts/references.yaml b/cmd/registry/patch/testdata/artifacts/references.yaml
new file mode 100644
index 0000000..ac439c4
--- /dev/null
+++ b/cmd/registry/patch/testdata/artifacts/references.yaml
@@ -0,0 +1,23 @@
+apiVersion: apigeeregistry/v1
+kind: ReferenceList
+metadata:
+  name: references
+  parent: apis/a
+  labels:
+    label1: label
+  annotations:
+    annotation1: annotation
+data:
+  displayName: Related References
+  description: References related to this API
+  references:
+    - id: github
+      displayName: GitHub Repo
+      category: apihub-source-code
+      resource: invalid-resource
+      uri: https://github.com/apigee/registry
+    - id: docs
+      displayName: GitHub Documentation
+      category: apihub-other
+      resource: invalid-resource
+      uri: https://apigee.github.io/registry/
diff --git a/cmd/registry/patch/testdata/artifacts/styleguide.yaml b/cmd/registry/patch/testdata/artifacts/styleguide.yaml
new file mode 100644
index 0000000..ff05cfe
--- /dev/null
+++ b/cmd/registry/patch/testdata/artifacts/styleguide.yaml
@@ -0,0 +1,24 @@
+apiVersion: apigeeregistry/v1
+kind: StyleGuide
+metadata:
+  name: styleguide
+data:
+  displayName: Sample Style Guide
+  mimeTypes:
+    - application/x.openapi+gzip;version=2
+  guidelines:
+    - id: refproperties
+      displayName: Govern Ref Properties
+      description: This guideline governs properties for ref fields on specs.
+      rules:
+        - id: norefsiblings
+          displayName: No Ref Siblings
+          description: An object exposing a $ref property cannot be further extended with additional properties.
+          linter: spectral
+          linterRulename: no-$ref-siblings
+          severity: ERROR
+          docUri: https://meta.stoplight.io/docs/spectral/4dec24461f3af-open-api-rules#no-ref-siblings
+      state: ACTIVE
+  linters:
+    - name: spectral
+      uri: https://github.com/stoplightio/spectral
diff --git a/cmd/registry/patch/testdata/artifacts/taxonomies.yaml b/cmd/registry/patch/testdata/artifacts/taxonomies.yaml
new file mode 100644
index 0000000..0dbdf0a
--- /dev/null
+++ b/cmd/registry/patch/testdata/artifacts/taxonomies.yaml
@@ -0,0 +1,53 @@
+apiVersion: apigeeregistry/v1
+kind: TaxonomyList
+metadata:
+  name: taxonomies
+data:
+  displayName: TaxonomyList
+  description: A list of taxonomies that can be used to classify resources in the registry
+  taxonomies:
+    - id: target-users
+      displayName: Target users
+      description: The intended users (consumers) of an API
+      adminApplied: false
+      singleSelection: false
+      searchExcluded: false
+      systemManaged: true
+      displayOrder: 0
+      elements:
+        - id: team
+          displayName: Team
+          description: Intended for exclusive use by the producing team
+        - id: internal
+          displayName: Internal
+          description: Available to internal teams
+        - id: partner
+          displayName: Partner
+          description: Available to select partners
+        - id: public
+          displayName: Public
+          description: Published for discovery by the general public
+    - id: style
+      displayName: Style (primary)
+      description: The primary architectural style of the API
+      adminApplied: false
+      singleSelection: true
+      searchExcluded: false
+      systemManaged: true
+      displayOrder: 1
+      elements:
+        - id: openapi
+          displayName: OpenAPI
+          description: https://spec.openapis.org/oas/latest.html
+        - id: grpc
+          displayName: gRPC
+          description: https://grpc.io
+        - id: graphql
+          displayName: GraphQL
+          description: https://graphql.org
+        - id: asyncapi
+          displayName: AsyncAPI
+          description: https://www.asyncapi.com
+        - id: soap
+          displayName: SOAP
+          description: https://en.wikipedia.org/wiki/Web_Services_Description_Language