From cd1aac614141121813cd8eac9da34b6985e58c0e Mon Sep 17 00:00:00 2001 From: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com> Date: Tue, 5 Nov 2024 19:49:47 -0500 Subject: [PATCH] feat(csharp): Support HTTP endpoints in GRPC service (#5104) feat(csharp): Support HTTP endpoints in GRPC service --- generators/csharp/codegen/package.json | 2 +- generators/csharp/model/package.json | 2 +- .../model/src/snippets/ExampleGenerator.ts | 2 +- generators/csharp/sdk/package.json | 2 +- .../csharp/sdk/src/SdkGeneratorContext.ts | 4 + .../sdk/src/endpoint/EndpointGenerator.ts | 4 +- .../WrappedRequestGenerator.ts | 57 +- generators/csharp/sdk/versions.yml | 7 + pnpm-lock.yaml | 24 +- .../.github/workflows/ci.yml | 0 .../csharp-grpc-proto-exhaustive}/.gitignore | 968 +++++++++--------- .../.mock/fern.config.json | 0 .../.mock/generators.yml | 7 + .../.mock/openapi/openapi.yml | 25 + .../.mock/overrides.yml | 0 .../.mock/proto/data/v1/data.proto | 0 .../.mock/proto/google/api/annotations.proto | 0 .../proto/google/api/field_behavior.proto | 0 .../.mock/proto/google/api/http.proto | 0 .../proto/data/v1/data.proto | 0 .../proto/google/api/annotations.proto | 0 .../proto/google/api/field_behavior.proto | 0 .../proto/google/api/http.proto | 0 .../snippet-templates.json | 0 .../snippet.json | 0 .../SeedApi.Test/Core/EnumSerializerTests.cs | 61 ++ .../src/SeedApi.Test/SeedApi.Test.csproj | 0 .../src/SeedApi}/Column.cs | 6 + .../SeedApi/Core/CollectionItemSerializer.cs | 0 .../src/SeedApi/Core/Constants.cs | 0 .../src/SeedApi/Core/DateTimeSerializer.cs | 0 .../src/SeedApi/Core/EnumSerializer.cs} | 4 +- .../src/SeedApi/Core/JsonConfiguration.cs | 2 +- .../src/SeedApi/Core/OneOfSerializer.cs | 26 +- .../src/SeedApi/Core/Public/Version.cs | 6 + .../src/SeedApi}/DeleteResponse.cs | 6 + .../src/SeedApi}/DescribeResponse.cs | 10 +- .../src/SeedApi}/FetchResponse.cs | 6 + .../src/SeedApi}/IndexedData.cs | 6 + .../src/SeedApi}/ListElement.cs | 6 + .../src/SeedApi}/ListResponse.cs | 6 + .../src/SeedApi}/Metadata.cs | 6 + .../src/SeedApi}/MetadataValue.cs | 7 +- .../src/SeedApi}/NamespaceSummary.cs | 8 +- .../src/SeedApi}/Pagination.cs | 6 + .../src/SeedApi}/QueryColumn.cs | 8 +- .../src/SeedApi}/QueryResponse.cs | 6 + .../src/SeedApi}/QueryResult.cs | 6 + .../src/SeedApi}/ScoredColumn.cs | 6 + .../src/SeedApi/SeedApi.csproj | 12 +- .../src/SeedApi}/UpdateResponse.cs | 6 + .../src/SeedApi}/UploadResponse.cs | 8 +- .../src/SeedApi}/Usage.cs | 8 +- .../.github/workflows/ci.yml | 69 ++ .../csharp-model/csharp-grpc-proto/.gitignore | 484 +++++++++ .../csharp-grpc-proto/.mock/fern.config.json | 1 + .../csharp-grpc-proto}/.mock/generators.yml | 2 +- .../csharp-grpc-proto/.mock/overrides.yml | 31 + .../.mock/proto/google/api/annotations.proto | 31 + .../.mock/proto/google/api/http.proto | 379 +++++++ .../.mock/proto/user/v1/user.proto | 38 + .../proto/google/api/annotations.proto | 31 + .../proto/google/api/http.proto | 379 +++++++ .../proto/user/v1/user.proto | 38 + .../csharp-grpc-proto/snippet-templates.json | 0 .../csharp-grpc-proto/snippet.json | 0 .../SeedApi.Test/Core/EnumSerializerTests.cs | 61 ++ .../src/SeedApi.Test/SeedApi.Test.csproj | 26 + .../SeedApi/Core/CollectionItemSerializer.cs | 91 ++ .../src/SeedApi/Core/Constants.cs | 7 + .../src/SeedApi/Core/DateTimeSerializer.cs | 22 + .../src/SeedApi/Core/EnumSerializer.cs | 53 + .../src/SeedApi/Core/JsonConfiguration.cs | 32 + .../src/SeedApi/Core/OneOfSerializer.cs | 69 ++ .../src/SeedApi/Core/Public/Version.cs | 6 + .../src/SeedApi/CreateResponse.cs | 42 + .../csharp-grpc-proto/src/SeedApi/Metadata.cs | 39 + .../src/SeedApi/MetadataValue.cs | 91 ++ .../src/SeedApi/SeedApi.csproj | 69 ++ .../src/SeedApi/UserModel.cs | 74 ++ .../.github/workflows/ci.yml | 69 ++ .../csharp-grpc-proto-exhaustive/.gitignore | 484 +++++++++ .../.mock/fern.config.json | 1 + .../.mock/generators.yml | 7 + .../.mock/openapi/openapi.yml | 25 + .../.mock/overrides.yml | 56 + .../.mock/proto/data/v1/data.proto | 213 ++++ .../.mock/proto/google/api/annotations.proto | 31 + .../proto/google/api/field_behavior.proto | 104 ++ .../.mock/proto/google/api/http.proto | 379 +++++++ .../csharp-grpc-proto-exhaustive/README.md | 87 ++ .../proto/data/v1/data.proto | 213 ++++ .../proto/google/api/annotations.proto | 31 + .../proto/google/api/field_behavior.proto | 104 ++ .../proto/google/api/http.proto | 379 +++++++ .../csharp-grpc-proto-exhaustive/reference.md | 318 ++++++ .../snippet-templates.json | 0 .../csharp-grpc-proto-exhaustive/snippet.json | 101 ++ .../SeedApi.Test/Core/EnumSerializerTests.cs | 61 ++ .../src/SeedApi.Test/Core/RawClientTests.cs | 113 ++ .../src/SeedApi.Test/SeedApi.Test.csproj | 26 + .../src/SeedApi.Test/TestClient.cs | 0 .../SeedApi/Core/CollectionItemSerializer.cs | 91 ++ .../src/SeedApi/Core/Constants.cs | 7 + .../src/SeedApi/Core/DateTimeSerializer.cs | 22 + .../src/SeedApi/Core/EnumSerializer.cs | 53 + .../src/SeedApi/Core/Extensions.cs | 0 .../src/SeedApi/Core/HeaderValue.cs | 17 + .../src/SeedApi/Core/Headers.cs | 17 + .../src/SeedApi/Core/HttpMethodExtensions.cs | 0 .../src/SeedApi/Core/JsonConfiguration.cs | 32 + .../src/SeedApi/Core/OneOfSerializer.cs | 69 ++ .../src/SeedApi/Core/Public/ClientOptions.cs | 57 ++ .../SeedApi/Core/Public/GrpcRequestOptions.cs | 11 +- .../src/SeedApi/Core/Public/RequestOptions.cs | 6 + .../Core/Public/SeedApiApiException.cs | 0 .../SeedApi/Core/Public/SeedApiEnvironment.cs | 6 + .../SeedApi/Core/Public/SeedApiException.cs | 0 .../src/SeedApi/Core/Public/Version.cs | 6 + .../src/SeedApi/Core/RawClient.cs | 152 +-- .../src/SeedApi/Core/RawGrpcClient.cs | 41 +- .../SeedApi/Dataservice/DataserviceClient.cs | 96 +- .../Dataservice/Requests/DeleteRequest.cs | 6 + .../Dataservice/Requests/DescribeRequest.cs | 6 + .../Dataservice/Requests/FetchRequest.cs | 6 + .../Dataservice/Requests/ListRequest.cs | 8 +- .../Dataservice/Requests/QueryRequest.cs | 6 + .../Dataservice/Requests/UpdateRequest.cs | 6 + .../Dataservice/Requests/UploadRequest.cs | 6 + .../src/SeedApi/SeedApi.csproj | 71 ++ .../src/SeedApi/SeedApiClient.cs | 35 + .../src/SeedApi/Types/Column.cs | 64 ++ .../src/SeedApi/Types/DeleteResponse.cs | 30 + .../src/SeedApi/Types/DescribeResponse.cs | 73 ++ .../src/SeedApi/Types/FetchResponse.cs | 65 ++ .../src/SeedApi/Types/IndexedData.cs | 50 + .../src/SeedApi/Types/ListElement.cs | 39 + .../src/SeedApi/Types/ListResponse.cs | 66 ++ .../src/SeedApi/Types/Metadata.cs | 39 + .../src/SeedApi/Types/MetadataValue.cs | 91 ++ .../src/SeedApi/Types/NamespaceSummary.cs | 39 + .../src/SeedApi/Types/Pagination.cs | 39 + .../src/SeedApi/Types/QueryColumn.cs | 75 ++ .../src/SeedApi/Types/QueryResponse.cs | 66 ++ .../src/SeedApi/Types/QueryResult.cs | 50 + .../src/SeedApi/Types/ScoredColumn.cs | 72 ++ .../src/SeedApi/Types/UpdateResponse.cs | 30 + .../src/SeedApi/Types/UploadResponse.cs | 39 + .../src/SeedApi/Types/Usage.cs | 39 + .../.github/workflows/ci.yml | 69 ++ seed/csharp-sdk/csharp-grpc-proto/.gitignore | 484 +++++++++ .../csharp-grpc-proto/.mock/fern.config.json | 1 + .../csharp-grpc-proto/.mock/generators.yml | 6 + .../csharp-grpc-proto/.mock/overrides.yml | 31 + .../.mock/proto/google/api/annotations.proto | 31 + .../.mock/proto/google/api/http.proto | 379 +++++++ .../.mock/proto/user/v1/user.proto | 38 + seed/csharp-sdk/csharp-grpc-proto/README.md | 87 ++ .../proto/google/api/annotations.proto | 31 + .../proto/google/api/http.proto | 379 +++++++ .../proto/user/v1/user.proto | 38 + .../csharp-sdk/csharp-grpc-proto/reference.md | 41 + .../csharp-grpc-proto/snippet-templates.json | 0 .../csharp-sdk/csharp-grpc-proto/snippet.json | 17 + .../SeedApi.Test/Core/EnumSerializerTests.cs | 61 ++ .../src/SeedApi.Test/Core/RawClientTests.cs | 113 ++ .../src/SeedApi.Test/SeedApi.Test.csproj | 26 + .../src/SeedApi.Test/TestClient.cs | 8 + .../SeedApi/Core/CollectionItemSerializer.cs | 91 ++ .../src/SeedApi/Core/Constants.cs | 7 + .../src/SeedApi/Core/DateTimeSerializer.cs | 22 + .../src/SeedApi/Core/EnumSerializer.cs | 53 + .../src/SeedApi/Core/Extensions.cs | 14 + .../src/SeedApi/Core/HeaderValue.cs | 17 + .../src/SeedApi/Core/Headers.cs | 17 + .../src/SeedApi/Core/HttpMethodExtensions.cs | 8 + .../src/SeedApi/Core/JsonConfiguration.cs | 32 + .../src/SeedApi/Core/OneOfSerializer.cs | 69 ++ .../src/SeedApi/Core/Public/ClientOptions.cs | 22 + .../SeedApi/Core/Public/GrpcRequestOptions.cs | 36 + .../src/SeedApi/Core/Public/RequestOptions.cs | 35 + .../Core/Public/SeedApiApiException.cs | 18 + .../SeedApi/Core/Public/SeedApiException.cs | 11 + .../src/SeedApi/Core/Public/Version.cs | 6 + .../src/SeedApi/Core/RawClient.cs | 192 ++++ .../src/SeedApi/Core/RawGrpcClient.cs | 79 ++ .../src/SeedApi/SeedApi.csproj | 69 ++ .../src/SeedApi/SeedApiClient.cs | 35 + .../src/SeedApi/Types/CreateResponse.cs | 42 + .../src/SeedApi/Types/Metadata.cs | 39 + .../src/SeedApi/Types/MetadataValue.cs | 91 ++ .../src/SeedApi/Types/UserModel.cs | 74 ++ .../Userservice/Requests/CreateRequest.cs | 59 ++ .../SeedApi/Userservice/UserserviceClient.cs | 60 ++ .../SeedApi/Dataservice/DataserviceClient.cs | 51 +- .../SeedApi/Dataservice/DataserviceClient.cs | 51 +- .../SeedApi/Dataservice/DataserviceClient.cs | 51 +- .../Unit/MockServer/BaseMockServerTest.cs | 39 - .../src/SeedApi/SeedApiClient.cs | 23 - 199 files changed, 10571 insertions(+), 784 deletions(-) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/.github/workflows/ci.yml (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/.gitignore (93%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/.mock/fern.config.json (100%) create mode 100644 seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/generators.yml create mode 100644 seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/openapi/openapi.yml rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/.mock/overrides.yml (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/.mock/proto/data/v1/data.proto (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/.mock/proto/google/api/annotations.proto (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/.mock/proto/google/api/field_behavior.proto (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/.mock/proto/google/api/http.proto (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/proto/data/v1/data.proto (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/proto/google/api/annotations.proto (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/proto/google/api/field_behavior.proto (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/proto/google/api/http.proto (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/snippet-templates.json (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/snippet.json (100%) create mode 100644 seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi.Test/Core/EnumSerializerTests.cs rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/src/SeedApi.Test/SeedApi.Test.csproj (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/Column.cs (93%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/src/SeedApi/Core/CollectionItemSerializer.cs (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/src/SeedApi/Core/Constants.cs (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/src/SeedApi/Core/DateTimeSerializer.cs (100%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/StringEnumSerializer.cs => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/EnumSerializer.cs} (94%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/src/SeedApi/Core/JsonConfiguration.cs (90%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/src/SeedApi/Core/OneOfSerializer.cs (71%) create mode 100644 seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/Version.cs rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/DeleteResponse.cs (83%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/DescribeResponse.cs (89%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/FetchResponse.cs (93%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/IndexedData.cs (91%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/ListElement.cs (87%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/ListResponse.cs (93%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/Metadata.cs (88%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/MetadataValue.cs (97%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/NamespaceSummary.cs (83%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/Pagination.cs (87%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/QueryColumn.cs (92%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/QueryResponse.cs (93%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/QueryResult.cs (91%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/ScoredColumn.cs (94%) rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto-exhaustive}/src/SeedApi/SeedApi.csproj (96%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/UpdateResponse.cs (83%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/UploadResponse.cs (83%) rename seed/{csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types => csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi}/Usage.cs (82%) create mode 100644 seed/csharp-model/csharp-grpc-proto/.github/workflows/ci.yml create mode 100644 seed/csharp-model/csharp-grpc-proto/.gitignore create mode 100644 seed/csharp-model/csharp-grpc-proto/.mock/fern.config.json rename seed/{csharp-sdk/grpc-proto-exhaustive => csharp-model/csharp-grpc-proto}/.mock/generators.yml (70%) create mode 100644 seed/csharp-model/csharp-grpc-proto/.mock/overrides.yml create mode 100644 seed/csharp-model/csharp-grpc-proto/.mock/proto/google/api/annotations.proto create mode 100644 seed/csharp-model/csharp-grpc-proto/.mock/proto/google/api/http.proto create mode 100644 seed/csharp-model/csharp-grpc-proto/.mock/proto/user/v1/user.proto create mode 100644 seed/csharp-model/csharp-grpc-proto/proto/google/api/annotations.proto create mode 100644 seed/csharp-model/csharp-grpc-proto/proto/google/api/http.proto create mode 100644 seed/csharp-model/csharp-grpc-proto/proto/user/v1/user.proto create mode 100644 seed/csharp-model/csharp-grpc-proto/snippet-templates.json create mode 100644 seed/csharp-model/csharp-grpc-proto/snippet.json create mode 100644 seed/csharp-model/csharp-grpc-proto/src/SeedApi.Test/Core/EnumSerializerTests.cs create mode 100644 seed/csharp-model/csharp-grpc-proto/src/SeedApi.Test/SeedApi.Test.csproj create mode 100644 seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/CollectionItemSerializer.cs create mode 100644 seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/Constants.cs create mode 100644 seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/DateTimeSerializer.cs create mode 100644 seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/EnumSerializer.cs create mode 100644 seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/JsonConfiguration.cs create mode 100644 seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/OneOfSerializer.cs create mode 100644 seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/Public/Version.cs create mode 100644 seed/csharp-model/csharp-grpc-proto/src/SeedApi/CreateResponse.cs create mode 100644 seed/csharp-model/csharp-grpc-proto/src/SeedApi/Metadata.cs create mode 100644 seed/csharp-model/csharp-grpc-proto/src/SeedApi/MetadataValue.cs create mode 100644 seed/csharp-model/csharp-grpc-proto/src/SeedApi/SeedApi.csproj create mode 100644 seed/csharp-model/csharp-grpc-proto/src/SeedApi/UserModel.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/.github/workflows/ci.yml create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/.gitignore create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/fern.config.json create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/generators.yml create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/openapi/openapi.yml create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/overrides.yml create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/data/v1/data.proto create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/google/api/annotations.proto create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/google/api/field_behavior.proto create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/google/api/http.proto create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/README.md create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/data/v1/data.proto create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/google/api/annotations.proto create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/google/api/field_behavior.proto create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/google/api/http.proto create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/reference.md create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/snippet-templates.json create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/snippet.json create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi.Test/Core/EnumSerializerTests.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi.Test/Core/RawClientTests.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi.Test/SeedApi.Test.csproj rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi.Test/TestClient.cs (100%) create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/CollectionItemSerializer.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Constants.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/DateTimeSerializer.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/EnumSerializer.cs rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Core/Extensions.cs (100%) create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/HeaderValue.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Headers.cs rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Core/HttpMethodExtensions.cs (100%) create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/JsonConfiguration.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/OneOfSerializer.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/ClientOptions.cs rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Core/Public/GrpcRequestOptions.cs (89%) rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Core/Public/RequestOptions.cs (79%) rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Core/Public/SeedApiApiException.cs (100%) create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/SeedApiEnvironment.cs rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Core/Public/SeedApiException.cs (100%) create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/Version.cs rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Core/RawClient.cs (51%) rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Core/RawGrpcClient.cs (69%) rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Dataservice/DataserviceClient.cs (74%) rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Dataservice/Requests/DeleteRequest.cs (90%) rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Dataservice/Requests/DescribeRequest.cs (83%) rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Dataservice/Requests/FetchRequest.cs (84%) rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Dataservice/Requests/ListRequest.cs (84%) rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Dataservice/Requests/QueryRequest.cs (94%) rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Dataservice/Requests/UpdateRequest.cs (91%) rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto-exhaustive}/src/SeedApi/Dataservice/Requests/UploadRequest.cs (87%) create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/SeedApi.csproj create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/SeedApiClient.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Column.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/DeleteResponse.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/DescribeResponse.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/FetchResponse.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/IndexedData.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/ListElement.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/ListResponse.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Metadata.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/MetadataValue.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/NamespaceSummary.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Pagination.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/QueryColumn.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/QueryResponse.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/QueryResult.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/ScoredColumn.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/UpdateResponse.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/UploadResponse.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Usage.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/.github/workflows/ci.yml create mode 100644 seed/csharp-sdk/csharp-grpc-proto/.gitignore create mode 100644 seed/csharp-sdk/csharp-grpc-proto/.mock/fern.config.json create mode 100644 seed/csharp-sdk/csharp-grpc-proto/.mock/generators.yml create mode 100644 seed/csharp-sdk/csharp-grpc-proto/.mock/overrides.yml create mode 100644 seed/csharp-sdk/csharp-grpc-proto/.mock/proto/google/api/annotations.proto create mode 100644 seed/csharp-sdk/csharp-grpc-proto/.mock/proto/google/api/http.proto create mode 100644 seed/csharp-sdk/csharp-grpc-proto/.mock/proto/user/v1/user.proto create mode 100644 seed/csharp-sdk/csharp-grpc-proto/README.md create mode 100644 seed/csharp-sdk/csharp-grpc-proto/proto/google/api/annotations.proto create mode 100644 seed/csharp-sdk/csharp-grpc-proto/proto/google/api/http.proto create mode 100644 seed/csharp-sdk/csharp-grpc-proto/proto/user/v1/user.proto create mode 100644 seed/csharp-sdk/csharp-grpc-proto/reference.md create mode 100644 seed/csharp-sdk/csharp-grpc-proto/snippet-templates.json create mode 100644 seed/csharp-sdk/csharp-grpc-proto/snippet.json create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/Core/EnumSerializerTests.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/Core/RawClientTests.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/SeedApi.Test.csproj create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/TestClient.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/CollectionItemSerializer.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Constants.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/DateTimeSerializer.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/EnumSerializer.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Extensions.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/HeaderValue.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Headers.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/HttpMethodExtensions.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/JsonConfiguration.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/OneOfSerializer.cs rename seed/csharp-sdk/{grpc-proto-exhaustive => csharp-grpc-proto}/src/SeedApi/Core/Public/ClientOptions.cs (59%) create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/GrpcRequestOptions.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/RequestOptions.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/SeedApiApiException.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/SeedApiException.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/Version.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/RawClient.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/RawGrpcClient.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/SeedApi.csproj create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/SeedApiClient.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/CreateResponse.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/Metadata.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/MetadataValue.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/UserModel.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Userservice/Requests/CreateRequest.cs create mode 100644 seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Userservice/UserserviceClient.cs delete mode 100644 seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi.Test/Unit/MockServer/BaseMockServerTest.cs delete mode 100644 seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/SeedApiClient.cs diff --git a/generators/csharp/codegen/package.json b/generators/csharp/codegen/package.json index d450e3b8dad..6556d292a4a 100644 --- a/generators/csharp/codegen/package.json +++ b/generators/csharp/codegen/package.json @@ -31,7 +31,7 @@ "@fern-api/fs-utils": "workspace:*", "@fern-api/generator-commons": "workspace:*", "@fern-api/logging-execa": "workspace:*", - "@fern-fern/ir-sdk": "^53.7.0", + "@fern-fern/ir-sdk": "^53.18.0", "lodash-es": "^4.17.21", "zod": "^3.22.3" }, diff --git a/generators/csharp/model/package.json b/generators/csharp/model/package.json index 548787c8789..5be5e323a5a 100644 --- a/generators/csharp/model/package.json +++ b/generators/csharp/model/package.json @@ -33,7 +33,7 @@ "@fern-api/csharp-codegen": "workspace:*", "@fern-api/fs-utils": "workspace:*", "@fern-api/generator-commons": "workspace:*", - "@fern-fern/ir-sdk": "^53.7.0", + "@fern-fern/ir-sdk": "^53.18.0", "zod": "^3.22.3", "@types/jest": "^29.5.12", "@types/node": "18.7.18", diff --git a/generators/csharp/model/src/snippets/ExampleGenerator.ts b/generators/csharp/model/src/snippets/ExampleGenerator.ts index 6e93f6ee225..ebc5681e689 100644 --- a/generators/csharp/model/src/snippets/ExampleGenerator.ts +++ b/generators/csharp/model/src/snippets/ExampleGenerator.ts @@ -208,7 +208,7 @@ export class ExampleGenerator { double: (p) => csharp.InstantiatedPrimitive.double(p), boolean: (p) => csharp.InstantiatedPrimitive.boolean(p), string: (p) => csharp.InstantiatedPrimitive.string(p.original), - datetime: (datetime) => csharp.InstantiatedPrimitive.dateTime(datetime, parseDatetimes), + datetime: (example) => csharp.InstantiatedPrimitive.dateTime(example.datetime, parseDatetimes), date: (dateString) => csharp.InstantiatedPrimitive.date(dateString), uuid: (p) => csharp.InstantiatedPrimitive.uuid(p), base64: (p) => csharp.InstantiatedPrimitive.string(p), diff --git a/generators/csharp/sdk/package.json b/generators/csharp/sdk/package.json index 03d3a6b4c67..460ac909fdb 100644 --- a/generators/csharp/sdk/package.json +++ b/generators/csharp/sdk/package.json @@ -38,7 +38,7 @@ "@fern-api/logger": "workspace:*", "@fern-fern/generator-cli-sdk": "0.0.17", "@fern-fern/generator-exec-sdk": "^0.0.898", - "@fern-fern/ir-sdk": "^53.7.0", + "@fern-fern/ir-sdk": "^53.18.0", "lodash-es": "^4.17.21", "url-join": "^5.0.0", "zod": "^3.22.3", diff --git a/generators/csharp/sdk/src/SdkGeneratorContext.ts b/generators/csharp/sdk/src/SdkGeneratorContext.ts index 560f9d1b3fd..067f0165146 100644 --- a/generators/csharp/sdk/src/SdkGeneratorContext.ts +++ b/generators/csharp/sdk/src/SdkGeneratorContext.ts @@ -432,6 +432,10 @@ export class SdkGeneratorContext extends AbstractCsharpGeneratorContext { return this.customConfig["extra-dependencies"] ?? {}; } diff --git a/generators/csharp/sdk/src/endpoint/EndpointGenerator.ts b/generators/csharp/sdk/src/endpoint/EndpointGenerator.ts index da7b8cba645..c4fa2c21206 100644 --- a/generators/csharp/sdk/src/endpoint/EndpointGenerator.ts +++ b/generators/csharp/sdk/src/endpoint/EndpointGenerator.ts @@ -32,7 +32,9 @@ export class EndpointGenerator extends AbstractEndpointGenerator { rawClient: RawClient; grpcClientInfo: GrpcClientInfo | undefined; }): csharp.Method { - if (grpcClientInfo != null) { + // If the service is a grpc service, grpcClientInfo will not be null or undefined, + // so any endpoint will be generated as a grpc endpoint, unless the transport is overriden by setting type to http + if (grpcClientInfo != null && endpoint.transport?.type !== "http") { return this.grpc.generate({ serviceId, endpoint, diff --git a/generators/csharp/sdk/src/wrapped-request/WrappedRequestGenerator.ts b/generators/csharp/sdk/src/wrapped-request/WrappedRequestGenerator.ts index 49a75e1448a..492c96140bf 100644 --- a/generators/csharp/sdk/src/wrapped-request/WrappedRequestGenerator.ts +++ b/generators/csharp/sdk/src/wrapped-request/WrappedRequestGenerator.ts @@ -46,6 +46,8 @@ export class WrappedRequestGenerator extends FileGenerator undefined, @@ -134,20 +139,22 @@ export class WrappedRequestGenerator extends FileGenerator- + Add support for calling HTTP endpoints and gRPC endoints within the same service. + irVersion: 53 - version: 1.8.5 createdAt: "2024-10-30" changelogEntry: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0230e69f423..4afde6317b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -221,8 +221,8 @@ importers: specifier: workspace:* version: link:../../../packages/commons/logging-execa '@fern-fern/ir-sdk': - specifier: ^53.7.0 - version: 53.7.2 + specifier: ^53.18.0 + version: 53.18.0 lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -270,8 +270,8 @@ importers: specifier: workspace:* version: link:../../commons '@fern-fern/ir-sdk': - specifier: ^53.7.0 - version: 53.7.2 + specifier: ^53.18.0 + version: 53.18.0 '@types/jest': specifier: ^29.5.12 version: 29.5.12 @@ -336,8 +336,8 @@ importers: specifier: ^0.0.898 version: 0.0.898 '@fern-fern/ir-sdk': - specifier: ^53.7.0 - version: 53.7.0 + specifier: ^53.18.0 + version: 53.18.0 '@types/jest': specifier: ^29.5.12 version: 29.5.12 @@ -3968,8 +3968,6 @@ importers: specifier: ^2.0.5 version: 2.0.5(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5) - packages/cli/cli/dist/local: {} - packages/cli/configuration: dependencies: '@fern-api/core-utils': @@ -7535,15 +7533,15 @@ packages: '@fern-fern/ir-sdk@53.15.0': resolution: {integrity: sha512-3ouCapVwfv9KAjgLLCAgGmt0A/GyXgZxtmEB5hVE31BVGHdEmGyoYKxtZy+EYULhZXFXZww5sekphKdAGGY0cg==} + '@fern-fern/ir-sdk@53.18.0': + resolution: {integrity: sha512-KXHiAn8wjL9VIjjR9z8fXso0O2oaCMUSy9BSYRiGjEOmbIBUhplxSXjM3wSEXQ19hiPpsRYJTCCjnaZVP0OVrw==} + '@fern-fern/ir-sdk@53.19.0': resolution: {integrity: sha512-mIi1S+fIc/+KnkvnFqJ+ZTXQkz4hdukkXSebx9VF9wRU78S4W1qEIX6Y7EqRz9/PFKrebe6SKXw+mn+hNqAWLA==} '@fern-fern/ir-sdk@53.7.0': resolution: {integrity: sha512-PoCj8yOep3kFeDZYORAzqPwVtCSNmbT2SfR/AoxKCcikeZ5i+4Um4ZXx1e6UaAy7dIYF5kWeRc6lptFBRoj7Gw==} - '@fern-fern/ir-sdk@53.7.2': - resolution: {integrity: sha512-mLw91KwIwzLnY36X2oHLo/2ue3WaOFWQBSKRdVR12/wui0+Q5G7tNyXhxgZxT8UzQPYIt4dcOp+qp5XT9UZh1Q==} - '@fern-fern/ir-sdk@53.8.0': resolution: {integrity: sha512-h5q0DuXyeCs61Nl0ueBYMtNhG5uiSKJqdqtgTON5RWjGH0aJAhGMfoRXoq5Sar4HLWHhbK/t/290O/iCJLE0hQ==} @@ -14622,12 +14620,12 @@ snapshots: '@fern-fern/ir-sdk@53.15.0': {} + '@fern-fern/ir-sdk@53.18.0': {} + '@fern-fern/ir-sdk@53.19.0': {} '@fern-fern/ir-sdk@53.7.0': {} - '@fern-fern/ir-sdk@53.7.2': {} - '@fern-fern/ir-sdk@53.8.0': {} '@fern-fern/ir-sdk@53.9.0': {} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/.github/workflows/ci.yml b/seed/csharp-model/csharp-grpc-proto-exhaustive/.github/workflows/ci.yml similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/.github/workflows/ci.yml rename to seed/csharp-model/csharp-grpc-proto-exhaustive/.github/workflows/ci.yml diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/.gitignore b/seed/csharp-model/csharp-grpc-proto-exhaustive/.gitignore similarity index 93% rename from seed/csharp-sdk/grpc-proto-exhaustive/.gitignore rename to seed/csharp-model/csharp-grpc-proto-exhaustive/.gitignore index 5e57f18055d..11014f2b33d 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/.gitignore +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/.gitignore @@ -1,484 +1,484 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from `dotnet new gitignore` - -# dotenv files -.env - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Ww][Ii][Nn]32/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET -project.lock.json -project.fragment.lock.json -artifacts/ - -# Tye -.tye/ - -# ASP.NET Scaffolding -ScaffoldingReadMe.txt - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.tlog -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Coverlet is a free, cross platform Code Coverage Tool -coverage*.json -coverage*.xml -coverage*.info - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio 6 auto-generated project file (contains which files were open etc.) -*.vbp - -# Visual Studio 6 workspace and project file (working project files containing files to include in project) -*.dsw -*.dsp - -# Visual Studio 6 technical files -*.ncb -*.aps - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# Visual Studio History (VSHistory) files -.vshistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ - -# Fody - auto-generated XML schema -FodyWeavers.xsd - -# VS Code files for those working on multiple tools -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -*.code-workspace - -# Local History for Visual Studio Code -.history/ - -# Windows Installer files from build outputs -*.cab -*.msi -*.msix -*.msm -*.msp - -# JetBrains Rider -*.sln.iml -.idea - -## -## Visual studio for Mac -## - - -# globs -Makefile.in -*.userprefs -*.usertasks -config.make -config.status -aclocal.m4 -install-sh -autom4te.cache/ -*.tar.gz -tarballs/ -test-results/ - -# Mac bundle stuff -*.dmg -*.app - -# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore -# Windows thumbnail cache files -Thumbs.db -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# Vim temporary swap files -*.swp +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +## This is based on `dotnet new gitignore` and customized by Fern + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +# [Rr]elease/ (Ignored by Fern) +# [Rr]eleases/ (Ignored by Fern) +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +# [Ll]og/ (Ignored by Fern) +# [Ll]ogs/ (Ignored by Fern) + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/.mock/fern.config.json b/seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/fern.config.json similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/.mock/fern.config.json rename to seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/fern.config.json diff --git a/seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/generators.yml b/seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/generators.yml new file mode 100644 index 00000000000..c23323621f2 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/generators.yml @@ -0,0 +1,7 @@ +api: + - path: openapi/openapi.yml + - proto: + root: proto + target: proto/data/v1/data.proto + overrides: overrides.yml + local-generation: true diff --git a/seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/openapi/openapi.yml b/seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/openapi/openapi.yml new file mode 100644 index 00000000000..56ea165b773 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/openapi/openapi.yml @@ -0,0 +1,25 @@ +openapi: 3.0.3 +info: + title: Test API + version: 1.0.0 +servers: + - url: https://localhost +tags: + - name: dataservice +paths: + /foo: + post: + tag: dataservice + x-fern-sdk-group-name: + - dataservice + x-fern-sdk-method-name: foo + operationId: foo + responses: + "200": + content: + application/json: + schema: + type: object + +components: + securitySchemes: {} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/.mock/overrides.yml b/seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/overrides.yml similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/.mock/overrides.yml rename to seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/overrides.yml diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/.mock/proto/data/v1/data.proto b/seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/proto/data/v1/data.proto similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/.mock/proto/data/v1/data.proto rename to seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/proto/data/v1/data.proto diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/.mock/proto/google/api/annotations.proto b/seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/proto/google/api/annotations.proto similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/.mock/proto/google/api/annotations.proto rename to seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/proto/google/api/annotations.proto diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/.mock/proto/google/api/field_behavior.proto b/seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/proto/google/api/field_behavior.proto similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/.mock/proto/google/api/field_behavior.proto rename to seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/proto/google/api/field_behavior.proto diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/.mock/proto/google/api/http.proto b/seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/proto/google/api/http.proto similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/.mock/proto/google/api/http.proto rename to seed/csharp-model/csharp-grpc-proto-exhaustive/.mock/proto/google/api/http.proto diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/proto/data/v1/data.proto b/seed/csharp-model/csharp-grpc-proto-exhaustive/proto/data/v1/data.proto similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/proto/data/v1/data.proto rename to seed/csharp-model/csharp-grpc-proto-exhaustive/proto/data/v1/data.proto diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/proto/google/api/annotations.proto b/seed/csharp-model/csharp-grpc-proto-exhaustive/proto/google/api/annotations.proto similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/proto/google/api/annotations.proto rename to seed/csharp-model/csharp-grpc-proto-exhaustive/proto/google/api/annotations.proto diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/proto/google/api/field_behavior.proto b/seed/csharp-model/csharp-grpc-proto-exhaustive/proto/google/api/field_behavior.proto similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/proto/google/api/field_behavior.proto rename to seed/csharp-model/csharp-grpc-proto-exhaustive/proto/google/api/field_behavior.proto diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/proto/google/api/http.proto b/seed/csharp-model/csharp-grpc-proto-exhaustive/proto/google/api/http.proto similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/proto/google/api/http.proto rename to seed/csharp-model/csharp-grpc-proto-exhaustive/proto/google/api/http.proto diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/snippet-templates.json b/seed/csharp-model/csharp-grpc-proto-exhaustive/snippet-templates.json similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/snippet-templates.json rename to seed/csharp-model/csharp-grpc-proto-exhaustive/snippet-templates.json diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/snippet.json b/seed/csharp-model/csharp-grpc-proto-exhaustive/snippet.json similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/snippet.json rename to seed/csharp-model/csharp-grpc-proto-exhaustive/snippet.json diff --git a/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi.Test/Core/EnumSerializerTests.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi.Test/Core/EnumSerializerTests.cs new file mode 100644 index 00000000000..532d182486b --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi.Test/Core/EnumSerializerTests.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; +using NUnit.Framework; +using SeedApi.Core; + +namespace SeedApi.Test.Core +{ + [TestFixture] + public class StringEnumSerializerTests + { + private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; + + private const DummyEnum KnownEnumValue2 = DummyEnum.KnownValue2; + private const string KnownEnumValue2String = "known_value2"; + + private static readonly string JsonWithKnownEnum2 = $$""" + { + "enum_property": "{{KnownEnumValue2String}}" + } + """; + + [Test] + public void ShouldParseKnownEnumValue2() + { + var obj = JsonSerializer.Deserialize(JsonWithKnownEnum2, JsonOptions); + Assert.That(obj, Is.Not.Null); + Assert.That(obj.EnumProperty, Is.EqualTo(KnownEnumValue2)); + } + + [Test] + public void ShouldSerializeKnownEnumValue2() + { + var json = JsonSerializer.SerializeToElement( + new DummyObject { EnumProperty = KnownEnumValue2 }, + JsonOptions + ); + TestContext.WriteLine("Serialized JSON: \n" + json); + var enumString = json.GetProperty("enum_property").GetString(); + Assert.That(enumString, Is.Not.Null); + Assert.That(enumString, Is.EqualTo(KnownEnumValue2String)); + } + } + + public class DummyObject + { + [JsonPropertyName("enum_property")] + public DummyEnum EnumProperty { get; set; } + } + + [JsonConverter(typeof(EnumSerializer))] + public enum DummyEnum + { + [EnumMember(Value = "known_value1")] + KnownValue1, + + [EnumMember(Value = "known_value2")] + KnownValue2, + } +} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi.Test/SeedApi.Test.csproj b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi.Test/SeedApi.Test.csproj similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi.Test/SeedApi.Test.csproj rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi.Test/SeedApi.Test.csproj diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/Column.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Column.cs similarity index 93% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/Column.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Column.cs index c30cac8ca30..602200b6dfb 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/Column.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Column.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -19,6 +20,11 @@ public record Column [JsonPropertyName("indexedData")] public IndexedData? IndexedData { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the Column type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/CollectionItemSerializer.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/CollectionItemSerializer.cs similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/CollectionItemSerializer.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/CollectionItemSerializer.cs diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Constants.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Constants.cs similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Constants.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Constants.cs diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/DateTimeSerializer.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/DateTimeSerializer.cs similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/DateTimeSerializer.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/DateTimeSerializer.cs diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/StringEnumSerializer.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/EnumSerializer.cs similarity index 94% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/StringEnumSerializer.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/EnumSerializer.cs index 5e064791aeb..ac5c0792fbe 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/StringEnumSerializer.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/EnumSerializer.cs @@ -4,13 +4,13 @@ namespace SeedApi.Core; -internal class StringEnumSerializer : JsonConverter +internal class EnumSerializer : JsonConverter where TEnum : struct, System.Enum { private readonly Dictionary _enumToString = new(); private readonly Dictionary _stringToEnum = new(); - public StringEnumSerializer() + public EnumSerializer() { var type = typeof(TEnum); var values = Enum.GetValues(type); diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/JsonConfiguration.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/JsonConfiguration.cs similarity index 90% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/JsonConfiguration.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/JsonConfiguration.cs index 5f8ae89f3e8..13a05f5111f 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/JsonConfiguration.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/JsonConfiguration.cs @@ -11,7 +11,7 @@ static JsonOptions() { JsonSerializerOptions = new JsonSerializerOptions { - Converters = { new DateTimeSerializer() }, + Converters = { new DateTimeSerializer(), new OneOfSerializer() }, WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }; diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/OneOfSerializer.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/OneOfSerializer.cs similarity index 71% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/OneOfSerializer.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/OneOfSerializer.cs index f37edfa989c..24ee9268e48 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/OneOfSerializer.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/OneOfSerializer.cs @@ -5,10 +5,9 @@ namespace SeedApi.Core; -internal class OneOfSerializer : JsonConverter - where TOneOf : IOneOf +internal class OneOfSerializer : JsonConverter { - public override TOneOf? Read( + public override IOneOf? Read( ref Utf8JsonReader reader, System.Type typeToConvert, JsonSerializerOptions options @@ -17,14 +16,14 @@ JsonSerializerOptions options if (reader.TokenType is JsonTokenType.Null) return default; - foreach (var (type, cast) in s_types) + foreach (var (type, cast) in GetOneOfTypes(typeToConvert)) { try { var readerCopy = reader; var result = JsonSerializer.Deserialize(ref readerCopy, type, options); reader.Skip(); - return (TOneOf)cast.Invoke(null, [result])!; + return (IOneOf)cast.Invoke(null, [result])!; } catch (JsonException) { } } @@ -34,20 +33,18 @@ JsonSerializerOptions options ); } - private static readonly (System.Type type, MethodInfo cast)[] s_types = GetOneOfTypes(); - - public override void Write(Utf8JsonWriter writer, TOneOf value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, IOneOf value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value.Value, options); } - private static (System.Type type, MethodInfo cast)[] GetOneOfTypes() + private static (System.Type type, MethodInfo cast)[] GetOneOfTypes(System.Type typeToConvert) { - var casts = typeof(TOneOf) + var casts = typeToConvert .GetRuntimeMethods() .Where(m => m.IsSpecialName && m.Name == "op_Implicit") .ToArray(); - var type = typeof(TOneOf); + var type = typeToConvert; while (type != null) { if ( @@ -62,6 +59,11 @@ private static (System.Type type, MethodInfo cast)[] GetOneOfTypes() type = type.BaseType; } - throw new InvalidOperationException($"{typeof(TOneOf)} isn't OneOf or OneOfBase"); + throw new InvalidOperationException($"{type} isn't OneOf or OneOfBase"); + } + + public override bool CanConvert(System.Type typeToConvert) + { + return typeof(IOneOf).IsAssignableFrom(typeToConvert); } } diff --git a/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/Version.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/Version.cs new file mode 100644 index 00000000000..f430a1bf84c --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/Version.cs @@ -0,0 +1,6 @@ +namespace SeedApi; + +internal class Version +{ + public const string Current = "0.0.1"; +} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/DeleteResponse.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/DeleteResponse.cs similarity index 83% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/DeleteResponse.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/DeleteResponse.cs index 8ae29f42747..def64fbd896 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/DeleteResponse.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/DeleteResponse.cs @@ -1,3 +1,4 @@ +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -6,6 +7,11 @@ namespace SeedApi; public record DeleteResponse { + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the DeleteResponse type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/DescribeResponse.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/DescribeResponse.cs similarity index 89% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/DescribeResponse.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/DescribeResponse.cs index b9cf18c97ed..fbd45c674b7 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/DescribeResponse.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/DescribeResponse.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -19,6 +20,11 @@ public record DescribeResponse [JsonPropertyName("totalCount")] public uint? TotalCount { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the DescribeResponse type into its Protobuf-equivalent representation. /// @@ -35,7 +41,7 @@ internal Proto.DescribeResponse ToProto() } if (Dimension != null) { - result.Dimension = Dimension ?? 0U; + result.Dimension = Dimension ?? 0; } if (Fullness != null) { @@ -43,7 +49,7 @@ internal Proto.DescribeResponse ToProto() } if (TotalCount != null) { - result.TotalCount = TotalCount ?? 0U; + result.TotalCount = TotalCount ?? 0; } return result; } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/FetchResponse.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/FetchResponse.cs similarity index 93% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/FetchResponse.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/FetchResponse.cs index c58c4004ee2..ae595dfdc41 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/FetchResponse.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/FetchResponse.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -16,6 +17,11 @@ public record FetchResponse [JsonPropertyName("usage")] public Usage? Usage { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the FetchResponse type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/IndexedData.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/IndexedData.cs similarity index 91% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/IndexedData.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/IndexedData.cs index 18060a8d708..242c4d0b135 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/IndexedData.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/IndexedData.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -13,6 +14,11 @@ public record IndexedData [JsonPropertyName("values")] public IEnumerable Values { get; set; } = new List(); + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the IndexedData type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/ListElement.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/ListElement.cs similarity index 87% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/ListElement.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/ListElement.cs index 3d61fb1c60a..2044bd62e56 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/ListElement.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/ListElement.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -10,6 +11,11 @@ public record ListElement [JsonPropertyName("id")] public string? Id { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the ListElement type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/ListResponse.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/ListResponse.cs similarity index 93% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/ListResponse.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/ListResponse.cs index 8147ccadb55..9d691519a85 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/ListResponse.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/ListResponse.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -19,6 +20,11 @@ public record ListResponse [JsonPropertyName("usage")] public Usage? Usage { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the ListResponse type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/Metadata.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Metadata.cs similarity index 88% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/Metadata.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Metadata.cs index a2a7de2f1dd..2e16aa50b3f 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/Metadata.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Metadata.cs @@ -1,3 +1,4 @@ +using SeedApi.Core; using Proto = Google.Protobuf.WellKnownTypes; #nullable enable @@ -11,6 +12,11 @@ public Metadata() { } public Metadata(IEnumerable> value) : base(value.ToDictionary(e => e.Key, e => e.Value)) { } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + internal Proto.Struct ToProto() { var result = new Proto.Struct(); diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/MetadataValue.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/MetadataValue.cs similarity index 97% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/MetadataValue.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/MetadataValue.cs index 4b8eb5175ff..2308676e891 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/MetadataValue.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/MetadataValue.cs @@ -1,4 +1,3 @@ -using System.Text.Json.Serialization; using OneOf; using SeedApi.Core; using Proto = Google.Protobuf.WellKnownTypes; @@ -7,11 +6,15 @@ namespace SeedApi; -[JsonConverter(typeof(OneOfSerializer))] public sealed class MetadataValue( OneOf, Metadata> value ) : OneOfBase, Metadata>(value) { + public override string ToString() + { + return JsonUtils.Serialize(this); + } + internal Proto.Value ToProto() { return Match( diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/NamespaceSummary.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/NamespaceSummary.cs similarity index 83% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/NamespaceSummary.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/NamespaceSummary.cs index eef9ffd8d83..da0573827da 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/NamespaceSummary.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/NamespaceSummary.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -10,6 +11,11 @@ public record NamespaceSummary [JsonPropertyName("count")] public uint? Count { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the NamespaceSummary type into its Protobuf-equivalent representation. /// @@ -18,7 +24,7 @@ internal Proto.NamespaceSummary ToProto() var result = new Proto.NamespaceSummary(); if (Count != null) { - result.Count = Count ?? 0U; + result.Count = Count ?? 0; } return result; } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/Pagination.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Pagination.cs similarity index 87% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/Pagination.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Pagination.cs index 5d744672692..87a67db48af 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/Pagination.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Pagination.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -10,6 +11,11 @@ public record Pagination [JsonPropertyName("next")] public string? Next { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the Pagination type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/QueryColumn.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/QueryColumn.cs similarity index 92% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/QueryColumn.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/QueryColumn.cs index 52a55fa3ca8..94c3d4f14af 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/QueryColumn.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/QueryColumn.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -22,6 +23,11 @@ public record QueryColumn [JsonPropertyName("indexedData")] public IndexedData? IndexedData { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the QueryColumn type into its Protobuf-equivalent representation. /// @@ -34,7 +40,7 @@ internal Proto.QueryColumn ToProto() } if (TopK != null) { - result.TopK = TopK ?? 0U; + result.TopK = TopK ?? 0; } if (Namespace != null) { diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/QueryResponse.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/QueryResponse.cs similarity index 93% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/QueryResponse.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/QueryResponse.cs index 1aad6296682..c4786b94639 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/QueryResponse.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/QueryResponse.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -19,6 +20,11 @@ public record QueryResponse [JsonPropertyName("usage")] public Usage? Usage { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the QueryResponse type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/QueryResult.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/QueryResult.cs similarity index 91% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/QueryResult.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/QueryResult.cs index e1787476991..8c8988af1fd 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/QueryResult.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/QueryResult.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -13,6 +14,11 @@ public record QueryResult [JsonPropertyName("namespace")] public string? Namespace { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the QueryResult type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/ScoredColumn.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/ScoredColumn.cs similarity index 94% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/ScoredColumn.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/ScoredColumn.cs index 2aad59ec31f..5ca5fa6e2f6 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/ScoredColumn.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/ScoredColumn.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -22,6 +23,11 @@ public record ScoredColumn [JsonPropertyName("indexedData")] public IndexedData? IndexedData { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the ScoredColumn type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/SeedApi.csproj b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/SeedApi.csproj similarity index 96% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/SeedApi.csproj rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/SeedApi.csproj index 03c65133966..317b0f9a176 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/SeedApi.csproj +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/SeedApi.csproj @@ -1,4 +1,4 @@ - + @@ -9,21 +9,21 @@ enable 0.0.1 README.md - https://github.com/grpc-proto-exhaustive/fern + https://github.com/csharp-grpc-proto-exhaustive/fern - + true - + - + - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/UpdateResponse.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/UpdateResponse.cs similarity index 83% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/UpdateResponse.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/UpdateResponse.cs index 11fb763f750..0d7e3f61b18 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/UpdateResponse.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/UpdateResponse.cs @@ -1,3 +1,4 @@ +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -6,6 +7,11 @@ namespace SeedApi; public record UpdateResponse { + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the UpdateResponse type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/UploadResponse.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/UploadResponse.cs similarity index 83% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/UploadResponse.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/UploadResponse.cs index 6a3f91e6651..23286a04145 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/UploadResponse.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/UploadResponse.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -10,6 +11,11 @@ public record UploadResponse [JsonPropertyName("count")] public uint? Count { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the UploadResponse type into its Protobuf-equivalent representation. /// @@ -18,7 +24,7 @@ internal Proto.UploadResponse ToProto() var result = new Proto.UploadResponse(); if (Count != null) { - result.Count = Count ?? 0U; + result.Count = Count ?? 0; } return result; } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/Usage.cs b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Usage.cs similarity index 82% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/Usage.cs rename to seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Usage.cs index f26eb14f874..e5921bf2191 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Types/Usage.cs +++ b/seed/csharp-model/csharp-grpc-proto-exhaustive/src/SeedApi/Usage.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -10,6 +11,11 @@ public record Usage [JsonPropertyName("units")] public uint? Units { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the Usage type into its Protobuf-equivalent representation. /// @@ -18,7 +24,7 @@ internal Proto.Usage ToProto() var result = new Proto.Usage(); if (Units != null) { - result.Units = Units ?? 0U; + result.Units = Units ?? 0; } return result; } diff --git a/seed/csharp-model/csharp-grpc-proto/.github/workflows/ci.yml b/seed/csharp-model/csharp-grpc-proto/.github/workflows/ci.yml new file mode 100644 index 00000000000..bc4fa1a98cb --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/.github/workflows/ci.yml @@ -0,0 +1,69 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - uses: actions/checkout@master + + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 8.x + + - name: Install tools + run: | + dotnet tool restore + + - name: Build Release + run: dotnet build src -c Release /p:ContinuousIntegrationBuild=true + + unit-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - uses: actions/checkout@master + + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 8.x + + - name: Install tools + run: | + dotnet tool restore + + - name: Run Tests + run: | + dotnet test src + + + publish: + needs: [compile] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 8.x + + - name: Publish + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_TOKEN }} + run: | + dotnet pack src -c Release + dotnet nuget push src/SeedApi/bin/Release/*.nupkg --api-key $NUGET_API_KEY --source "nuget.org" diff --git a/seed/csharp-model/csharp-grpc-proto/.gitignore b/seed/csharp-model/csharp-grpc-proto/.gitignore new file mode 100644 index 00000000000..11014f2b33d --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/.gitignore @@ -0,0 +1,484 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +## This is based on `dotnet new gitignore` and customized by Fern + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +# [Rr]elease/ (Ignored by Fern) +# [Rr]eleases/ (Ignored by Fern) +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +# [Ll]og/ (Ignored by Fern) +# [Ll]ogs/ (Ignored by Fern) + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/seed/csharp-model/csharp-grpc-proto/.mock/fern.config.json b/seed/csharp-model/csharp-grpc-proto/.mock/fern.config.json new file mode 100644 index 00000000000..4c8e54ac313 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/.mock/fern.config.json @@ -0,0 +1 @@ +{"organization": "fern-test", "version": "*"} \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/.mock/generators.yml b/seed/csharp-model/csharp-grpc-proto/.mock/generators.yml similarity index 70% rename from seed/csharp-sdk/grpc-proto-exhaustive/.mock/generators.yml rename to seed/csharp-model/csharp-grpc-proto/.mock/generators.yml index 972ed6d7b73..d6b509d39ea 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/.mock/generators.yml +++ b/seed/csharp-model/csharp-grpc-proto/.mock/generators.yml @@ -1,6 +1,6 @@ api: - proto: root: proto - target: proto/data/v1/data.proto + target: proto/user/v1/user.proto overrides: overrides.yml local-generation: true \ No newline at end of file diff --git a/seed/csharp-model/csharp-grpc-proto/.mock/overrides.yml b/seed/csharp-model/csharp-grpc-proto/.mock/overrides.yml new file mode 100644 index 00000000000..bb6db99b0e4 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/.mock/overrides.yml @@ -0,0 +1,31 @@ +components: + schemas: + UserModel: + properties: + metadata: + $ref: '#/components/schemas/Metadata' + + CreateRequest: + properties: + metadata: + $ref: '#/components/schemas/Metadata' + + Metadata: + oneOf: + - type: object + additionalProperties: + $ref: '#/components/schemas/MetadataValue' + - type: object + x-fern-encoding: + proto: + type: google.protobuf.Struct + + MetadataValue: + oneOf: + - type: number + format: double + - type: string + - type: boolean + x-fern-encoding: + proto: + type: google.protobuf.Value diff --git a/seed/csharp-model/csharp-grpc-proto/.mock/proto/google/api/annotations.proto b/seed/csharp-model/csharp-grpc-proto/.mock/proto/google/api/annotations.proto new file mode 100644 index 00000000000..8ff42098404 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/.mock/proto/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright 2015 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} \ No newline at end of file diff --git a/seed/csharp-model/csharp-grpc-proto/.mock/proto/google/api/http.proto b/seed/csharp-model/csharp-grpc-proto/.mock/proto/google/api/http.proto new file mode 100644 index 00000000000..c8392381eb9 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/.mock/proto/google/api/http.proto @@ -0,0 +1,379 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They +// are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL +// query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP +// request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} \ No newline at end of file diff --git a/seed/csharp-model/csharp-grpc-proto/.mock/proto/user/v1/user.proto b/seed/csharp-model/csharp-grpc-proto/.mock/proto/user/v1/user.proto new file mode 100644 index 00000000000..28542ac965a --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/.mock/proto/user/v1/user.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +package user.v1; + +import "google/api/annotations.proto"; +import "google/protobuf/struct.proto"; + +option csharp_namespace = "User.V1"; +option go_package = "user/v1"; + +message UserModel { + string username = 1; + string email = 2; + uint32 age = 3; + float weight = 4; + google.protobuf.Struct metadata = 5; +} + +message CreateRequest { + string username = 1; + string email = 2; + uint32 age = 3; + float weight = 4; + google.protobuf.Struct metadata = 5; +} + +message CreateResponse { + UserModel user = 1; +} + +service UserService { + rpc Create(CreateRequest) returns (CreateResponse) { + option (google.api.http) = { + post: "/users" + body: "*" + }; + } +} \ No newline at end of file diff --git a/seed/csharp-model/csharp-grpc-proto/proto/google/api/annotations.proto b/seed/csharp-model/csharp-grpc-proto/proto/google/api/annotations.proto new file mode 100644 index 00000000000..8ff42098404 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/proto/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright 2015 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} \ No newline at end of file diff --git a/seed/csharp-model/csharp-grpc-proto/proto/google/api/http.proto b/seed/csharp-model/csharp-grpc-proto/proto/google/api/http.proto new file mode 100644 index 00000000000..c8392381eb9 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/proto/google/api/http.proto @@ -0,0 +1,379 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They +// are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL +// query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP +// request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} \ No newline at end of file diff --git a/seed/csharp-model/csharp-grpc-proto/proto/user/v1/user.proto b/seed/csharp-model/csharp-grpc-proto/proto/user/v1/user.proto new file mode 100644 index 00000000000..28542ac965a --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/proto/user/v1/user.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +package user.v1; + +import "google/api/annotations.proto"; +import "google/protobuf/struct.proto"; + +option csharp_namespace = "User.V1"; +option go_package = "user/v1"; + +message UserModel { + string username = 1; + string email = 2; + uint32 age = 3; + float weight = 4; + google.protobuf.Struct metadata = 5; +} + +message CreateRequest { + string username = 1; + string email = 2; + uint32 age = 3; + float weight = 4; + google.protobuf.Struct metadata = 5; +} + +message CreateResponse { + UserModel user = 1; +} + +service UserService { + rpc Create(CreateRequest) returns (CreateResponse) { + option (google.api.http) = { + post: "/users" + body: "*" + }; + } +} \ No newline at end of file diff --git a/seed/csharp-model/csharp-grpc-proto/snippet-templates.json b/seed/csharp-model/csharp-grpc-proto/snippet-templates.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/csharp-model/csharp-grpc-proto/snippet.json b/seed/csharp-model/csharp-grpc-proto/snippet.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/csharp-model/csharp-grpc-proto/src/SeedApi.Test/Core/EnumSerializerTests.cs b/seed/csharp-model/csharp-grpc-proto/src/SeedApi.Test/Core/EnumSerializerTests.cs new file mode 100644 index 00000000000..532d182486b --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/src/SeedApi.Test/Core/EnumSerializerTests.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; +using NUnit.Framework; +using SeedApi.Core; + +namespace SeedApi.Test.Core +{ + [TestFixture] + public class StringEnumSerializerTests + { + private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; + + private const DummyEnum KnownEnumValue2 = DummyEnum.KnownValue2; + private const string KnownEnumValue2String = "known_value2"; + + private static readonly string JsonWithKnownEnum2 = $$""" + { + "enum_property": "{{KnownEnumValue2String}}" + } + """; + + [Test] + public void ShouldParseKnownEnumValue2() + { + var obj = JsonSerializer.Deserialize(JsonWithKnownEnum2, JsonOptions); + Assert.That(obj, Is.Not.Null); + Assert.That(obj.EnumProperty, Is.EqualTo(KnownEnumValue2)); + } + + [Test] + public void ShouldSerializeKnownEnumValue2() + { + var json = JsonSerializer.SerializeToElement( + new DummyObject { EnumProperty = KnownEnumValue2 }, + JsonOptions + ); + TestContext.WriteLine("Serialized JSON: \n" + json); + var enumString = json.GetProperty("enum_property").GetString(); + Assert.That(enumString, Is.Not.Null); + Assert.That(enumString, Is.EqualTo(KnownEnumValue2String)); + } + } + + public class DummyObject + { + [JsonPropertyName("enum_property")] + public DummyEnum EnumProperty { get; set; } + } + + [JsonConverter(typeof(EnumSerializer))] + public enum DummyEnum + { + [EnumMember(Value = "known_value1")] + KnownValue1, + + [EnumMember(Value = "known_value2")] + KnownValue2, + } +} diff --git a/seed/csharp-model/csharp-grpc-proto/src/SeedApi.Test/SeedApi.Test.csproj b/seed/csharp-model/csharp-grpc-proto/src/SeedApi.Test/SeedApi.Test.csproj new file mode 100644 index 00000000000..c5be29f92d9 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/src/SeedApi.Test/SeedApi.Test.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/CollectionItemSerializer.cs b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/CollectionItemSerializer.cs new file mode 100644 index 00000000000..af2c9adf7a7 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/CollectionItemSerializer.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedApi.Core; + +/// +/// Json collection converter. +/// +/// Type of item to convert. +/// Converter to use for individual items. +internal class CollectionItemSerializer + : JsonConverter> + where TConverterType : JsonConverter +{ + /// + /// Reads a json string and deserializes it into an object. + /// + /// Json reader. + /// Type to convert. + /// Serializer options. + /// Created object. + public override IEnumerable? Read( + ref Utf8JsonReader reader, + System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType == JsonTokenType.Null) + { + return default; + } + + var jsonSerializerOptions = new JsonSerializerOptions(options); + jsonSerializerOptions.Converters.Clear(); + jsonSerializerOptions.Converters.Add(Activator.CreateInstance()); + + var returnValue = new List(); + + while (reader.TokenType != JsonTokenType.EndArray) + { + if (reader.TokenType != JsonTokenType.StartArray) + { + var item = (TDatatype)( + JsonSerializer.Deserialize(ref reader, typeof(TDatatype), jsonSerializerOptions) + ?? throw new Exception( + $"Failed to deserialize collection item of type {typeof(TDatatype)}" + ) + ); + returnValue.Add(item); + } + + reader.Read(); + } + + return returnValue; + } + + /// + /// Writes a json string. + /// + /// Json writer. + /// Value to write. + /// Serializer options. + public override void Write( + Utf8JsonWriter writer, + IEnumerable? value, + JsonSerializerOptions options + ) + { + if (value == null) + { + writer.WriteNullValue(); + return; + } + + JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions(options); + jsonSerializerOptions.Converters.Clear(); + jsonSerializerOptions.Converters.Add(Activator.CreateInstance()); + + writer.WriteStartArray(); + + foreach (var data in value) + { + JsonSerializer.Serialize(writer, data, jsonSerializerOptions); + } + + writer.WriteEndArray(); + } +} diff --git a/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/Constants.cs b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/Constants.cs new file mode 100644 index 00000000000..ccf4e963cc8 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/Constants.cs @@ -0,0 +1,7 @@ +namespace SeedApi.Core; + +internal static class Constants +{ + public const string DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffK"; + public const string DateFormat = "yyyy-MM-dd"; +} diff --git a/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/DateTimeSerializer.cs b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/DateTimeSerializer.cs new file mode 100644 index 00000000000..a39de9c28d7 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/DateTimeSerializer.cs @@ -0,0 +1,22 @@ +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedApi.Core; + +internal class DateTimeSerializer : JsonConverter +{ + public override DateTime Read( + ref Utf8JsonReader reader, + System.Type typeToConvert, + JsonSerializerOptions options + ) + { + return DateTime.Parse(reader.GetString()!, null, DateTimeStyles.RoundtripKind); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString(Constants.DateTimeFormat)); + } +} diff --git a/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/EnumSerializer.cs b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/EnumSerializer.cs new file mode 100644 index 00000000000..ac5c0792fbe --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/EnumSerializer.cs @@ -0,0 +1,53 @@ +using System.Runtime.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedApi.Core; + +internal class EnumSerializer : JsonConverter + where TEnum : struct, System.Enum +{ + private readonly Dictionary _enumToString = new(); + private readonly Dictionary _stringToEnum = new(); + + public EnumSerializer() + { + var type = typeof(TEnum); + var values = Enum.GetValues(type); + + foreach (var value in values) + { + var enumValue = (TEnum)value; + var enumMember = type.GetMember(enumValue.ToString())[0]; + var attr = enumMember + .GetCustomAttributes(typeof(EnumMemberAttribute), false) + .Cast() + .FirstOrDefault(); + + var stringValue = + attr?.Value + ?? value.ToString() + ?? throw new Exception("Unexpected null enum toString value"); + + _enumToString.Add(enumValue, stringValue); + _stringToEnum.Add(stringValue, enumValue); + } + } + + public override TEnum Read( + ref Utf8JsonReader reader, + System.Type typeToConvert, + JsonSerializerOptions options + ) + { + var stringValue = + reader.GetString() + ?? throw new Exception("The JSON value could not be read as a string."); + return _stringToEnum.TryGetValue(stringValue, out var enumValue) ? enumValue : default; + } + + public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) + { + writer.WriteStringValue(_enumToString[value]); + } +} diff --git a/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/JsonConfiguration.cs b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/JsonConfiguration.cs new file mode 100644 index 00000000000..13a05f5111f --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/JsonConfiguration.cs @@ -0,0 +1,32 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedApi.Core; + +internal static class JsonOptions +{ + public static readonly JsonSerializerOptions JsonSerializerOptions; + + static JsonOptions() + { + JsonSerializerOptions = new JsonSerializerOptions + { + Converters = { new DateTimeSerializer(), new OneOfSerializer() }, + WriteIndented = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + }; + } +} + +internal static class JsonUtils +{ + public static string Serialize(T obj) + { + return JsonSerializer.Serialize(obj, JsonOptions.JsonSerializerOptions); + } + + public static T Deserialize(string json) + { + return JsonSerializer.Deserialize(json, JsonOptions.JsonSerializerOptions)!; + } +} diff --git a/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/OneOfSerializer.cs b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/OneOfSerializer.cs new file mode 100644 index 00000000000..24ee9268e48 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/OneOfSerializer.cs @@ -0,0 +1,69 @@ +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +using OneOf; + +namespace SeedApi.Core; + +internal class OneOfSerializer : JsonConverter +{ + public override IOneOf? Read( + ref Utf8JsonReader reader, + System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType is JsonTokenType.Null) + return default; + + foreach (var (type, cast) in GetOneOfTypes(typeToConvert)) + { + try + { + var readerCopy = reader; + var result = JsonSerializer.Deserialize(ref readerCopy, type, options); + reader.Skip(); + return (IOneOf)cast.Invoke(null, [result])!; + } + catch (JsonException) { } + } + + throw new JsonException( + $"Cannot deserialize into one of the supported types for {typeToConvert}" + ); + } + + public override void Write(Utf8JsonWriter writer, IOneOf value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Value, options); + } + + private static (System.Type type, MethodInfo cast)[] GetOneOfTypes(System.Type typeToConvert) + { + var casts = typeToConvert + .GetRuntimeMethods() + .Where(m => m.IsSpecialName && m.Name == "op_Implicit") + .ToArray(); + var type = typeToConvert; + while (type != null) + { + if ( + type.IsGenericType + && (type.Name.StartsWith("OneOf`") || type.Name.StartsWith("OneOfBase`")) + ) + { + return type.GetGenericArguments() + .Select(t => (t, casts.First(c => c.GetParameters()[0].ParameterType == t))) + .ToArray(); + } + + type = type.BaseType; + } + throw new InvalidOperationException($"{type} isn't OneOf or OneOfBase"); + } + + public override bool CanConvert(System.Type typeToConvert) + { + return typeof(IOneOf).IsAssignableFrom(typeToConvert); + } +} diff --git a/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/Public/Version.cs b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/Public/Version.cs new file mode 100644 index 00000000000..f430a1bf84c --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Core/Public/Version.cs @@ -0,0 +1,6 @@ +namespace SeedApi; + +internal class Version +{ + public const string Current = "0.0.1"; +} diff --git a/seed/csharp-model/csharp-grpc-proto/src/SeedApi/CreateResponse.cs b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/CreateResponse.cs new file mode 100644 index 00000000000..608e09ded5f --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/CreateResponse.cs @@ -0,0 +1,42 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = User.V1; + +#nullable enable + +namespace SeedApi; + +public record CreateResponse +{ + [JsonPropertyName("user")] + public UserModel? User { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the CreateResponse type into its Protobuf-equivalent representation. + /// + internal Proto.CreateResponse ToProto() + { + var result = new Proto.CreateResponse(); + if (User != null) + { + result.User = User.ToProto(); + } + return result; + } + + /// + /// Returns a new CreateResponse type from its Protobuf-equivalent representation. + /// + internal static CreateResponse FromProto(Proto.CreateResponse value) + { + return new CreateResponse + { + User = value.User != null ? UserModel.FromProto(value.User) : null, + }; + } +} diff --git a/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Metadata.cs b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Metadata.cs new file mode 100644 index 00000000000..2e16aa50b3f --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/Metadata.cs @@ -0,0 +1,39 @@ +using SeedApi.Core; +using Proto = Google.Protobuf.WellKnownTypes; + +#nullable enable + +namespace SeedApi; + +public sealed class Metadata : Dictionary +{ + public Metadata() { } + + public Metadata(IEnumerable> value) + : base(value.ToDictionary(e => e.Key, e => e.Value)) { } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + internal Proto.Struct ToProto() + { + var result = new Proto.Struct(); + foreach (var kvp in this) + { + result.Fields[kvp.Key] = kvp.Value?.ToProto(); + } + return result; + } + + internal static Metadata FromProto(Proto.Struct value) + { + var result = new Metadata(); + foreach (var kvp in value.Fields) + { + result[kvp.Key] = kvp.Value != null ? MetadataValue.FromProto(kvp.Value) : null; + } + return result; + } +} diff --git a/seed/csharp-model/csharp-grpc-proto/src/SeedApi/MetadataValue.cs b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/MetadataValue.cs new file mode 100644 index 00000000000..2308676e891 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/MetadataValue.cs @@ -0,0 +1,91 @@ +using OneOf; +using SeedApi.Core; +using Proto = Google.Protobuf.WellKnownTypes; + +#nullable enable + +namespace SeedApi; + +public sealed class MetadataValue( + OneOf, Metadata> value +) : OneOfBase, Metadata>(value) +{ + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + internal Proto.Value ToProto() + { + return Match( + Proto.Value.ForString, + Proto.Value.ForNumber, + Proto.Value.ForBool, + list => new Proto.Value + { + ListValue = new Proto.ListValue + { + Values = { list.Select(item => item?.ToProto()) }, + }, + }, + nested => new Proto.Value { StructValue = nested.ToProto() } + ); + } + + internal static MetadataValue? FromProto(Proto.Value value) + { + return value.KindCase switch + { + Proto.Value.KindOneofCase.StringValue => value.StringValue, + Proto.Value.KindOneofCase.NumberValue => value.NumberValue, + Proto.Value.KindOneofCase.BoolValue => value.BoolValue, + Proto.Value.KindOneofCase.ListValue => value + .ListValue.Values.Select(FromProto) + .ToList(), + Proto.Value.KindOneofCase.StructValue => Metadata.FromProto(value.StructValue), + _ => null, + }; + } + + public static implicit operator MetadataValue(string value) => new(value); + + public static implicit operator MetadataValue(bool value) => new(value); + + public static implicit operator MetadataValue(double value) => new(value); + + public static implicit operator MetadataValue(Metadata value) => new(value); + + public static implicit operator MetadataValue(MetadataValue?[] value) => new(value); + + public static implicit operator MetadataValue(List value) => new(value); + + public static implicit operator MetadataValue(string[] value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(double[] value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(double?[] value) => + new(value.Select(v => v != null ? new MetadataValue(v.Value) : null).ToList()); + + public static implicit operator MetadataValue(bool[] value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(bool?[] value) => + new(value.Select(v => v != null ? new MetadataValue(v.Value) : null).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => v != null ? new MetadataValue(v.Value) : null).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => v != null ? new MetadataValue(v.Value) : null).ToList()); +} diff --git a/seed/csharp-model/csharp-grpc-proto/src/SeedApi/SeedApi.csproj b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/SeedApi.csproj new file mode 100644 index 00000000000..ea256910f2f --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/SeedApi.csproj @@ -0,0 +1,69 @@ + + + + + net462;net8.0;net7.0;net6.0;netstandard2.0 + enable + false + 12 + enable + 0.0.1 + README.md + https://github.com/csharp-grpc-proto/fern + + + + true + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + + + + + <_Parameter1>SeedApi.Test + + + + diff --git a/seed/csharp-model/csharp-grpc-proto/src/SeedApi/UserModel.cs b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/UserModel.cs new file mode 100644 index 00000000000..986ace0fbd1 --- /dev/null +++ b/seed/csharp-model/csharp-grpc-proto/src/SeedApi/UserModel.cs @@ -0,0 +1,74 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = User.V1; + +#nullable enable + +namespace SeedApi; + +public record UserModel +{ + [JsonPropertyName("username")] + public string? Username { get; set; } + + [JsonPropertyName("email")] + public string? Email { get; set; } + + [JsonPropertyName("age")] + public uint? Age { get; set; } + + [JsonPropertyName("weight")] + public float? Weight { get; set; } + + [JsonPropertyName("metadata")] + public Metadata? Metadata { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the UserModel type into its Protobuf-equivalent representation. + /// + internal Proto.UserModel ToProto() + { + var result = new Proto.UserModel(); + if (Username != null) + { + result.Username = Username ?? ""; + } + if (Email != null) + { + result.Email = Email ?? ""; + } + if (Age != null) + { + result.Age = Age ?? 0; + } + if (Weight != null) + { + result.Weight = Weight ?? 0.0f; + } + if (Metadata != null) + { + result.Metadata = Metadata.ToProto(); + } + return result; + } + + /// + /// Returns a new UserModel type from its Protobuf-equivalent representation. + /// + internal static UserModel FromProto(Proto.UserModel value) + { + return new UserModel + { + Username = value.Username, + Email = value.Email, + Age = value.Age, + Weight = value.Weight, + Metadata = value.Metadata != null ? Metadata.FromProto(value.Metadata) : null, + }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.github/workflows/ci.yml b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.github/workflows/ci.yml new file mode 100644 index 00000000000..bc4fa1a98cb --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.github/workflows/ci.yml @@ -0,0 +1,69 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - uses: actions/checkout@master + + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 8.x + + - name: Install tools + run: | + dotnet tool restore + + - name: Build Release + run: dotnet build src -c Release /p:ContinuousIntegrationBuild=true + + unit-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - uses: actions/checkout@master + + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 8.x + + - name: Install tools + run: | + dotnet tool restore + + - name: Run Tests + run: | + dotnet test src + + + publish: + needs: [compile] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 8.x + + - name: Publish + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_TOKEN }} + run: | + dotnet pack src -c Release + dotnet nuget push src/SeedApi/bin/Release/*.nupkg --api-key $NUGET_API_KEY --source "nuget.org" diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.gitignore b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.gitignore new file mode 100644 index 00000000000..11014f2b33d --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.gitignore @@ -0,0 +1,484 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +## This is based on `dotnet new gitignore` and customized by Fern + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +# [Rr]elease/ (Ignored by Fern) +# [Rr]eleases/ (Ignored by Fern) +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +# [Ll]og/ (Ignored by Fern) +# [Ll]ogs/ (Ignored by Fern) + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/fern.config.json b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/fern.config.json new file mode 100644 index 00000000000..4c8e54ac313 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/fern.config.json @@ -0,0 +1 @@ +{"organization": "fern-test", "version": "*"} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/generators.yml b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/generators.yml new file mode 100644 index 00000000000..c23323621f2 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/generators.yml @@ -0,0 +1,7 @@ +api: + - path: openapi/openapi.yml + - proto: + root: proto + target: proto/data/v1/data.proto + overrides: overrides.yml + local-generation: true diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/openapi/openapi.yml b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/openapi/openapi.yml new file mode 100644 index 00000000000..56ea165b773 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/openapi/openapi.yml @@ -0,0 +1,25 @@ +openapi: 3.0.3 +info: + title: Test API + version: 1.0.0 +servers: + - url: https://localhost +tags: + - name: dataservice +paths: + /foo: + post: + tag: dataservice + x-fern-sdk-group-name: + - dataservice + x-fern-sdk-method-name: foo + operationId: foo + responses: + "200": + content: + application/json: + schema: + type: object + +components: + securitySchemes: {} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/overrides.yml b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/overrides.yml new file mode 100644 index 00000000000..062b98c2ccb --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/overrides.yml @@ -0,0 +1,56 @@ + +paths: + /data/fetch: + get: + x-fern-request-name: FetchRequest + /data/list: + get: + x-fern-request-name: ListRequest +components: + schemas: + Metadata: + oneOf: + - type: object + additionalProperties: + $ref: '#/components/schemas/MetadataValue' + - type: object + x-fern-encoding: + proto: + type: google.protobuf.Struct + MetadataValue: + oneOf: + - type: number + format: double + - type: string + - type: boolean + x-fern-encoding: + proto: + type: google.protobuf.Value + DeleteRequest: + properties: + filter: + $ref: '#/components/schemas/Metadata' + DescribeRequest: + properties: + filter: + $ref: '#/components/schemas/Metadata' + QueryRequest: + properties: + filter: + $ref: '#/components/schemas/Metadata' + QueryColumn: + properties: + filter: + $ref: '#/components/schemas/Metadata' + ScoredColumn: + properties: + metadata: + $ref: '#/components/schemas/Metadata' + UpdateRequest: + properties: + setMetadata: + $ref: '#/components/schemas/Metadata' + Column: + properties: + metadata: + $ref: '#/components/schemas/Metadata' \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/data/v1/data.proto b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/data/v1/data.proto new file mode 100644 index 00000000000..dc3b07ece42 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/data/v1/data.proto @@ -0,0 +1,213 @@ +syntax = "proto3"; + +package data.v1; + +import "google/protobuf/struct.proto"; +import "google/api/annotations.proto"; +import "google/api/field_behavior.proto"; + +option csharp_namespace = "Data.V1.Grpc"; +option go_package = "github.com/acme.co/data-go-grpc"; + +message IndexedData { + repeated uint32 indices = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + repeated float values = 2 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message Column { + string id = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + repeated float values = 2 [ + (google.api.field_behavior) = REQUIRED + ]; + google.protobuf.Struct metadata = 3; + IndexedData indexed_data = 4; +} + +message ScoredColumn { + string id = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + float score = 2; + repeated float values = 3; + google.protobuf.Struct metadata = 4; + IndexedData indexed_data = 5; +} + +message UploadRequest { + repeated Column columns = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + string namespace = 2; +} + +message UploadResponse { + uint32 count = 1; +} + +message DeleteRequest { + repeated string ids = 1; + bool delete_all = 2; + string namespace = 3; + google.protobuf.Struct filter = 4; +} + +message DeleteResponse {} + +message FetchRequest { + repeated string ids = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + string namespace = 2; +} + +message FetchResponse { + map columns = 1; + string namespace = 2; + optional Usage usage = 3; +} + +message ListRequest { + optional string prefix = 1; + optional uint32 limit = 2; + optional string pagination_token = 3; + string namespace = 4; +} + +message Pagination { + string next = 1; +} + +message ListElement { + string id = 1; +} + +message ListResponse { + repeated ListElement columns = 1; + optional Pagination pagination = 2; + string namespace = 3; + optional Usage usage = 4; +} + +message QueryColumn { + repeated float values = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + uint32 top_k = 2; + string namespace = 3; + google.protobuf.Struct filter = 4; + IndexedData indexed_data = 5; +} + +message QueryRequest { + string namespace = 1; + uint32 top_k = 2 [ + (google.api.field_behavior) = REQUIRED + ]; + google.protobuf.Struct filter = 3; + bool include_values = 4; + bool include_metadata = 5; + repeated QueryColumn queries = 6 [ + deprecated = true + ]; + repeated float column = 7; + string id = 8; + IndexedData indexed_data = 9; +} + +message QueryResult { + repeated ScoredColumn matches = 1; + string namespace = 2; +} + +message QueryResponse { + repeated QueryResult results = 1 [deprecated=true]; + repeated ScoredColumn matches = 2; + string namespace = 3; + optional Usage usage = 4; +} + +message Usage { + optional uint32 units = 1; +} + +message UpdateRequest { + string id = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + repeated float values = 2; + google.protobuf.Struct set_metadata = 3; + string namespace = 4; + IndexedData indexed_data = 5; +} + +message UpdateResponse {} + +message DescribeRequest { + google.protobuf.Struct filter = 1; +} + +message NamespaceSummary { + uint32 count = 1; +} + +message DescribeResponse { + map namespaces = 1; + uint32 dimension = 2; + float fullness = 3; + uint32 total_count = 4; +} + +service DataService { + rpc Upload(UploadRequest) returns (UploadResponse) { + option (google.api.http) = { + post: "/data" + body: "*" + }; + } + + rpc Delete(DeleteRequest) returns (DeleteResponse) { + option (google.api.http) = { + post: "/data/delete" + body: "*" + }; + } + + rpc Fetch(FetchRequest) returns (FetchResponse) { + option (google.api.http) = { + get: "/data/fetch" + }; + } + + rpc List(ListRequest) returns (ListResponse) { + option (google.api.http) = { + get: "/data/list" + }; + } + + rpc Query(QueryRequest) returns (QueryResponse) { + option (google.api.http) = { + post: "/data/query" + body: "*" + }; + } + + rpc Update(UpdateRequest) returns (UpdateResponse) { + option (google.api.http) = { + post: "/data/update" + body: "*" + }; + } + + rpc Describe(DescribeRequest) returns (DescribeResponse) { + option (google.api.http) = { + post: "/data/describe" + body: "*" + }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/google/api/annotations.proto b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/google/api/annotations.proto new file mode 100644 index 00000000000..8ff42098404 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright 2015 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/google/api/field_behavior.proto b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/google/api/field_behavior.proto new file mode 100644 index 00000000000..128799c558d --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/google/api/field_behavior.proto @@ -0,0 +1,104 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "FieldBehaviorProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.FieldOptions { + // A designation of a specific field behavior (required, output only, etc.) + // in protobuf messages. + // + // Examples: + // + // string name = 1 [(google.api.field_behavior) = REQUIRED]; + // State state = 1 [(google.api.field_behavior) = OUTPUT_ONLY]; + // google.protobuf.Duration ttl = 1 + // [(google.api.field_behavior) = INPUT_ONLY]; + // google.protobuf.Timestamp expire_time = 1 + // [(google.api.field_behavior) = OUTPUT_ONLY, + // (google.api.field_behavior) = IMMUTABLE]; + repeated google.api.FieldBehavior field_behavior = 1052; +} + +// An indicator of the behavior of a given field (for example, that a field +// is required in requests, or given as output but ignored as input). +// This **does not** change the behavior in protocol buffers itself; it only +// denotes the behavior and may affect how API tooling handles the field. +// +// Note: This enum **may** receive new values in the future. +enum FieldBehavior { + // Conventional default for enums. Do not use this. + FIELD_BEHAVIOR_UNSPECIFIED = 0; + + // Specifically denotes a field as optional. + // While all fields in protocol buffers are optional, this may be specified + // for emphasis if appropriate. + OPTIONAL = 1; + + // Denotes a field as required. + // This indicates that the field **must** be provided as part of the request, + // and failure to do so will cause an error (usually `INVALID_ARGUMENT`). + REQUIRED = 2; + + // Denotes a field as output only. + // This indicates that the field is provided in responses, but including the + // field in a request does nothing (the server *must* ignore it and + // *must not* throw an error as a result of the field's presence). + OUTPUT_ONLY = 3; + + // Denotes a field as input only. + // This indicates that the field is provided in requests, and the + // corresponding field is not included in output. + INPUT_ONLY = 4; + + // Denotes a field as immutable. + // This indicates that the field may be set once in a request to create a + // resource, but may not be changed thereafter. + IMMUTABLE = 5; + + // Denotes that a (repeated) field is an unordered list. + // This indicates that the service may provide the elements of the list + // in any arbitrary order, rather than the order the user originally + // provided. Additionally, the list's order may or may not be stable. + UNORDERED_LIST = 6; + + // Denotes that this field returns a non-empty default value if not set. + // This indicates that if the user provides the empty value in a request, + // a non-empty value will be returned. The user will not be aware of what + // non-empty value to expect. + NON_EMPTY_DEFAULT = 7; + + // Denotes that the field in a resource (a message annotated with + // google.api.resource) is used in the resource name to uniquely identify the + // resource. For AIP-compliant APIs, this should only be applied to the + // `name` field on the resource. + // + // This behavior should not be applied to references to other resources within + // the message. + // + // The identifier field of resources often have different field behavior + // depending on the request it is embedded in (e.g. for Create methods name + // is optional and unused, while for Update methods it is required). Instead + // of method-specific annotations, only `IDENTIFIER` is required. + IDENTIFIER = 8; +} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/google/api/http.proto b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/google/api/http.proto new file mode 100644 index 00000000000..c8392381eb9 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/.mock/proto/google/api/http.proto @@ -0,0 +1,379 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They +// are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL +// query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP +// request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/README.md b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/README.md new file mode 100644 index 00000000000..16b6b2aa0f3 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/README.md @@ -0,0 +1,87 @@ +# Seed C# Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FC%23) +[![nuget shield](https://img.shields.io/nuget/v/SeedApi)](https://nuget.org/packages/SeedApi) + +The Seed C# library provides convenient access to the Seed API from C#. + +## Installation + +```sh +nuget install SeedApi +``` + +## Usage + +Instantiate and use the client with the following: + +```csharp +using SeedApi; + +var client = new SeedApiClient(); +await client.Dataservice.FooAsync(); +``` + +## Exception Handling + +When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error +will be thrown. + +```csharp +using SeedApi; + +try { + var response = await client.Dataservice.FooAsync(...); +} catch (SeedApiApiException e) { + System.Console.WriteLine(e.Body); + System.Console.WriteLine(e.StatusCode); +} +``` + +## Advanced + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retriable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retriable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +Use the `MaxRetries` request option to configure this behavior. + +```csharp +var response = await client.Dataservice.FooAsync( + ..., + new RequestOptions { + MaxRetries: 0 // Override MaxRetries at the request level + } +); +``` + +### Timeouts + +The SDK defaults to a 30 second timeout. Use the `Timeout` option to configure this behavior. + +```csharp +var response = await client.Dataservice.FooAsync( + ..., + new RequestOptions { + Timeout: TimeSpan.FromSeconds(3) // Override timeout to 3s + } +); +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/data/v1/data.proto b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/data/v1/data.proto new file mode 100644 index 00000000000..dc3b07ece42 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/data/v1/data.proto @@ -0,0 +1,213 @@ +syntax = "proto3"; + +package data.v1; + +import "google/protobuf/struct.proto"; +import "google/api/annotations.proto"; +import "google/api/field_behavior.proto"; + +option csharp_namespace = "Data.V1.Grpc"; +option go_package = "github.com/acme.co/data-go-grpc"; + +message IndexedData { + repeated uint32 indices = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + repeated float values = 2 [ + (google.api.field_behavior) = REQUIRED + ]; +} + +message Column { + string id = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + repeated float values = 2 [ + (google.api.field_behavior) = REQUIRED + ]; + google.protobuf.Struct metadata = 3; + IndexedData indexed_data = 4; +} + +message ScoredColumn { + string id = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + float score = 2; + repeated float values = 3; + google.protobuf.Struct metadata = 4; + IndexedData indexed_data = 5; +} + +message UploadRequest { + repeated Column columns = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + string namespace = 2; +} + +message UploadResponse { + uint32 count = 1; +} + +message DeleteRequest { + repeated string ids = 1; + bool delete_all = 2; + string namespace = 3; + google.protobuf.Struct filter = 4; +} + +message DeleteResponse {} + +message FetchRequest { + repeated string ids = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + string namespace = 2; +} + +message FetchResponse { + map columns = 1; + string namespace = 2; + optional Usage usage = 3; +} + +message ListRequest { + optional string prefix = 1; + optional uint32 limit = 2; + optional string pagination_token = 3; + string namespace = 4; +} + +message Pagination { + string next = 1; +} + +message ListElement { + string id = 1; +} + +message ListResponse { + repeated ListElement columns = 1; + optional Pagination pagination = 2; + string namespace = 3; + optional Usage usage = 4; +} + +message QueryColumn { + repeated float values = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + uint32 top_k = 2; + string namespace = 3; + google.protobuf.Struct filter = 4; + IndexedData indexed_data = 5; +} + +message QueryRequest { + string namespace = 1; + uint32 top_k = 2 [ + (google.api.field_behavior) = REQUIRED + ]; + google.protobuf.Struct filter = 3; + bool include_values = 4; + bool include_metadata = 5; + repeated QueryColumn queries = 6 [ + deprecated = true + ]; + repeated float column = 7; + string id = 8; + IndexedData indexed_data = 9; +} + +message QueryResult { + repeated ScoredColumn matches = 1; + string namespace = 2; +} + +message QueryResponse { + repeated QueryResult results = 1 [deprecated=true]; + repeated ScoredColumn matches = 2; + string namespace = 3; + optional Usage usage = 4; +} + +message Usage { + optional uint32 units = 1; +} + +message UpdateRequest { + string id = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + repeated float values = 2; + google.protobuf.Struct set_metadata = 3; + string namespace = 4; + IndexedData indexed_data = 5; +} + +message UpdateResponse {} + +message DescribeRequest { + google.protobuf.Struct filter = 1; +} + +message NamespaceSummary { + uint32 count = 1; +} + +message DescribeResponse { + map namespaces = 1; + uint32 dimension = 2; + float fullness = 3; + uint32 total_count = 4; +} + +service DataService { + rpc Upload(UploadRequest) returns (UploadResponse) { + option (google.api.http) = { + post: "/data" + body: "*" + }; + } + + rpc Delete(DeleteRequest) returns (DeleteResponse) { + option (google.api.http) = { + post: "/data/delete" + body: "*" + }; + } + + rpc Fetch(FetchRequest) returns (FetchResponse) { + option (google.api.http) = { + get: "/data/fetch" + }; + } + + rpc List(ListRequest) returns (ListResponse) { + option (google.api.http) = { + get: "/data/list" + }; + } + + rpc Query(QueryRequest) returns (QueryResponse) { + option (google.api.http) = { + post: "/data/query" + body: "*" + }; + } + + rpc Update(UpdateRequest) returns (UpdateResponse) { + option (google.api.http) = { + post: "/data/update" + body: "*" + }; + } + + rpc Describe(DescribeRequest) returns (DescribeResponse) { + option (google.api.http) = { + post: "/data/describe" + body: "*" + }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/google/api/annotations.proto b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/google/api/annotations.proto new file mode 100644 index 00000000000..8ff42098404 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright 2015 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/google/api/field_behavior.proto b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/google/api/field_behavior.proto new file mode 100644 index 00000000000..128799c558d --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/google/api/field_behavior.proto @@ -0,0 +1,104 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "FieldBehaviorProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.FieldOptions { + // A designation of a specific field behavior (required, output only, etc.) + // in protobuf messages. + // + // Examples: + // + // string name = 1 [(google.api.field_behavior) = REQUIRED]; + // State state = 1 [(google.api.field_behavior) = OUTPUT_ONLY]; + // google.protobuf.Duration ttl = 1 + // [(google.api.field_behavior) = INPUT_ONLY]; + // google.protobuf.Timestamp expire_time = 1 + // [(google.api.field_behavior) = OUTPUT_ONLY, + // (google.api.field_behavior) = IMMUTABLE]; + repeated google.api.FieldBehavior field_behavior = 1052; +} + +// An indicator of the behavior of a given field (for example, that a field +// is required in requests, or given as output but ignored as input). +// This **does not** change the behavior in protocol buffers itself; it only +// denotes the behavior and may affect how API tooling handles the field. +// +// Note: This enum **may** receive new values in the future. +enum FieldBehavior { + // Conventional default for enums. Do not use this. + FIELD_BEHAVIOR_UNSPECIFIED = 0; + + // Specifically denotes a field as optional. + // While all fields in protocol buffers are optional, this may be specified + // for emphasis if appropriate. + OPTIONAL = 1; + + // Denotes a field as required. + // This indicates that the field **must** be provided as part of the request, + // and failure to do so will cause an error (usually `INVALID_ARGUMENT`). + REQUIRED = 2; + + // Denotes a field as output only. + // This indicates that the field is provided in responses, but including the + // field in a request does nothing (the server *must* ignore it and + // *must not* throw an error as a result of the field's presence). + OUTPUT_ONLY = 3; + + // Denotes a field as input only. + // This indicates that the field is provided in requests, and the + // corresponding field is not included in output. + INPUT_ONLY = 4; + + // Denotes a field as immutable. + // This indicates that the field may be set once in a request to create a + // resource, but may not be changed thereafter. + IMMUTABLE = 5; + + // Denotes that a (repeated) field is an unordered list. + // This indicates that the service may provide the elements of the list + // in any arbitrary order, rather than the order the user originally + // provided. Additionally, the list's order may or may not be stable. + UNORDERED_LIST = 6; + + // Denotes that this field returns a non-empty default value if not set. + // This indicates that if the user provides the empty value in a request, + // a non-empty value will be returned. The user will not be aware of what + // non-empty value to expect. + NON_EMPTY_DEFAULT = 7; + + // Denotes that the field in a resource (a message annotated with + // google.api.resource) is used in the resource name to uniquely identify the + // resource. For AIP-compliant APIs, this should only be applied to the + // `name` field on the resource. + // + // This behavior should not be applied to references to other resources within + // the message. + // + // The identifier field of resources often have different field behavior + // depending on the request it is embedded in (e.g. for Create methods name + // is optional and unused, while for Update methods it is required). Instead + // of method-specific annotations, only `IDENTIFIER` is required. + IDENTIFIER = 8; +} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/google/api/http.proto b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/google/api/http.proto new file mode 100644 index 00000000000..c8392381eb9 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/proto/google/api/http.proto @@ -0,0 +1,379 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They +// are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL +// query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP +// request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/reference.md b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/reference.md new file mode 100644 index 00000000000..b5e56eaf804 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/reference.md @@ -0,0 +1,318 @@ +# Reference +## DataService +
client.Dataservice.FooAsync() -> object +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Dataservice.FooAsync(); +``` +
+
+
+
+ + +
+
+
+ +
client.Dataservice.UploadAsync(UploadRequest { ... }) -> UploadResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Dataservice.UploadAsync( + new UploadRequest + { + Columns = new List() + { + new Column + { + Id = "id", + Values = new List() { 1.1f }, + }, + }, + } +); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `UploadRequest` + +
+
+
+
+ + +
+
+
+ +
client.Dataservice.DeleteAsync(DeleteRequest { ... }) -> DeleteResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Dataservice.DeleteAsync(new DeleteRequest()); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `DeleteRequest` + +
+
+
+
+ + +
+
+
+ +
client.Dataservice.DescribeAsync(DescribeRequest { ... }) -> DescribeResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Dataservice.DescribeAsync(new DescribeRequest()); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `DescribeRequest` + +
+
+
+
+ + +
+
+
+ +
client.Dataservice.FetchAsync(FetchRequest { ... }) -> FetchResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Dataservice.FetchAsync(new FetchRequest()); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `FetchRequest` + +
+
+
+
+ + +
+
+
+ +
client.Dataservice.ListAsync(ListRequest { ... }) -> ListResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Dataservice.ListAsync(new ListRequest()); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ListRequest` + +
+
+
+
+ + +
+
+
+ +
client.Dataservice.QueryAsync(QueryRequest { ... }) -> QueryResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Dataservice.QueryAsync(new QueryRequest { TopK = 1 }); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `QueryRequest` + +
+
+
+
+ + +
+
+
+ +
client.Dataservice.UpdateAsync(UpdateRequest { ... }) -> UpdateResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Dataservice.UpdateAsync(new UpdateRequest { Id = "id" }); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `UpdateRequest` + +
+
+
+
+ + +
+
+
diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/snippet-templates.json b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/snippet-templates.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/snippet.json b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/snippet.json new file mode 100644 index 00000000000..c73a4a70093 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/snippet.json @@ -0,0 +1,101 @@ +{ + "types": {}, + "endpoints": [ + { + "example_identifier": null, + "id": { + "path": "/foo", + "method": "POST", + "identifier_override": "endpoint_dataservice.foo" + }, + "snippet": { + "type": "typescript", + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.FooAsync();\n" + } + }, + { + "example_identifier": null, + "id": { + "path": "/data", + "method": "POST", + "identifier_override": "endpoint_dataservice.upload" + }, + "snippet": { + "type": "typescript", + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.UploadAsync(\n new UploadRequest\n {\n Columns = new List()\n {\n new Column\n {\n Id = \"id\",\n Values = new List() { 1.1f },\n },\n },\n }\n);\n" + } + }, + { + "example_identifier": null, + "id": { + "path": "/data/delete", + "method": "POST", + "identifier_override": "endpoint_dataservice.delete" + }, + "snippet": { + "type": "typescript", + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.DeleteAsync(new DeleteRequest());\n" + } + }, + { + "example_identifier": null, + "id": { + "path": "/data/describe", + "method": "POST", + "identifier_override": "endpoint_dataservice.describe" + }, + "snippet": { + "type": "typescript", + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.DescribeAsync(new DescribeRequest());\n" + } + }, + { + "example_identifier": null, + "id": { + "path": "/data/fetch", + "method": "GET", + "identifier_override": "endpoint_dataservice.fetch" + }, + "snippet": { + "type": "typescript", + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.FetchAsync(new FetchRequest());\n" + } + }, + { + "example_identifier": null, + "id": { + "path": "/data/list", + "method": "GET", + "identifier_override": "endpoint_dataservice.list" + }, + "snippet": { + "type": "typescript", + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.ListAsync(new ListRequest());\n" + } + }, + { + "example_identifier": null, + "id": { + "path": "/data/query", + "method": "POST", + "identifier_override": "endpoint_dataservice.query" + }, + "snippet": { + "type": "typescript", + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.QueryAsync(new QueryRequest { TopK = 1 });\n" + } + }, + { + "example_identifier": null, + "id": { + "path": "/data/update", + "method": "POST", + "identifier_override": "endpoint_dataservice.update" + }, + "snippet": { + "type": "typescript", + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.UpdateAsync(new UpdateRequest { Id = \"id\" });\n" + } + } + ] +} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi.Test/Core/EnumSerializerTests.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi.Test/Core/EnumSerializerTests.cs new file mode 100644 index 00000000000..532d182486b --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi.Test/Core/EnumSerializerTests.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; +using NUnit.Framework; +using SeedApi.Core; + +namespace SeedApi.Test.Core +{ + [TestFixture] + public class StringEnumSerializerTests + { + private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; + + private const DummyEnum KnownEnumValue2 = DummyEnum.KnownValue2; + private const string KnownEnumValue2String = "known_value2"; + + private static readonly string JsonWithKnownEnum2 = $$""" + { + "enum_property": "{{KnownEnumValue2String}}" + } + """; + + [Test] + public void ShouldParseKnownEnumValue2() + { + var obj = JsonSerializer.Deserialize(JsonWithKnownEnum2, JsonOptions); + Assert.That(obj, Is.Not.Null); + Assert.That(obj.EnumProperty, Is.EqualTo(KnownEnumValue2)); + } + + [Test] + public void ShouldSerializeKnownEnumValue2() + { + var json = JsonSerializer.SerializeToElement( + new DummyObject { EnumProperty = KnownEnumValue2 }, + JsonOptions + ); + TestContext.WriteLine("Serialized JSON: \n" + json); + var enumString = json.GetProperty("enum_property").GetString(); + Assert.That(enumString, Is.Not.Null); + Assert.That(enumString, Is.EqualTo(KnownEnumValue2String)); + } + } + + public class DummyObject + { + [JsonPropertyName("enum_property")] + public DummyEnum EnumProperty { get; set; } + } + + [JsonConverter(typeof(EnumSerializer))] + public enum DummyEnum + { + [EnumMember(Value = "known_value1")] + KnownValue1, + + [EnumMember(Value = "known_value2")] + KnownValue2, + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi.Test/Core/RawClientTests.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi.Test/Core/RawClientTests.cs new file mode 100644 index 00000000000..df141253267 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi.Test/Core/RawClientTests.cs @@ -0,0 +1,113 @@ +using System; +using System.Net.Http; +using FluentAssertions; +using NUnit.Framework; +using SeedApi.Core; +using WireMock.Server; +using SystemTask = System.Threading.Tasks.Task; +using WireMockRequest = WireMock.RequestBuilders.Request; +using WireMockResponse = WireMock.ResponseBuilders.Response; + +namespace SeedApi.Test.Core +{ + [TestFixture] + public class RawClientTests + { + private WireMockServer _server; + private HttpClient _httpClient; + private RawClient _rawClient; + private string _baseUrl; + private const int _maxRetries = 3; + + [SetUp] + public void SetUp() + { + _server = WireMockServer.Start(); + _baseUrl = _server.Url ?? ""; + _httpClient = new HttpClient { BaseAddress = new Uri(_baseUrl) }; + _rawClient = new RawClient( + new ClientOptions() { HttpClient = _httpClient, MaxRetries = _maxRetries } + ); + } + + [Test] + [TestCase(408)] + [TestCase(429)] + [TestCase(500)] + [TestCase(504)] + public async SystemTask MakeRequestAsync_ShouldRetry_OnRetryableStatusCodes(int statusCode) + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WillSetStateTo("Server Error") + .RespondWith(WireMockResponse.Create().WithStatusCode(statusCode)); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WhenStateIs("Server Error") + .WillSetStateTo("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(statusCode)); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WhenStateIs("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new RawClient.BaseApiRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + }; + + var response = await _rawClient.MakeRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(_maxRetries)); + } + + [Test] + [TestCase(400)] + [TestCase(409)] + public async SystemTask MakeRequestAsync_ShouldRetry_OnNonRetryableStatusCodes( + int statusCode + ) + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WillSetStateTo("Server Error") + .RespondWith( + WireMockResponse.Create().WithStatusCode(statusCode).WithBody("Failure") + ); + + var request = new RawClient.BaseApiRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + }; + + var response = await _rawClient.MakeRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(statusCode)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Failure")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + } + + [TearDown] + public void TearDown() + { + _server.Dispose(); + _httpClient.Dispose(); + } + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi.Test/SeedApi.Test.csproj b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi.Test/SeedApi.Test.csproj new file mode 100644 index 00000000000..c5be29f92d9 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi.Test/SeedApi.Test.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi.Test/TestClient.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi.Test/TestClient.cs similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi.Test/TestClient.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi.Test/TestClient.cs diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/CollectionItemSerializer.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/CollectionItemSerializer.cs new file mode 100644 index 00000000000..af2c9adf7a7 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/CollectionItemSerializer.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedApi.Core; + +/// +/// Json collection converter. +/// +/// Type of item to convert. +/// Converter to use for individual items. +internal class CollectionItemSerializer + : JsonConverter> + where TConverterType : JsonConverter +{ + /// + /// Reads a json string and deserializes it into an object. + /// + /// Json reader. + /// Type to convert. + /// Serializer options. + /// Created object. + public override IEnumerable? Read( + ref Utf8JsonReader reader, + System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType == JsonTokenType.Null) + { + return default; + } + + var jsonSerializerOptions = new JsonSerializerOptions(options); + jsonSerializerOptions.Converters.Clear(); + jsonSerializerOptions.Converters.Add(Activator.CreateInstance()); + + var returnValue = new List(); + + while (reader.TokenType != JsonTokenType.EndArray) + { + if (reader.TokenType != JsonTokenType.StartArray) + { + var item = (TDatatype)( + JsonSerializer.Deserialize(ref reader, typeof(TDatatype), jsonSerializerOptions) + ?? throw new Exception( + $"Failed to deserialize collection item of type {typeof(TDatatype)}" + ) + ); + returnValue.Add(item); + } + + reader.Read(); + } + + return returnValue; + } + + /// + /// Writes a json string. + /// + /// Json writer. + /// Value to write. + /// Serializer options. + public override void Write( + Utf8JsonWriter writer, + IEnumerable? value, + JsonSerializerOptions options + ) + { + if (value == null) + { + writer.WriteNullValue(); + return; + } + + JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions(options); + jsonSerializerOptions.Converters.Clear(); + jsonSerializerOptions.Converters.Add(Activator.CreateInstance()); + + writer.WriteStartArray(); + + foreach (var data in value) + { + JsonSerializer.Serialize(writer, data, jsonSerializerOptions); + } + + writer.WriteEndArray(); + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Constants.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Constants.cs new file mode 100644 index 00000000000..ccf4e963cc8 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Constants.cs @@ -0,0 +1,7 @@ +namespace SeedApi.Core; + +internal static class Constants +{ + public const string DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffK"; + public const string DateFormat = "yyyy-MM-dd"; +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/DateTimeSerializer.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/DateTimeSerializer.cs new file mode 100644 index 00000000000..a39de9c28d7 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/DateTimeSerializer.cs @@ -0,0 +1,22 @@ +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedApi.Core; + +internal class DateTimeSerializer : JsonConverter +{ + public override DateTime Read( + ref Utf8JsonReader reader, + System.Type typeToConvert, + JsonSerializerOptions options + ) + { + return DateTime.Parse(reader.GetString()!, null, DateTimeStyles.RoundtripKind); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString(Constants.DateTimeFormat)); + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/EnumSerializer.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/EnumSerializer.cs new file mode 100644 index 00000000000..ac5c0792fbe --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/EnumSerializer.cs @@ -0,0 +1,53 @@ +using System.Runtime.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedApi.Core; + +internal class EnumSerializer : JsonConverter + where TEnum : struct, System.Enum +{ + private readonly Dictionary _enumToString = new(); + private readonly Dictionary _stringToEnum = new(); + + public EnumSerializer() + { + var type = typeof(TEnum); + var values = Enum.GetValues(type); + + foreach (var value in values) + { + var enumValue = (TEnum)value; + var enumMember = type.GetMember(enumValue.ToString())[0]; + var attr = enumMember + .GetCustomAttributes(typeof(EnumMemberAttribute), false) + .Cast() + .FirstOrDefault(); + + var stringValue = + attr?.Value + ?? value.ToString() + ?? throw new Exception("Unexpected null enum toString value"); + + _enumToString.Add(enumValue, stringValue); + _stringToEnum.Add(stringValue, enumValue); + } + } + + public override TEnum Read( + ref Utf8JsonReader reader, + System.Type typeToConvert, + JsonSerializerOptions options + ) + { + var stringValue = + reader.GetString() + ?? throw new Exception("The JSON value could not be read as a string."); + return _stringToEnum.TryGetValue(stringValue, out var enumValue) ? enumValue : default; + } + + public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) + { + writer.WriteStringValue(_enumToString[value]); + } +} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Extensions.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Extensions.cs similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Extensions.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Extensions.cs diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/HeaderValue.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/HeaderValue.cs new file mode 100644 index 00000000000..30df1c51646 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/HeaderValue.cs @@ -0,0 +1,17 @@ +using OneOf; + +namespace SeedApi.Core; + +internal sealed class HeaderValue(OneOf> value) + : OneOfBase>(value) +{ + public static implicit operator HeaderValue(string value) + { + return new HeaderValue(value); + } + + public static implicit operator HeaderValue(Func value) + { + return new HeaderValue(value); + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Headers.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Headers.cs new file mode 100644 index 00000000000..24bf3179299 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Headers.cs @@ -0,0 +1,17 @@ +namespace SeedApi.Core; + +internal sealed class Headers : Dictionary +{ + public Headers() { } + + public Headers(Dictionary value) + { + foreach (var kvp in value) + { + this[kvp.Key] = new HeaderValue(kvp.Value); + } + } + + public Headers(IEnumerable> value) + : base(value.ToDictionary(e => e.Key, e => e.Value)) { } +} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/HttpMethodExtensions.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/HttpMethodExtensions.cs similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/HttpMethodExtensions.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/HttpMethodExtensions.cs diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/JsonConfiguration.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/JsonConfiguration.cs new file mode 100644 index 00000000000..13a05f5111f --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/JsonConfiguration.cs @@ -0,0 +1,32 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedApi.Core; + +internal static class JsonOptions +{ + public static readonly JsonSerializerOptions JsonSerializerOptions; + + static JsonOptions() + { + JsonSerializerOptions = new JsonSerializerOptions + { + Converters = { new DateTimeSerializer(), new OneOfSerializer() }, + WriteIndented = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + }; + } +} + +internal static class JsonUtils +{ + public static string Serialize(T obj) + { + return JsonSerializer.Serialize(obj, JsonOptions.JsonSerializerOptions); + } + + public static T Deserialize(string json) + { + return JsonSerializer.Deserialize(json, JsonOptions.JsonSerializerOptions)!; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/OneOfSerializer.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/OneOfSerializer.cs new file mode 100644 index 00000000000..24ee9268e48 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/OneOfSerializer.cs @@ -0,0 +1,69 @@ +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +using OneOf; + +namespace SeedApi.Core; + +internal class OneOfSerializer : JsonConverter +{ + public override IOneOf? Read( + ref Utf8JsonReader reader, + System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType is JsonTokenType.Null) + return default; + + foreach (var (type, cast) in GetOneOfTypes(typeToConvert)) + { + try + { + var readerCopy = reader; + var result = JsonSerializer.Deserialize(ref readerCopy, type, options); + reader.Skip(); + return (IOneOf)cast.Invoke(null, [result])!; + } + catch (JsonException) { } + } + + throw new JsonException( + $"Cannot deserialize into one of the supported types for {typeToConvert}" + ); + } + + public override void Write(Utf8JsonWriter writer, IOneOf value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Value, options); + } + + private static (System.Type type, MethodInfo cast)[] GetOneOfTypes(System.Type typeToConvert) + { + var casts = typeToConvert + .GetRuntimeMethods() + .Where(m => m.IsSpecialName && m.Name == "op_Implicit") + .ToArray(); + var type = typeToConvert; + while (type != null) + { + if ( + type.IsGenericType + && (type.Name.StartsWith("OneOf`") || type.Name.StartsWith("OneOfBase`")) + ) + { + return type.GetGenericArguments() + .Select(t => (t, casts.First(c => c.GetParameters()[0].ParameterType == t))) + .ToArray(); + } + + type = type.BaseType; + } + throw new InvalidOperationException($"{type} isn't OneOf or OneOfBase"); + } + + public override bool CanConvert(System.Type typeToConvert) + { + return typeof(IOneOf).IsAssignableFrom(typeToConvert); + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/ClientOptions.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/ClientOptions.cs new file mode 100644 index 00000000000..b3264cb28bb --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/ClientOptions.cs @@ -0,0 +1,57 @@ +using System; +using System.Net.Http; +using Grpc.Net.Client; +using SeedApi.Core; + +#nullable enable + +namespace SeedApi; + +public partial class ClientOptions +{ + /// + /// The Base URL for the API. + /// + public string BaseUrl { get; init; } = SeedApiEnvironment.Default; + + /// + /// The http client used to make requests. + /// + public HttpClient HttpClient { get; init; } = new HttpClient(); + + /// + /// The http client used to make requests. + /// + public int MaxRetries { get; init; } = 2; + + /// + /// The timeout for the request. + /// + public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(30); + + /// + /// The options used for gRPC client endpoints. + /// + public GrpcChannelOptions? GrpcOptions { get; init; } + + /// + /// The http headers sent with the request. + /// + internal Headers Headers { get; init; } = new(); + + /// + /// Clones this and returns a new instance + /// + internal ClientOptions Clone() + { + return new ClientOptions + { + BaseUrl = BaseUrl, + HttpClient = HttpClient, + MaxRetries = MaxRetries, + Timeout = Timeout, + Headers = new Headers(new Dictionary(Headers)), + }; + ; + } +} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Public/GrpcRequestOptions.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/GrpcRequestOptions.cs similarity index 89% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Public/GrpcRequestOptions.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/GrpcRequestOptions.cs index 62ebcc17968..48c37927ea1 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Public/GrpcRequestOptions.cs +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/GrpcRequestOptions.cs @@ -1,6 +1,7 @@ using System; using System.Net.Http; using Grpc.Core; +using SeedApi.Core; namespace SeedApi; @@ -18,11 +19,6 @@ public partial class GrpcRequestOptions /// public TimeSpan? Timeout { get; init; } - /// - /// Headers to be sent with this particular request. - /// - public Dictionary Headers { get; init; } = new Dictionary(); - /// /// Options for write operations. /// @@ -32,4 +28,9 @@ public partial class GrpcRequestOptions /// Client-side call credentials. Provide authorization with per-call granularity. /// public CallCredentials? CallCredentials { get; init; } + + /// + /// Headers to be sent with this particular request. + /// + internal Headers Headers { get; init; } = new(); } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Public/RequestOptions.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/RequestOptions.cs similarity index 79% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Public/RequestOptions.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/RequestOptions.cs index 32ac7dbd6ac..6e0923d6f60 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Public/RequestOptions.cs +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/RequestOptions.cs @@ -1,5 +1,6 @@ using System; using System.Net.Http; +using SeedApi.Core; #nullable enable @@ -26,4 +27,9 @@ public partial class RequestOptions /// The timeout for the request. /// public TimeSpan? Timeout { get; init; } + + /// + /// The http headers sent with the request. + /// + internal Headers Headers { get; init; } = new(); } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Public/SeedApiApiException.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/SeedApiApiException.cs similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Public/SeedApiApiException.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/SeedApiApiException.cs diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/SeedApiEnvironment.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/SeedApiEnvironment.cs new file mode 100644 index 00000000000..14ffe5ab6c8 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/SeedApiEnvironment.cs @@ -0,0 +1,6 @@ +namespace SeedApi; + +public class SeedApiEnvironment +{ + public static string Default = "https://localhost"; +} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Public/SeedApiException.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/SeedApiException.cs similarity index 100% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Public/SeedApiException.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/SeedApiException.cs diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/Version.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/Version.cs new file mode 100644 index 00000000000..f430a1bf84c --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/Public/Version.cs @@ -0,0 +1,6 @@ +namespace SeedApi; + +internal class Version +{ + public const string Current = "0.0.1"; +} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/RawClient.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/RawClient.cs similarity index 51% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/RawClient.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/RawClient.cs index b09c0e8add8..0cc389e831a 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/RawClient.cs +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/RawClient.cs @@ -9,79 +9,35 @@ namespace SeedApi.Core; /// /// Utility class for making raw HTTP requests to the API. /// -internal class RawClient( - Dictionary headers, - Dictionary> headerSuppliers, - ClientOptions clientOptions -) +internal class RawClient(ClientOptions clientOptions) { - /// - /// The gRPC client used to make requests. - /// - public readonly RawGrpcClient Grpc = new RawGrpcClient(headers, headerSuppliers, clientOptions); + private const int InitialRetryDelayMs = 1000; + private const int MaxRetryDelayMs = 60000; - /// - /// The http client used to make requests. - /// - public readonly ClientOptions Options = clientOptions; + private readonly Lazy _grpc = new(() => new RawGrpcClient(clientOptions)); /// - /// Global headers to be sent with every request. + /// The gRPC client used to make requests. /// - private readonly Dictionary _headers = headers; + public RawGrpcClient Grpc => _grpc.Value; /// - /// Global headers to be sent with every request. These headers take - /// precedence over the others. + /// The client options applied on every request. /// - private readonly Dictionary> _headerSuppliers = headerSuppliers; + public readonly ClientOptions Options = clientOptions; public async Task MakeRequestAsync( BaseApiRequest request, CancellationToken cancellationToken = default ) { - var url = BuildUrl(request); - var httpRequest = new HttpRequestMessage(request.Method, url); - if (request.ContentType != null) - { - request.Headers.Add("Content-Type", request.ContentType); - } - // Add global headers to the request - foreach (var header in _headers) - { - httpRequest.Headers.Add(header.Key, header.Value); - } - // Add global headers to the request from supplier - foreach (var header in _headerSuppliers) - { - httpRequest.Headers.Add(header.Key, header.Value.Invoke()); - } - // Add request headers to the request - foreach (var header in request.Headers) - { - httpRequest.Headers.Add(header.Key, header.Value); - } - // Add the request body to the request - if (request is JsonApiRequest jsonRequest) - { - if (jsonRequest.Body != null) - { - httpRequest.Content = new StringContent( - JsonUtils.Serialize(jsonRequest.Body), - Encoding.UTF8, - "application/json" - ); - } - } - else if (request is StreamApiRequest { Body: not null } streamRequest) - { - httpRequest.Content = new StreamContent(streamRequest.Body); - } - // Send the request - var httpClient = request.Options?.HttpClient ?? Options.HttpClient; - var response = await httpClient.SendAsync(httpRequest, cancellationToken); - return new ApiResponse { StatusCode = (int)response.StatusCode, Raw = response }; + // Apply the request timeout. + var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + var timeout = request.Options?.Timeout ?? Options.Timeout; + cts.CancelAfter(timeout); + + // Send the request. + return await SendWithRetriesAsync(request, cts.Token); } public record BaseApiRequest @@ -96,7 +52,7 @@ public record BaseApiRequest public Dictionary Query { get; init; } = new(); - public Dictionary Headers { get; init; } = new(); + public Headers Headers { get; init; } = new(); public RequestOptions? Options { get; init; } } @@ -127,7 +83,67 @@ public record ApiResponse public required HttpResponseMessage Raw { get; init; } } - private string BuildUrl(BaseApiRequest request) + private async Task SendWithRetriesAsync( + BaseApiRequest request, + CancellationToken cancellationToken + ) + { + var httpClient = request.Options?.HttpClient ?? Options.HttpClient; + var maxRetries = request.Options?.MaxRetries ?? Options.MaxRetries; + var response = await httpClient.SendAsync(BuildHttpRequest(request), cancellationToken); + for (var i = 0; i < maxRetries; i++) + { + if (!ShouldRetry(response)) + { + break; + } + var delayMs = Math.Min(InitialRetryDelayMs * (int)Math.Pow(2, i), MaxRetryDelayMs); + await System.Threading.Tasks.Task.Delay(delayMs, cancellationToken); + response = await httpClient.SendAsync(BuildHttpRequest(request), cancellationToken); + } + return new ApiResponse { StatusCode = (int)response.StatusCode, Raw = response }; + } + + private static bool ShouldRetry(HttpResponseMessage response) + { + var statusCode = (int)response.StatusCode; + return statusCode is 408 or 429 or >= 500; + } + + private HttpRequestMessage BuildHttpRequest(BaseApiRequest request) + { + var url = BuildUrl(request); + var httpRequest = new HttpRequestMessage(request.Method, url); + switch (request) + { + // Add the request body to the request. + case JsonApiRequest jsonRequest: + { + if (jsonRequest.Body != null) + { + httpRequest.Content = new StringContent( + JsonUtils.Serialize(jsonRequest.Body), + Encoding.UTF8, + "application/json" + ); + } + break; + } + case StreamApiRequest { Body: not null } streamRequest: + httpRequest.Content = new StreamContent(streamRequest.Body); + break; + } + if (request.ContentType != null) + { + request.Headers.Add("Content-Type", request.ContentType); + } + SetHeaders(httpRequest, Options.Headers); + SetHeaders(httpRequest, request.Headers); + SetHeaders(httpRequest, request.Options?.Headers ?? new Headers()); + return httpRequest; + } + + private static string BuildUrl(BaseApiRequest request) { var baseUrl = request.Options?.BaseUrl ?? request.BaseUrl; var trimmedBaseUrl = baseUrl.TrimEnd('/'); @@ -158,7 +174,19 @@ private string BuildUrl(BaseApiRequest request) return current; } ); - url = url.Substring(0, url.Length - 1); + url = url[..^1]; return url; } + + private static void SetHeaders(HttpRequestMessage httpRequest, Headers headers) + { + foreach (var header in headers) + { + var value = header.Value?.Match(str => str, func => func.Invoke()); + if (value != null) + { + httpRequest.Headers.TryAddWithoutValidation(header.Key, value); + } + } + } } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/RawGrpcClient.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/RawGrpcClient.cs similarity index 69% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/RawGrpcClient.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/RawGrpcClient.cs index 9350a768bc2..657be1d946d 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/RawGrpcClient.cs +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Core/RawGrpcClient.cs @@ -2,9 +2,7 @@ using Grpc.Core; using Grpc.Net.Client; -#nullable enable - -namespace SeedApi; +namespace SeedApi.Core; /// /// Utility class for making gRPC requests to the API. @@ -17,18 +15,10 @@ internal class RawGrpcClient public readonly GrpcChannel Channel; private readonly ClientOptions _clientOptions; - private readonly Dictionary _headers; - private readonly Dictionary> _headerSuppliers; - public RawGrpcClient( - Dictionary headers, - Dictionary> headerSuppliers, - ClientOptions clientOptions - ) + public RawGrpcClient(ClientOptions clientOptions) { _clientOptions = clientOptions; - _headers = new Dictionary(headers); - _headerSuppliers = new Dictionary>(headerSuppliers); var grpcOptions = PrepareGrpcChannelOptions(); Channel = @@ -48,18 +38,9 @@ public CallOptions CreateCallOptions( ) { var metadata = new global::Grpc.Core.Metadata(); - foreach (var header in _headers) - { - metadata.Add(header.Key, header.Value); - } - foreach (var header in _headerSuppliers) - { - metadata.Add(header.Key, header.Value.Invoke()); - } - foreach (var header in options.Headers) - { - metadata.Add(header.Key, header.Value); - } + SetHeaders(metadata, _clientOptions.Headers); + SetHeaders(metadata, options.Headers); + var timeout = options.Timeout ?? _clientOptions.Timeout; var deadline = DateTime.UtcNow.Add(timeout); return new CallOptions( @@ -72,6 +53,18 @@ public CallOptions CreateCallOptions( ); } + private void SetHeaders(global::Grpc.Core.Metadata metadata, Headers headers) + { + foreach (var header in headers) + { + var value = header.Value?.Match(str => str, func => func.Invoke()); + if (value != null) + { + metadata.Add(header.Key, value); + } + } + } + private GrpcChannelOptions? PrepareGrpcChannelOptions() { var grpcChannelOptions = _clientOptions.GrpcOptions; diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/DataserviceClient.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/DataserviceClient.cs similarity index 74% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/DataserviceClient.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/DataserviceClient.cs index f8ccfbf0a3a..9bd2a610bf3 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/DataserviceClient.cs +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/DataserviceClient.cs @@ -1,3 +1,5 @@ +using System.Net.Http; +using System.Text.Json; using System.Threading; using Data.V1.Grpc; using Grpc.Core; @@ -22,6 +24,63 @@ internal DataserviceClient(RawClient client) _dataService = new DataService.DataServiceClient(_grpc.Channel); } + /// + /// + /// await client.Dataservice.FooAsync(); + /// + /// + public async Task FooAsync( + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + var response = await _client.MakeRequestAsync( + new RawClient.JsonApiRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Post, + Path = "foo", + Options = options, + }, + cancellationToken + ); + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + if (response.StatusCode is >= 200 and < 400) + { + try + { + return JsonUtils.Deserialize(responseBody)!; + } + catch (JsonException e) + { + throw new SeedApiException("Failed to deserialize response", e); + } + } + + throw new SeedApiApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + + /// + /// + /// await client.Dataservice.UploadAsync( + /// new UploadRequest + /// { + /// Columns = new List<Column>() + /// { + /// new Column + /// { + /// Id = "id", + /// Values = new List<float>() { 1.1f }, + /// }, + /// }, + /// } + /// ); + /// + /// public async Task UploadAsync( UploadRequest request, GrpcRequestOptions? options = null, @@ -34,7 +93,6 @@ public async Task UploadAsync( options ?? new GrpcRequestOptions(), cancellationToken ); - ; var call = _dataService.UploadAsync(request.ToProto(), callOptions); var response = await call.ConfigureAwait(false); return UploadResponse.FromProto(response); @@ -54,6 +112,11 @@ public async Task UploadAsync( } } + /// + /// + /// await client.Dataservice.DeleteAsync(new DeleteRequest()); + /// + /// public async Task DeleteAsync( DeleteRequest request, GrpcRequestOptions? options = null, @@ -66,7 +129,6 @@ public async Task DeleteAsync( options ?? new GrpcRequestOptions(), cancellationToken ); - ; var call = _dataService.DeleteAsync(request.ToProto(), callOptions); var response = await call.ConfigureAwait(false); return DeleteResponse.FromProto(response); @@ -86,6 +148,11 @@ public async Task DeleteAsync( } } + /// + /// + /// await client.Dataservice.DescribeAsync(new DescribeRequest()); + /// + /// public async Task DescribeAsync( DescribeRequest request, GrpcRequestOptions? options = null, @@ -98,7 +165,6 @@ public async Task DescribeAsync( options ?? new GrpcRequestOptions(), cancellationToken ); - ; var call = _dataService.DescribeAsync(request.ToProto(), callOptions); var response = await call.ConfigureAwait(false); return DescribeResponse.FromProto(response); @@ -118,6 +184,11 @@ public async Task DescribeAsync( } } + /// + /// + /// await client.Dataservice.FetchAsync(new FetchRequest()); + /// + /// public async Task FetchAsync( FetchRequest request, GrpcRequestOptions? options = null, @@ -130,7 +201,6 @@ public async Task FetchAsync( options ?? new GrpcRequestOptions(), cancellationToken ); - ; var call = _dataService.FetchAsync(request.ToProto(), callOptions); var response = await call.ConfigureAwait(false); return FetchResponse.FromProto(response); @@ -150,6 +220,11 @@ public async Task FetchAsync( } } + /// + /// + /// await client.Dataservice.ListAsync(new ListRequest()); + /// + /// public async Task ListAsync( ListRequest request, GrpcRequestOptions? options = null, @@ -162,7 +237,6 @@ public async Task ListAsync( options ?? new GrpcRequestOptions(), cancellationToken ); - ; var call = _dataService.ListAsync(request.ToProto(), callOptions); var response = await call.ConfigureAwait(false); return ListResponse.FromProto(response); @@ -182,6 +256,11 @@ public async Task ListAsync( } } + /// + /// + /// await client.Dataservice.QueryAsync(new QueryRequest { TopK = 1 }); + /// + /// public async Task QueryAsync( QueryRequest request, GrpcRequestOptions? options = null, @@ -194,7 +273,6 @@ public async Task QueryAsync( options ?? new GrpcRequestOptions(), cancellationToken ); - ; var call = _dataService.QueryAsync(request.ToProto(), callOptions); var response = await call.ConfigureAwait(false); return QueryResponse.FromProto(response); @@ -214,6 +292,11 @@ public async Task QueryAsync( } } + /// + /// + /// await client.Dataservice.UpdateAsync(new UpdateRequest { Id = "id" }); + /// + /// public async Task UpdateAsync( UpdateRequest request, GrpcRequestOptions? options = null, @@ -226,7 +309,6 @@ public async Task UpdateAsync( options ?? new GrpcRequestOptions(), cancellationToken ); - ; var call = _dataService.UpdateAsync(request.ToProto(), callOptions); var response = await call.ConfigureAwait(false); return UpdateResponse.FromProto(response); diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/DeleteRequest.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/DeleteRequest.cs similarity index 90% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/DeleteRequest.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/DeleteRequest.cs index 23e89c08ca9..10d785c72b7 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/DeleteRequest.cs +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/DeleteRequest.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -19,6 +20,11 @@ public record DeleteRequest [JsonPropertyName("filter")] public Metadata? Filter { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the DeleteRequest type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/DescribeRequest.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/DescribeRequest.cs similarity index 83% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/DescribeRequest.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/DescribeRequest.cs index 80d31c1c0d6..a9de06c69f4 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/DescribeRequest.cs +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/DescribeRequest.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -10,6 +11,11 @@ public record DescribeRequest [JsonPropertyName("filter")] public Metadata? Filter { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the DescribeRequest type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/FetchRequest.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/FetchRequest.cs similarity index 84% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/FetchRequest.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/FetchRequest.cs index c82bb8c5a52..70ea06edfc4 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/FetchRequest.cs +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/FetchRequest.cs @@ -1,3 +1,4 @@ +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -10,6 +11,11 @@ public record FetchRequest public string? Namespace { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the FetchRequest type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/ListRequest.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/ListRequest.cs similarity index 84% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/ListRequest.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/ListRequest.cs index c7120cd4256..afc15a6b93d 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/ListRequest.cs +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/ListRequest.cs @@ -1,3 +1,4 @@ +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -14,6 +15,11 @@ public record ListRequest public string? Namespace { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the ListRequest type into its Protobuf-equivalent representation. /// @@ -26,7 +32,7 @@ internal Proto.ListRequest ToProto() } if (Limit != null) { - result.Limit = Limit ?? 0U; + result.Limit = Limit ?? 0; } if (PaginationToken != null) { diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/QueryRequest.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/QueryRequest.cs similarity index 94% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/QueryRequest.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/QueryRequest.cs index e4cf6468f11..a47fc99c7a4 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/QueryRequest.cs +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/QueryRequest.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -34,6 +35,11 @@ public record QueryRequest [JsonPropertyName("indexedData")] public IndexedData? IndexedData { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the QueryRequest type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/UpdateRequest.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/UpdateRequest.cs similarity index 91% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/UpdateRequest.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/UpdateRequest.cs index c76bd5e490f..4f4977cddc3 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/UpdateRequest.cs +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/UpdateRequest.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -22,6 +23,11 @@ public record UpdateRequest [JsonPropertyName("indexedData")] public IndexedData? IndexedData { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the UpdateRequest type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/UploadRequest.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/UploadRequest.cs similarity index 87% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/UploadRequest.cs rename to seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/UploadRequest.cs index ab3391b1a13..8421016d0df 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/UploadRequest.cs +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Dataservice/Requests/UploadRequest.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using SeedApi.Core; using Proto = Data.V1.Grpc; #nullable enable @@ -13,6 +14,11 @@ public record UploadRequest [JsonPropertyName("namespace")] public string? Namespace { get; set; } + public override string ToString() + { + return JsonUtils.Serialize(this); + } + /// /// Maps the UploadRequest type into its Protobuf-equivalent representation. /// diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/SeedApi.csproj b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/SeedApi.csproj new file mode 100644 index 00000000000..317b0f9a176 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/SeedApi.csproj @@ -0,0 +1,71 @@ + + + + + net462;net8.0;net7.0;net6.0;netstandard2.0 + enable + false + 12 + enable + 0.0.1 + README.md + https://github.com/csharp-grpc-proto-exhaustive/fern + + + + true + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + + + + + + + <_Parameter1>SeedApi.Test + + + + diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/SeedApiClient.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/SeedApiClient.cs new file mode 100644 index 00000000000..34ed4743dfe --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/SeedApiClient.cs @@ -0,0 +1,35 @@ +using SeedApi.Core; + +#nullable enable + +namespace SeedApi; + +public partial class SeedApiClient +{ + private RawClient _client; + + public SeedApiClient(ClientOptions? clientOptions = null) + { + var defaultHeaders = new Headers( + new Dictionary() + { + { "X-Fern-Language", "C#" }, + { "X-Fern-SDK-Name", "SeedApi" }, + { "X-Fern-SDK-Version", Version.Current }, + { "User-Agent", "Ferncsharp-grpc-proto-exhaustive/0.0.1" }, + } + ); + clientOptions ??= new ClientOptions(); + foreach (var header in defaultHeaders) + { + if (!clientOptions.Headers.ContainsKey(header.Key)) + { + clientOptions.Headers[header.Key] = header.Value; + } + } + _client = new RawClient(clientOptions); + Dataservice = new DataserviceClient(_client); + } + + public DataserviceClient Dataservice { get; init; } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Column.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Column.cs new file mode 100644 index 00000000000..602200b6dfb --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Column.cs @@ -0,0 +1,64 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record Column +{ + [JsonPropertyName("id")] + public required string Id { get; set; } + + [JsonPropertyName("values")] + public IEnumerable Values { get; set; } = new List(); + + [JsonPropertyName("metadata")] + public Metadata? Metadata { get; set; } + + [JsonPropertyName("indexedData")] + public IndexedData? IndexedData { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the Column type into its Protobuf-equivalent representation. + /// + internal Proto.Column ToProto() + { + var result = new Proto.Column(); + result.Id = Id; + if (Values.Any()) + { + result.Values.AddRange(Values); + } + if (Metadata != null) + { + result.Metadata = Metadata.ToProto(); + } + if (IndexedData != null) + { + result.IndexedData = IndexedData.ToProto(); + } + return result; + } + + /// + /// Returns a new Column type from its Protobuf-equivalent representation. + /// + internal static Column FromProto(Proto.Column value) + { + return new Column + { + Id = value.Id, + Values = value.Values?.ToList() ?? new List(), + Metadata = value.Metadata != null ? Metadata.FromProto(value.Metadata) : null, + IndexedData = + value.IndexedData != null ? IndexedData.FromProto(value.IndexedData) : null, + }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/DeleteResponse.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/DeleteResponse.cs new file mode 100644 index 00000000000..def64fbd896 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/DeleteResponse.cs @@ -0,0 +1,30 @@ +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record DeleteResponse +{ + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the DeleteResponse type into its Protobuf-equivalent representation. + /// + internal Proto.DeleteResponse ToProto() + { + return new Proto.DeleteResponse(); + } + + /// + /// Returns a new DeleteResponse type from its Protobuf-equivalent representation. + /// + internal static DeleteResponse FromProto(Proto.DeleteResponse value) + { + return new DeleteResponse(); + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/DescribeResponse.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/DescribeResponse.cs new file mode 100644 index 00000000000..fbd45c674b7 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/DescribeResponse.cs @@ -0,0 +1,73 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record DescribeResponse +{ + [JsonPropertyName("namespaces")] + public Dictionary? Namespaces { get; set; } + + [JsonPropertyName("dimension")] + public uint? Dimension { get; set; } + + [JsonPropertyName("fullness")] + public float? Fullness { get; set; } + + [JsonPropertyName("totalCount")] + public uint? TotalCount { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the DescribeResponse type into its Protobuf-equivalent representation. + /// + internal Proto.DescribeResponse ToProto() + { + var result = new Proto.DescribeResponse(); + if (Namespaces != null && Namespaces.Any()) + { + foreach (var kvp in Namespaces) + { + result.Namespaces.Add(kvp.Key, kvp.Value.ToProto()); + } + ; + } + if (Dimension != null) + { + result.Dimension = Dimension ?? 0; + } + if (Fullness != null) + { + result.Fullness = Fullness ?? 0.0f; + } + if (TotalCount != null) + { + result.TotalCount = TotalCount ?? 0; + } + return result; + } + + /// + /// Returns a new DescribeResponse type from its Protobuf-equivalent representation. + /// + internal static DescribeResponse FromProto(Proto.DescribeResponse value) + { + return new DescribeResponse + { + Namespaces = value.Namespaces?.ToDictionary( + kvp => kvp.Key, + kvp => NamespaceSummary.FromProto(kvp.Value) + ), + Dimension = value.Dimension, + Fullness = value.Fullness, + TotalCount = value.TotalCount, + }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/FetchResponse.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/FetchResponse.cs new file mode 100644 index 00000000000..ae595dfdc41 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/FetchResponse.cs @@ -0,0 +1,65 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record FetchResponse +{ + [JsonPropertyName("columns")] + public Dictionary? Columns { get; set; } + + [JsonPropertyName("namespace")] + public string? Namespace { get; set; } + + [JsonPropertyName("usage")] + public Usage? Usage { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the FetchResponse type into its Protobuf-equivalent representation. + /// + internal Proto.FetchResponse ToProto() + { + var result = new Proto.FetchResponse(); + if (Columns != null && Columns.Any()) + { + foreach (var kvp in Columns) + { + result.Columns.Add(kvp.Key, kvp.Value.ToProto()); + } + ; + } + if (Namespace != null) + { + result.Namespace = Namespace ?? ""; + } + if (Usage != null) + { + result.Usage = Usage.ToProto(); + } + return result; + } + + /// + /// Returns a new FetchResponse type from its Protobuf-equivalent representation. + /// + internal static FetchResponse FromProto(Proto.FetchResponse value) + { + return new FetchResponse + { + Columns = value.Columns?.ToDictionary( + kvp => kvp.Key, + kvp => Column.FromProto(kvp.Value) + ), + Namespace = value.Namespace, + Usage = value.Usage != null ? Usage.FromProto(value.Usage) : null, + }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/IndexedData.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/IndexedData.cs new file mode 100644 index 00000000000..242c4d0b135 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/IndexedData.cs @@ -0,0 +1,50 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record IndexedData +{ + [JsonPropertyName("indices")] + public IEnumerable Indices { get; set; } = new List(); + + [JsonPropertyName("values")] + public IEnumerable Values { get; set; } = new List(); + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the IndexedData type into its Protobuf-equivalent representation. + /// + internal Proto.IndexedData ToProto() + { + var result = new Proto.IndexedData(); + if (Indices.Any()) + { + result.Indices.AddRange(Indices); + } + if (Values.Any()) + { + result.Values.AddRange(Values); + } + return result; + } + + /// + /// Returns a new IndexedData type from its Protobuf-equivalent representation. + /// + internal static IndexedData FromProto(Proto.IndexedData value) + { + return new IndexedData + { + Indices = value.Indices?.ToList() ?? new List(), + Values = value.Values?.ToList() ?? new List(), + }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/ListElement.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/ListElement.cs new file mode 100644 index 00000000000..2044bd62e56 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/ListElement.cs @@ -0,0 +1,39 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record ListElement +{ + [JsonPropertyName("id")] + public string? Id { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the ListElement type into its Protobuf-equivalent representation. + /// + internal Proto.ListElement ToProto() + { + var result = new Proto.ListElement(); + if (Id != null) + { + result.Id = Id ?? ""; + } + return result; + } + + /// + /// Returns a new ListElement type from its Protobuf-equivalent representation. + /// + internal static ListElement FromProto(Proto.ListElement value) + { + return new ListElement { Id = value.Id }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/ListResponse.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/ListResponse.cs new file mode 100644 index 00000000000..9d691519a85 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/ListResponse.cs @@ -0,0 +1,66 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record ListResponse +{ + [JsonPropertyName("columns")] + public IEnumerable? Columns { get; set; } + + [JsonPropertyName("pagination")] + public Pagination? Pagination { get; set; } + + [JsonPropertyName("namespace")] + public string? Namespace { get; set; } + + [JsonPropertyName("usage")] + public Usage? Usage { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the ListResponse type into its Protobuf-equivalent representation. + /// + internal Proto.ListResponse ToProto() + { + var result = new Proto.ListResponse(); + if (Columns != null && Columns.Any()) + { + result.Columns.AddRange(Columns.Select(elem => elem.ToProto())); + } + if (Pagination != null) + { + result.Pagination = Pagination.ToProto(); + } + if (Namespace != null) + { + result.Namespace = Namespace ?? ""; + } + if (Usage != null) + { + result.Usage = Usage.ToProto(); + } + return result; + } + + /// + /// Returns a new ListResponse type from its Protobuf-equivalent representation. + /// + internal static ListResponse FromProto(Proto.ListResponse value) + { + return new ListResponse + { + Columns = value.Columns?.Select(ListElement.FromProto), + Pagination = value.Pagination != null ? Pagination.FromProto(value.Pagination) : null, + Namespace = value.Namespace, + Usage = value.Usage != null ? Usage.FromProto(value.Usage) : null, + }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Metadata.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Metadata.cs new file mode 100644 index 00000000000..2e16aa50b3f --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Metadata.cs @@ -0,0 +1,39 @@ +using SeedApi.Core; +using Proto = Google.Protobuf.WellKnownTypes; + +#nullable enable + +namespace SeedApi; + +public sealed class Metadata : Dictionary +{ + public Metadata() { } + + public Metadata(IEnumerable> value) + : base(value.ToDictionary(e => e.Key, e => e.Value)) { } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + internal Proto.Struct ToProto() + { + var result = new Proto.Struct(); + foreach (var kvp in this) + { + result.Fields[kvp.Key] = kvp.Value?.ToProto(); + } + return result; + } + + internal static Metadata FromProto(Proto.Struct value) + { + var result = new Metadata(); + foreach (var kvp in value.Fields) + { + result[kvp.Key] = kvp.Value != null ? MetadataValue.FromProto(kvp.Value) : null; + } + return result; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/MetadataValue.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/MetadataValue.cs new file mode 100644 index 00000000000..2308676e891 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/MetadataValue.cs @@ -0,0 +1,91 @@ +using OneOf; +using SeedApi.Core; +using Proto = Google.Protobuf.WellKnownTypes; + +#nullable enable + +namespace SeedApi; + +public sealed class MetadataValue( + OneOf, Metadata> value +) : OneOfBase, Metadata>(value) +{ + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + internal Proto.Value ToProto() + { + return Match( + Proto.Value.ForString, + Proto.Value.ForNumber, + Proto.Value.ForBool, + list => new Proto.Value + { + ListValue = new Proto.ListValue + { + Values = { list.Select(item => item?.ToProto()) }, + }, + }, + nested => new Proto.Value { StructValue = nested.ToProto() } + ); + } + + internal static MetadataValue? FromProto(Proto.Value value) + { + return value.KindCase switch + { + Proto.Value.KindOneofCase.StringValue => value.StringValue, + Proto.Value.KindOneofCase.NumberValue => value.NumberValue, + Proto.Value.KindOneofCase.BoolValue => value.BoolValue, + Proto.Value.KindOneofCase.ListValue => value + .ListValue.Values.Select(FromProto) + .ToList(), + Proto.Value.KindOneofCase.StructValue => Metadata.FromProto(value.StructValue), + _ => null, + }; + } + + public static implicit operator MetadataValue(string value) => new(value); + + public static implicit operator MetadataValue(bool value) => new(value); + + public static implicit operator MetadataValue(double value) => new(value); + + public static implicit operator MetadataValue(Metadata value) => new(value); + + public static implicit operator MetadataValue(MetadataValue?[] value) => new(value); + + public static implicit operator MetadataValue(List value) => new(value); + + public static implicit operator MetadataValue(string[] value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(double[] value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(double?[] value) => + new(value.Select(v => v != null ? new MetadataValue(v.Value) : null).ToList()); + + public static implicit operator MetadataValue(bool[] value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(bool?[] value) => + new(value.Select(v => v != null ? new MetadataValue(v.Value) : null).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => v != null ? new MetadataValue(v.Value) : null).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => v != null ? new MetadataValue(v.Value) : null).ToList()); +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/NamespaceSummary.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/NamespaceSummary.cs new file mode 100644 index 00000000000..da0573827da --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/NamespaceSummary.cs @@ -0,0 +1,39 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record NamespaceSummary +{ + [JsonPropertyName("count")] + public uint? Count { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the NamespaceSummary type into its Protobuf-equivalent representation. + /// + internal Proto.NamespaceSummary ToProto() + { + var result = new Proto.NamespaceSummary(); + if (Count != null) + { + result.Count = Count ?? 0; + } + return result; + } + + /// + /// Returns a new NamespaceSummary type from its Protobuf-equivalent representation. + /// + internal static NamespaceSummary FromProto(Proto.NamespaceSummary value) + { + return new NamespaceSummary { Count = value.Count }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Pagination.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Pagination.cs new file mode 100644 index 00000000000..87a67db48af --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Pagination.cs @@ -0,0 +1,39 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record Pagination +{ + [JsonPropertyName("next")] + public string? Next { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the Pagination type into its Protobuf-equivalent representation. + /// + internal Proto.Pagination ToProto() + { + var result = new Proto.Pagination(); + if (Next != null) + { + result.Next = Next ?? ""; + } + return result; + } + + /// + /// Returns a new Pagination type from its Protobuf-equivalent representation. + /// + internal static Pagination FromProto(Proto.Pagination value) + { + return new Pagination { Next = value.Next }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/QueryColumn.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/QueryColumn.cs new file mode 100644 index 00000000000..94c3d4f14af --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/QueryColumn.cs @@ -0,0 +1,75 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record QueryColumn +{ + [JsonPropertyName("values")] + public IEnumerable Values { get; set; } = new List(); + + [JsonPropertyName("topK")] + public uint? TopK { get; set; } + + [JsonPropertyName("namespace")] + public string? Namespace { get; set; } + + [JsonPropertyName("filter")] + public Metadata? Filter { get; set; } + + [JsonPropertyName("indexedData")] + public IndexedData? IndexedData { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the QueryColumn type into its Protobuf-equivalent representation. + /// + internal Proto.QueryColumn ToProto() + { + var result = new Proto.QueryColumn(); + if (Values.Any()) + { + result.Values.AddRange(Values); + } + if (TopK != null) + { + result.TopK = TopK ?? 0; + } + if (Namespace != null) + { + result.Namespace = Namespace ?? ""; + } + if (Filter != null) + { + result.Filter = Filter.ToProto(); + } + if (IndexedData != null) + { + result.IndexedData = IndexedData.ToProto(); + } + return result; + } + + /// + /// Returns a new QueryColumn type from its Protobuf-equivalent representation. + /// + internal static QueryColumn FromProto(Proto.QueryColumn value) + { + return new QueryColumn + { + Values = value.Values?.ToList() ?? new List(), + TopK = value.TopK, + Namespace = value.Namespace, + Filter = value.Filter != null ? Metadata.FromProto(value.Filter) : null, + IndexedData = + value.IndexedData != null ? IndexedData.FromProto(value.IndexedData) : null, + }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/QueryResponse.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/QueryResponse.cs new file mode 100644 index 00000000000..c4786b94639 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/QueryResponse.cs @@ -0,0 +1,66 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record QueryResponse +{ + [JsonPropertyName("results")] + public IEnumerable? Results { get; set; } + + [JsonPropertyName("matches")] + public IEnumerable? Matches { get; set; } + + [JsonPropertyName("namespace")] + public string? Namespace { get; set; } + + [JsonPropertyName("usage")] + public Usage? Usage { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the QueryResponse type into its Protobuf-equivalent representation. + /// + internal Proto.QueryResponse ToProto() + { + var result = new Proto.QueryResponse(); + if (Results != null && Results.Any()) + { + result.Results.AddRange(Results.Select(elem => elem.ToProto())); + } + if (Matches != null && Matches.Any()) + { + result.Matches.AddRange(Matches.Select(elem => elem.ToProto())); + } + if (Namespace != null) + { + result.Namespace = Namespace ?? ""; + } + if (Usage != null) + { + result.Usage = Usage.ToProto(); + } + return result; + } + + /// + /// Returns a new QueryResponse type from its Protobuf-equivalent representation. + /// + internal static QueryResponse FromProto(Proto.QueryResponse value) + { + return new QueryResponse + { + Results = value.Results?.Select(QueryResult.FromProto), + Matches = value.Matches?.Select(ScoredColumn.FromProto), + Namespace = value.Namespace, + Usage = value.Usage != null ? Usage.FromProto(value.Usage) : null, + }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/QueryResult.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/QueryResult.cs new file mode 100644 index 00000000000..8c8988af1fd --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/QueryResult.cs @@ -0,0 +1,50 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record QueryResult +{ + [JsonPropertyName("matches")] + public IEnumerable? Matches { get; set; } + + [JsonPropertyName("namespace")] + public string? Namespace { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the QueryResult type into its Protobuf-equivalent representation. + /// + internal Proto.QueryResult ToProto() + { + var result = new Proto.QueryResult(); + if (Matches != null && Matches.Any()) + { + result.Matches.AddRange(Matches.Select(elem => elem.ToProto())); + } + if (Namespace != null) + { + result.Namespace = Namespace ?? ""; + } + return result; + } + + /// + /// Returns a new QueryResult type from its Protobuf-equivalent representation. + /// + internal static QueryResult FromProto(Proto.QueryResult value) + { + return new QueryResult + { + Matches = value.Matches?.Select(ScoredColumn.FromProto), + Namespace = value.Namespace, + }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/ScoredColumn.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/ScoredColumn.cs new file mode 100644 index 00000000000..5ca5fa6e2f6 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/ScoredColumn.cs @@ -0,0 +1,72 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record ScoredColumn +{ + [JsonPropertyName("id")] + public required string Id { get; set; } + + [JsonPropertyName("score")] + public float? Score { get; set; } + + [JsonPropertyName("values")] + public IEnumerable? Values { get; set; } + + [JsonPropertyName("metadata")] + public Metadata? Metadata { get; set; } + + [JsonPropertyName("indexedData")] + public IndexedData? IndexedData { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the ScoredColumn type into its Protobuf-equivalent representation. + /// + internal Proto.ScoredColumn ToProto() + { + var result = new Proto.ScoredColumn(); + result.Id = Id; + if (Score != null) + { + result.Score = Score ?? 0.0f; + } + if (Values != null && Values.Any()) + { + result.Values.AddRange(Values); + } + if (Metadata != null) + { + result.Metadata = Metadata.ToProto(); + } + if (IndexedData != null) + { + result.IndexedData = IndexedData.ToProto(); + } + return result; + } + + /// + /// Returns a new ScoredColumn type from its Protobuf-equivalent representation. + /// + internal static ScoredColumn FromProto(Proto.ScoredColumn value) + { + return new ScoredColumn + { + Id = value.Id, + Score = value.Score, + Values = value.Values?.ToList(), + Metadata = value.Metadata != null ? Metadata.FromProto(value.Metadata) : null, + IndexedData = + value.IndexedData != null ? IndexedData.FromProto(value.IndexedData) : null, + }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/UpdateResponse.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/UpdateResponse.cs new file mode 100644 index 00000000000..0d7e3f61b18 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/UpdateResponse.cs @@ -0,0 +1,30 @@ +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record UpdateResponse +{ + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the UpdateResponse type into its Protobuf-equivalent representation. + /// + internal Proto.UpdateResponse ToProto() + { + return new Proto.UpdateResponse(); + } + + /// + /// Returns a new UpdateResponse type from its Protobuf-equivalent representation. + /// + internal static UpdateResponse FromProto(Proto.UpdateResponse value) + { + return new UpdateResponse(); + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/UploadResponse.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/UploadResponse.cs new file mode 100644 index 00000000000..23286a04145 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/UploadResponse.cs @@ -0,0 +1,39 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record UploadResponse +{ + [JsonPropertyName("count")] + public uint? Count { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the UploadResponse type into its Protobuf-equivalent representation. + /// + internal Proto.UploadResponse ToProto() + { + var result = new Proto.UploadResponse(); + if (Count != null) + { + result.Count = Count ?? 0; + } + return result; + } + + /// + /// Returns a new UploadResponse type from its Protobuf-equivalent representation. + /// + internal static UploadResponse FromProto(Proto.UploadResponse value) + { + return new UploadResponse { Count = value.Count }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Usage.cs b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Usage.cs new file mode 100644 index 00000000000..e5921bf2191 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto-exhaustive/src/SeedApi/Types/Usage.cs @@ -0,0 +1,39 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = Data.V1.Grpc; + +#nullable enable + +namespace SeedApi; + +public record Usage +{ + [JsonPropertyName("units")] + public uint? Units { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the Usage type into its Protobuf-equivalent representation. + /// + internal Proto.Usage ToProto() + { + var result = new Proto.Usage(); + if (Units != null) + { + result.Units = Units ?? 0; + } + return result; + } + + /// + /// Returns a new Usage type from its Protobuf-equivalent representation. + /// + internal static Usage FromProto(Proto.Usage value) + { + return new Usage { Units = value.Units }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/.github/workflows/ci.yml b/seed/csharp-sdk/csharp-grpc-proto/.github/workflows/ci.yml new file mode 100644 index 00000000000..bc4fa1a98cb --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/.github/workflows/ci.yml @@ -0,0 +1,69 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - uses: actions/checkout@master + + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 8.x + + - name: Install tools + run: | + dotnet tool restore + + - name: Build Release + run: dotnet build src -c Release /p:ContinuousIntegrationBuild=true + + unit-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - uses: actions/checkout@master + + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 8.x + + - name: Install tools + run: | + dotnet tool restore + + - name: Run Tests + run: | + dotnet test src + + + publish: + needs: [compile] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 8.x + + - name: Publish + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_TOKEN }} + run: | + dotnet pack src -c Release + dotnet nuget push src/SeedApi/bin/Release/*.nupkg --api-key $NUGET_API_KEY --source "nuget.org" diff --git a/seed/csharp-sdk/csharp-grpc-proto/.gitignore b/seed/csharp-sdk/csharp-grpc-proto/.gitignore new file mode 100644 index 00000000000..11014f2b33d --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/.gitignore @@ -0,0 +1,484 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +## This is based on `dotnet new gitignore` and customized by Fern + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +# [Rr]elease/ (Ignored by Fern) +# [Rr]eleases/ (Ignored by Fern) +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +# [Ll]og/ (Ignored by Fern) +# [Ll]ogs/ (Ignored by Fern) + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/seed/csharp-sdk/csharp-grpc-proto/.mock/fern.config.json b/seed/csharp-sdk/csharp-grpc-proto/.mock/fern.config.json new file mode 100644 index 00000000000..4c8e54ac313 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/.mock/fern.config.json @@ -0,0 +1 @@ +{"organization": "fern-test", "version": "*"} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto/.mock/generators.yml b/seed/csharp-sdk/csharp-grpc-proto/.mock/generators.yml new file mode 100644 index 00000000000..d6b509d39ea --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/.mock/generators.yml @@ -0,0 +1,6 @@ +api: + - proto: + root: proto + target: proto/user/v1/user.proto + overrides: overrides.yml + local-generation: true \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto/.mock/overrides.yml b/seed/csharp-sdk/csharp-grpc-proto/.mock/overrides.yml new file mode 100644 index 00000000000..bb6db99b0e4 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/.mock/overrides.yml @@ -0,0 +1,31 @@ +components: + schemas: + UserModel: + properties: + metadata: + $ref: '#/components/schemas/Metadata' + + CreateRequest: + properties: + metadata: + $ref: '#/components/schemas/Metadata' + + Metadata: + oneOf: + - type: object + additionalProperties: + $ref: '#/components/schemas/MetadataValue' + - type: object + x-fern-encoding: + proto: + type: google.protobuf.Struct + + MetadataValue: + oneOf: + - type: number + format: double + - type: string + - type: boolean + x-fern-encoding: + proto: + type: google.protobuf.Value diff --git a/seed/csharp-sdk/csharp-grpc-proto/.mock/proto/google/api/annotations.proto b/seed/csharp-sdk/csharp-grpc-proto/.mock/proto/google/api/annotations.proto new file mode 100644 index 00000000000..8ff42098404 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/.mock/proto/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright 2015 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto/.mock/proto/google/api/http.proto b/seed/csharp-sdk/csharp-grpc-proto/.mock/proto/google/api/http.proto new file mode 100644 index 00000000000..c8392381eb9 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/.mock/proto/google/api/http.proto @@ -0,0 +1,379 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They +// are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL +// query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP +// request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto/.mock/proto/user/v1/user.proto b/seed/csharp-sdk/csharp-grpc-proto/.mock/proto/user/v1/user.proto new file mode 100644 index 00000000000..28542ac965a --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/.mock/proto/user/v1/user.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +package user.v1; + +import "google/api/annotations.proto"; +import "google/protobuf/struct.proto"; + +option csharp_namespace = "User.V1"; +option go_package = "user/v1"; + +message UserModel { + string username = 1; + string email = 2; + uint32 age = 3; + float weight = 4; + google.protobuf.Struct metadata = 5; +} + +message CreateRequest { + string username = 1; + string email = 2; + uint32 age = 3; + float weight = 4; + google.protobuf.Struct metadata = 5; +} + +message CreateResponse { + UserModel user = 1; +} + +service UserService { + rpc Create(CreateRequest) returns (CreateResponse) { + option (google.api.http) = { + post: "/users" + body: "*" + }; + } +} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto/README.md b/seed/csharp-sdk/csharp-grpc-proto/README.md new file mode 100644 index 00000000000..fd73574bd96 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/README.md @@ -0,0 +1,87 @@ +# Seed C# Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FC%23) +[![nuget shield](https://img.shields.io/nuget/v/SeedApi)](https://nuget.org/packages/SeedApi) + +The Seed C# library provides convenient access to the Seed API from C#. + +## Installation + +```sh +nuget install SeedApi +``` + +## Usage + +Instantiate and use the client with the following: + +```csharp +using SeedApi; + +var client = new SeedApiClient(); +await client.Userservice.CreateAsync(new CreateRequest()); +``` + +## Exception Handling + +When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error +will be thrown. + +```csharp +using SeedApi; + +try { + var response = await client.Userservice.CreateAsync(...); +} catch (SeedApiApiException e) { + System.Console.WriteLine(e.Body); + System.Console.WriteLine(e.StatusCode); +} +``` + +## Advanced + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retriable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retriable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +Use the `MaxRetries` request option to configure this behavior. + +```csharp +var response = await client.Userservice.CreateAsync( + ..., + new RequestOptions { + MaxRetries: 0 // Override MaxRetries at the request level + } +); +``` + +### Timeouts + +The SDK defaults to a 30 second timeout. Use the `Timeout` option to configure this behavior. + +```csharp +var response = await client.Userservice.CreateAsync( + ..., + new RequestOptions { + Timeout: TimeSpan.FromSeconds(3) // Override timeout to 3s + } +); +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto/proto/google/api/annotations.proto b/seed/csharp-sdk/csharp-grpc-proto/proto/google/api/annotations.proto new file mode 100644 index 00000000000..8ff42098404 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/proto/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright 2015 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto/proto/google/api/http.proto b/seed/csharp-sdk/csharp-grpc-proto/proto/google/api/http.proto new file mode 100644 index 00000000000..c8392381eb9 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/proto/google/api/http.proto @@ -0,0 +1,379 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They +// are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL +// query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP +// request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto/proto/user/v1/user.proto b/seed/csharp-sdk/csharp-grpc-proto/proto/user/v1/user.proto new file mode 100644 index 00000000000..28542ac965a --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/proto/user/v1/user.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +package user.v1; + +import "google/api/annotations.proto"; +import "google/protobuf/struct.proto"; + +option csharp_namespace = "User.V1"; +option go_package = "user/v1"; + +message UserModel { + string username = 1; + string email = 2; + uint32 age = 3; + float weight = 4; + google.protobuf.Struct metadata = 5; +} + +message CreateRequest { + string username = 1; + string email = 2; + uint32 age = 3; + float weight = 4; + google.protobuf.Struct metadata = 5; +} + +message CreateResponse { + UserModel user = 1; +} + +service UserService { + rpc Create(CreateRequest) returns (CreateResponse) { + option (google.api.http) = { + post: "/users" + body: "*" + }; + } +} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto/reference.md b/seed/csharp-sdk/csharp-grpc-proto/reference.md new file mode 100644 index 00000000000..1614ed804be --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/reference.md @@ -0,0 +1,41 @@ +# Reference +## UserService +
client.Userservice.CreateAsync(CreateRequest { ... }) -> CreateResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Userservice.CreateAsync(new CreateRequest()); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `CreateRequest` + +
+
+
+
+ + +
+
+
diff --git a/seed/csharp-sdk/csharp-grpc-proto/snippet-templates.json b/seed/csharp-sdk/csharp-grpc-proto/snippet-templates.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/csharp-sdk/csharp-grpc-proto/snippet.json b/seed/csharp-sdk/csharp-grpc-proto/snippet.json new file mode 100644 index 00000000000..bdaa9e73c20 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/snippet.json @@ -0,0 +1,17 @@ +{ + "types": {}, + "endpoints": [ + { + "example_identifier": null, + "id": { + "path": "/users", + "method": "POST", + "identifier_override": "endpoint_userservice.create" + }, + "snippet": { + "type": "typescript", + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Userservice.CreateAsync(new CreateRequest());\n" + } + } + ] +} \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/Core/EnumSerializerTests.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/Core/EnumSerializerTests.cs new file mode 100644 index 00000000000..532d182486b --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/Core/EnumSerializerTests.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; +using NUnit.Framework; +using SeedApi.Core; + +namespace SeedApi.Test.Core +{ + [TestFixture] + public class StringEnumSerializerTests + { + private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; + + private const DummyEnum KnownEnumValue2 = DummyEnum.KnownValue2; + private const string KnownEnumValue2String = "known_value2"; + + private static readonly string JsonWithKnownEnum2 = $$""" + { + "enum_property": "{{KnownEnumValue2String}}" + } + """; + + [Test] + public void ShouldParseKnownEnumValue2() + { + var obj = JsonSerializer.Deserialize(JsonWithKnownEnum2, JsonOptions); + Assert.That(obj, Is.Not.Null); + Assert.That(obj.EnumProperty, Is.EqualTo(KnownEnumValue2)); + } + + [Test] + public void ShouldSerializeKnownEnumValue2() + { + var json = JsonSerializer.SerializeToElement( + new DummyObject { EnumProperty = KnownEnumValue2 }, + JsonOptions + ); + TestContext.WriteLine("Serialized JSON: \n" + json); + var enumString = json.GetProperty("enum_property").GetString(); + Assert.That(enumString, Is.Not.Null); + Assert.That(enumString, Is.EqualTo(KnownEnumValue2String)); + } + } + + public class DummyObject + { + [JsonPropertyName("enum_property")] + public DummyEnum EnumProperty { get; set; } + } + + [JsonConverter(typeof(EnumSerializer))] + public enum DummyEnum + { + [EnumMember(Value = "known_value1")] + KnownValue1, + + [EnumMember(Value = "known_value2")] + KnownValue2, + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/Core/RawClientTests.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/Core/RawClientTests.cs new file mode 100644 index 00000000000..df141253267 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/Core/RawClientTests.cs @@ -0,0 +1,113 @@ +using System; +using System.Net.Http; +using FluentAssertions; +using NUnit.Framework; +using SeedApi.Core; +using WireMock.Server; +using SystemTask = System.Threading.Tasks.Task; +using WireMockRequest = WireMock.RequestBuilders.Request; +using WireMockResponse = WireMock.ResponseBuilders.Response; + +namespace SeedApi.Test.Core +{ + [TestFixture] + public class RawClientTests + { + private WireMockServer _server; + private HttpClient _httpClient; + private RawClient _rawClient; + private string _baseUrl; + private const int _maxRetries = 3; + + [SetUp] + public void SetUp() + { + _server = WireMockServer.Start(); + _baseUrl = _server.Url ?? ""; + _httpClient = new HttpClient { BaseAddress = new Uri(_baseUrl) }; + _rawClient = new RawClient( + new ClientOptions() { HttpClient = _httpClient, MaxRetries = _maxRetries } + ); + } + + [Test] + [TestCase(408)] + [TestCase(429)] + [TestCase(500)] + [TestCase(504)] + public async SystemTask MakeRequestAsync_ShouldRetry_OnRetryableStatusCodes(int statusCode) + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WillSetStateTo("Server Error") + .RespondWith(WireMockResponse.Create().WithStatusCode(statusCode)); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WhenStateIs("Server Error") + .WillSetStateTo("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(statusCode)); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WhenStateIs("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new RawClient.BaseApiRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + }; + + var response = await _rawClient.MakeRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(_maxRetries)); + } + + [Test] + [TestCase(400)] + [TestCase(409)] + public async SystemTask MakeRequestAsync_ShouldRetry_OnNonRetryableStatusCodes( + int statusCode + ) + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WillSetStateTo("Server Error") + .RespondWith( + WireMockResponse.Create().WithStatusCode(statusCode).WithBody("Failure") + ); + + var request = new RawClient.BaseApiRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + }; + + var response = await _rawClient.MakeRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(statusCode)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Failure")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + } + + [TearDown] + public void TearDown() + { + _server.Dispose(); + _httpClient.Dispose(); + } + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/SeedApi.Test.csproj b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/SeedApi.Test.csproj new file mode 100644 index 00000000000..c5be29f92d9 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/SeedApi.Test.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/TestClient.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/TestClient.cs new file mode 100644 index 00000000000..f1550b51fff --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi.Test/TestClient.cs @@ -0,0 +1,8 @@ +using NUnit.Framework; + +#nullable enable + +namespace SeedApi.Test; + +[TestFixture] +public class TestClient { } diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/CollectionItemSerializer.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/CollectionItemSerializer.cs new file mode 100644 index 00000000000..af2c9adf7a7 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/CollectionItemSerializer.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedApi.Core; + +/// +/// Json collection converter. +/// +/// Type of item to convert. +/// Converter to use for individual items. +internal class CollectionItemSerializer + : JsonConverter> + where TConverterType : JsonConverter +{ + /// + /// Reads a json string and deserializes it into an object. + /// + /// Json reader. + /// Type to convert. + /// Serializer options. + /// Created object. + public override IEnumerable? Read( + ref Utf8JsonReader reader, + System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType == JsonTokenType.Null) + { + return default; + } + + var jsonSerializerOptions = new JsonSerializerOptions(options); + jsonSerializerOptions.Converters.Clear(); + jsonSerializerOptions.Converters.Add(Activator.CreateInstance()); + + var returnValue = new List(); + + while (reader.TokenType != JsonTokenType.EndArray) + { + if (reader.TokenType != JsonTokenType.StartArray) + { + var item = (TDatatype)( + JsonSerializer.Deserialize(ref reader, typeof(TDatatype), jsonSerializerOptions) + ?? throw new Exception( + $"Failed to deserialize collection item of type {typeof(TDatatype)}" + ) + ); + returnValue.Add(item); + } + + reader.Read(); + } + + return returnValue; + } + + /// + /// Writes a json string. + /// + /// Json writer. + /// Value to write. + /// Serializer options. + public override void Write( + Utf8JsonWriter writer, + IEnumerable? value, + JsonSerializerOptions options + ) + { + if (value == null) + { + writer.WriteNullValue(); + return; + } + + JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions(options); + jsonSerializerOptions.Converters.Clear(); + jsonSerializerOptions.Converters.Add(Activator.CreateInstance()); + + writer.WriteStartArray(); + + foreach (var data in value) + { + JsonSerializer.Serialize(writer, data, jsonSerializerOptions); + } + + writer.WriteEndArray(); + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Constants.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Constants.cs new file mode 100644 index 00000000000..ccf4e963cc8 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Constants.cs @@ -0,0 +1,7 @@ +namespace SeedApi.Core; + +internal static class Constants +{ + public const string DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffK"; + public const string DateFormat = "yyyy-MM-dd"; +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/DateTimeSerializer.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/DateTimeSerializer.cs new file mode 100644 index 00000000000..a39de9c28d7 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/DateTimeSerializer.cs @@ -0,0 +1,22 @@ +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedApi.Core; + +internal class DateTimeSerializer : JsonConverter +{ + public override DateTime Read( + ref Utf8JsonReader reader, + System.Type typeToConvert, + JsonSerializerOptions options + ) + { + return DateTime.Parse(reader.GetString()!, null, DateTimeStyles.RoundtripKind); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString(Constants.DateTimeFormat)); + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/EnumSerializer.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/EnumSerializer.cs new file mode 100644 index 00000000000..ac5c0792fbe --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/EnumSerializer.cs @@ -0,0 +1,53 @@ +using System.Runtime.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedApi.Core; + +internal class EnumSerializer : JsonConverter + where TEnum : struct, System.Enum +{ + private readonly Dictionary _enumToString = new(); + private readonly Dictionary _stringToEnum = new(); + + public EnumSerializer() + { + var type = typeof(TEnum); + var values = Enum.GetValues(type); + + foreach (var value in values) + { + var enumValue = (TEnum)value; + var enumMember = type.GetMember(enumValue.ToString())[0]; + var attr = enumMember + .GetCustomAttributes(typeof(EnumMemberAttribute), false) + .Cast() + .FirstOrDefault(); + + var stringValue = + attr?.Value + ?? value.ToString() + ?? throw new Exception("Unexpected null enum toString value"); + + _enumToString.Add(enumValue, stringValue); + _stringToEnum.Add(stringValue, enumValue); + } + } + + public override TEnum Read( + ref Utf8JsonReader reader, + System.Type typeToConvert, + JsonSerializerOptions options + ) + { + var stringValue = + reader.GetString() + ?? throw new Exception("The JSON value could not be read as a string."); + return _stringToEnum.TryGetValue(stringValue, out var enumValue) ? enumValue : default; + } + + public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) + { + writer.WriteStringValue(_enumToString[value]); + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Extensions.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Extensions.cs new file mode 100644 index 00000000000..2e70f2719a2 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Extensions.cs @@ -0,0 +1,14 @@ +using System.Runtime.Serialization; + +namespace SeedApi.Core; + +internal static class Extensions +{ + public static string Stringify(this Enum value) + { + var field = value.GetType().GetField(value.ToString()); + var attribute = (EnumMemberAttribute) + Attribute.GetCustomAttribute(field, typeof(EnumMemberAttribute)); + return attribute?.Value ?? value.ToString(); + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/HeaderValue.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/HeaderValue.cs new file mode 100644 index 00000000000..30df1c51646 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/HeaderValue.cs @@ -0,0 +1,17 @@ +using OneOf; + +namespace SeedApi.Core; + +internal sealed class HeaderValue(OneOf> value) + : OneOfBase>(value) +{ + public static implicit operator HeaderValue(string value) + { + return new HeaderValue(value); + } + + public static implicit operator HeaderValue(Func value) + { + return new HeaderValue(value); + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Headers.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Headers.cs new file mode 100644 index 00000000000..24bf3179299 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Headers.cs @@ -0,0 +1,17 @@ +namespace SeedApi.Core; + +internal sealed class Headers : Dictionary +{ + public Headers() { } + + public Headers(Dictionary value) + { + foreach (var kvp in value) + { + this[kvp.Key] = new HeaderValue(kvp.Value); + } + } + + public Headers(IEnumerable> value) + : base(value.ToDictionary(e => e.Key, e => e.Value)) { } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/HttpMethodExtensions.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/HttpMethodExtensions.cs new file mode 100644 index 00000000000..130464dace1 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/HttpMethodExtensions.cs @@ -0,0 +1,8 @@ +using System.Net.Http; + +namespace SeedApi.Core; + +internal static class HttpMethodExtensions +{ + public static readonly HttpMethod Patch = new("PATCH"); +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/JsonConfiguration.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/JsonConfiguration.cs new file mode 100644 index 00000000000..13a05f5111f --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/JsonConfiguration.cs @@ -0,0 +1,32 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedApi.Core; + +internal static class JsonOptions +{ + public static readonly JsonSerializerOptions JsonSerializerOptions; + + static JsonOptions() + { + JsonSerializerOptions = new JsonSerializerOptions + { + Converters = { new DateTimeSerializer(), new OneOfSerializer() }, + WriteIndented = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + }; + } +} + +internal static class JsonUtils +{ + public static string Serialize(T obj) + { + return JsonSerializer.Serialize(obj, JsonOptions.JsonSerializerOptions); + } + + public static T Deserialize(string json) + { + return JsonSerializer.Deserialize(json, JsonOptions.JsonSerializerOptions)!; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/OneOfSerializer.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/OneOfSerializer.cs new file mode 100644 index 00000000000..24ee9268e48 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/OneOfSerializer.cs @@ -0,0 +1,69 @@ +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +using OneOf; + +namespace SeedApi.Core; + +internal class OneOfSerializer : JsonConverter +{ + public override IOneOf? Read( + ref Utf8JsonReader reader, + System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType is JsonTokenType.Null) + return default; + + foreach (var (type, cast) in GetOneOfTypes(typeToConvert)) + { + try + { + var readerCopy = reader; + var result = JsonSerializer.Deserialize(ref readerCopy, type, options); + reader.Skip(); + return (IOneOf)cast.Invoke(null, [result])!; + } + catch (JsonException) { } + } + + throw new JsonException( + $"Cannot deserialize into one of the supported types for {typeToConvert}" + ); + } + + public override void Write(Utf8JsonWriter writer, IOneOf value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Value, options); + } + + private static (System.Type type, MethodInfo cast)[] GetOneOfTypes(System.Type typeToConvert) + { + var casts = typeToConvert + .GetRuntimeMethods() + .Where(m => m.IsSpecialName && m.Name == "op_Implicit") + .ToArray(); + var type = typeToConvert; + while (type != null) + { + if ( + type.IsGenericType + && (type.Name.StartsWith("OneOf`") || type.Name.StartsWith("OneOfBase`")) + ) + { + return type.GetGenericArguments() + .Select(t => (t, casts.First(c => c.GetParameters()[0].ParameterType == t))) + .ToArray(); + } + + type = type.BaseType; + } + throw new InvalidOperationException($"{type} isn't OneOf or OneOfBase"); + } + + public override bool CanConvert(System.Type typeToConvert) + { + return typeof(IOneOf).IsAssignableFrom(typeToConvert); + } +} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Public/ClientOptions.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/ClientOptions.cs similarity index 59% rename from seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Public/ClientOptions.cs rename to seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/ClientOptions.cs index f98a5df2696..e4f22c22965 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/Core/Public/ClientOptions.cs +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/ClientOptions.cs @@ -1,6 +1,7 @@ using System; using System.Net.Http; using Grpc.Net.Client; +using SeedApi.Core; #nullable enable @@ -32,4 +33,25 @@ public partial class ClientOptions /// The options used for gRPC client endpoints. /// public GrpcChannelOptions? GrpcOptions { get; init; } + + /// + /// The http headers sent with the request. + /// + internal Headers Headers { get; init; } = new(); + + /// + /// Clones this and returns a new instance + /// + internal ClientOptions Clone() + { + return new ClientOptions + { + BaseUrl = BaseUrl, + HttpClient = HttpClient, + MaxRetries = MaxRetries, + Timeout = Timeout, + Headers = new Headers(new Dictionary(Headers)), + }; + ; + } } diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/GrpcRequestOptions.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/GrpcRequestOptions.cs new file mode 100644 index 00000000000..48c37927ea1 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/GrpcRequestOptions.cs @@ -0,0 +1,36 @@ +using System; +using System.Net.Http; +using Grpc.Core; +using SeedApi.Core; + +namespace SeedApi; + +#nullable enable + +public partial class GrpcRequestOptions +{ + /// + /// The maximum number of retry attempts. + /// + public int? MaxRetries { get; init; } + + /// + /// The timeout for the request. + /// + public TimeSpan? Timeout { get; init; } + + /// + /// Options for write operations. + /// + public WriteOptions? WriteOptions { get; init; } + + /// + /// Client-side call credentials. Provide authorization with per-call granularity. + /// + public CallCredentials? CallCredentials { get; init; } + + /// + /// Headers to be sent with this particular request. + /// + internal Headers Headers { get; init; } = new(); +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/RequestOptions.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/RequestOptions.cs new file mode 100644 index 00000000000..6e0923d6f60 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/RequestOptions.cs @@ -0,0 +1,35 @@ +using System; +using System.Net.Http; +using SeedApi.Core; + +#nullable enable + +namespace SeedApi; + +public partial class RequestOptions +{ + /// + /// The Base URL for the API. + /// + public string? BaseUrl { get; init; } + + /// + /// The http client used to make requests. + /// + public HttpClient? HttpClient { get; init; } + + /// + /// The http client used to make requests. + /// + public int? MaxRetries { get; init; } + + /// + /// The timeout for the request. + /// + public TimeSpan? Timeout { get; init; } + + /// + /// The http headers sent with the request. + /// + internal Headers Headers { get; init; } = new(); +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/SeedApiApiException.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/SeedApiApiException.cs new file mode 100644 index 00000000000..8c81259a788 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/SeedApiApiException.cs @@ -0,0 +1,18 @@ +namespace SeedApi; + +/// +/// This exception type will be thrown for any non-2XX API responses. +/// +public class SeedApiApiException(string message, int statusCode, object body) + : SeedApiException(message) +{ + /// + /// The error code of the response that triggered the exception. + /// + public int StatusCode => statusCode; + + /// + /// The body of the response that triggered the exception. + /// + public object Body => body; +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/SeedApiException.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/SeedApiException.cs new file mode 100644 index 00000000000..14d9b8d9ddd --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/SeedApiException.cs @@ -0,0 +1,11 @@ +using System; + +#nullable enable + +namespace SeedApi; + +/// +/// Base exception class for all exceptions thrown by the SDK. +/// +public class SeedApiException(string message, Exception? innerException = null) + : Exception(message, innerException) { } diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/Version.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/Version.cs new file mode 100644 index 00000000000..f430a1bf84c --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/Public/Version.cs @@ -0,0 +1,6 @@ +namespace SeedApi; + +internal class Version +{ + public const string Current = "0.0.1"; +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/RawClient.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/RawClient.cs new file mode 100644 index 00000000000..0cc389e831a --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/RawClient.cs @@ -0,0 +1,192 @@ +using System.Net.Http; +using System.Text; +using System.Threading; + +namespace SeedApi.Core; + +#nullable enable + +/// +/// Utility class for making raw HTTP requests to the API. +/// +internal class RawClient(ClientOptions clientOptions) +{ + private const int InitialRetryDelayMs = 1000; + private const int MaxRetryDelayMs = 60000; + + private readonly Lazy _grpc = new(() => new RawGrpcClient(clientOptions)); + + /// + /// The gRPC client used to make requests. + /// + public RawGrpcClient Grpc => _grpc.Value; + + /// + /// The client options applied on every request. + /// + public readonly ClientOptions Options = clientOptions; + + public async Task MakeRequestAsync( + BaseApiRequest request, + CancellationToken cancellationToken = default + ) + { + // Apply the request timeout. + var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + var timeout = request.Options?.Timeout ?? Options.Timeout; + cts.CancelAfter(timeout); + + // Send the request. + return await SendWithRetriesAsync(request, cts.Token); + } + + public record BaseApiRequest + { + public required string BaseUrl { get; init; } + + public required HttpMethod Method { get; init; } + + public required string Path { get; init; } + + public string? ContentType { get; init; } + + public Dictionary Query { get; init; } = new(); + + public Headers Headers { get; init; } = new(); + + public RequestOptions? Options { get; init; } + } + + /// + /// The request object to be sent for streaming uploads. + /// + public record StreamApiRequest : BaseApiRequest + { + public Stream? Body { get; init; } + } + + /// + /// The request object to be sent for JSON APIs. + /// + public record JsonApiRequest : BaseApiRequest + { + public object? Body { get; init; } + } + + /// + /// The response object returned from the API. + /// + public record ApiResponse + { + public required int StatusCode { get; init; } + + public required HttpResponseMessage Raw { get; init; } + } + + private async Task SendWithRetriesAsync( + BaseApiRequest request, + CancellationToken cancellationToken + ) + { + var httpClient = request.Options?.HttpClient ?? Options.HttpClient; + var maxRetries = request.Options?.MaxRetries ?? Options.MaxRetries; + var response = await httpClient.SendAsync(BuildHttpRequest(request), cancellationToken); + for (var i = 0; i < maxRetries; i++) + { + if (!ShouldRetry(response)) + { + break; + } + var delayMs = Math.Min(InitialRetryDelayMs * (int)Math.Pow(2, i), MaxRetryDelayMs); + await System.Threading.Tasks.Task.Delay(delayMs, cancellationToken); + response = await httpClient.SendAsync(BuildHttpRequest(request), cancellationToken); + } + return new ApiResponse { StatusCode = (int)response.StatusCode, Raw = response }; + } + + private static bool ShouldRetry(HttpResponseMessage response) + { + var statusCode = (int)response.StatusCode; + return statusCode is 408 or 429 or >= 500; + } + + private HttpRequestMessage BuildHttpRequest(BaseApiRequest request) + { + var url = BuildUrl(request); + var httpRequest = new HttpRequestMessage(request.Method, url); + switch (request) + { + // Add the request body to the request. + case JsonApiRequest jsonRequest: + { + if (jsonRequest.Body != null) + { + httpRequest.Content = new StringContent( + JsonUtils.Serialize(jsonRequest.Body), + Encoding.UTF8, + "application/json" + ); + } + break; + } + case StreamApiRequest { Body: not null } streamRequest: + httpRequest.Content = new StreamContent(streamRequest.Body); + break; + } + if (request.ContentType != null) + { + request.Headers.Add("Content-Type", request.ContentType); + } + SetHeaders(httpRequest, Options.Headers); + SetHeaders(httpRequest, request.Headers); + SetHeaders(httpRequest, request.Options?.Headers ?? new Headers()); + return httpRequest; + } + + private static string BuildUrl(BaseApiRequest request) + { + var baseUrl = request.Options?.BaseUrl ?? request.BaseUrl; + var trimmedBaseUrl = baseUrl.TrimEnd('/'); + var trimmedBasePath = request.Path.TrimStart('/'); + var url = $"{trimmedBaseUrl}/{trimmedBasePath}"; + if (request.Query.Count <= 0) + return url; + url += "?"; + url = request.Query.Aggregate( + url, + (current, queryItem) => + { + if (queryItem.Value is System.Collections.IEnumerable collection and not string) + { + var items = collection + .Cast() + .Select(value => $"{queryItem.Key}={value}") + .ToList(); + if (items.Any()) + { + current += string.Join("&", items) + "&"; + } + } + else + { + current += $"{queryItem.Key}={queryItem.Value}&"; + } + return current; + } + ); + url = url[..^1]; + return url; + } + + private static void SetHeaders(HttpRequestMessage httpRequest, Headers headers) + { + foreach (var header in headers) + { + var value = header.Value?.Match(str => str, func => func.Invoke()); + if (value != null) + { + httpRequest.Headers.TryAddWithoutValidation(header.Key, value); + } + } + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/RawGrpcClient.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/RawGrpcClient.cs new file mode 100644 index 00000000000..657be1d946d --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Core/RawGrpcClient.cs @@ -0,0 +1,79 @@ +using System; +using Grpc.Core; +using Grpc.Net.Client; + +namespace SeedApi.Core; + +/// +/// Utility class for making gRPC requests to the API. +/// +internal class RawGrpcClient +{ + /// + /// The gRPC channel used to make requests. + /// + public readonly GrpcChannel Channel; + + private readonly ClientOptions _clientOptions; + + public RawGrpcClient(ClientOptions clientOptions) + { + _clientOptions = clientOptions; + + var grpcOptions = PrepareGrpcChannelOptions(); + Channel = + grpcOptions != null + ? GrpcChannel.ForAddress(_clientOptions.BaseUrl, grpcOptions) + : GrpcChannel.ForAddress(_clientOptions.BaseUrl); + } + + /// + /// Prepares the gRPC metadata associated with the given request. + /// The provided request headers take precedence over the headers + /// associated with this client (which are sent on _every_ request). + /// + public CallOptions CreateCallOptions( + GrpcRequestOptions options, + CancellationToken cancellationToken = default + ) + { + var metadata = new global::Grpc.Core.Metadata(); + SetHeaders(metadata, _clientOptions.Headers); + SetHeaders(metadata, options.Headers); + + var timeout = options.Timeout ?? _clientOptions.Timeout; + var deadline = DateTime.UtcNow.Add(timeout); + return new CallOptions( + metadata, + deadline, + cancellationToken, + options.WriteOptions, + null, + options.CallCredentials + ); + } + + private void SetHeaders(global::Grpc.Core.Metadata metadata, Headers headers) + { + foreach (var header in headers) + { + var value = header.Value?.Match(str => str, func => func.Invoke()); + if (value != null) + { + metadata.Add(header.Key, value); + } + } + } + + private GrpcChannelOptions? PrepareGrpcChannelOptions() + { + var grpcChannelOptions = _clientOptions.GrpcOptions; + if (grpcChannelOptions == null) + { + return null; + } + grpcChannelOptions.HttpClient ??= _clientOptions.HttpClient; + grpcChannelOptions.MaxRetryAttempts ??= _clientOptions.MaxRetries; + return grpcChannelOptions; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/SeedApi.csproj b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/SeedApi.csproj new file mode 100644 index 00000000000..ea256910f2f --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/SeedApi.csproj @@ -0,0 +1,69 @@ + + + + + net462;net8.0;net7.0;net6.0;netstandard2.0 + enable + false + 12 + enable + 0.0.1 + README.md + https://github.com/csharp-grpc-proto/fern + + + + true + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + + + + + <_Parameter1>SeedApi.Test + + + + diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/SeedApiClient.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/SeedApiClient.cs new file mode 100644 index 00000000000..896fdd03f18 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/SeedApiClient.cs @@ -0,0 +1,35 @@ +using SeedApi.Core; + +#nullable enable + +namespace SeedApi; + +public partial class SeedApiClient +{ + private RawClient _client; + + public SeedApiClient(ClientOptions? clientOptions = null) + { + var defaultHeaders = new Headers( + new Dictionary() + { + { "X-Fern-Language", "C#" }, + { "X-Fern-SDK-Name", "SeedApi" }, + { "X-Fern-SDK-Version", Version.Current }, + { "User-Agent", "Ferncsharp-grpc-proto/0.0.1" }, + } + ); + clientOptions ??= new ClientOptions(); + foreach (var header in defaultHeaders) + { + if (!clientOptions.Headers.ContainsKey(header.Key)) + { + clientOptions.Headers[header.Key] = header.Value; + } + } + _client = new RawClient(clientOptions); + Userservice = new UserserviceClient(_client); + } + + public UserserviceClient Userservice { get; init; } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/CreateResponse.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/CreateResponse.cs new file mode 100644 index 00000000000..608e09ded5f --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/CreateResponse.cs @@ -0,0 +1,42 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = User.V1; + +#nullable enable + +namespace SeedApi; + +public record CreateResponse +{ + [JsonPropertyName("user")] + public UserModel? User { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the CreateResponse type into its Protobuf-equivalent representation. + /// + internal Proto.CreateResponse ToProto() + { + var result = new Proto.CreateResponse(); + if (User != null) + { + result.User = User.ToProto(); + } + return result; + } + + /// + /// Returns a new CreateResponse type from its Protobuf-equivalent representation. + /// + internal static CreateResponse FromProto(Proto.CreateResponse value) + { + return new CreateResponse + { + User = value.User != null ? UserModel.FromProto(value.User) : null, + }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/Metadata.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/Metadata.cs new file mode 100644 index 00000000000..2e16aa50b3f --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/Metadata.cs @@ -0,0 +1,39 @@ +using SeedApi.Core; +using Proto = Google.Protobuf.WellKnownTypes; + +#nullable enable + +namespace SeedApi; + +public sealed class Metadata : Dictionary +{ + public Metadata() { } + + public Metadata(IEnumerable> value) + : base(value.ToDictionary(e => e.Key, e => e.Value)) { } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + internal Proto.Struct ToProto() + { + var result = new Proto.Struct(); + foreach (var kvp in this) + { + result.Fields[kvp.Key] = kvp.Value?.ToProto(); + } + return result; + } + + internal static Metadata FromProto(Proto.Struct value) + { + var result = new Metadata(); + foreach (var kvp in value.Fields) + { + result[kvp.Key] = kvp.Value != null ? MetadataValue.FromProto(kvp.Value) : null; + } + return result; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/MetadataValue.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/MetadataValue.cs new file mode 100644 index 00000000000..2308676e891 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/MetadataValue.cs @@ -0,0 +1,91 @@ +using OneOf; +using SeedApi.Core; +using Proto = Google.Protobuf.WellKnownTypes; + +#nullable enable + +namespace SeedApi; + +public sealed class MetadataValue( + OneOf, Metadata> value +) : OneOfBase, Metadata>(value) +{ + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + internal Proto.Value ToProto() + { + return Match( + Proto.Value.ForString, + Proto.Value.ForNumber, + Proto.Value.ForBool, + list => new Proto.Value + { + ListValue = new Proto.ListValue + { + Values = { list.Select(item => item?.ToProto()) }, + }, + }, + nested => new Proto.Value { StructValue = nested.ToProto() } + ); + } + + internal static MetadataValue? FromProto(Proto.Value value) + { + return value.KindCase switch + { + Proto.Value.KindOneofCase.StringValue => value.StringValue, + Proto.Value.KindOneofCase.NumberValue => value.NumberValue, + Proto.Value.KindOneofCase.BoolValue => value.BoolValue, + Proto.Value.KindOneofCase.ListValue => value + .ListValue.Values.Select(FromProto) + .ToList(), + Proto.Value.KindOneofCase.StructValue => Metadata.FromProto(value.StructValue), + _ => null, + }; + } + + public static implicit operator MetadataValue(string value) => new(value); + + public static implicit operator MetadataValue(bool value) => new(value); + + public static implicit operator MetadataValue(double value) => new(value); + + public static implicit operator MetadataValue(Metadata value) => new(value); + + public static implicit operator MetadataValue(MetadataValue?[] value) => new(value); + + public static implicit operator MetadataValue(List value) => new(value); + + public static implicit operator MetadataValue(string[] value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(double[] value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(double?[] value) => + new(value.Select(v => v != null ? new MetadataValue(v.Value) : null).ToList()); + + public static implicit operator MetadataValue(bool[] value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(bool?[] value) => + new(value.Select(v => v != null ? new MetadataValue(v.Value) : null).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => v != null ? new MetadataValue(v.Value) : null).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => new MetadataValue(v)).ToList()); + + public static implicit operator MetadataValue(List value) => + new(value.Select(v => v != null ? new MetadataValue(v.Value) : null).ToList()); +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/UserModel.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/UserModel.cs new file mode 100644 index 00000000000..986ace0fbd1 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Types/UserModel.cs @@ -0,0 +1,74 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = User.V1; + +#nullable enable + +namespace SeedApi; + +public record UserModel +{ + [JsonPropertyName("username")] + public string? Username { get; set; } + + [JsonPropertyName("email")] + public string? Email { get; set; } + + [JsonPropertyName("age")] + public uint? Age { get; set; } + + [JsonPropertyName("weight")] + public float? Weight { get; set; } + + [JsonPropertyName("metadata")] + public Metadata? Metadata { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the UserModel type into its Protobuf-equivalent representation. + /// + internal Proto.UserModel ToProto() + { + var result = new Proto.UserModel(); + if (Username != null) + { + result.Username = Username ?? ""; + } + if (Email != null) + { + result.Email = Email ?? ""; + } + if (Age != null) + { + result.Age = Age ?? 0; + } + if (Weight != null) + { + result.Weight = Weight ?? 0.0f; + } + if (Metadata != null) + { + result.Metadata = Metadata.ToProto(); + } + return result; + } + + /// + /// Returns a new UserModel type from its Protobuf-equivalent representation. + /// + internal static UserModel FromProto(Proto.UserModel value) + { + return new UserModel + { + Username = value.Username, + Email = value.Email, + Age = value.Age, + Weight = value.Weight, + Metadata = value.Metadata != null ? Metadata.FromProto(value.Metadata) : null, + }; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Userservice/Requests/CreateRequest.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Userservice/Requests/CreateRequest.cs new file mode 100644 index 00000000000..f441a389685 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Userservice/Requests/CreateRequest.cs @@ -0,0 +1,59 @@ +using System.Text.Json.Serialization; +using SeedApi.Core; +using Proto = User.V1; + +#nullable enable + +namespace SeedApi; + +public record CreateRequest +{ + [JsonPropertyName("username")] + public string? Username { get; set; } + + [JsonPropertyName("email")] + public string? Email { get; set; } + + [JsonPropertyName("age")] + public uint? Age { get; set; } + + [JsonPropertyName("weight")] + public float? Weight { get; set; } + + [JsonPropertyName("metadata")] + public Metadata? Metadata { get; set; } + + public override string ToString() + { + return JsonUtils.Serialize(this); + } + + /// + /// Maps the CreateRequest type into its Protobuf-equivalent representation. + /// + internal Proto.CreateRequest ToProto() + { + var result = new Proto.CreateRequest(); + if (Username != null) + { + result.Username = Username ?? ""; + } + if (Email != null) + { + result.Email = Email ?? ""; + } + if (Age != null) + { + result.Age = Age ?? 0; + } + if (Weight != null) + { + result.Weight = Weight ?? 0.0f; + } + if (Metadata != null) + { + result.Metadata = Metadata.ToProto(); + } + return result; + } +} diff --git a/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Userservice/UserserviceClient.cs b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Userservice/UserserviceClient.cs new file mode 100644 index 00000000000..b742ef024a2 --- /dev/null +++ b/seed/csharp-sdk/csharp-grpc-proto/src/SeedApi/Userservice/UserserviceClient.cs @@ -0,0 +1,60 @@ +using System.Threading; +using Grpc.Core; +using SeedApi.Core; +using User.V1; + +#nullable enable + +namespace SeedApi; + +public partial class UserserviceClient +{ + private RawClient _client; + + private RawGrpcClient _grpc; + + private UserService.UserServiceClient _userService; + + internal UserserviceClient(RawClient client) + { + _client = client; + _grpc = _client.Grpc; + _userService = new UserService.UserServiceClient(_grpc.Channel); + } + + /// + /// + /// await client.Userservice.CreateAsync(new CreateRequest()); + /// + /// + public async Task CreateAsync( + CreateRequest request, + GrpcRequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + try + { + var callOptions = _grpc.CreateCallOptions( + options ?? new GrpcRequestOptions(), + cancellationToken + ); + var call = _userService.CreateAsync(request.ToProto(), callOptions); + var response = await call.ConfigureAwait(false); + return CreateResponse.FromProto(response); + } + catch (RpcException rpc) + { + var statusCode = (int)rpc.StatusCode; + throw new SeedApiApiException( + $"Error with gRPC status code {statusCode}", + statusCode, + rpc.Message + ); + } + catch (Exception e) + { + throw new SeedApiException("Error", e); + } + } +} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Dataservice/DataserviceClient.cs b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Dataservice/DataserviceClient.cs index fd876a03711..9bd2a610bf3 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Dataservice/DataserviceClient.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Dataservice/DataserviceClient.cs @@ -1,3 +1,5 @@ +using System.Net.Http; +using System.Text.Json; using System.Threading; using Data.V1.Grpc; using Grpc.Core; @@ -28,33 +30,38 @@ internal DataserviceClient(RawClient client) /// /// public async Task FooAsync( - GrpcRequestOptions? options = null, + RequestOptions? options = null, CancellationToken cancellationToken = default ) { - try + var response = await _client.MakeRequestAsync( + new RawClient.JsonApiRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Post, + Path = "foo", + Options = options, + }, + cancellationToken + ); + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + if (response.StatusCode is >= 200 and < 400) { - var callOptions = _grpc.CreateCallOptions( - options ?? new GrpcRequestOptions(), - cancellationToken - ); - var call = _dataService.FooAsync(null, callOptions); - var response = await call.ConfigureAwait(false); - return object.FromProto(response); - } - catch (RpcException rpc) - { - var statusCode = (int)rpc.StatusCode; - throw new SeedApiApiException( - $"Error with gRPC status code {statusCode}", - statusCode, - rpc.Message - ); - } - catch (Exception e) - { - throw new SeedApiException("Error", e); + try + { + return JsonUtils.Deserialize(responseBody)!; + } + catch (JsonException e) + { + throw new SeedApiException("Failed to deserialize response", e); + } } + + throw new SeedApiApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); } /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Dataservice/DataserviceClient.cs b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Dataservice/DataserviceClient.cs index fd876a03711..9bd2a610bf3 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Dataservice/DataserviceClient.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Dataservice/DataserviceClient.cs @@ -1,3 +1,5 @@ +using System.Net.Http; +using System.Text.Json; using System.Threading; using Data.V1.Grpc; using Grpc.Core; @@ -28,33 +30,38 @@ internal DataserviceClient(RawClient client) /// /// public async Task FooAsync( - GrpcRequestOptions? options = null, + RequestOptions? options = null, CancellationToken cancellationToken = default ) { - try + var response = await _client.MakeRequestAsync( + new RawClient.JsonApiRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Post, + Path = "foo", + Options = options, + }, + cancellationToken + ); + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + if (response.StatusCode is >= 200 and < 400) { - var callOptions = _grpc.CreateCallOptions( - options ?? new GrpcRequestOptions(), - cancellationToken - ); - var call = _dataService.FooAsync(null, callOptions); - var response = await call.ConfigureAwait(false); - return object.FromProto(response); - } - catch (RpcException rpc) - { - var statusCode = (int)rpc.StatusCode; - throw new SeedApiApiException( - $"Error with gRPC status code {statusCode}", - statusCode, - rpc.Message - ); - } - catch (Exception e) - { - throw new SeedApiException("Error", e); + try + { + return JsonUtils.Deserialize(responseBody)!; + } + catch (JsonException e) + { + throw new SeedApiException("Failed to deserialize response", e); + } } + + throw new SeedApiApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); } /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Dataservice/DataserviceClient.cs b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Dataservice/DataserviceClient.cs index 30a2cfcf383..9aa28678958 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Dataservice/DataserviceClient.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Dataservice/DataserviceClient.cs @@ -1,3 +1,5 @@ +using System.Net.Http; +using System.Text.Json; using System.Threading; using Data.V1.Grpc; using Grpc.Core; @@ -28,33 +30,38 @@ internal DataserviceClient(RawClient client) /// /// public async Task FooAsync( - GrpcRequestOptions? options = null, + RequestOptions? options = null, CancellationToken cancellationToken = default ) { - try + var response = await _client.MakeRequestAsync( + new RawClient.JsonApiRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Post, + Path = "foo", + Options = options, + }, + cancellationToken + ); + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + if (response.StatusCode is >= 200 and < 400) { - var callOptions = _grpc.CreateCallOptions( - options ?? new GrpcRequestOptions(), - cancellationToken - ); - var call = _dataService.FooAsync(null, callOptions); - var response = await call.ConfigureAwait(false); - return object.FromProto(response); - } - catch (RpcException rpc) - { - var statusCode = (int)rpc.StatusCode; - throw new SeedApiApiException( - $"Error with gRPC status code {statusCode}", - statusCode, - rpc.Message - ); - } - catch (Exception e) - { - throw new SeedApiException("Error", e); + try + { + return JsonUtils.Deserialize(responseBody)!; + } + catch (JsonException e) + { + throw new SeedApiException("Failed to deserialize response", e); + } } + + throw new SeedApiApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); } /// diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi.Test/Unit/MockServer/BaseMockServerTest.cs b/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi.Test/Unit/MockServer/BaseMockServerTest.cs deleted file mode 100644 index d60f783110a..00000000000 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi.Test/Unit/MockServer/BaseMockServerTest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using NUnit.Framework; -using SeedApi; -using WireMock.Logging; -using WireMock.Server; -using WireMock.Settings; - -#nullable enable - -namespace SeedApi.Test.Unit.MockServer; - -[SetUpFixture] -public class BaseMockServerTest -{ - protected static WireMockServer Server { get; set; } = null!; - - protected static SeedApiClient Client { get; set; } = null!; - - protected static RequestOptions RequestOptions { get; set; } = null!; - - [OneTimeSetUp] - public void GlobalSetup() - { - // Start the WireMock server - Server = WireMockServer.Start( - new WireMockServerSettings { Logger = new WireMockConsoleLogger() } - ); - - // Initialize the Client - Client = new SeedApiClient(); - - RequestOptions = new RequestOptions { BaseUrl = Server.Urls[0] }; - } - - [OneTimeTearDown] - public void GlobalTeardown() - { - Server.Stop(); - } -} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/SeedApiClient.cs b/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/SeedApiClient.cs deleted file mode 100644 index 7e4c7cbdf53..00000000000 --- a/seed/csharp-sdk/grpc-proto-exhaustive/src/SeedApi/SeedApiClient.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using SeedApi.Core; - -#nullable enable - -namespace SeedApi; - -public partial class SeedApiClient -{ - private RawClient _client; - - public SeedApiClient(ClientOptions? clientOptions = null) - { - _client = new RawClient( - new Dictionary() { { "X-Fern-Language", "C#" } }, - new Dictionary>(), - clientOptions ?? new ClientOptions() - ); - Dataservice = new DataserviceClient(_client); - } - - public DataserviceClient Dataservice { get; init; } -}