From 06e9ea73373b50ff7203636dc3b14134fd10ad1d Mon Sep 17 00:00:00 2001
From: Travis Prescott <trpresco@microsoft.com>
Date: Wed, 1 Nov 2023 11:17:38 -0500
Subject: [PATCH 1/6] =?UTF-8?q?=EF=BB=BFFix=20#2571.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 ...erencedVisibilityFix_2023-11-01-16-22.json | 10 ++++++++
 packages/openapi3/test/metadata.test.ts       | 24 +++++++++++++++++++
 2 files changed, 34 insertions(+)
 create mode 100644 common/changes/@typespec/openapi3/unreferencedVisibilityFix_2023-11-01-16-22.json

diff --git a/common/changes/@typespec/openapi3/unreferencedVisibilityFix_2023-11-01-16-22.json b/common/changes/@typespec/openapi3/unreferencedVisibilityFix_2023-11-01-16-22.json
new file mode 100644
index 0000000000..209fbbe494
--- /dev/null
+++ b/common/changes/@typespec/openapi3/unreferencedVisibilityFix_2023-11-01-16-22.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@typespec/openapi3",
+      "comment": "Emitter will now emit all properties on unreferenced schemas.",
+      "type": "none"
+    }
+  ],
+  "packageName": "@typespec/openapi3"
+}
\ No newline at end of file
diff --git a/packages/openapi3/test/metadata.test.ts b/packages/openapi3/test/metadata.test.ts
index 12f762f69d..87bd863d37 100644
--- a/packages/openapi3/test/metadata.test.ts
+++ b/packages/openapi3/test/metadata.test.ts
@@ -2,6 +2,30 @@ import { deepStrictEqual } from "assert";
 import { openApiFor } from "./test-host.js";
 
 describe("openapi3: metadata", () => {
+  it("will expose all properties on unreferenced models", async () => {
+    const res = await openApiFor(`
+      model M {
+        @visibility("read") r: string;
+        @visibility("create", "update") uc?: string;
+        @visibility("read", "create") rc?: string;
+        @visibility("read", "update", "create") ruc?: string;
+      }
+    `);
+
+    deepStrictEqual(res.components.schemas, {
+      M: {
+        type: "object",
+        properties: {
+          r: { type: "string", readOnly: true },
+          uc: { type: "string" },
+          rc: { type: "string" },
+          ruc: { type: "string" },
+        },
+        required: ["r"],
+      },
+    });
+  });
+
   it("will expose create visibility properties on PATCH model using @requestVisibility", async () => {
     const res = await openApiFor(`
       model M {

From a114e7edfcbc5f719c494b48e399c798e8838aab Mon Sep 17 00:00:00 2001
From: Travis Prescott <trpresco@microsoft.com>
Date: Wed, 1 Nov 2023 12:12:15 -0500
Subject: [PATCH 2/6] Regen sample

---
 .../@typespec/openapi3/openapi.yaml           | 39 ++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml
index 464f8a85ca..affea0069b 100644
--- a/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml
+++ b/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml
@@ -103,6 +103,8 @@ components:
         id:
           type: string
           readOnly: true
+        secret:
+          type: string
         name:
           type: string
         test:
@@ -112,7 +114,7 @@ components:
         relatives:
           type: array
           items:
-            $ref: '#/components/schemas/PersonRelative'
+            $ref: '#/components/schemas/PersonRelativeReadOrCreateOrUpdateOrDeleteOrQueryItem'
     Person:
       type: object
       required:
@@ -174,6 +176,31 @@ components:
           type: array
           items:
             $ref: '#/components/schemas/PersonRelativeCreateOrUpdateItem'
+    PersonReadOrCreateOrUpdateOrDeleteOrQueryItem:
+      type: object
+      required:
+        - id
+        - secret
+        - name
+        - test
+        - other
+        - relatives
+      properties:
+        id:
+          type: string
+          readOnly: true
+        secret:
+          type: string
+        name:
+          type: string
+        test:
+          type: string
+        other:
+          type: string
+        relatives:
+          type: array
+          items:
+            $ref: '#/components/schemas/PersonRelativeReadOrCreateOrUpdateOrDeleteOrQueryItem'
     PersonRelative:
       type: object
       required:
@@ -204,6 +231,16 @@ components:
           $ref: '#/components/schemas/PersonCreateOrUpdateItem'
         relationship:
           type: string
+    PersonRelativeReadOrCreateOrUpdateOrDeleteOrQueryItem:
+      type: object
+      required:
+        - person
+        - relationship
+      properties:
+        person:
+          $ref: '#/components/schemas/PersonReadOrCreateOrUpdateOrDeleteOrQueryItem'
+        relationship:
+          type: string
     PersonRelativeUpdateItem:
       type: object
       required:

From 4173f8b6089a52c7a354c3c0e375872d75b73e28 Mon Sep 17 00:00:00 2001
From: Travis Prescott <trpresco@microsoft.com>
Date: Wed, 1 Nov 2023 12:47:33 -0500
Subject: [PATCH 3/6] =?UTF-8?q?=EF=BB=BFUpdate=20naming=20logic.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/openapi3/src/openapi.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/openapi3/src/openapi.ts b/packages/openapi3/src/openapi.ts
index d0f68966a9..c0fcc7091c 100644
--- a/packages/openapi3/src/openapi.ts
+++ b/packages/openapi3/src/openapi.ts
@@ -1295,7 +1295,7 @@ function createOAPIEmitter(
           !paramModels.has(type) &&
           !shouldInline(program, type)
         ) {
-          callSchemaEmitter(type, Visibility.Read);
+          callSchemaEmitter(type, Visibility.All);
         }
       };
       const skipSubNamespaces = isGlobalNamespace(program, serviceNamespace);

From e38aa3cc2748404ae77f0c6a5c25be7b15ced020 Mon Sep 17 00:00:00 2001
From: Travis Prescott <trpresco@microsoft.com>
Date: Thu, 16 Nov 2023 11:04:29 -0800
Subject: [PATCH 4/6] =?UTF-8?q?=EF=BB=BFRevert=20changes.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/samples/specs/visibility/visibility.tsp    |  4 ++++
 .../visibility/@typespec/openapi3/openapi.yaml      | 13 +++++++++++--
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/packages/samples/specs/visibility/visibility.tsp b/packages/samples/specs/visibility/visibility.tsp
index dce916cbd6..0a26d8e5cd 100644
--- a/packages/samples/specs/visibility/visibility.tsp
+++ b/packages/samples/specs/visibility/visibility.tsp
@@ -72,6 +72,10 @@ namespace Hello {
   @post op create(@body person: WritablePerson): ReadablePerson;
   @put op update(@body person: WriteableOptionalPerson): ReadablePerson;
 
+  @route("/hello/optional")
+  @get
+  op getOptional(): OptionalPerson;
+
   @TypeSpec.Rest.listsResource(Person)
   op list(): ListResult<Person>;
 
diff --git a/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml
index affea0069b..7d8b247c96 100644
--- a/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml
+++ b/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml
@@ -70,6 +70,17 @@ paths:
           application/json:
             schema:
               $ref: '#/components/schemas/PersonUpdate'
+  /hello/hello/optional:
+    get:
+      operationId: Hello_getOptional
+      parameters: []
+      responses:
+        '200':
+          description: The request has succeeded.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/OptionalPerson'
   /hello/{id}:
     get:
       operationId: Hello_read
@@ -103,8 +114,6 @@ components:
         id:
           type: string
           readOnly: true
-        secret:
-          type: string
         name:
           type: string
         test:

From 69ebe03968255eed2422680e0a5c2875abaa3d3f Mon Sep 17 00:00:00 2001
From: Travis Prescott <trpresco@microsoft.com>
Date: Tue, 28 Nov 2023 14:09:59 -0800
Subject: [PATCH 5/6] =?UTF-8?q?=EF=BB=BFAdd=20tests.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/openapi3/test/metadata.test.ts | 66 ++++++++++++++++++++++++-
 1 file changed, 65 insertions(+), 1 deletion(-)

diff --git a/packages/openapi3/test/metadata.test.ts b/packages/openapi3/test/metadata.test.ts
index 87bd863d37..c9e2a2ed25 100644
--- a/packages/openapi3/test/metadata.test.ts
+++ b/packages/openapi3/test/metadata.test.ts
@@ -2,7 +2,7 @@ import { deepStrictEqual } from "assert";
 import { openApiFor } from "./test-host.js";
 
 describe("openapi3: metadata", () => {
-  it("will expose all properties on unreferenced models", async () => {
+  it("will expose all properties on unreferenced models but filter properties on referenced models", async () => {
     const res = await openApiFor(`
       model M {
         @visibility("read") r: string;
@@ -26,6 +26,70 @@ describe("openapi3: metadata", () => {
     });
   });
 
+  it("prioritizes read visibility when referenced and unreferenced models share schemas", async () => {
+    const res = await openApiFor(`
+      model Shared {
+        @visibility("create", "update") password: string;
+        prop: string;
+      }
+
+      model Unreferenced {
+        @visibility("read") r: string;
+        @visibility("create") c: string;
+        shared: Shared;
+      }
+
+      model Referenced {
+        @visibility("read") r: string;
+        @visibility("create") c: string;
+        shared: Shared;
+      }
+
+      @get op get(): Referenced;
+    `);
+
+    deepStrictEqual(res.components.schemas, {
+      Referenced: {
+        type: "object",
+        properties: {
+          r: { type: "string", readOnly: true },
+          shared: { $ref: "#/components/schemas/Shared" },
+        },
+        required: ["r", "shared"],
+      },
+      Shared: {
+        type: "object",
+        required: ["prop"],
+        properties: {
+          prop: {
+            type: "string",
+          },
+        },
+      },
+      SharedReadOrCreateOrUpdateOrDelete: {
+        type: "object",
+        required: ["password", "prop"],
+        properties: {
+          password: {
+            type: "string",
+          },
+          prop: {
+            type: "string",
+          },
+        },
+      },
+      Unreferenced: {
+        type: "object",
+        properties: {
+          c: { type: "string" },
+          r: { type: "string", readOnly: true },
+          shared: { $ref: "#/components/schemas/SharedReadOrCreateOrUpdateOrDelete" },
+        },
+        required: ["r", "c", "shared"],
+      },
+    });
+  });
+
   it("will expose create visibility properties on PATCH model using @requestVisibility", async () => {
     const res = await openApiFor(`
       model M {

From f4440b568b7644ac12d4d8bd508f1687c63840e7 Mon Sep 17 00:00:00 2001
From: Travis Prescott <trpresco@microsoft.com>
Date: Wed, 29 Nov 2023 13:54:49 -0800
Subject: [PATCH 6/6] CI fixes.

---
 packages/openapi3/src/visibility-usage.ts           |  3 ++-
 packages/openapi3/test/metadata.test.ts             |  4 ++--
 packages/samples/specs/visibility/visibility.tsp    |  4 ----
 .../visibility/@typespec/openapi3/openapi.yaml      | 13 ++-----------
 4 files changed, 6 insertions(+), 18 deletions(-)

diff --git a/packages/openapi3/src/visibility-usage.ts b/packages/openapi3/src/visibility-usage.ts
index a7f069033f..6aa62b4e7d 100644
--- a/packages/openapi3/src/visibility-usage.ts
+++ b/packages/openapi3/src/visibility-usage.ts
@@ -34,9 +34,10 @@ export function resolveVisibilityUsage(
   const reachableTypes = new Set<Type>(usages.keys());
 
   if (!omitUnreachableTypes) {
+    // Evaluate all unreferenced types and the types they reference with Visibility.All
     const trackType = (type: Type) => {
       if (!usages.has(type)) {
-        navigateReferencedTypes(type, Visibility.Read, (type, vis) =>
+        navigateReferencedTypes(type, Visibility.All, (type, vis) =>
           trackUsageExact(usages, type, vis)
         );
       }
diff --git a/packages/openapi3/test/metadata.test.ts b/packages/openapi3/test/metadata.test.ts
index c9e2a2ed25..cadf76625a 100644
--- a/packages/openapi3/test/metadata.test.ts
+++ b/packages/openapi3/test/metadata.test.ts
@@ -66,7 +66,7 @@ describe("openapi3: metadata", () => {
           },
         },
       },
-      SharedReadOrCreateOrUpdateOrDelete: {
+      SharedReadOrCreateOrUpdateOrDeleteOrQuery: {
         type: "object",
         required: ["password", "prop"],
         properties: {
@@ -83,7 +83,7 @@ describe("openapi3: metadata", () => {
         properties: {
           c: { type: "string" },
           r: { type: "string", readOnly: true },
-          shared: { $ref: "#/components/schemas/SharedReadOrCreateOrUpdateOrDelete" },
+          shared: { $ref: "#/components/schemas/SharedReadOrCreateOrUpdateOrDeleteOrQuery" },
         },
         required: ["r", "c", "shared"],
       },
diff --git a/packages/samples/specs/visibility/visibility.tsp b/packages/samples/specs/visibility/visibility.tsp
index 0a26d8e5cd..dce916cbd6 100644
--- a/packages/samples/specs/visibility/visibility.tsp
+++ b/packages/samples/specs/visibility/visibility.tsp
@@ -72,10 +72,6 @@ namespace Hello {
   @post op create(@body person: WritablePerson): ReadablePerson;
   @put op update(@body person: WriteableOptionalPerson): ReadablePerson;
 
-  @route("/hello/optional")
-  @get
-  op getOptional(): OptionalPerson;
-
   @TypeSpec.Rest.listsResource(Person)
   op list(): ListResult<Person>;
 
diff --git a/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml
index 7d8b247c96..affea0069b 100644
--- a/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml
+++ b/packages/samples/test/output/visibility/@typespec/openapi3/openapi.yaml
@@ -70,17 +70,6 @@ paths:
           application/json:
             schema:
               $ref: '#/components/schemas/PersonUpdate'
-  /hello/hello/optional:
-    get:
-      operationId: Hello_getOptional
-      parameters: []
-      responses:
-        '200':
-          description: The request has succeeded.
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/OptionalPerson'
   /hello/{id}:
     get:
       operationId: Hello_read
@@ -114,6 +103,8 @@ components:
         id:
           type: string
           readOnly: true
+        secret:
+          type: string
         name:
           type: string
         test: