├── .apibuilder ├── .tracked_files └── config ├── .delta ├── .dockerignore ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .java-version ├── .travis.yml ├── CONTRIBUTING.md ├── DEVELOPER.md ├── Dockerfile ├── Jenkinsfile ├── LICENSE ├── README.md ├── android-generator └── src │ ├── main │ └── scala │ │ └── models │ │ └── generator │ │ └── android │ │ ├── AndroidClasses.scala │ │ ├── AndroidJavaUtil.scala │ │ ├── AndroidRxClasses.scala │ │ ├── ApiBuilderComments.scala │ │ ├── BaseAndroidCodeGenerator.scala │ │ └── RetrofitUtil.scala │ └── test │ └── scala │ └── models │ └── generator │ └── android │ ├── AndroidJavaUtilTest.scala │ ├── RetrofitUtilTest.scala │ └── TestAndroidClasses.scala ├── build.sbt ├── csharp-generator └── src │ ├── main │ └── scala │ │ └── generator │ │ └── csharp │ │ ├── CSharpGenerator.scala │ │ ├── Names.scala │ │ └── RecordBuilder.scala │ └── test │ └── scala │ └── generator │ └── csharp │ └── CSharpGeneratorSpec.scala ├── csv-generator └── src │ ├── main │ └── scala │ │ └── models │ │ └── generator │ │ └── csv │ │ └── CsvGenerator.scala │ └── test │ └── scala │ └── models │ └── generator │ └── csv │ └── CsvGeneratorTest.scala ├── deploy └── apibuilder-generator │ ├── Chart.yaml │ ├── requirements.yaml │ └── values.yaml ├── elm-generator └── src │ ├── main │ └── scala │ │ └── generator │ │ └── elm │ │ ├── ElmCode.scala │ │ ├── ElmCommon.scala │ │ ├── ElmEnum.scala │ │ ├── ElmGenerator.scala │ │ ├── ElmJson.scala │ │ ├── ElmModel.scala │ │ ├── ElmResource.scala │ │ ├── ElmTypeLookup.scala │ │ ├── ElmUnion.scala │ │ ├── Imports.scala │ │ ├── Names.scala │ │ ├── NamespaceParser.scala │ │ └── Util.scala │ └── test │ └── scala │ └── generator │ └── elm │ └── ElmGeneratorSpec.scala ├── examples ├── union-of-unions │ ├── .apibuilder │ │ ├── .tracked_files │ │ └── config │ ├── .gitignore │ ├── README.md │ ├── api │ │ └── test │ │ │ └── SerializationSpec.scala │ ├── apibuilder-union-of-unions.json │ ├── build.sbt │ ├── generated │ │ └── app │ │ │ └── ApicollectiveApibuilderUnionOfUnionsV0Client.scala │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ └── update.sh ├── union-types-discriminator │ ├── .apidoc │ ├── README.md │ ├── api.json │ ├── api │ │ ├── api.json │ │ ├── app │ │ │ └── controllers │ │ │ │ └── Users.scala │ │ ├── conf │ │ │ ├── application.conf │ │ │ └── routes │ │ ├── service.json │ │ └── test │ │ │ └── controllers │ │ │ └── UsersSpec.scala │ ├── build.sbt │ ├── generated │ │ └── app │ │ │ └── BryzekApidocExampleUnionTypesDiscriminatorV0Client.scala │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ ├── ruby-client-example.rb │ └── script │ │ └── update-generated-code └── union-types │ ├── .apidoc │ ├── README.md │ ├── api.json │ ├── api │ ├── app │ │ └── controllers │ │ │ └── Users.scala │ ├── conf │ │ ├── application.conf │ │ └── routes │ ├── service.json │ └── test │ │ └── controllers │ │ └── UsersSpec.scala │ ├── build.sbt │ ├── generated │ └── app │ │ └── BryzekApidocExampleUnionTypesV0Client.scala │ └── project │ ├── build.properties │ └── plugins.sbt ├── generated ├── app │ ├── ApicollectiveApibuilderCommonV0Models.scala │ ├── ApicollectiveApibuilderGeneratorV0Client.scala │ ├── ApicollectiveApibuilderPostmanCollectionV21V0Models.scala │ ├── ApicollectiveApibuilderSpecV0Client.scala │ └── ApicollectivePostmanGeneratorAttributesV0Models.scala └── test │ ├── ApicollectiveApibuilderCommonV0ModelsGens.scala │ ├── ApicollectiveApibuilderGeneratorV0ModelsGens.scala │ └── ApicollectiveApibuilderSpecV0ModelsGens.scala ├── generator ├── app │ ├── controllers │ │ ├── Generators.scala │ │ ├── Healthchecks.scala │ │ └── Invocations.scala │ └── lib │ │ ├── ErrorHandler.scala │ │ ├── Filters.scala │ │ ├── LoggingFilter.scala │ │ ├── ServiceApidocBug.scala │ │ └── Validation.scala └── conf │ ├── application.conf │ ├── application.production.conf │ ├── base.conf │ ├── logback.xml │ └── routes ├── go-generator └── src │ ├── main │ └── scala │ │ └── models │ │ ├── ApiBuilderComments.scala │ │ ├── Code.scala │ │ ├── Defaults.scala │ │ ├── Formatter.scala │ │ ├── GoClientGenerator.scala │ │ ├── GoType.scala │ │ ├── GoUtil.scala │ │ ├── Headers.scala │ │ ├── ImportBuilder.scala │ │ ├── ImportPath.scala │ │ ├── ResponseBuilder.scala │ │ └── UrlValues.scala │ └── test │ └── scala │ └── models │ ├── FormatterSpec.scala │ ├── GoReceiverDefaultsSpec.scala │ ├── GoUtilSpec.scala │ └── ImportBuilderSpec.scala ├── graphql-generator └── src │ ├── main │ └── scala │ │ └── generator │ │ └── graphql │ │ ├── GraphQLApolloGenerator.scala │ │ └── GraphQLSchemaGenerator.scala │ └── test │ └── scala │ └── generator │ └── graphql │ ├── GraphQLApolloGeneratorSpec.scala │ ├── GraphQLSchemaGeneratorSpec.scala │ └── helpers │ └── TestHelpers.scala ├── java-aws-lambda-pojos ├── README.md └── src │ ├── main │ └── scala │ │ └── models │ │ └── generator │ │ └── javaAwsLambdaPojos │ │ ├── ApiBuilderComments.scala │ │ ├── BaseJavaAwsLambdaPOJOCodeGenerator.scala │ │ ├── JavaAwsLambdaPOJOClasses.scala │ │ └── JavaAwsLambdaPOJOUtil.scala │ └── test │ └── scala │ └── models │ └── generator │ └── javaAwsLambdaPojos │ ├── AwsLambdaJavaPOJOClassesSpec.scala │ └── JavaAwsLambdaPOJOUtilTest.scala ├── java-generator └── src │ ├── main │ └── scala │ │ └── models │ │ └── generator │ │ ├── ApiBuilderComments.scala │ │ ├── JavaClasses.scala │ │ ├── JavaDatatype.scala │ │ └── JavaUtil.scala │ └── test │ └── scala │ └── models │ └── generator │ └── JavaClassesSpec.scala ├── kotlin-generator └── src │ ├── main │ └── scala │ │ └── models │ │ └── generator │ │ └── kotlin │ │ ├── KotlinGenerator.scala │ │ ├── KotlinUtil.scala │ │ └── RetrofitUtil.scala │ └── test │ └── scala │ └── models │ └── generator │ └── kotlin │ ├── KotlinGeneratorTest.scala │ ├── KotlinTestHelper.scala │ └── KotlinUtilTest.scala ├── lib └── src │ ├── main │ └── scala │ │ ├── AbstractApiBuilderComments.scala │ │ ├── Constants.scala │ │ ├── Datatype.scala │ │ ├── Methods.scala │ │ ├── Pager.scala │ │ ├── Review.scala │ │ ├── Role.scala │ │ ├── Text.scala │ │ ├── UrlKey.scala │ │ ├── VersionTag.scala │ │ ├── VersionedName.scala │ │ └── generator │ │ ├── CodeGenTarget.scala │ │ ├── CodeGenerator.scala │ │ ├── GeneratorUtil.scala │ │ ├── ServiceFileNames.scala │ │ └── Status.scala │ └── test │ ├── resources │ ├── apidoc │ │ └── lib │ │ │ └── anorm │ │ │ └── util.txt │ ├── example-response-with-unit-type.txt │ ├── example-union-types-ning-client.txt │ ├── example-union-types-play-23.txt │ ├── example-union-types-play-29-scala-3.txt │ ├── example-union-types-ruby-client.txt │ ├── examples │ │ ├── apidoc-api.json │ │ ├── apidoc-example-union-types-discriminator.json │ │ ├── apidoc-example-union-types-primitives.json │ │ ├── apidoc-example-union-types.json │ │ ├── apidoc-generator.json │ │ ├── built-in-types.json │ │ ├── collection-json-defaults.json │ │ ├── date-time-types.json │ │ ├── interfaces.json │ │ ├── quality.json │ │ ├── recursive-types.json │ │ ├── reference-service.json │ │ ├── reference-with-imports.json │ │ ├── response-with-reserved-scala-keyword.json │ │ └── response-with-unit-type.json │ ├── generator │ │ └── anorm │ │ │ ├── audit.txt │ │ │ ├── cap-name-conversions-26.txt │ │ │ ├── cap-name-conversions-28.txt │ │ │ ├── cap-name.txt │ │ │ ├── datetime-conversions-java.txt │ │ │ ├── datetime-conversions-joda.txt │ │ │ ├── datetime-java.txt │ │ │ ├── datetime-joda.txt │ │ │ ├── enum-conversions-24.txt │ │ │ ├── enum-conversions-26.txt │ │ │ ├── enum-conversions-28.txt │ │ │ ├── enum.txt │ │ │ ├── list-conversions-24.txt │ │ │ ├── list-conversions-26.txt │ │ │ ├── list-conversions-28.txt │ │ │ ├── list.txt │ │ │ ├── location-conversions-24.txt │ │ │ ├── location-conversions-26.txt │ │ │ ├── location-conversions-28.txt │ │ │ ├── location-parsers.txt │ │ │ ├── name-conversions-24.txt │ │ │ ├── name-conversions-26.txt │ │ │ ├── name-conversions-28.txt │ │ │ ├── name.txt │ │ │ ├── reference-conversions-24.txt │ │ │ ├── reference-conversions-26.txt │ │ │ ├── reference-conversions-28.txt │ │ │ ├── reference.txt │ │ │ ├── union-conversions-24.txt │ │ │ ├── union-conversions-26.txt │ │ │ ├── union-conversions-28.txt │ │ │ ├── union-parsers.txt │ │ │ ├── user-conversions-24.txt │ │ │ ├── user-conversions-26.txt │ │ │ ├── user-conversions-28.txt │ │ │ └── user.txt │ ├── generators │ │ ├── ScalaCaseClassesInterfacesSpec.interfaceWithNoFields.json │ │ ├── ScalaCaseClassesInterfacesSpec.interfaceWithSingleField.json │ │ ├── ScalaCaseClassesUnionInterfacesSpec.interfaceHasFields.json │ │ ├── ScalaCaseClassesUnionInterfacesSpec.interfaceHasNoFields.json │ │ ├── ScalaCaseClassesUnionInterfacesSpec.interfaceHasSameName.json │ │ ├── ScalaCaseClassesUnionInterfacesSpec.interfaceHasSameNameAndFields.json │ │ ├── collection-json-defaults-ahc-client.txt │ │ ├── collection-json-defaults-ning-client.txt │ │ ├── collection-json-defaults-play-23.txt │ │ ├── collection-json-defaults-ruby-client.txt │ │ ├── collection-json-defaults-user-case-class.txt │ │ ├── collection-json-defaults-user-patch-case-class.txt │ │ ├── generators │ │ │ └── reference-spec-ruby-client.txt │ │ ├── java-built-in-types.txt │ │ ├── play-2-bindable-age-group.txt │ │ ├── play-2-bindable-java-instant.txt │ │ ├── play-2-bindable-java-offsetdatetime.txt │ │ ├── play-2-bindable-joda-date-time.txt │ │ ├── play-2-bindable-reference-api-object.txt │ │ ├── play-2-json-spec-quality-healthcheck-readers.txt │ │ ├── play-2-json-spec-quality-healthcheck-writers.txt │ │ ├── play-2-json-spec-quality-plan-readers.txt │ │ ├── play-2-json-spec-quality-plan-writers.txt │ │ ├── play-2-route-reference-api.routes │ │ ├── play-2-route-util-reference-get-users.txt │ │ ├── play-2-standalone-json-spec-quality.txt │ │ ├── play-22-built-in-types.txt │ │ ├── play-23-built-in-types.txt │ │ ├── play-24-built-in-types.txt │ │ ├── play-25-built-in-types.txt │ │ ├── play-26-apidoc-api.txt │ │ ├── play-26-built-in-types.txt │ │ ├── play-26-envelope.txt │ │ ├── play-27-apidoc-api.txt │ │ ├── play-27-built-in-types.txt │ │ ├── play-27-java-instant.txt │ │ ├── play-27-java-offsetdatetime.txt │ │ ├── play-27-joda-date-time.txt │ │ ├── play-28-apidoc-api.txt │ │ ├── play-28-built-in-types.txt │ │ ├── play-28-java-instant.txt │ │ ├── play-28-java-offsetdatetime.txt │ │ ├── play-28-joda-date-time.txt │ │ ├── play2-client-generator-spec-errors-package-no-models.txt │ │ ├── play2-client-generator-spec-errors-package.txt │ │ ├── reference-spec-member-case-class.txt │ │ ├── reference-spec-ning-client.txt │ │ ├── reference-spec-play-23.txt │ │ ├── reference-spec-ruby-client.txt │ │ ├── reference-spec-user-case-class.txt │ │ ├── reference-with-imports-spec-ning-client.txt │ │ ├── reference-with-imports-spec-play-23.txt │ │ ├── ruby-built-in-types.txt │ │ ├── ruby-client-primitive-object-list.txt │ │ ├── ruby-client-primitive-object-map.txt │ │ ├── ruby-client-primitive-object-response-list.txt │ │ ├── ruby-client-primitive-object-response-map.txt │ │ ├── ruby-client-primitive-object-response-singleton.txt │ │ ├── ruby-client-primitive-object-singleton.txt │ │ ├── scala-models-interfaces.txt │ │ ├── scala-primitive-object-list.txt │ │ ├── scala-primitive-object-map.txt │ │ ├── scala-primitive-object-response-list.txt │ │ ├── scala-primitive-object-response-map.txt │ │ ├── scala-primitive-object-response-singleton.txt │ │ └── scala-primitive-object-singleton.txt │ ├── http4s │ │ ├── apidoc-api │ │ │ ├── 015 │ │ │ │ ├── BryzekApidocApiV0Client.scala.txt │ │ │ │ ├── BryzekApidocApiV0ClientOnly.scala.txt │ │ │ │ ├── BryzekApidocApiV0JsonOnly.scala.txt │ │ │ │ ├── BryzekApidocApiV0MockClient.scala.txt │ │ │ │ ├── BryzekApidocApiV0ModelsJson.scala.txt │ │ │ │ ├── BryzekApidocApiV0ModelsOnly.scala.txt │ │ │ │ └── BryzekApidocApiV0Server.scala.txt │ │ │ ├── 017 │ │ │ │ ├── BryzekApidocApiV0Client.scala.txt │ │ │ │ ├── BryzekApidocApiV0ClientOnly.scala.txt │ │ │ │ ├── BryzekApidocApiV0JsonOnly.scala.txt │ │ │ │ ├── BryzekApidocApiV0MockClient.scala.txt │ │ │ │ ├── BryzekApidocApiV0ModelsJson.scala.txt │ │ │ │ ├── BryzekApidocApiV0ModelsOnly.scala.txt │ │ │ │ └── BryzekApidocApiV0Server.scala.txt │ │ │ ├── 018 │ │ │ │ ├── BryzekApidocApiV0Client.scala.txt │ │ │ │ ├── BryzekApidocApiV0ClientOnly.scala.txt │ │ │ │ ├── BryzekApidocApiV0JsonOnly.scala.txt │ │ │ │ ├── BryzekApidocApiV0MockClient.scala.txt │ │ │ │ ├── BryzekApidocApiV0ModelsJson.scala.txt │ │ │ │ ├── BryzekApidocApiV0ModelsOnly.scala.txt │ │ │ │ └── BryzekApidocApiV0Server.scala.txt │ │ │ ├── 020_instant │ │ │ │ ├── BryzekApidocApiV0Client.scala.txt │ │ │ │ ├── BryzekApidocApiV0ClientOnly.scala.txt │ │ │ │ ├── BryzekApidocApiV0JsonOnly.scala.txt │ │ │ │ ├── BryzekApidocApiV0MockClient.scala.txt │ │ │ │ ├── BryzekApidocApiV0ModelsJson.scala.txt │ │ │ │ ├── BryzekApidocApiV0ModelsOnly.scala.txt │ │ │ │ └── BryzekApidocApiV0Server.scala.txt │ │ │ ├── 022 │ │ │ │ ├── BryzekApidocApiV0Client.scala.txt │ │ │ │ ├── BryzekApidocApiV0ClientOnly.scala.txt │ │ │ │ ├── BryzekApidocApiV0JsonOnly.scala.txt │ │ │ │ ├── BryzekApidocApiV0MockClient.scala.txt │ │ │ │ ├── BryzekApidocApiV0ModelsJson.scala.txt │ │ │ │ ├── BryzekApidocApiV0ModelsOnly.scala.txt │ │ │ │ └── BryzekApidocApiV0Server.scala.txt │ │ │ └── 023 │ │ │ │ ├── BryzekApidocApiV0Client.scala.txt │ │ │ │ ├── BryzekApidocApiV0ClientOnly.scala.txt │ │ │ │ ├── BryzekApidocApiV0JsonOnly.scala.txt │ │ │ │ ├── BryzekApidocApiV0MockClient.scala.txt │ │ │ │ ├── BryzekApidocApiV0ModelsJson.scala.txt │ │ │ │ └── BryzekApidocApiV0ModelsOnly.scala.txt │ │ ├── date-time │ │ │ ├── 022_instant │ │ │ │ ├── ApibuilderTimeTypesV0Client.scala.txt │ │ │ │ └── ApibuilderTimeTypesV0Server.scala.txt │ │ │ ├── 022_joda │ │ │ │ ├── ApibuilderTimeTypesV0Client.scala.txt │ │ │ │ └── ApibuilderTimeTypesV0Server.scala.txt │ │ │ └── 022_offsetdatetime │ │ │ │ ├── ApibuilderTimeTypesV0Client.scala.txt │ │ │ │ └── ApibuilderTimeTypesV0Server.scala.txt │ │ ├── mock-client │ │ │ ├── http4s_022_date-time-instant.txt │ │ │ ├── http4s_022_date-time-joda.txt │ │ │ └── http4s_022_date-time-offsetdatetime.txt │ │ └── server │ │ │ ├── form-params-017.txt │ │ │ ├── form-params-018.txt │ │ │ ├── form-params-020.txt │ │ │ ├── form-params-022.txt │ │ │ ├── form-params.json │ │ │ ├── imported-types-017.txt │ │ │ ├── imported-types-018.txt │ │ │ ├── imported-types-020.txt │ │ │ ├── imported-types-022.txt │ │ │ ├── imported-types.json │ │ │ ├── kebab-and-snake-case-018.txt │ │ │ ├── kebab-and-snake-case-020.txt │ │ │ ├── kebab-and-snake-case-022.txt │ │ │ ├── kebab-and-snake-case.json │ │ │ ├── path-params-015.txt │ │ │ ├── path-params-017.txt │ │ │ ├── path-params-018.txt │ │ │ ├── path-params-020.txt │ │ │ ├── path-params-022.txt │ │ │ ├── path-params.json │ │ │ ├── query-params-017.txt │ │ │ ├── query-params-018.txt │ │ │ ├── query-params-020.txt │ │ │ ├── query-params-022.txt │ │ │ ├── query-params.json │ │ │ ├── response-types-017.txt │ │ │ ├── response-types-018.txt │ │ │ ├── response-types-020.txt │ │ │ ├── response-types-022.txt │ │ │ ├── response-types.json │ │ │ ├── status-codes-017.txt │ │ │ ├── status-codes-018.txt │ │ │ ├── status-codes-020.txt │ │ │ ├── status-codes-022.txt │ │ │ └── status-codes.json │ ├── play2-json-spec-model-readers.txt │ ├── play2 │ │ ├── mock-client │ │ │ ├── play27_date-time-instant.txt │ │ │ ├── play27_date-time-joda.txt │ │ │ ├── play27_date-time-offsetdatetime.txt │ │ │ ├── play28_date-time-instant.txt │ │ │ ├── play28_date-time-joda.txt │ │ │ ├── play28_date-time-offsetdatetime.txt │ │ │ └── play28_empty_bodies.txt │ │ └── route │ │ │ ├── date-time-instant.txt │ │ │ ├── date-time-joda.txt │ │ │ └── date-time-offsetdatetime.txt │ ├── play2enums-example.txt │ ├── play2enums-json-example.txt │ ├── ruby-client-generator-gilt-0.0.1-test.txt │ ├── ruby-gem-enums.txt │ ├── scala-mock-factories-reference-service.txt │ ├── scala-nested-union-models-case-classes.txt │ ├── scala-nested-union-models-json-union-type-readers-inner-type.txt │ ├── scala-nested-union-models-json-union-type-readers-outer-type.txt │ ├── scala-union-enums-json.txt │ ├── scala-union-models-case-classes.txt │ ├── scala-union-models-json-union-type-readers.txt │ ├── scala-union-models-json-union-type-writers.txt │ ├── scala-union-models-json.txt │ ├── union-types-discriminator-service-play-24.txt │ └── union-types-discriminator-service-play-27.txt │ └── scala │ ├── MethodsSpec.scala │ ├── ReviewSpec.scala │ ├── RoleSpec.scala │ ├── TestHelper.scala │ ├── TestHelperSpec.scala │ ├── TextSpec.scala │ ├── UrlKeySpec.scala │ ├── VersionTagSpec.scala │ ├── generator │ ├── GeneratorUtilSpec.scala │ └── ServiceFileNamesSpec.scala │ └── helpers │ ├── ServiceHelpers.scala │ └── TestHelpers.scala ├── postman-generator ├── README.md ├── spec │ └── postman-generator-attributes.json └── src │ ├── main │ └── scala │ │ ├── examples │ │ ├── ExampleJson.scala │ │ └── RandomStringGenerator.scala │ │ ├── generator │ │ ├── DependantOperationResolver.scala │ │ ├── Heuristics.scala │ │ ├── PathParamsFinder.scala │ │ ├── PostmanCollectionGenerator.scala │ │ ├── PostmanGeneratorConstants.scala │ │ ├── PostmanItemBuilder.scala │ │ ├── PredefinedCollectionItems.scala │ │ ├── ServiceImportResolver.scala │ │ ├── SetupCleanupFolderBuilder.scala │ │ └── Utils.scala │ │ └── models │ │ ├── AttributeValueReader.scala │ │ ├── attributes │ │ └── PostmanAttributes.scala │ │ ├── operation │ │ └── DependantOperations.scala │ │ └── service │ │ └── ResolvedService.scala │ └── test │ ├── resources │ ├── apibuilder │ │ ├── test-service-1.json │ │ └── test-service-operation-deps.json │ └── postman │ │ ├── expected-1.json │ │ └── expected-operation-deps.json │ └── scala │ ├── generator │ ├── DependantOperationResolverSpec.scala │ ├── FileInputOutputGeneratorTests.scala │ ├── HeuristicsSpec.scala │ ├── PathParamsFinderSpec.scala │ ├── PostmanCollectionGeneratorSpec.scala │ ├── PostmanItemBuilderSpec.scala │ ├── PostmanTestAssertionsSpec.scala │ ├── ServiceImportResolverSpec.scala │ ├── SetupCleanupFolderBuilderSpec.scala │ └── TestFixtures.scala │ └── testUtils │ └── TestPostmanCollectionGenerator.scala ├── project ├── build.properties └── plugins.sbt ├── reference-api ├── api.json └── service.json ├── ruby-generator └── src │ ├── main │ └── scala │ │ └── models │ │ ├── ApiBuilderComments.scala │ │ ├── FeatureMigration.scala │ │ ├── Headers.scala │ │ ├── RubyClientGenerator.scala │ │ ├── RubyHttpClient.scala │ │ ├── RubyPrimitiveWrapper.scala │ │ └── Util.scala │ └── test │ └── scala │ └── models │ ├── ApiBuilderCommentsSpec.scala │ ├── BuiltInTypesSpec.scala │ ├── ExampleUnionTypesSpec.scala │ ├── FeatureMigrationSpec.scala │ ├── ResponsesWithUnitTypeSpec.scala │ ├── RubyClientGeneratorSpec.scala │ ├── RubyClientPrimitiveObjectSpec.scala │ ├── RubyReceiverDefaultsSpec.scala │ └── RubyUtilSpec.scala ├── scala-generator └── src │ ├── main │ └── scala │ │ ├── models │ │ ├── ApiBuilderComments.scala │ │ ├── Attributes.scala │ │ ├── Headers.scala │ │ ├── JsonImports.scala │ │ ├── Play26Controllers.scala │ │ ├── Play2Bindables.scala │ │ ├── Play2ClientGenerator.scala │ │ ├── Play2Json.scala │ │ ├── Play2Models.scala │ │ ├── Play2RouteGenerator.scala │ │ ├── Play2StandaloneModels.scala │ │ ├── ScalaCheck.scala │ │ ├── Util.scala │ │ ├── generator │ │ │ ├── CaseClassBuilder.scala │ │ │ ├── DiscriminatorValue.scala │ │ │ ├── Namespaces.scala │ │ │ ├── PrimitiveWrapper.scala │ │ │ ├── ScalaCaseClasses.scala │ │ │ ├── ScalaClientAuthClasses.scala │ │ │ ├── ScalaClientCommon.scala │ │ │ ├── ScalaClientMethodConfig.scala │ │ │ ├── ScalaClientMethodGenerator.scala │ │ │ ├── ScalaDatatype.scala │ │ │ ├── ScalaEnums.scala │ │ │ ├── ScalaGeneratorUtil.scala │ │ │ ├── ScalaService.scala │ │ │ ├── ScalaUnionDiscriminatorGenerator.scala │ │ │ ├── ScalaUtil.scala │ │ │ ├── UnionTypeUndefinedModel.scala │ │ │ ├── anorm │ │ │ │ ├── Conversions.scala │ │ │ │ └── ParserGenerator.scala │ │ │ └── mock │ │ │ │ ├── MockClientGenerator.scala │ │ │ │ └── MockFactoriesGenerator.scala │ │ ├── http4s │ │ │ ├── CirceJson.scala │ │ │ ├── Generator.scala │ │ │ ├── Http4sClient.scala │ │ │ ├── Http4sScalaClientCommon.scala │ │ │ ├── ScalaClientMethodGenerator.scala │ │ │ ├── ScalaGeneratorUtil.scala │ │ │ ├── ScalaService.scala │ │ │ ├── mock │ │ │ │ ├── Http4s018MockClientGenerator.scala │ │ │ │ ├── Http4s020MockClientGenerator.scala │ │ │ │ └── Http4s022MockClientGenerator.scala │ │ │ └── server │ │ │ │ ├── Http4sServer.scala │ │ │ │ ├── HttpStatusCodes.scala │ │ │ │ └── Route.scala │ │ ├── ning │ │ │ ├── NingClientGenerator.scala │ │ │ └── PathSegment.scala │ │ └── play │ │ │ ├── Play26Generator.scala │ │ │ └── files │ │ │ ├── Client.scala │ │ │ ├── MockClient.scala │ │ │ ├── Models.scala │ │ │ ├── ModelsBindables.scala │ │ │ ├── ModelsBodyParsers.scala │ │ │ ├── ModelsGens.scala │ │ │ ├── ModelsJson.scala │ │ │ └── Routes.scala │ │ └── utils │ │ └── ScalaFormatter.scala │ └── test │ └── scala │ ├── models │ ├── ApiBuilderCommentsSpec.scala │ ├── ExampleUnionTypesSpec.scala │ ├── ExampleUnionTypesWithDiscriminatorSpec.scala │ ├── FieldDefaultHelper.scala │ ├── JsonImportsSpec.scala │ ├── Play26ControllersSpec.scala │ ├── Play2BindablesSpec.scala │ ├── Play2ClientGeneratorSpec.scala │ ├── Play2JsonSpec.scala │ ├── Play2RouteGeneratorSpec.scala │ ├── Play2StandaloneModelsJsonSpec.scala │ ├── RecursiveJsonSpec.scala │ ├── ScalaCheckGeneratorSpec.scala │ ├── ScalaGeneratorUtilSpec.scala │ ├── ScalaNestedUnionSpec.scala │ ├── ScalaUnionSpec.scala │ ├── UndefinedUnionWithInterfaceSpec.scala │ ├── generator │ │ ├── CollectionJsonDefaultsSpec.scala │ │ ├── NamespacesSpec.scala │ │ ├── ReferenceSpec.scala │ │ ├── ReferenceWithImportsSpec.scala │ │ ├── ScalaAnnotationsSpec.scala │ │ ├── ScalaCaseClassesInterfacesSpec.scala │ │ ├── ScalaCaseClassesUnionInterfacesSpec.scala │ │ ├── ScalaDatatypeSpec.scala │ │ ├── ScalaDefaultsSpec.scala │ │ ├── ScalaEnumsSpec.scala │ │ ├── ScalaOperationSpec.scala │ │ ├── ScalaPrimitiveObjectSpec.scala │ │ ├── ScalaUtilSpec.scala │ │ ├── TargetSpec.scala │ │ ├── anorm │ │ │ ├── ParserGenerator24Spec.scala │ │ │ ├── ParserGenerator26Spec.scala │ │ │ └── ParserGenerator28Scala2Spec.scala │ │ └── mock │ │ │ ├── MockClientGeneratorSpec.scala │ │ │ └── MockFactoriesGeneratorSpec.scala │ ├── http4s │ │ ├── CirceDefaultsSpec.scala │ │ ├── Http4sClientGeneratorSpec.scala │ │ ├── Http4sClientMockGeneratorSpec.scala │ │ ├── Http4sGeneratorSpec.scala │ │ └── Http4sServerGeneratorSpec.scala │ └── play │ │ ├── Gens.scala │ │ ├── Helpers.scala │ │ ├── Play26EnvelopeGeneratorSpec.scala │ │ ├── Play26GeneratorSpec.scala │ │ └── files │ │ ├── ModelsBodyParsersSpec.scala │ │ ├── ModelsGensSpec.scala │ │ └── ModelsJsonSpec.scala │ └── utils │ └── ScalaFormatterSpec.scala └── scripts └── update-examples.rb /.apibuilder/.tracked_files: -------------------------------------------------------------------------------- 1 | --- 2 | apicollective: 3 | apibuilder-common: 4 | play_2_x_json: 5 | - generated/app/ApicollectiveApibuilderCommonV0Models.scala 6 | apibuilder-generator: 7 | play_2_8_client: 8 | - generated/app/ApicollectiveApibuilderGeneratorV0Client.scala 9 | play_2_x_routes: 10 | - generator/conf/routes 11 | apibuilder-postman-collection-v2-1: 12 | play_2_x_json: 13 | - generated/app/ApicollectiveApibuilderPostmanCollectionV21V0Models.scala 14 | apibuilder-spec: 15 | play_2_8_client: 16 | - generated/app/ApicollectiveApibuilderSpecV0Client.scala 17 | postman-generator-attributes: 18 | play_2_x_json: 19 | - generated/app/ApicollectivePostmanGeneratorAttributesV0Models.scala 20 | -------------------------------------------------------------------------------- /.apibuilder/config: -------------------------------------------------------------------------------- 1 | code: 2 | apicollective: 3 | apibuilder-common: 4 | version: latest 5 | generators: 6 | play_2_x_json: generated/app 7 | apibuilder-spec: 8 | version: latest 9 | generators: 10 | play_2_8_client: generated/app 11 | apibuilder-generator: 12 | version: latest 13 | generators: 14 | play_2_8_client: generated/app 15 | play_2_x_routes: generator/conf/routes 16 | apibuilder-postman-collection-v2-1: 17 | version: latest 18 | generators: 19 | play_2_x_json: generated/app 20 | postman-generator-attributes: 21 | version: latest 22 | generators: 23 | play_2_x_json: generated/app 24 | -------------------------------------------------------------------------------- /.delta: -------------------------------------------------------------------------------- 1 | builds: 2 | - root: 3 | cluster: k8s 4 | instance.type: t3.small 5 | port.container: 9000 6 | port.host: 7072 7 | version: 1.3 8 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | example-union-types/api/logs 2 | example-union-types-discriminator/api/logs 3 | generator/logs 4 | example-union-types/api/target 5 | example-union-types/generated/target 6 | example-union-types/project/project/target 7 | example-union-types/project/target 8 | example-union-types/target 9 | example-union-types-discriminator/api/target 10 | example-union-types-discriminator/generated/target 11 | example-union-types-discriminator/project/project/target 12 | example-union-types-discriminator/project/target 13 | example-union-types-discriminator/target 14 | generated/target 15 | generator/target 16 | java-generator/target 17 | lib/target 18 | project/project/target 19 | project/target 20 | ruby-generator/target 21 | scala-generator/target 22 | kotlin-generator/target 23 | target 24 | .git 25 | .ivy2 26 | *.swp 27 | *.swo 28 | .DS_STORE 29 | *.md 30 | !README.md 31 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | push: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | java: [ '17' ] 17 | scala: [ '2.13.11' ] 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Set up JDK 21 | uses: actions/setup-java@v2 22 | with: 23 | java-version: ${{ matrix.java }} 24 | distribution: 'zulu' 25 | - name: print Java version 26 | run: java -version 27 | - name: Build 28 | run: sbt ++${{ matrix.scala }} clean test 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | logs/ 3 | .ivy2 4 | *.downloaded.scala 5 | *.downloaded.rb 6 | *.swp 7 | *.swo 8 | .bsp/ 9 | .idea/ 10 | .idea_modules/ 11 | .DS_Store 12 | out 13 | -------------------------------------------------------------------------------- /.java-version: -------------------------------------------------------------------------------- 1 | 17 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | jdk: 3 | - openjdk13 4 | cache: 5 | directories: 6 | - $HOME/.ivy2/cache 7 | - $HOME/.sbt/boot/ 8 | script: 9 | - sbt clean test 10 | # Tricks to avoid unnecessary cache updates 11 | - find $HOME/.sbt -name "*.lock" | xargs rm -f 12 | - find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm -f 13 | branches: 14 | only: 15 | main 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | See https://github.com/gilt/standards/blob/1.0.0/CONTRIBUTIONS.md -------------------------------------------------------------------------------- /DEVELOPER.md: -------------------------------------------------------------------------------- 1 | Running 2 | ======= 3 | sbt 4 | > project generator 5 | > run 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM flowdocker/play_builder:latest-java17 as builder 2 | ADD . /opt/play 3 | WORKDIR /opt/play 4 | RUN sbt 'project generator' clean stage 5 | 6 | FROM flowdocker/play:latest-java17 7 | COPY --from=builder /opt/play /opt/play 8 | WORKDIR /opt/play/generator/target/universal/stage 9 | ENTRYPOINT ["java", "-jar", "/root/environment-provider.jar", "--service", "play", "apibuilder-generator", "bin/apibuilder-generator-generator"] 10 | HEALTHCHECK --interval=5s --timeout=5s --retries=10 \ 11 | CMD curl -f http://localhost:9000/_internal_/healthcheck || exit 1 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2018 Gilt Groupe, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/apicollective/apibuilder-generator.svg?branch=main)](https://travis-ci.org/apicollective/apibuilder-generator) 2 | 3 | apibuilder-generator 4 | ================ 5 | Code generators for https://www.apibuilder.io 6 | -------------------------------------------------------------------------------- /android-generator/src/main/scala/models/generator/android/AndroidClasses.scala: -------------------------------------------------------------------------------- 1 | package models.generator.android 2 | 3 | import com.squareup.javapoet.ClassName 4 | import retrofit2.Call 5 | 6 | object AndroidClasses 7 | extends BaseAndroidCodeGenerator { 8 | override def getJavaDocFileHeader() = "WARNING: not all features (notably unions) and data types work with android generator yet. \n" + 9 | "Android generator is designed to be used in an android application, but should work in any java codebase as long as you import jackson and retrofit2 libraries. \n" + 10 | "If you are considering using this library, would like to request/discuss features, or would like to share how you're using it, please contact android-feedback@gilt.com \n" 11 | 12 | override def getRetrofitReturnTypeWrapperClass() = ClassName.get(classOf[Call[Void]]) 13 | } 14 | -------------------------------------------------------------------------------- /android-generator/src/main/scala/models/generator/android/AndroidRxClasses.scala: -------------------------------------------------------------------------------- 1 | package models.generator.android 2 | 3 | import com.squareup.javapoet.ClassName 4 | import io.reactivex.Single 5 | 6 | object AndroidRxClasses 7 | extends BaseAndroidCodeGenerator 8 | { 9 | override def getJavaDocFileHeader() = "WARNING: not all features (notably unions) and data types work with android generator yet. \n" + 10 | "Android generator is designed to be used in an android application, but should work in any java codebase as long as you import jackson, retrofit2 and RxJava2 libraries. \n" + 11 | "If you are considering using this library, would like to request/discuss features, or would like to share how you're using it, please contact android-feedback@gilt.com \n" 12 | 13 | override def getRetrofitReturnTypeWrapperClass() = ClassName.get(classOf[Single[Void]]) 14 | 15 | } 16 | -------------------------------------------------------------------------------- /android-generator/src/main/scala/models/generator/android/ApiBuilderComments.scala: -------------------------------------------------------------------------------- 1 | package models.generator.android 2 | 3 | import lib.AbstractApiBuilderComments 4 | 5 | /** 6 | * 7 | * Author: jkenny 8 | * Date: 28/05/2015 9 | */ 10 | case class ApiBuilderComments(override val version: String, override val userAgent: Option[String]) extends AbstractApiBuilderComments(version, userAgent) { 11 | 12 | val forClassFile: String = elements.mkString("", "\n","\n") 13 | 14 | } 15 | -------------------------------------------------------------------------------- /android-generator/src/main/scala/models/generator/android/RetrofitUtil.scala: -------------------------------------------------------------------------------- 1 | package models.generator.android 2 | 3 | object RetrofitUtil { 4 | 5 | def toRetrofitPath(path: String): String = { 6 | 7 | //1. remove trailing slash, this is explained in https://github.com/square/retrofit/issues/1049 8 | 9 | val path1 = if(path.head == '/') 10 | path.tail 11 | else 12 | path 13 | 14 | //2. now replace :pathvars with {pathvars} 15 | 16 | val path2 = path1.replaceAll(":(.*?)/","{$1}/") 17 | 18 | //3. now do the same for when :pathvar is the last one in the string 19 | 20 | val path3 = path2.replaceAll(":(.*?)$","{$1}") 21 | 22 | path3 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /csharp-generator/src/main/scala/generator/csharp/Names.scala: -------------------------------------------------------------------------------- 1 | package generator.csharp 2 | 3 | import lib.Text 4 | 5 | object Names { 6 | 7 | def pascalCase(name: String): String = maybeQuote(Text.pascalCase(name)) 8 | 9 | def camelCase(name: String): String = maybeQuote(Text.snakeToCamelCase(name)) 10 | 11 | def maybeQuote(name: String): String = { 12 | if (Keywords.contains(name)) { 13 | s"@#{name}" 14 | } else { 15 | name 16 | } 17 | } 18 | 19 | // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ 20 | private val Keywords: Set[String] = Set( 21 | "abstract", 22 | "as", 23 | "base", 24 | "bool", 25 | "break", 26 | "byte", 27 | "case", 28 | "catch", 29 | "char", 30 | "checked", 31 | "class", 32 | "const", 33 | "continue", 34 | "decimal", 35 | "default", 36 | "delegate", 37 | "do", 38 | "double", 39 | "else", 40 | "enum", 41 | "event", 42 | "explicit", 43 | "extern", 44 | "false", 45 | "finally", 46 | "fixed", 47 | "float", 48 | "for", 49 | "foreach", 50 | "goto", 51 | "if", 52 | "implicit", 53 | "in", 54 | "int", 55 | "interface", 56 | "internal", 57 | "is", 58 | "lock", 59 | "long", 60 | "namespace", 61 | "new", 62 | "null", 63 | "object", 64 | "operator", 65 | "out", 66 | "override", 67 | "params", 68 | "private", 69 | "protected", 70 | "public", 71 | "readonly", 72 | "ref", 73 | "return", 74 | "sbyte", 75 | "sealed", 76 | "short", 77 | "sizeof", 78 | "stackalloc", 79 | "static", 80 | "string", 81 | "struct", 82 | "switch", 83 | "this", 84 | "throw", 85 | "true", 86 | "try", 87 | "typeof", 88 | "uint", 89 | "ulong", 90 | "unchecked", 91 | "unsafe", 92 | "ushort", 93 | "using", 94 | "virtual", 95 | "void", 96 | "volatile", 97 | "while" 98 | ) 99 | } 100 | -------------------------------------------------------------------------------- /csharp-generator/src/main/scala/generator/csharp/RecordBuilder.scala: -------------------------------------------------------------------------------- 1 | package generator.csharp 2 | 3 | case class RecordField(name: String, `type`: String, required: Boolean) 4 | 5 | case class RecordBuilder( 6 | name: Option[String] = None, 7 | fields: Seq[RecordField] = Nil 8 | ) { 9 | def withName(name: String): RecordBuilder = { 10 | this.copy( 11 | name = Some(name) 12 | ) 13 | } 14 | 15 | def withField(field: RecordField): RecordBuilder = { 16 | this.copy( 17 | fields = fields ++ Seq(field) 18 | ) 19 | } 20 | 21 | def build: String = { 22 | val n = name.getOrElse { 23 | sys.error("Missing class name") 24 | } 25 | Seq( 26 | s"public record $n (", 27 | fields.map { f => 28 | val opt = if (f.required) { "" } else { "?" } 29 | s"${f.`type`}$opt ${f.name}" 30 | }.mkString(",\n").trim.indent(2).stripTrailing(), 31 | ");", 32 | ).mkString("\n") 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /csv-generator/src/main/scala/models/generator/csv/CsvGenerator.scala: -------------------------------------------------------------------------------- 1 | package models.generator.csv 2 | 3 | import java.io.StringWriter 4 | 5 | import io.apibuilder.generator.v0.models.{File, InvocationForm} 6 | import io.apibuilder.spec.v0.models.{Operation, Resource, Service} 7 | import lib.generator.CodeGenerator 8 | import org.apache.commons.csv.{CSVFormat, CSVPrinter} 9 | 10 | class CsvGenerator extends CodeGenerator { 11 | 12 | final val EMPTY = "" 13 | 14 | override def invoke(form: InvocationForm): Either[Seq[String], Seq[File]] = { 15 | Right(generateSourceFiles(form.service)) 16 | } 17 | 18 | def generateSourceFiles(service: Service): Seq[File] = { 19 | Seq(generateResourcesFile(service.resources)) 20 | } 21 | 22 | def generateResourcesFile(resources: Seq[Resource]): File = { 23 | 24 | def makeRow(resource: Resource, operation: Operation): Seq[String] = { 25 | 26 | val method = operation.method.toString 27 | val path = resource.path.getOrElse(EMPTY) + operation.path 28 | val description = operation.description.getOrElse(EMPTY) 29 | 30 | Seq(method, path, description) 31 | } 32 | 33 | val rows: Seq[Seq[String]] = for { 34 | resource <- resources 35 | operation <- resource.operations 36 | } yield makeRow(resource, operation) 37 | 38 | 39 | generateSourceFile("resources.csv", Seq("method", "path", "description"), rows) 40 | } 41 | 42 | def generateSourceFile(fileName: String, header: Seq[String], rows: Seq[Seq[String]]): File = { 43 | 44 | val stringWriter = new StringWriter 45 | val csvPrinter = new CSVPrinter(stringWriter, CSVFormat.DEFAULT) 46 | 47 | assert(rows.headOption.forall{row => 48 | row.length == header.length 49 | }, "All lines must have same number of fields as header line") 50 | 51 | csvPrinter.printRecords(header.toArray) 52 | rows.map(row => csvPrinter.printRecords(row.toArray)) 53 | csvPrinter.flush() 54 | 55 | File( 56 | name = fileName, 57 | contents = stringWriter.toString 58 | ) 59 | } 60 | 61 | } 62 | 63 | object CsvGenerator extends CsvGenerator -------------------------------------------------------------------------------- /deploy/apibuilder-generator/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: Manual Deploy 3 | name: apibuilder-generator 4 | version: 0.0.1 5 | -------------------------------------------------------------------------------- /deploy/apibuilder-generator/requirements.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: flow-generic 3 | version: ^1.0.0 4 | repository: https://flow.jfrog.io/artifactory/api/helm/generic-charts-helm 5 | -------------------------------------------------------------------------------- /deploy/apibuilder-generator/values.yaml: -------------------------------------------------------------------------------- 1 | nameOverride: "apibuilder-generator" 2 | fullnameOverride: "apibuilder-generator" 3 | 4 | team: foundation 5 | 6 | image: 7 | repository: flowcommerce/apibuilder-generator 8 | 9 | resources: 10 | limits: 11 | memory: "3400Mi" 12 | cpu: 1 13 | requests: 14 | memory: "3400Mi" 15 | cpu: .05 16 | 17 | jvmOpts: 18 | memory: 2600m 19 | 20 | istio: 21 | alb: true 22 | 23 | istioIngress: 24 | enabled: true 25 | gateways: 26 | - key: generator-apibuilder-io 27 | tld: apibuilder.io 28 | selector: ingressgateway-apibuilder-io 29 | dns: true 30 | hosts: 31 | - generator.apibuilder.io 32 | 33 | istioService: 34 | live: 35 | hosts: 36 | - apibuilder-generator 37 | - generator.apibuilder.io 38 | gateways: 39 | - mesh 40 | - generator-apibuilder-io 41 | stages: 42 | - deployment: live 43 | weight: 100 44 | 45 | deployments: 46 | live: 47 | minReplicas: 2 48 | maxReplicas: 2 49 | maxUnavailable: 1 50 | serviceAccountName: "flow-prod-eks-apicollective-apibuilder-generator-sa" 51 | serviceAccountIamRole: "arn:aws:iam::479720515435:role/flow-prod-eks-apicollective-role" 52 | 53 | nodeSelector: 54 | karpenter/role: workers 55 | kubernetes.io/arch: amd64 56 | tolerations: 57 | - key: "role" 58 | operator: "Equal" 59 | value: "workers" 60 | effect: "NoSchedule" 61 | 62 | rolloutResource: 63 | enabled: false 64 | 65 | canary: 66 | enabled: false 67 | 68 | -------------------------------------------------------------------------------- /elm-generator/src/main/scala/generator/elm/ElmCommon.scala: -------------------------------------------------------------------------------- 1 | package generator.elm 2 | 3 | case class ElmCommon(args: GenArgs) { 4 | 5 | def generate(): String = { 6 | args.imports.addAs("Json.Encode", "Encode") 7 | args.imports.addExposing("Http", "Header, Expect") 8 | """ 9 | |encodeOptional : (a -> Encode.Value) -> Maybe a -> Encode.Value 10 | |encodeOptional encoder value = 11 | | case value of 12 | | Just v -> 13 | | encoder v 14 | | 15 | | Nothing -> 16 | | Encode.null 17 | | 18 | |boolToString : Bool -> String 19 | |boolToString value = 20 | | if value then 21 | | "true" 22 | | 23 | | else 24 | | "false" 25 | | 26 | |type alias UnitResponse = 27 | | {} 28 | | 29 | |type alias HttpRequestParams msg = 30 | | { apiHost: String 31 | | , communityId: String 32 | | , headers : List Header 33 | | , expect : Expect msg 34 | | } 35 | |""".stripMargin 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /elm-generator/src/main/scala/generator/elm/ElmJson.scala: -------------------------------------------------------------------------------- 1 | package generator.elm 2 | 3 | object ElmJson { 4 | def encoderName(t: ElmType): String = encoderName(t.declaration) 5 | private[elm] def encoderName(name: String): String = s"${Names.camelCase(name)}Encoder" 6 | } 7 | 8 | case class ElmJson(imports: Imports) { 9 | 10 | def encoder(name: String)(contents: String): ElmFunction = { 11 | imports.addAs("Json.Encode", "Encode") 12 | val n = ElmJson.encoderName(name) 13 | val code = Seq( 14 | s"$n : ${Names.pascalCase(name)} -> Encode.Value", 15 | s"$n instance =", 16 | contents.indent(4) 17 | ).mkString("\n") 18 | ElmFunction(name = n, code = code) 19 | } 20 | 21 | 22 | def decoderName(name: String): String = { 23 | s"${Names.camelCase(name)}Decoder" 24 | } 25 | 26 | def decoder(name: String)(contents: String): ElmFunction = { 27 | val n = decoderName(name) 28 | imports.addAs("Json.Decode", "Decode") 29 | val code = Seq( 30 | s"$n : Decode.Decoder ${Names.pascalCase(name)}", 31 | s"$n =", 32 | contents.indent(4) 33 | ).mkString("\n") 34 | ElmFunction(name = n, code = code) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /elm-generator/src/main/scala/generator/elm/Imports.scala: -------------------------------------------------------------------------------- 1 | package generator.elm 2 | 3 | import scala.collection.concurrent.TrieMap 4 | 5 | case class Imports() { 6 | private sealed trait ExposingAllValue 7 | private object ExposingAllValue { 8 | case object Wildcard extends ExposingAllValue 9 | case class Types(types: Seq[String]) extends ExposingAllValue 10 | } 11 | 12 | private val allAs: TrieMap[String, String] = TrieMap[String, String]() 13 | private val exposingAll: TrieMap[String, ExposingAllValue] = TrieMap[String, ExposingAllValue]() 14 | 15 | def addAs(name: String, as: String): Unit = { 16 | allAs.put(name, as).foreach { existing => 17 | assert(existing == as, s"Import $name previously added as '$existing' - must match") 18 | } 19 | () 20 | } 21 | 22 | def addExposingAll(name: String): Unit = { 23 | exposingAll.put(name, ExposingAllValue.Wildcard) 24 | () 25 | } 26 | 27 | def addExposing(name: String, types: String): Unit = addExposing(name, Seq(types)) 28 | 29 | private def addExposing(name: String, types: Seq[String]): Unit = { 30 | exposingAll.get(name) match { 31 | case None => exposingAll.put(name, ExposingAllValue.Types(types)) 32 | case Some(ExposingAllValue.Wildcard) => () 33 | case Some(ExposingAllValue.Types(existing)) => exposingAll.put(name, ExposingAllValue.Types(existing ++ types)) 34 | } 35 | () 36 | } 37 | 38 | def generateCode(): String = { 39 | ( 40 | allAs.keysIterator.toSeq.sorted.map { name => 41 | val alias = allAs(name) 42 | if (alias == name) { 43 | s"import $name" 44 | } else { 45 | s"import $name as ${allAs(name)}" 46 | } 47 | } ++ exposingAll.keysIterator.toSeq.sorted.map { name => 48 | exposingAll(name) match { 49 | case ExposingAllValue.Wildcard => s"import $name exposing (..)" 50 | case ExposingAllValue.Types(types) => s"import $name exposing (${types.distinct.sorted.mkString(", ")})" 51 | } 52 | } 53 | ).mkString("\n") 54 | } 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /elm-generator/src/main/scala/generator/elm/Names.scala: -------------------------------------------------------------------------------- 1 | package generator.elm 2 | 3 | import lib.Text 4 | 5 | object Names { 6 | 7 | private def withNamespace(imports: Imports, name: String)(f: String => String): String = { 8 | NamespaceParser.parse(name) match { 9 | case ParsedName.Local(name) => f(name) 10 | case ParsedName.Imported(namespace, name) => { 11 | imports.addExposingAll(s"Generated.${Names.pascalCase(namespace)}") 12 | f(name) 13 | } 14 | } 15 | } 16 | 17 | def camelCase(imports: Imports, name: String): String = withNamespace(imports, name)(camelCase) 18 | 19 | def pascalCase(name: String): String = maybeQuote(Text.pascalCase(name)) 20 | 21 | def camelCase(name: String): String = maybeQuote(Text.snakeToCamelCase(name)) 22 | 23 | def wrapInQuotes(name: String): String = { 24 | // TODO: Escape 25 | s"\"$name\"" 26 | } 27 | 28 | def maybeQuote(name: String): String = { 29 | if (Keywords.contains(name)) { 30 | name + "_" 31 | } else { 32 | name 33 | } 34 | } 35 | 36 | // https://github.com/elm/compiler/blob/d07679322ef5d71de1bd2b987ddc660a85599b87/compiler/src/Parse/Primitives/Keyword.hs#L3-L12 37 | private val Keywords: Set[String] = Set( 38 | "type", 39 | "alias", 40 | "port", 41 | "if", 42 | "then", 43 | "else", 44 | "case", 45 | "of", 46 | "let", 47 | "in", 48 | "infex", 49 | "left", 50 | "right", 51 | "non", 52 | "module", 53 | "import", 54 | "exposing", 55 | "as", 56 | "where", 57 | "effect", 58 | "command", 59 | "subscription", 60 | "jsonTrue", 61 | "jsonFalse", 62 | "jsonNull" 63 | ) 64 | } 65 | -------------------------------------------------------------------------------- /elm-generator/src/main/scala/generator/elm/NamespaceParser.scala: -------------------------------------------------------------------------------- 1 | package generator.elm 2 | 3 | sealed trait ParsedName 4 | 5 | object ParsedName { 6 | case class Local(name: String) extends ParsedName 7 | case class Imported(namespace: String, name: String) extends ParsedName 8 | } 9 | 10 | object NamespaceParser { 11 | 12 | /** 13 | * returns true if the value is 'v0', 'v1', etc indicating a version number 14 | */ 15 | def isVersion(value: String): Boolean = { 16 | if (value.startsWith("v")) { 17 | value.drop(1).toLongOption.isDefined 18 | } else { 19 | false 20 | } 21 | } 22 | 23 | private val TypeIndicators = Seq("enums", "models", "unions") 24 | private def isTypeIndicator(value: String): Boolean = TypeIndicators.contains(value) 25 | 26 | def parse(name: String): ParsedName = { 27 | name.split("\\.").filterNot(isVersion).filterNot(isTypeIndicator).toList match { 28 | case Nil => sys.error("Failed to parse name") 29 | case name :: Nil => ParsedName.Local(name = name) 30 | case multiple => { 31 | ParsedName.Imported( 32 | namespace = multiple.dropRight(1).mkString("_"), 33 | name = multiple.last 34 | ) 35 | } 36 | } 37 | } 38 | } 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /elm-generator/src/main/scala/generator/elm/Util.scala: -------------------------------------------------------------------------------- 1 | package generator.elm 2 | 3 | object Util { 4 | 5 | def maybeWrapInParens(prefix: String, contents: String): String = { 6 | s"$prefix ${maybeWrapInParens(contents)}" 7 | } 8 | 9 | def maybeWrapInParens(contents: String): String = { 10 | val i = contents.indexOf(" ") 11 | if (i > 0) { 12 | s"($contents)" 13 | } else { 14 | contents 15 | } 16 | } 17 | 18 | def wrapInParens(prefix: String, contents: String): String = s"($prefix $contents)" 19 | 20 | def wrapInQuotes(contents: String): String = { 21 | s""""$contents"""" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /elm-generator/src/test/scala/generator/elm/ElmGeneratorSpec.scala: -------------------------------------------------------------------------------- 1 | package generator.elm 2 | 3 | import helpers.{ServiceHelpers, TestHelpers} 4 | import io.apibuilder.generator.v0.models.{File, InvocationForm} 5 | import io.apibuilder.spec.v0.models.Service 6 | import org.scalatest.funspec.AnyFunSpec 7 | import org.scalatest.matchers.must.Matchers 8 | 9 | class ElmGeneratorSpec extends AnyFunSpec with Matchers 10 | with ServiceHelpers 11 | with TestHelpers 12 | { 13 | 14 | private def makeInvocationForm(service: Service = makeService()): InvocationForm = { 15 | InvocationForm( 16 | service = service, 17 | attributes = Nil, 18 | userAgent = None, 19 | importedServices = None 20 | ) 21 | } 22 | 23 | private def setupValid(service: Service): Seq[File] = { 24 | rightOrErrors { 25 | ElmGenerator.invoke( 26 | makeInvocationForm(service = service) 27 | ) 28 | } 29 | } 30 | 31 | private def genModels(service: Service): String = { 32 | expectValid { 33 | ElmGenerator().generateModels(GenArgs(service)) 34 | } 35 | } 36 | 37 | it("invoke must returns errors") { 38 | leftOrErrors { 39 | ElmGenerator.invoke( 40 | makeInvocationForm() 41 | ) 42 | } 43 | } 44 | 45 | it("generates nice filename") { 46 | setupValid( 47 | makeService( 48 | name = "foo", 49 | namespace = "io.apibuilder", 50 | models = Seq(makeModel("bar")) 51 | ) 52 | ).head.name mustBe "Generated/IoApibuilder.elm" 53 | } 54 | 55 | it("enum") { 56 | val file = setupValid( 57 | makeService( 58 | name = "foo", 59 | namespace = "io.apibuilder", 60 | models = Seq(makeModel()), 61 | enums = Seq(makeEnum( 62 | name = "newsletter_key", 63 | plural = "newsletter_keys", 64 | values = Seq( 65 | makeEnumValue(name = "general"), 66 | makeEnumValue(name = "billing"), 67 | ) 68 | )) 69 | ) 70 | ) 71 | println(s"File: $file") 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/union-of-unions/.apibuilder/.tracked_files: -------------------------------------------------------------------------------- 1 | --- 2 | apicollective: 3 | apibuilder-union-of-unions: 4 | play_2_6_client: 5 | - generated/app/ApicollectiveApibuilderUnionOfUnionsV0Client.scala 6 | -------------------------------------------------------------------------------- /examples/union-of-unions/.apibuilder/config: -------------------------------------------------------------------------------- 1 | code: 2 | apicollective: 3 | apibuilder-union-of-unions: 4 | version: latest 5 | generators: 6 | play_2_6_client: generated/app 7 | -------------------------------------------------------------------------------- /examples/union-of-unions/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | logs/ 3 | .ivy2 4 | *.downloaded.scala 5 | *.downloaded.rb 6 | *.swp 7 | *.swo 8 | .bsp/ 9 | .idea/ 10 | .idea_modules/ 11 | .DS_Store 12 | out 13 | -------------------------------------------------------------------------------- /examples/union-of-unions/README.md: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | 4 | Provides an end to end example of json serialization/deserialization of a union type 5 | that itself includes a union type. 6 | 7 | Updating Code 8 | ============= 9 | ``` 10 | ./update.sh 11 | ``` 12 | -------------------------------------------------------------------------------- /examples/union-of-unions/apibuilder-union-of-unions.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apibuilder-union-of-unions", 3 | 4 | "unions": { 5 | "party": { 6 | "discriminator": "discriminator", 7 | "types": [ 8 | { "type": "user", "default": true }, 9 | { "type": "group" } 10 | ] 11 | }, 12 | "user": { 13 | "discriminator": "discriminator", 14 | "types": [ 15 | { "type": "registered_user" }, 16 | { "type": "guest_user" } 17 | ] 18 | } 19 | }, 20 | 21 | "models": { 22 | 23 | "registered_user": { 24 | "fields": [ 25 | { "name": "guid", "type": "uuid", "description": "Internal unique identifier for this user." }, 26 | { "name": "email", "type": "string" } 27 | ] 28 | }, 29 | 30 | "guest_user": { 31 | "fields": [ 32 | { "name": "email", "type": "string", "required": false } 33 | ] 34 | }, 35 | 36 | "group": { 37 | "fields": [ 38 | { "name": "name", "type": "string" } 39 | ] 40 | } 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /examples/union-of-unions/build.sbt: -------------------------------------------------------------------------------- 1 | name := "apibuilder-union-of-unions" 2 | 3 | organization := "io.apibuilder.generator" 4 | 5 | ThisBuild / scalaVersion := "2.13.11" 6 | 7 | lazy val generated = project 8 | .in(file("generated")) 9 | .enablePlugins(PlayScala) 10 | .settings( 11 | libraryDependencies ++= Seq( 12 | ws 13 | ) 14 | ) 15 | 16 | lazy val api = project 17 | .in(file("api")) 18 | .dependsOn(generated) 19 | .aggregate(generated) 20 | .enablePlugins(PlayScala) 21 | .settings( 22 | libraryDependencies ++= Seq( 23 | ws, 24 | specs2 % Test, 25 | "org.scalatestplus.play" %% "scalatestplus-play" % "5.1.0", 26 | "org.scalatest" %% "scalatest" % "3.2.10" % Test, 27 | ) 28 | ) 29 | -------------------------------------------------------------------------------- /examples/union-of-unions/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.5.5 2 | -------------------------------------------------------------------------------- /examples/union-of-unions/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | logLevel := Level.Warn 2 | 3 | resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" 4 | 5 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.5") 6 | -------------------------------------------------------------------------------- /examples/union-of-unions/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PROFILE=localhost apibuilder upload apicollective apibuilder-union-of-unions apibuilder-union-of-unions.json --version `sem-info tag latest` 4 | PROFILE=localhost apibuilder update 5 | -------------------------------------------------------------------------------- /examples/union-types-discriminator/.apidoc: -------------------------------------------------------------------------------- 1 | code: 2 | bryzek: 3 | apidoc-example-union-types-discriminator: 4 | version: latest 5 | generators: 6 | play_2_4_client: generated/app 7 | play_2_x_routes: api/conf/routes -------------------------------------------------------------------------------- /examples/union-types-discriminator/README.md: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | This is a simple skeleton application demonstrating the user of union 4 | types with the scala clients. 5 | 6 | 7 | Updating Code 8 | ============= 9 | 10 | PROFILE=localhost apidoc upload bryzek apidoc-example-union-types-discriminator ./api.json --version `sem-info tag latest` 11 | PROFILE=localhost apidoc update 12 | -------------------------------------------------------------------------------- /examples/union-types-discriminator/api.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apidoc-example-union-types-discriminator", 3 | 4 | "enums": { 5 | "system_user": { 6 | "values": [ 7 | { "name": "system" }, 8 | { "name": "anonymous" } 9 | ] 10 | } 11 | }, 12 | 13 | "unions": { 14 | "user": { 15 | "discriminator": "discriminator", 16 | "types": [ 17 | { 18 | "type": "registered_user" 19 | }, 20 | { 21 | "type": "guest_user" 22 | }, 23 | { 24 | "type": "system_user" 25 | }, 26 | { 27 | "type": "string" 28 | } 29 | ] 30 | } 31 | }, 32 | 33 | "models": { 34 | "registered_user": { 35 | "fields": [ 36 | { 37 | "name": "id", 38 | "type": "string" 39 | }, 40 | { 41 | "name": "email", 42 | "type": "string" 43 | } 44 | ] 45 | }, 46 | "guest_user": { 47 | "fields": [ 48 | { 49 | "name": "id", 50 | "type": "string", 51 | "description": "Internal unique identifier for this user." 52 | }, 53 | { 54 | "name": "email", 55 | "type": "string", "required": false 56 | } 57 | ] 58 | } 59 | }, 60 | 61 | "resources": { 62 | "user": { 63 | "operations": [ 64 | { 65 | "method": "GET", 66 | "responses": { 67 | "200": { 68 | "type": "[user]" 69 | } 70 | } 71 | }, 72 | { 73 | "method": "GET", 74 | "path": "/:id", 75 | "responses": { 76 | "200": { 77 | "type": "user" 78 | }, 79 | "404": { "type": "unit" } 80 | } 81 | }, 82 | { 83 | "method": "POST", 84 | "body": { 85 | "type": "user" 86 | }, 87 | "responses": { 88 | "201": { 89 | "type": "user" 90 | } 91 | } 92 | } 93 | ] 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /examples/union-types-discriminator/api/api.json: -------------------------------------------------------------------------------- 1 | { 2 | "apidoc": { 3 | "version": "0.9.6" 4 | }, 5 | 6 | "name": "apidoc Example Union Types", 7 | 8 | "enums": { 9 | "foo": { 10 | "values": [ 11 | { "name": "a" } 12 | ] 13 | }, 14 | "bar": { 15 | "values": [ 16 | { "name": "b" } 17 | ] 18 | } 19 | }, 20 | 21 | "unions": { 22 | "user": { 23 | "types": [ 24 | { "type": "registered_user" }, 25 | { "type": "guest_user" }, 26 | { "type": "uuid" } 27 | ] 28 | }, 29 | "foobar": { 30 | "types": [ 31 | { "type": "foo" }, 32 | { "type": "bar" } 33 | ] 34 | } 35 | }, 36 | 37 | "models": { 38 | 39 | "registered_user": { 40 | "fields": [ 41 | { "name": "guid", "type": "uuid", "description": "Internal unique identifier for this user." }, 42 | { "name": "email", "type": "string" }, 43 | { "name": "preference", "type": "foobar" } 44 | ] 45 | }, 46 | 47 | "guest_user": { 48 | "fields": [ 49 | { "name": "guid", "type": "uuid", "description": "Internal unique identifier for this user." }, 50 | { "name": "email", "type": "string" } 51 | ] 52 | } 53 | }, 54 | 55 | "resources": { 56 | "user": { 57 | "operations": [ 58 | { 59 | "method": "GET", 60 | "responses": { 61 | "200": { "type": "[user]" } 62 | } 63 | }, 64 | 65 | { 66 | "method": "GET", 67 | "path": "/:guid", 68 | "responses": { 69 | "200": { "type": "user" }, 70 | "404": { "type": "unit" } 71 | } 72 | }, 73 | 74 | { 75 | "method": "POST", 76 | "body": { "type": "user" }, 77 | "responses": { 78 | "201": { "type": "user" } 79 | } 80 | } 81 | 82 | ] 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /examples/union-types-discriminator/api/conf/application.conf: -------------------------------------------------------------------------------- 1 | play.i18n.langs="en" 2 | play.http.secret.key="development" 3 | 4 | evolutionplugin=disabled 5 | -------------------------------------------------------------------------------- /examples/union-types-discriminator/api/conf/routes: -------------------------------------------------------------------------------- 1 | # Generated by API Builder - https://www.apibuilder.io 2 | # Service version: 0.3.46 3 | # apidoc:0.11.19 http://localhost:9000/bryzek/apidoc-example-union-types-discriminator/0.3.46/play_2_x_routes 4 | 5 | GET /users controllers.Users.get() 6 | GET /users/:id controllers.Users.getById(id: String) 7 | POST /users controllers.Users.post() -------------------------------------------------------------------------------- /examples/union-types-discriminator/build.sbt: -------------------------------------------------------------------------------- 1 | import play.PlayImport.PlayKeys._ 2 | 3 | name := "apidoc-example-union-types-discriminator" 4 | 5 | organization := "io.apibuilder.generator" 6 | 7 | scalaVersion in ThisBuild := "2.11.7" 8 | 9 | lazy val generated = project 10 | .in(file("generated")) 11 | .enablePlugins(PlayScala) 12 | .settings( 13 | libraryDependencies ++= Seq( 14 | ws 15 | ) 16 | ) 17 | 18 | lazy val api = project 19 | .in(file("api")) 20 | .dependsOn(generated) 21 | .aggregate(generated) 22 | .enablePlugins(PlayScala) 23 | .settings( 24 | routesImport += "io.apibuilder.example.union.types.discriminator.v0._", 25 | libraryDependencies ++= Seq( 26 | ws, 27 | specs2 % Test, 28 | "org.scalatest" %% "scalatest" % "2.2.5" % Test, 29 | "org.scalatestplus" %% "play" % "1.4.0-M4" % Test 30 | ) 31 | ) 32 | -------------------------------------------------------------------------------- /examples/union-types-discriminator/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.9 2 | -------------------------------------------------------------------------------- /examples/union-types-discriminator/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // Comment to get more information during initialization 2 | logLevel := Level.Warn 3 | 4 | // The Typesafe repository 5 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" 6 | 7 | // Use the Play sbt plugin for Play projects 8 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.3") 9 | -------------------------------------------------------------------------------- /examples/union-types-discriminator/ruby-client-example.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Download the ruby client from apidoc 4 | load 'ruby_client.txt' 5 | 6 | client = Com::Gilt::Apidoc::Example::Union::Types::V0::Client.new("http://localhost:9003") 7 | 8 | def extract_guid(user) 9 | if user.respond_to?(:guid) 10 | user.guid 11 | elsif user.respond_to?(:value) 12 | user.value 13 | else 14 | raise "Cannot find guid for user: " + user.inspect 15 | end 16 | end 17 | 18 | puts "Fetching all users" 19 | guids = client.users.get.map do |u| 20 | puts u.inspect 21 | extract_guid(u) 22 | end 23 | 24 | guids.each do |guid| 25 | puts "Fetching user with guid[#{guid}]" 26 | u = client.users.get_by_guid(guid) 27 | puts " --> Got user with guid: #{extract_guid(u)}" 28 | end 29 | 30 | 31 | puts "Creating a guest user" 32 | guid = "f3973f60-be9f-11e3-b1b6-0800200c9a67" 33 | user = Com::Gilt::Apidoc::Example::Union::Types::V0::Models::GuestUser.new(:guid => guid, :email => "#{guid}@gilttest.com") 34 | puts user.to_json 35 | client.users.post(user) 36 | 37 | puts "Creating a registered user" 38 | guid = "f3973f60-be9f-11e3-b1b6-0800200c9a66" 39 | user = Com::Gilt::Apidoc::Example::Union::Types::V0::Models::RegisteredUser.new(:guid => guid, :email => "#{guid}@gilttest.com", :preference => Com::Gilt::Apidoc::Example::Union::Types::V0::Models::Foo.a) 40 | puts user.to_json 41 | client.users.post(user) 42 | 43 | puts "Creating a user uuid wrapper" 44 | guid = "f3973f60-be9f-11e3-b1b6-0800200c9a65" 45 | user = Com::Gilt::Apidoc::Example::Union::Types::V0::Models::UserUuidWrapper.new(:value => guid) 46 | puts user.to_json 47 | client.users.post(user) 48 | 49 | puts "DONE" 50 | -------------------------------------------------------------------------------- /examples/union-types-discriminator/script/update-generated-code: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | server = "http://localhost:9000" 4 | project = "apidoc-example-union-types" 5 | 6 | data = { 7 | "play_2_3_client" => "generated/app/ApidocExampleUnionTypesClient.scala", 8 | "play_2_x_routes" => "api/conf/routes" 9 | } 10 | 11 | data.each do |target, path| 12 | cmd = "curl --silent #{server}/gilt/#{project}/latest/#{target} > #{path}" 13 | puts cmd 14 | system(cmd) 15 | end 16 | -------------------------------------------------------------------------------- /examples/union-types/.apidoc: -------------------------------------------------------------------------------- 1 | code: 2 | bryzek: 3 | apidoc-example-union-types: 4 | version: latest 5 | generators: 6 | play_2_4_client: generated/app 7 | play_2_x_routes: api/conf/routes 8 | -------------------------------------------------------------------------------- /examples/union-types/README.md: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | This is a simple skeleton application demonstrating the user of union 4 | types with the scala clients. 5 | 6 | 7 | Updating Code 8 | ============= 9 | 10 | PROFILE=localhost apidoc upload bryzek apidoc-example-union-types api.json --version `sem-info tag latest` 11 | PROFILE=localhost apidoc update 12 | -------------------------------------------------------------------------------- /examples/union-types/api.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apidoc-example-union-types", 3 | 4 | "enums": { 5 | "foo": { 6 | "values": [ 7 | { "name": "a" } 8 | ] 9 | }, 10 | "bar": { 11 | "values": [ 12 | { "name": "b" } 13 | ] 14 | } 15 | }, 16 | 17 | "unions": { 18 | "user": { 19 | "types": [ 20 | { "type": "registered_user" }, 21 | { "type": "guest_user" }, 22 | { "type": "uuid" } 23 | ] 24 | }, 25 | "foobar": { 26 | "types": [ 27 | { "type": "foo" }, 28 | { "type": "bar" } 29 | ] 30 | } 31 | }, 32 | 33 | "models": { 34 | 35 | "registered_user": { 36 | "fields": [ 37 | { "name": "guid", "type": "uuid", "description": "Internal unique identifier for this user." }, 38 | { "name": "email", "type": "string" }, 39 | { "name": "preference", "type": "foobar" } 40 | ] 41 | }, 42 | 43 | "guest_user": { 44 | "fields": [ 45 | { "name": "guid", "type": "uuid", "description": "Internal unique identifier for this user." }, 46 | { "name": "email", "type": "string" } 47 | ] 48 | } 49 | }, 50 | 51 | "resources": { 52 | "user": { 53 | "operations": [ 54 | { 55 | "method": "GET", 56 | "responses": { 57 | "200": { "type": "[user]" } 58 | } 59 | }, 60 | 61 | { 62 | "method": "GET", 63 | "path": "/:guid", 64 | "responses": { 65 | "200": { "type": "user" }, 66 | "404": { "type": "unit" } 67 | } 68 | }, 69 | 70 | { 71 | "method": "POST", 72 | "body": { "type": "user" }, 73 | "responses": { 74 | "201": { "type": "user" } 75 | } 76 | } 77 | 78 | ] 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /examples/union-types/api/conf/application.conf: -------------------------------------------------------------------------------- 1 | play.i18n.langs="en" 2 | play.http.secret.key="development" 3 | 4 | evolutionplugin=disabled 5 | -------------------------------------------------------------------------------- /examples/union-types/api/conf/routes: -------------------------------------------------------------------------------- 1 | # Generated by API Builder - https://www.apibuilder.io 2 | # Service version: 0.3.46 3 | # apidoc:0.11.19 http://localhost:9000/bryzek/apidoc-example-union-types/0.3.46/play_2_x_routes 4 | 5 | GET /users controllers.Users.get() 6 | GET /users/:guid controllers.Users.getByGuid(guid: _root_.java.util.UUID) 7 | POST /users controllers.Users.post() -------------------------------------------------------------------------------- /examples/union-types/build.sbt: -------------------------------------------------------------------------------- 1 | import play.PlayImport.PlayKeys._ 2 | 3 | name := "apidoc-example-union-types" 4 | 5 | organization := "io.apibuilder.generator" 6 | 7 | scalaVersion in ThisBuild := "2.11.7" 8 | 9 | lazy val generated = project 10 | .in(file("generated")) 11 | .enablePlugins(PlayScala) 12 | .settings( 13 | libraryDependencies ++= Seq( 14 | ws 15 | ) 16 | ) 17 | 18 | lazy val api = project 19 | .in(file("api")) 20 | .dependsOn(generated) 21 | .aggregate(generated) 22 | .enablePlugins(PlayScala) 23 | .settings( 24 | routesImport += "io.apibuilder.example.union.types.v0._", 25 | libraryDependencies ++= Seq( 26 | ws, 27 | specs2 % Test, 28 | "org.scalatest" %% "scalatest" % "2.2.5" % Test, 29 | "org.scalatestplus" %% "play" % "1.4.0-M4" % Test 30 | ) 31 | ) 32 | -------------------------------------------------------------------------------- /examples/union-types/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.9 2 | -------------------------------------------------------------------------------- /examples/union-types/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // Comment to get more information during initialization 2 | logLevel := Level.Warn 3 | 4 | // The Typesafe repository 5 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" 6 | 7 | // Use the Play sbt plugin for Play projects 8 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.3") 9 | -------------------------------------------------------------------------------- /generator/app/controllers/Healthchecks.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api.libs.json._ 4 | import play.api.mvc._ 5 | 6 | class Healthchecks extends InjectedController { 7 | 8 | private val Result = Json.toJson(Map("status" -> "healthy")) 9 | 10 | def get() = Action { 11 | Ok(Result) 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /generator/app/controllers/Invocations.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import io.apibuilder.generator.v0.models.json._ 4 | import io.apibuilder.generator.v0.models.{Invocation, InvocationForm} 5 | import lib.{ServiceApidocBug, Validation} 6 | import play.api.libs.json._ 7 | import play.api.mvc._ 8 | 9 | class Invocations extends InjectedController { 10 | 11 | def getByKey(key: String): Action[AnyContent] = Action { _ => 12 | Conflict(Json.toJson(Validation.error( 13 | s"Use HTTPS POST (not GET) to invoke the generator with key '$key'" 14 | ))) 15 | } 16 | 17 | def postByKey(key: String): Action[AnyContent] = Action { request => 18 | request.body.asJson match { 19 | case None => Conflict(Json.toJson(Validation.error("Must provide form data (JSON)"))) 20 | case Some(incomingJs) => { 21 | val js = ServiceApidocBug.rewrite(incomingJs) 22 | Generators.findGenerator(key).map(_.generator) match { 23 | case Some(generator) => 24 | js.validate[InvocationForm] match { 25 | case e: JsError => Conflict(Json.toJson(Validation.invalidJson(e))) 26 | case s: JsSuccess[InvocationForm] => { 27 | val form = s.get 28 | generator.invoke(form) match { 29 | case Left(errors) => Conflict(Json.toJson(Validation.errors(errors))) 30 | case Right(sourceFiles) => 31 | // Also send back single source for backwards compatibility 32 | val singleSource = sourceFiles.map(_.contents).mkString("\n\n").trim 33 | Ok(Json.toJson(Invocation(singleSource, sourceFiles))) 34 | } 35 | } 36 | } 37 | case _ => NotFound 38 | } 39 | } 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /generator/app/lib/ErrorHandler.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import io.apibuilder.generator.v0.models.json._ 4 | import play.api.Logging 5 | import play.api.libs.json.Json 6 | import play.api.mvc.Results.{BadRequest, InternalServerError} 7 | import play.api.mvc.{RequestHeader, Result} 8 | 9 | import scala.concurrent.Future 10 | 11 | class ErrorHandler 12 | extends play.api.http.HttpErrorHandler with Logging { 13 | 14 | override def onClientError(request: RequestHeader, statusCode: Int, message: String = ""): Future[Result] = { 15 | logger.warn(s"client error '${request.method} ${request.path}' req[$request] statusCode[$statusCode] message[$message]") 16 | val msg = if (message.isEmpty) { 17 | statusCode.toString 18 | } else { 19 | message 20 | } 21 | Future.successful(BadRequest(Json.toJson(Validation.serverError(s"Bad Request: $msg")))) 22 | } 23 | 24 | def onServerError(request: RequestHeader, exception: Throwable): Future[Result] = { 25 | logger.error(exception.toString, exception) 26 | Future.successful(InternalServerError(Json.toJson(Validation.serverError()))) 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /generator/app/lib/Filters.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import javax.inject.Inject 4 | 5 | import play.api.http.EnabledFilters 6 | import play.filters.gzip.GzipFilter 7 | import play.http.DefaultHttpFilters 8 | 9 | class Filters @Inject() ( 10 | defaultFilters: EnabledFilters, 11 | gzip: GzipFilter, 12 | log: LoggingFilter) 13 | extends DefaultHttpFilters(defaultFilters.filters :+ gzip :+ log: _*) 14 | -------------------------------------------------------------------------------- /generator/app/lib/LoggingFilter.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import akka.stream.Materializer 4 | import javax.inject.Inject 5 | import play.api.Logging 6 | import play.api.mvc._ 7 | 8 | import scala.concurrent.{ExecutionContext, Future} 9 | 10 | class LoggingFilter @Inject ()(implicit val mat: Materializer, ec: ExecutionContext) extends Filter with Logging { 11 | def apply(nextFilter: (RequestHeader) => Future[Result]) 12 | (requestHeader: RequestHeader): Future[Result] = { 13 | val startTime = System.currentTimeMillis 14 | nextFilter(requestHeader).map { result => 15 | val endTime = System.currentTimeMillis 16 | val requestTime = endTime - startTime 17 | logger.info(s"${requestHeader.method} ${requestHeader.uri} " + 18 | s"took ${requestTime}ms and returned ${result.header.status}") 19 | result.withHeaders("Request-Time" -> requestTime.toString) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /generator/app/lib/ServiceApidocBug.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import play.api.libs.json.{JsArray, JsObject, JsDefined, JsValue, Json} 4 | 5 | /** 6 | * Version 0.4.28 of apibuilder-validation has an invalid Service spec definition which makes 7 | * the apidoc field required. This dependency is pulled in by apibuilder-graphql. Once we can 8 | * migrate the generator to scala3 we can pull in the latest dependencies. Until then to keep 9 | * things working, we inject a default "apidoc" node where not specified. 10 | */ 11 | object ServiceApidocBug { 12 | private val DefaultApiDoc = Json.obj( 13 | "apidoc" -> Json.obj( 14 | "version" -> "0.16.0" 15 | ) 16 | ) 17 | 18 | def rewrite(js: JsValue): JsValue = { 19 | js match { 20 | case o: JsObject => rewriteObject(o) 21 | case _ => js 22 | } 23 | } 24 | 25 | private def rewriteObject(js: JsObject): JsObject = { 26 | rewriteImportedServices( 27 | rewriteService(js) 28 | ) 29 | } 30 | 31 | private def rewriteService(js: JsObject): JsObject = { 32 | js \ "service" match { 33 | case JsDefined(svc: JsObject) => { 34 | js ++ Json.obj("service" -> maybeAddApidoc(svc)) 35 | } 36 | case _ => js 37 | } 38 | } 39 | 40 | private def rewriteImportedServices(js: JsObject): JsObject = { 41 | js \ "imported_services" match { 42 | case JsDefined(svc: JsArray) => { 43 | js ++ Json.obj( 44 | "imported_services" -> JsArray( 45 | svc.value.toSeq.map { 46 | case o: JsObject => maybeAddApidoc(o) 47 | case o => o 48 | } 49 | ) 50 | ) 51 | } 52 | case _ => js 53 | } 54 | } 55 | 56 | private def maybeAddApidoc(js: JsObject): JsObject = { 57 | if (js.fields.contains("apidoc")) { 58 | js 59 | } else { 60 | js ++ DefaultApiDoc 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /generator/app/lib/Validation.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import io.apibuilder.generator.v0.models.Error 4 | import play.api.libs.json.JsError 5 | 6 | object Validation { 7 | 8 | private val InvalidJsonCode = "invalid_json" 9 | private val ErrorCode = "validation_error" 10 | private val ServerError = "server_error" 11 | 12 | def invalidJson(errors: JsError): Seq[Error] = { 13 | Seq(Error(InvalidJsonCode, errors.toString)) 14 | } 15 | 16 | def error(message: String): Seq[Error] = { 17 | errors(Seq(message)) 18 | } 19 | 20 | def errors(messages: Seq[String]): Seq[Error] = { 21 | messages.map { msg => Error(ErrorCode, msg) } 22 | } 23 | 24 | def serverError(error: String = "Internal Server Error"): Seq[Error] = { 25 | Seq(Error(ServerError, error)) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /generator/conf/application.conf: -------------------------------------------------------------------------------- 1 | include "base.conf" 2 | 3 | play.http.secret.key="development" 4 | 5 | -------------------------------------------------------------------------------- /generator/conf/application.production.conf: -------------------------------------------------------------------------------- 1 | include "base.conf" 2 | 3 | play.http.secret.key=${?CONF_PLAY_CRYPTO_SECRET} 4 | -------------------------------------------------------------------------------- /generator/conf/base.conf: -------------------------------------------------------------------------------- 1 | play.i18n.langs=["en"] 2 | 3 | evolutionplugin=disabled 4 | 5 | play.http.requestHandler = "play.http.DefaultHttpRequestHandler" 6 | play.http.errorHandler = "lib.ErrorHandler" 7 | play.http.filters = "lib.Filters" 8 | play.http.parser.maxMemoryBuffer=10M 9 | 10 | play.filters.disabled += "play.filters.hosts.AllowedHostsFilter" 11 | -------------------------------------------------------------------------------- /generator/conf/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /generator/conf/routes: -------------------------------------------------------------------------------- 1 | # Generated by API Builder - https://www.apibuilder.io 2 | # Service version: 0.16.53 3 | # User agent: apibuilder app.apibuilder.io/apicollective/apibuilder-generator/latest/play_2_x_routes 4 | 5 | GET /generators controllers.Generators.get(key: _root_.scala.Option[String], limit: Int ?= 100, offset: Int ?= 0) 6 | GET /generators/:key controllers.Generators.getByKey(key: String) 7 | GET /_internal_/healthcheck controllers.Healthchecks.get() 8 | POST /invocations/:key controllers.Invocations.postByKey(key: String) -------------------------------------------------------------------------------- /go-generator/src/main/scala/models/ApiBuilderComments.scala: -------------------------------------------------------------------------------- 1 | package go.models 2 | 3 | import lib.AbstractApiBuilderComments 4 | 5 | /** 6 | * @param version e.g. 1.2.3 Used to inject headers on the full version and the major version number 7 | */ 8 | case class ApiBuilderComments(override val version: String, override val userAgent: Option[String]) extends AbstractApiBuilderComments(version, userAgent) { 9 | 10 | val comments: String = GoUtil.textToComment(elements) 11 | 12 | } 13 | -------------------------------------------------------------------------------- /go-generator/src/main/scala/models/GoClientGenerator.scala: -------------------------------------------------------------------------------- 1 | package go.models 2 | 3 | import io.apibuilder.generator.v0.models.{File, InvocationForm} 4 | import lib.generator.CodeGenerator 5 | 6 | object GoClientGenerator extends CodeGenerator { 7 | 8 | override def invoke(form: InvocationForm): Either[Seq[String], Seq[File]] = { 9 | Code(form).generate() match { 10 | case None => { 11 | Left(Seq("No enums, models, or unions were found and thus no client was generated")) 12 | } 13 | case Some(code) => { 14 | Right( 15 | Seq( 16 | File( 17 | name = GoUtil.packageName(form.service.name) + ".go", 18 | contents = code 19 | ) 20 | ) 21 | ) 22 | } 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /go-generator/src/main/scala/models/Headers.scala: -------------------------------------------------------------------------------- 1 | package go.models 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | import lib.VersionTag 5 | 6 | case class Headers( 7 | importBuilder: ImportBuilder, 8 | form: InvocationForm 9 | ) { 10 | 11 | private val versionMajor: Option[Int] = VersionTag(form.service.version).major 12 | 13 | private val VersionMajorName = "VersionMajor" 14 | private val VersionMajorHeaderName = "X-Apidoc-Version-Major" 15 | 16 | private val constants = Seq( 17 | form.service.baseUrl.map { url => ("BaseUrl" -> url) }, 18 | Some(("UserAgent", form.userAgent.getOrElse("apibuilder-go_1_5_client-unknown"))), 19 | Some(("Version", form.service.version)), 20 | versionMajor.map { major => (VersionMajorName, major.toString) } 21 | ).flatten 22 | 23 | def generate(): String = { 24 | constants.map { pair => 25 | val name = GoUtil.publicName(pair._1) 26 | if (pair._1 == VersionMajorName) { 27 | s"const $name = ${pair._2}" 28 | } else { 29 | s"const $name = ${GoUtil.wrapInQuotes(pair._2)}" 30 | } 31 | }.mkString("\n") 32 | } 33 | 34 | def all() = Seq( 35 | Some("User-Agent" -> s"UserAgent"), 36 | Some("X-Apidoc-Version" -> s"Version"), 37 | versionMajor.map { _ => 38 | VersionMajorHeaderName -> s"${importBuilder.ensureImport("strconv")}.Itoa($VersionMajorName)" 39 | } 40 | ).flatten ++ form.service.headers.filter(!_.default.isEmpty).map { h => 41 | (h.name -> GoUtil.wrapInQuotes(h.default.get)) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /go-generator/src/main/scala/models/ImportPath.scala: -------------------------------------------------------------------------------- 1 | package go.models 2 | 3 | import lib.Text 4 | 5 | case class ImportPath(url: String, alias: String) extends Ordered[ImportPath] { 6 | 7 | def compare(other: ImportPath) = { 8 | url == other.url match { 9 | case false => url.compare(other.url) 10 | case true => alias.compare(other.alias) 11 | } 12 | } 13 | 14 | } 15 | 16 | 17 | object ImportPath { 18 | 19 | // Ex: io.flow.carrier.account.v0.unions.expandable_carrier_account 20 | private val ApibuilderUrlPattern = """^(.+)\.v\d+\.\w+\.?([^\.]*)$""".r 21 | 22 | def apply(value: String, mappings: Map[String, String]): ImportPath = { 23 | value match { 24 | case ApibuilderUrlPattern(pkg, _) => { 25 | 26 | mappings.keys.toSeq.sortBy(_.length).reverse.find { key => 27 | pkg.startsWith(s"${key}.") 28 | } match { 29 | case None => { 30 | val defaultUrl = pkg.split("\\.").mkString("/") 31 | ImportPath(defaultUrl, defaultAlias(defaultUrl)) 32 | } 33 | 34 | case Some(domain) => { 35 | val p = pkg.replace(s"${domain}.", "") // Ex: carrier.account 36 | val url = mappings(domain) + "/" + p.split("\\.").mkString("/") 37 | val alias = Text.snakeToCamelCase(p) 38 | ImportPath(url, GoUtil.quoteNameIfKeyword(alias)) 39 | } 40 | } 41 | } 42 | 43 | case _ => { 44 | ImportPath(value, defaultAlias(value)) 45 | } 46 | } 47 | } 48 | 49 | /** 50 | * This returns the default alias for the specified import as go 51 | * will interpret it. For example, if you import "net/http", the 52 | * default alias is 'http'. 53 | */ 54 | def defaultAlias(name: String): String = { 55 | GoUtil.quoteNameIfKeyword(name.split("/").last) 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /go-generator/src/test/scala/models/FormatterSpec.scala: -------------------------------------------------------------------------------- 1 | package go.models 2 | 3 | import Formatter._ 4 | import org.scalatest.funspec.AnyFunSpec 5 | import org.scalatest.matchers.should.Matchers 6 | 7 | class FormatterSpec extends AnyFunSpec with Matchers { 8 | 9 | it("assignment") { 10 | """ 11 | CalendarWeekdays Calendar = "Weekdays" 12 | CalendarEveryday = "Everyday" 13 | """.trim.table() should be(""" 14 | CalendarWeekdays Calendar = "Weekdays" 15 | CalendarEveryday = "Everyday" 16 | """.trim) 17 | } 18 | 19 | it("leaves comments alone") { 20 | """ 21 | // This is a comment 22 | a b 23 | c d 24 | """.trim.table() should be(""" 25 | // This is a comment 26 | a b 27 | c d 28 | """.trim) 29 | } 30 | 31 | it("table") { 32 | "a".table() should be("a") 33 | "a b".table() should be("a b") 34 | 35 | """ 36 | a b 37 | c d 38 | """.trim.table() should be(""" 39 | a b 40 | c d 41 | """.trim) 42 | 43 | """ 44 | a b 45 | foo 46 | c d 47 | """.trim.table() should be(""" 48 | a b 49 | foo 50 | c d 51 | """.trim) 52 | } 53 | 54 | it("table w/ 3 columns") { 55 | """ 56 | a b 57 | a b c 58 | apple bat cat 59 | """.trim.table() should be(""" 60 | a b 61 | a b c 62 | apple bat cat 63 | """.trim) 64 | } 65 | 66 | it("table preserves leading spaces") { 67 | " a".table() should be(" a") 68 | } 69 | 70 | it("indent") { 71 | "foo".indentString(0) should be("foo") 72 | "foo".indentString(1) should be("\tfoo") 73 | "foo".indentString(2) should be("\t\tfoo") 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /go-generator/src/test/scala/models/GoUtilSpec.scala: -------------------------------------------------------------------------------- 1 | package go.models 2 | 3 | import org.scalatest.funspec.AnyFunSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | class GoUtilSpec extends AnyFunSpec with Matchers { 7 | 8 | it("quoteNameIfKeyword") { 9 | GoUtil.quoteNameIfKeyword("foo") should be("foo") 10 | GoUtil.quoteNameIfKeyword("int") should be("int_") 11 | } 12 | 13 | it("textToComment") { 14 | GoUtil.textToComment(None) should be("") 15 | GoUtil.textToComment(Some("foo")) should be(""" 16 | /** 17 | * foo 18 | */ 19 | """.trim + "\n") 20 | } 21 | 22 | it("textToSingleLineComment") { 23 | GoUtil.textToSingleLineComment(None) should be("") 24 | GoUtil.textToSingleLineComment(Some("foo")) should be("// foo\n") 25 | } 26 | 27 | it("wrapInQuotes") { 28 | GoUtil.wrapInQuotes("foo") should be(""""foo"""") 29 | } 30 | 31 | it("publicName") { 32 | GoUtil.publicName("foo") should be("Foo") 33 | GoUtil.publicName("Foo") should be("Foo") 34 | GoUtil.publicName("fooBar") should be("FooBar") 35 | GoUtil.publicName("foo_Bar") should be("FooBar") 36 | GoUtil.publicName("fooBar") should be("FooBar") 37 | GoUtil.publicName("int") should be("Int") 38 | } 39 | 40 | it("privateName") { 41 | GoUtil.privateName("foo") should be("foo") 42 | GoUtil.privateName("Foo") should be("foo") 43 | GoUtil.privateName("fooBar") should be("fooBar") 44 | GoUtil.privateName("foo_Bar") should be("fooBar") 45 | GoUtil.privateName("fooBar") should be("fooBar") 46 | GoUtil.privateName("int") should be("int_") 47 | } 48 | 49 | it("packageName") { 50 | GoUtil.packageName("foo") should be("foo") 51 | GoUtil.packageName("Foo") should be("foo") 52 | GoUtil.packageName("fooBar") should be("foobar") 53 | GoUtil.packageName("foo_Bar") should be("foobar") 54 | GoUtil.packageName("fooBar") should be("foobar") 55 | GoUtil.packageName("int") should be("int_") 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /graphql-generator/src/main/scala/generator/graphql/GraphQLApolloGenerator.scala: -------------------------------------------------------------------------------- 1 | package generator.graphql 2 | 3 | import cats.data.Validated.{Invalid, Valid} 4 | import io.apibuilder.generator.v0.models.{File, InvocationForm} 5 | import io.apibuilder.graphql.GraphQLCodeGenerator 6 | import io.apibuilder.validation.{ApiBuilderService, MultiService} 7 | import lib.generator.CodeGenerator 8 | 9 | object GraphQLApolloGenerator extends CodeGenerator { 10 | 11 | override def invoke(form: InvocationForm): Either[Seq[String], Seq[File]] = { 12 | GraphQLCodeGenerator.Default.generate(toMultiService(form)) match { 13 | case Valid(r) => Right(r.files) 14 | case Invalid(errors) => Left(errors.toNonEmptyList.toList) 15 | } 16 | } 17 | 18 | def toMultiService(form: InvocationForm): MultiService = { 19 | MultiService( 20 | (Seq(form.service) ++ form.importedServices.getOrElse(Nil)).map(ApiBuilderService.apply).toList 21 | ) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /graphql-generator/src/main/scala/generator/graphql/GraphQLSchemaGenerator.scala: -------------------------------------------------------------------------------- 1 | package generator.graphql 2 | 3 | import io.apibuilder.generator.v0.models.{File, InvocationForm} 4 | import lib.generator.CodeGenerator 5 | 6 | object GraphQLSchemaGenerator extends CodeGenerator { 7 | 8 | override def invoke(form: InvocationForm): Either[Seq[String], Seq[File]] = { 9 | GraphQLApolloGenerator.invoke(form).map { r => 10 | r.headOption.toSeq 11 | } 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /graphql-generator/src/test/scala/generator/graphql/GraphQLApolloGeneratorSpec.scala: -------------------------------------------------------------------------------- 1 | package generator.graphql 2 | 3 | import generator.graphql.helpers.TestHelpers 4 | import io.apibuilder.builders.ApiBuilderServiceBuilders 5 | import io.apibuilder.generator.v0.models.InvocationForm 6 | import org.scalatest.matchers.must.Matchers 7 | import org.scalatest.wordspec.AnyWordSpec 8 | 9 | class GraphQLApolloGeneratorSpec extends AnyWordSpec with Matchers 10 | with ApiBuilderServiceBuilders 11 | with TestHelpers 12 | { 13 | "Services with no types" in { 14 | // TODO: Update apibuilder-validation before running these tests 15 | /* 16 | val s = makeService() 17 | rightOrErrors(GraphQLApolloGenerator.invoke(InvocationForm(s))).map(_.name) must equal( 18 | Seq("schema.graphql", "resolvers.ts", "type-metadata.ts") 19 | ) 20 | */ 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /graphql-generator/src/test/scala/generator/graphql/GraphQLSchemaGeneratorSpec.scala: -------------------------------------------------------------------------------- 1 | package generator.graphql 2 | 3 | import generator.graphql.helpers.TestHelpers 4 | import io.apibuilder.builders.ApiBuilderServiceBuilders 5 | import org.scalatest.matchers.must.Matchers 6 | import org.scalatest.wordspec.AnyWordSpec 7 | 8 | class GraphQLSchemaGeneratorSpec extends AnyWordSpec with Matchers 9 | with ApiBuilderServiceBuilders 10 | with TestHelpers 11 | { 12 | 13 | "Services with no types" in { 14 | // TODO: Update apibuilder-validation before running these tests 15 | /* 16 | val s = makeService() 17 | rightOrErrors(GraphQLSchemaGenerator.invoke(InvocationForm(s))).map(_.name) must equal( 18 | Seq("schema.graphql") 19 | ) 20 | */ 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /graphql-generator/src/test/scala/generator/graphql/helpers/TestHelpers.scala: -------------------------------------------------------------------------------- 1 | package generator.graphql.helpers 2 | 3 | trait TestHelpers { 4 | def rightOrErrors[T](value: Either[_, T]): T = { 5 | value match { 6 | case Right(r) => r 7 | case Left(errors) => sys.error(s"Expected valid value but got: ${errors}") 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /java-aws-lambda-pojos/src/main/scala/models/generator/javaAwsLambdaPojos/ApiBuilderComments.scala: -------------------------------------------------------------------------------- 1 | package models.generator.javaAwsLambdaPojos 2 | 3 | import lib.AbstractApiBuilderComments 4 | 5 | case class ApiBuilderComments(override val version: String, override val userAgent: Option[String]) extends AbstractApiBuilderComments(version, userAgent) { 6 | 7 | val forClassFile: String = elements.mkString("", "\n","\n") 8 | 9 | } 10 | -------------------------------------------------------------------------------- /java-aws-lambda-pojos/src/main/scala/models/generator/javaAwsLambdaPojos/JavaAwsLambdaPOJOClasses.scala: -------------------------------------------------------------------------------- 1 | package models.generator.javaAwsLambdaPojos 2 | 3 | object JavaAwsLambdaPOJOClasses 4 | extends BaseJavaAwsLambdaPOJOCodeGenerator { 5 | override def getJavaDocFileHeader() = "WARNING: not all features (notably unions) and data types work with the java generator yet. \n" + 6 | "Java POJO generator is designed to be used with AWS Lambdas. \n" + 7 | "If you are considering using this library, would like to request/discuss features, or would like to share how you're using it, please contact daniel.kirby@gilt.com \n" 8 | 9 | } 10 | -------------------------------------------------------------------------------- /java-generator/src/main/scala/models/generator/ApiBuilderComments.scala: -------------------------------------------------------------------------------- 1 | package models.generator 2 | 3 | import lib.AbstractApiBuilderComments 4 | 5 | /** 6 | * 7 | * Author: jkenny 8 | * Date: 28/05/2015 9 | */ 10 | case class ApiBuilderComments(override val version: String, override val userAgent: Option[String]) extends AbstractApiBuilderComments(version, userAgent) { 11 | 12 | val forClassFile: String = JavaUtil.textToComment(elements) 13 | 14 | } 15 | -------------------------------------------------------------------------------- /java-generator/src/main/scala/models/generator/JavaUtil.scala: -------------------------------------------------------------------------------- 1 | package models.generator 2 | 3 | import lib.Text 4 | 5 | /** 6 | * 7 | * Author: jkenny 8 | * Date: 28/05/2015 9 | */ 10 | object JavaUtil { 11 | private val ReservedWords = Set( 12 | "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", 13 | "default", "do", "double", "else", "enum", "extends", "final", "finally", "float", "for", "goto", 14 | "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", 15 | "package", "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", 16 | "this", "throw", "throws", "transient", "try", "void", "volatile", "while") 17 | 18 | def checkForReservedWord(word: String): String = 19 | if (ReservedWords.contains(word)) word + "_" 20 | else word 21 | 22 | def textToComment(text: String): String = textToComment(Seq(text)) 23 | 24 | def textToComment(text: Seq[String]): String = { 25 | "/**\n * " + text.mkString("\n * ") + "\n */" 26 | } 27 | 28 | def toClassName(modelName: String) = { 29 | // We don't support upper case class names so if a word is upper case then make it lower case 30 | def checkForUpperCase(word: String): String = 31 | if (word == word.toUpperCase) word.toLowerCase 32 | else word 33 | 34 | Text.safeName(Text.splitIntoWords(modelName).map { checkForUpperCase(_).capitalize }.mkString) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /kotlin-generator/src/main/scala/models/generator/kotlin/RetrofitUtil.scala: -------------------------------------------------------------------------------- 1 | package models.generator.kotlin 2 | 3 | object RetrofitUtil { 4 | 5 | def toRetrofitPath(path: String): String = { 6 | 7 | //1. remove trailing slash, this is explained in https://github.com/square/retrofit/issues/1049 8 | 9 | val path1 = if(path.head == '/') 10 | path.tail 11 | else 12 | path 13 | 14 | //2. now replace :pathvars with {pathvars} 15 | 16 | val path2 = path1.replaceAll(":(.*?)/","{$1}/") 17 | 18 | //3. now do the same for when :pathvar is the last one in the string 19 | 20 | val path3 = path2.replaceAll(":(.*?)$","{$1}") 21 | 22 | path3 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /lib/src/main/scala/AbstractApiBuilderComments.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | class AbstractApiBuilderComments(val version: String, val userAgent: Option[String]) { 4 | 5 | val elements: Seq[String] = Seq( 6 | Some(s"Generated by API Builder - ${Constants.ApiBuilderUrl}"), 7 | Some(s"Service version: ${version}"), 8 | userAgent.map { a => s"User agent: $a" } 9 | ).flatten 10 | 11 | } -------------------------------------------------------------------------------- /lib/src/main/scala/Constants.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | object Constants { 4 | 5 | val ApiBuilderUrl = "https://www.apibuilder.io" 6 | 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/main/scala/Methods.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | object Methods { 4 | 5 | val MethodsNotAcceptingBodies = Seq("GET", "DELETE") 6 | 7 | def isJsonDocumentMethod(verb: String): Boolean = { 8 | !MethodsNotAcceptingBodies.contains(verb.toUpperCase) 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/main/scala/Pager.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | object Pager { 4 | 5 | /** 6 | * Iterator that takes two functions: 7 | * pagerFunction: Method to return a page of results 8 | * perObjectFunction: Function to call on each element 9 | * 10 | * Example: 11 | * Pager.eachPage[Subscription] { offset => 12 | * SubscriptionsDao.findAll( 13 | * Authorization.All, 14 | * organization = Some(organization), 15 | * publication = Some(publication), 16 | * limit = 100, 17 | * offset = offset 18 | * ) 19 | * } { subscription => 20 | * println(subscription) 21 | * } 22 | */ 23 | def eachPage[T]( 24 | pagerFunction: Int => Seq[T] 25 | )( 26 | perObjectFunction: T => Unit 27 | ): Unit = { 28 | var offset = 0 29 | var haveMore = true 30 | 31 | while (haveMore) { 32 | val objects = pagerFunction(offset) 33 | haveMore = objects.nonEmpty 34 | offset += objects.size 35 | objects.foreach { perObjectFunction(_) } 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/main/scala/Review.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | case class Review(key: String, name: String) 4 | 5 | object Review { 6 | 7 | val Accept: Review = Review("accept", "Accept") 8 | val Decline: Review = Review("decline", "Decline") 9 | 10 | val All: Seq[Review] = Seq(Accept, Decline) 11 | 12 | def fromString(key: String): Option[Review] = { 13 | val lowerKey = key.toLowerCase 14 | All.find(_.key == lowerKey) 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/main/scala/Role.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | case class Role(key: String, name: String) 4 | 5 | object Role { 6 | 7 | val Admin: Role = Role("admin", "Admin") 8 | val Member: Role = Role("member", "Member") 9 | 10 | val All: Seq[Role] = Seq(Member, Admin) 11 | 12 | def fromString(key: String): Option[Role] = { 13 | val lowerKey = key.toLowerCase 14 | All.find(_.key == lowerKey) 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/main/scala/UrlKey.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | object UrlKey { 4 | 5 | private val MinKeyLength = 3 6 | 7 | // Only want lower case letters and dashes 8 | private val Regexp1 = """([^0-9a-z\-\_\.])""".r 9 | 10 | // Turn multiple dashes into single dashes 11 | private val Regexp2 = """(\-+)""".r 12 | 13 | // Turn multiple underscores into single underscore 14 | private val Regexp3 = """(\_+)""".r 15 | 16 | private val RegexpLeadingSpaces = """^\-+""".r 17 | private val RegexpTrailingSpaces = """\-+$""".r 18 | 19 | def generate(value: String): String = { 20 | generate(format(value), 0) 21 | } 22 | 23 | @scala.annotation.tailrec 24 | private def generate(value: String, suffix: Int): String = { 25 | val key = if (suffix <= 0) { value } else { s"$value-1" } 26 | validate(key) match { 27 | case Nil => key 28 | case _ => generate(key, suffix + 1) 29 | } 30 | } 31 | 32 | def format(value: String): String = { 33 | RegexpTrailingSpaces.replaceAllIn( 34 | RegexpLeadingSpaces.replaceAllIn( 35 | Regexp3.replaceAllIn( 36 | Regexp2.replaceAllIn( 37 | Regexp1.replaceAllIn(value.toLowerCase.trim, _ => "-"), 38 | _ => "-" 39 | ), _ => "_" 40 | ), _ => ""), 41 | _ => "" 42 | ) 43 | } 44 | 45 | def validate(key: String, label: String = "Key"): Seq[String] = { 46 | val generated = UrlKey.format(key) 47 | if (key.length < MinKeyLength) { 48 | Seq(s"$label must be at least $MinKeyLength characters") 49 | } else if (key != generated) { 50 | Seq(s"$label must be in all lower case and contain alphanumerics only (-, _, and . are supported). A valid ${label.toLowerCase} would be: $generated") 51 | } else { 52 | Nil 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/main/scala/VersionedName.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | case class VersionedName( 4 | name: String, 5 | version: Option[String] = None 6 | ) extends Ordered[VersionedName] { 7 | 8 | private[lib] val versionTag = version.map(VersionTag(_)) 9 | 10 | val label: String = version match { 11 | case None => name 12 | case Some(v) => s"$name:$v" 13 | } 14 | 15 | override def compare(that: VersionedName): Int = { 16 | if (versionTag.isEmpty && that.versionTag.isEmpty) { 17 | 0 18 | } else if (versionTag.isEmpty && that.versionTag.nonEmpty) { 19 | 1 20 | } else if (versionTag.nonEmpty && that.versionTag.isEmpty) { 21 | -1 22 | } else { 23 | versionTag.get.compare(that.versionTag.get) 24 | } 25 | } 26 | 27 | } 28 | 29 | -------------------------------------------------------------------------------- /lib/src/main/scala/generator/CodeGenTarget.scala: -------------------------------------------------------------------------------- 1 | package lib.generator 2 | 3 | import io.apibuilder.generator.v0.models.Generator 4 | 5 | case class CodeGenTarget(metaData: Generator, status: Status, codeGenerator: Option[CodeGenerator]) 6 | 7 | -------------------------------------------------------------------------------- /lib/src/main/scala/generator/CodeGenerator.scala: -------------------------------------------------------------------------------- 1 | package lib.generator 2 | 3 | import io.apibuilder.generator.v0.models.{File, InvocationForm} 4 | 5 | trait CodeGenerator { 6 | 7 | /** 8 | * Invokes the code generators, returning either a list of errors 9 | * or the result of the code generation. 10 | */ 11 | def invoke(form: InvocationForm): Either[Seq[String], Seq[File]] 12 | 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/main/scala/generator/Status.scala: -------------------------------------------------------------------------------- 1 | package lib.generator 2 | 3 | sealed trait Status 4 | 5 | object Status { 6 | case object Production extends Status 7 | case object Alpha extends Status 8 | case object Beta extends Status 9 | case object InDevelopment extends Status 10 | case object Proposal extends Status 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/test/resources/apidoc/lib/anorm/util.txt: -------------------------------------------------------------------------------- 1 | package me.apidoc.lib.anorm.parsers.util { 2 | 3 | sealed trait Config with _root_.scala.Product with _root_.scala.Serializable { 4 | def name(column: String): String 5 | } 6 | 7 | object Config { 8 | final case class Prefix(prefix: String) extends Config { 9 | override def name(column: String): String = s"${prefix}_$column" 10 | } 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/test/resources/examples/apidoc-example-union-types-primitives.json: -------------------------------------------------------------------------------- 1 | { 2 | "apidoc": { 3 | "version": "0.11.17" 4 | }, 5 | "name": "apidoc-example-union-types-primitives", 6 | "organization": { 7 | "key": "bryzek" 8 | }, 9 | "application": { 10 | "key": "apidoc-example-union-types-primitives" 11 | }, 12 | "namespace": "io.apibuilder.example.union.types.v0", 13 | "version": "0.3.46", 14 | "info": {}, 15 | "headers": [], 16 | "imports": [], 17 | "enums": [], 18 | "unions": [ 19 | { 20 | "name": "baz", 21 | "plural": "bazes", 22 | "types": [ 23 | { 24 | "type": "integer", 25 | "attributes": [] 26 | } 27 | ], 28 | "attributes": [] 29 | } 30 | ], 31 | "models": [], 32 | "resources": [], 33 | "attributes": [] 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/test/resources/examples/response-with-unit-type.json: -------------------------------------------------------------------------------- 1 | { 2 | "apidoc": { 3 | "version": "0.9.6" 4 | }, 5 | "name": "apidoc response with unit type", 6 | "organization": { 7 | "key": "bryzek" 8 | }, 9 | "application": { 10 | "key": "apidoc-response-with-unit-type" 11 | }, 12 | "namespace": "io.apibuilder.test", 13 | "version": "0.0.1-dev", 14 | "headers": [], 15 | "info": [], 16 | "imports": [], 17 | "enums": [], 18 | "unions": [], 19 | "attributes": [], 20 | 21 | "models": [ 22 | { 23 | "name": "user", 24 | "plural": "users", 25 | "fields": [ 26 | { 27 | "name": "guid", 28 | "type": "uuid", 29 | "description": "Internal unique identifier for this user.", 30 | "required": true, 31 | "attributes": [] 32 | } 33 | ], 34 | "attributes": [] 35 | } 36 | ], 37 | "resources": [ 38 | { 39 | "type": "user", 40 | "plural": "users", 41 | "attributes": [], 42 | "operations": [ 43 | { 44 | "method": "POST", 45 | "path": "/users", 46 | "attributes": [], 47 | "parameters": [], 48 | "responses": [ 49 | { 50 | "headers": [], 51 | "code": { 52 | "integer": { 53 | "value": 200 54 | } 55 | }, 56 | "type": "unit" 57 | } 58 | ] 59 | } 60 | ] 61 | } 62 | ] 63 | } 64 | -------------------------------------------------------------------------------- /lib/src/test/resources/generator/anorm/audit.txt: -------------------------------------------------------------------------------- 1 | import anorm._ 2 | 3 | package io.flow.common.v0.anorm { 4 | 5 | object Reference { 6 | 7 | def parserByPrefix(prefix: String, separator: String = ".") = parser( 8 | guid = s"${prefix}${separator}guid" 9 | ) 10 | 11 | def parser( 12 | guid: String 13 | ): RowParser[io.flow.common.v0.models.Reference] = { 14 | SqlParser.get[_root_.java.util.UUID](guid) map { 15 | case guid => { 16 | io.flow.common.v0.models.Reference( 17 | guid = guid 18 | ) 19 | } 20 | } 21 | } 22 | } 23 | 24 | object Audit { 25 | 26 | def parserByPrefix(prefix: String, separator: String = ".") = parser( 27 | createdAt = s"${prefix}${separator}created_at", 28 | createdByPrefix = s"${prefix}${separator}created_by", 29 | updatedAt = s"${prefix}${separator}updated_at", 30 | updatedByPrefix = s"${prefix}${separator}updated_by" 31 | ) 32 | 33 | def parser( 34 | createdAt: String, 35 | createdByPrefix: String, 36 | updatedAt: String, 37 | updatedByPrefix: String 38 | ): RowParser[io.flow.common.v0.models.Audit] = { 39 | SqlParser.get[_root_.org.joda.time.DateTime](createdAt) ~ 40 | Reference.parserByPrefix(updatedByPrefix, "_") ~ 41 | SqlParser.get[_root_.org.joda.time.DateTime](updatedAt) ~ 42 | Reference.parserByPrefix(updatedByPrefix, "_") map { 43 | case createdAt ~ createdBy ~ updatedAt ~ updatedBy => { 44 | io.flow.common.v0.models.Audit( 45 | createdAt = createdAt, 46 | createdBy = createdBy, 47 | updatedAt = updatedAt, 48 | updatedBy = updatedBy 49 | ) 50 | } 51 | } 52 | } 53 | 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/test/resources/generator/anorm/cap-name.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by API Builder - https://www.apibuilder.io 3 | * Service version: 1.0.0 4 | */ 5 | import anorm._ 6 | 7 | package test.apidoc.apidoctest.v0.anorm.parsers { 8 | 9 | import test.apidoc.apidoctest.v0.anorm.conversions.Standard._ 10 | 11 | import test.apidoc.apidoctest.v0.anorm.conversions.Types._ 12 | 13 | object Name { 14 | 15 | def parserWithPrefix(prefix: String, sep: String = "_"): RowParser[test.apidoc.apidoctest.v0.models.Name] = parser(prefixOpt = Some(s"$prefix$sep")) 16 | 17 | def parser( 18 | first: String = "First", 19 | last: String = "Last", 20 | prefixOpt: Option[String] = None 21 | ): RowParser[test.apidoc.apidoctest.v0.models.Name] = { 22 | SqlParser.str(prefixOpt.getOrElse("") + first).? ~ 23 | SqlParser.str(prefixOpt.getOrElse("") + last).? map { 24 | case first ~ last => { 25 | test.apidoc.apidoctest.v0.models.Name( 26 | first = first, 27 | last = last 28 | ) 29 | } 30 | } 31 | } 32 | 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generator/anorm/datetime-java.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by API Builder - https://www.apibuilder.io 3 | * Service version: 1.0.0 4 | */ 5 | import anorm._ 6 | 7 | package test.apidoc.apidoctest.v0.anorm.parsers { 8 | 9 | import test.apidoc.apidoctest.v0.anorm.conversions.Standard._ 10 | 11 | import test.apidoc.apidoctest.v0.anorm.conversions.Types._ 12 | 13 | object Reference { 14 | 15 | def parserWithPrefix(prefix: String, sep: String = "_"): RowParser[test.apidoc.apidoctest.v0.models.Reference] = parser(prefixOpt = Some(s"$prefix$sep")) 16 | 17 | def parser( 18 | date: String = "date", 19 | time: String = "time", 20 | prefixOpt: Option[String] = None 21 | ): RowParser[test.apidoc.apidoctest.v0.models.Reference] = { 22 | SqlParser.get[_root_.java.time.LocalDate](prefixOpt.getOrElse("") + date) ~ 23 | SqlParser.get[_root_.java.time.Instant](prefixOpt.getOrElse("") + time) map { 24 | case date ~ time => { 25 | test.apidoc.apidoctest.v0.models.Reference( 26 | date = date, 27 | time = time 28 | ) 29 | } 30 | } 31 | } 32 | 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generator/anorm/datetime-joda.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by API Builder - https://www.apibuilder.io 3 | * Service version: 1.0.0 4 | */ 5 | import anorm._ 6 | 7 | package test.apidoc.apidoctest.v0.anorm.parsers { 8 | 9 | import test.apidoc.apidoctest.v0.anorm.conversions.Standard._ 10 | 11 | import test.apidoc.apidoctest.v0.anorm.conversions.Types._ 12 | 13 | object Reference { 14 | 15 | def parserWithPrefix(prefix: String, sep: String = "_"): RowParser[test.apidoc.apidoctest.v0.models.Reference] = parser(prefixOpt = Some(s"$prefix$sep")) 16 | 17 | def parser( 18 | date: String = "date", 19 | time: String = "time", 20 | prefixOpt: Option[String] = None 21 | ): RowParser[test.apidoc.apidoctest.v0.models.Reference] = { 22 | SqlParser.get[_root_.org.joda.time.LocalDate](prefixOpt.getOrElse("") + date) ~ 23 | SqlParser.get[_root_.org.joda.time.DateTime](prefixOpt.getOrElse("") + time) map { 24 | case date ~ time => { 25 | test.apidoc.apidoctest.v0.models.Reference( 26 | date = date, 27 | time = time 28 | ) 29 | } 30 | } 31 | } 32 | 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generator/anorm/enum.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by API Builder - https://www.apibuilder.io 3 | * Service version: 1.0.0 4 | */ 5 | import anorm._ 6 | 7 | package test.apidoc.apidoctest.v0.anorm.parsers { 8 | 9 | import test.apidoc.apidoctest.v0.anorm.conversions.Standard._ 10 | 11 | import test.apidoc.apidoctest.v0.anorm.conversions.Types._ 12 | 13 | object Status { 14 | 15 | def parserWithPrefix(prefix: String, sep: String = "_"): RowParser[test.apidoc.apidoctest.v0.models.Status] = parser(prefixOpt = Some(s"$prefix$sep")) 16 | 17 | def parser(name: String = "status", prefixOpt: Option[String] = None): RowParser[test.apidoc.apidoctest.v0.models.Status] = { 18 | SqlParser.str(prefixOpt.getOrElse("") + name) map { 19 | case value => test.apidoc.apidoctest.v0.models.Status(value) 20 | } 21 | } 22 | 23 | } 24 | 25 | object User { 26 | 27 | def parserWithPrefix(prefix: String, sep: String = "_"): RowParser[test.apidoc.apidoctest.v0.models.User] = parser(prefixOpt = Some(s"$prefix$sep")) 28 | 29 | def parser( 30 | guid: String = "guid", 31 | status: String = "status", 32 | prefixOpt: Option[String] = None 33 | ): RowParser[test.apidoc.apidoctest.v0.models.User] = { 34 | SqlParser.get[_root_.java.util.UUID](prefixOpt.getOrElse("") + guid) ~ 35 | test.apidoc.apidoctest.v0.anorm.parsers.Status.parser(prefixOpt.getOrElse("") + status) map { 36 | case guid ~ status => { 37 | test.apidoc.apidoctest.v0.models.User( 38 | guid = guid, 39 | status = status 40 | ) 41 | } 42 | } 43 | } 44 | 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generator/anorm/list.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by API Builder - https://www.apibuilder.io 3 | * Service version: 1.0.0 4 | */ 5 | import anorm._ 6 | 7 | package test.apidoc.apidoctest.v0.anorm.parsers { 8 | 9 | import test.apidoc.apidoctest.v0.anorm.conversions.Standard._ 10 | 11 | import test.apidoc.apidoctest.v0.anorm.conversions.Types._ 12 | 13 | object User { 14 | 15 | def parserWithPrefix(prefix: String, sep: String = "_"): RowParser[test.apidoc.apidoctest.v0.models.User] = parser(prefixOpt = Some(s"$prefix$sep")) 16 | 17 | def parser( 18 | guid: String = "guid", 19 | emails: String = "emails", 20 | prefixOpt: Option[String] = None 21 | ): RowParser[test.apidoc.apidoctest.v0.models.User] = { 22 | SqlParser.get[_root_.java.util.UUID](prefixOpt.getOrElse("") + guid) ~ 23 | SqlParser.get[Seq[String]](prefixOpt.getOrElse("") + emails) map { 24 | case guid ~ emails => { 25 | test.apidoc.apidoctest.v0.models.User( 26 | guid = guid, 27 | emails = emails 28 | ) 29 | } 30 | } 31 | } 32 | 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generator/anorm/location-parsers.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by API Builder - https://www.apibuilder.io 3 | * Service version: 1.0.0 4 | */ 5 | import anorm._ 6 | 7 | package test.apidoc.apidoctest.v0.anorm.parsers { 8 | 9 | import test.apidoc.apidoctest.v0.anorm.conversions.Standard._ 10 | 11 | import test.apidoc.apidoctest.v0.anorm.conversions.Types._ 12 | 13 | object Location { 14 | 15 | def parserWithPrefix(prefix: String, sep: String = "_"): RowParser[test.apidoc.apidoctest.v0.models.Location] = parser(prefixOpt = Some(s"$prefix$sep")) 16 | 17 | def parser( 18 | ipAddress: String = "ip_address", 19 | prefixOpt: Option[String] = None 20 | ): RowParser[test.apidoc.apidoctest.v0.models.Location] = { 21 | SqlParser.str(prefixOpt.getOrElse("") + ipAddress) map { 22 | case ipAddress => { 23 | test.apidoc.apidoctest.v0.models.Location( 24 | ipAddress = ipAddress 25 | ) 26 | } 27 | } 28 | } 29 | 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generator/anorm/name.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by API Builder - https://www.apibuilder.io 3 | * Service version: 1.0.0 4 | */ 5 | import anorm._ 6 | 7 | package test.apidoc.apidoctest.v0.anorm.parsers { 8 | 9 | import test.apidoc.apidoctest.v0.anorm.conversions.Standard._ 10 | 11 | import test.apidoc.apidoctest.v0.anorm.conversions.Types._ 12 | 13 | object Name { 14 | 15 | def parserWithPrefix(prefix: String, sep: String = "_"): RowParser[test.apidoc.apidoctest.v0.models.Name] = parser(prefixOpt = Some(s"$prefix$sep")) 16 | 17 | def parser( 18 | first: String = "first", 19 | last: String = "last", 20 | prefixOpt: Option[String] = None 21 | ): RowParser[test.apidoc.apidoctest.v0.models.Name] = { 22 | SqlParser.str(prefixOpt.getOrElse("") + first).? ~ 23 | SqlParser.str(prefixOpt.getOrElse("") + last).? map { 24 | case first ~ last => { 25 | test.apidoc.apidoctest.v0.models.Name( 26 | first = first, 27 | last = last 28 | ) 29 | } 30 | } 31 | } 32 | 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generator/anorm/reference.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by API Builder - https://www.apibuilder.io 3 | * Service version: 1.0.0 4 | */ 5 | import anorm._ 6 | 7 | package test.apidoc.apidoctest.v0.anorm.parsers { 8 | 9 | import test.apidoc.apidoctest.v0.anorm.conversions.Standard._ 10 | 11 | import test.apidoc.apidoctest.v0.anorm.conversions.Types._ 12 | 13 | object Reference { 14 | 15 | def parserWithPrefix(prefix: String, sep: String = "_"): RowParser[test.apidoc.apidoctest.v0.models.Reference] = parser(prefixOpt = Some(s"$prefix$sep")) 16 | 17 | def parser( 18 | guid: String = "guid", 19 | prefixOpt: Option[String] = None 20 | ): RowParser[test.apidoc.apidoctest.v0.models.Reference] = { 21 | SqlParser.get[_root_.java.util.UUID](prefixOpt.getOrElse("") + guid) map { 22 | case guid => { 23 | test.apidoc.apidoctest.v0.models.Reference( 24 | guid = guid 25 | ) 26 | } 27 | } 28 | } 29 | 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generator/anorm/union-parsers.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by API Builder - https://www.apibuilder.io 3 | * Service version: 1.0.0 4 | */ 5 | import anorm._ 6 | 7 | package test.apidoc.apidoctest.v0.anorm.parsers { 8 | 9 | import test.apidoc.apidoctest.v0.anorm.conversions.Standard._ 10 | 11 | import test.apidoc.apidoctest.v0.anorm.conversions.Types._ 12 | 13 | object GuestUser { 14 | 15 | def parserWithPrefix(prefix: String, sep: String = "_"): RowParser[test.apidoc.apidoctest.v0.models.GuestUser] = parser(prefixOpt = Some(s"$prefix$sep")) 16 | 17 | def parser( 18 | guid: String = "guid", 19 | prefixOpt: Option[String] = None 20 | ): RowParser[test.apidoc.apidoctest.v0.models.GuestUser] = { 21 | SqlParser.get[_root_.java.util.UUID](prefixOpt.getOrElse("") + guid) map { 22 | case guid => { 23 | test.apidoc.apidoctest.v0.models.GuestUser( 24 | guid = guid 25 | ) 26 | } 27 | } 28 | } 29 | 30 | } 31 | 32 | object RegisteredUser { 33 | 34 | def parserWithPrefix(prefix: String, sep: String = "_"): RowParser[test.apidoc.apidoctest.v0.models.RegisteredUser] = parser(prefixOpt = Some(s"$prefix$sep")) 35 | 36 | def parser( 37 | guid: String = "guid", 38 | prefixOpt: Option[String] = None 39 | ): RowParser[test.apidoc.apidoctest.v0.models.RegisteredUser] = { 40 | SqlParser.get[_root_.java.util.UUID](prefixOpt.getOrElse("") + guid) map { 41 | case guid => { 42 | test.apidoc.apidoctest.v0.models.RegisteredUser( 43 | guid = guid 44 | ) 45 | } 46 | } 47 | } 48 | 49 | } 50 | 51 | object User { 52 | 53 | def parserWithPrefix(prefix: String, sep: String = "_") = { 54 | test.apidoc.apidoctest.v0.anorm.parsers.GuestUser.parser(prefixOpt = Some(s"$prefix$sep")) | 55 | test.apidoc.apidoctest.v0.anorm.parsers.RegisteredUser.parser(prefixOpt = Some(s"$prefix$sep")) 56 | } 57 | 58 | def parser() = { 59 | test.apidoc.apidoctest.v0.anorm.parsers.GuestUser.parser() | 60 | test.apidoc.apidoctest.v0.anorm.parsers.RegisteredUser.parser() 61 | } 62 | 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/ScalaCaseClassesInterfacesSpec.interfaceWithNoFields.json: -------------------------------------------------------------------------------- 1 | trait Person -------------------------------------------------------------------------------- /lib/src/test/resources/generators/ScalaCaseClassesInterfacesSpec.interfaceWithSingleField.json: -------------------------------------------------------------------------------- 1 | trait Person { 2 | def first: String 3 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/ScalaCaseClassesUnionInterfacesSpec.interfaceHasFields.json: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by API Builder - https://www.apibuilder.io 3 | * Service version: 0.0.1 4 | */ 5 | package test.models { 6 | 7 | trait Foo { 8 | def id: String 9 | } 10 | sealed trait Person extends Foo { 11 | def id: String 12 | 13 | } 14 | final case class User( 15 | override val id: String 16 | ) extends Person 17 | 18 | /** 19 | * Provides future compatibility in clients - in the future, when a type is added 20 | * to the union Person, it will need to be handled in the client code. This 21 | * implementation will deserialize these future types as an instance of this class. 22 | * 23 | * @param description Information about the type that we received that is undefined in this version of 24 | * the client. 25 | */ 26 | 27 | final case class PersonUndefinedType( 28 | description: String 29 | ) extends Person { 30 | override def id: String = ??? 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/ScalaCaseClassesUnionInterfacesSpec.interfaceHasNoFields.json: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by API Builder - https://www.apibuilder.io 3 | * Service version: 0.0.1 4 | */ 5 | package test.models { 6 | 7 | trait Foo 8 | sealed trait Person extends Foo 9 | final case class User( 10 | id: String 11 | ) extends Person 12 | 13 | /** 14 | * Provides future compatibility in clients - in the future, when a type is added 15 | * to the union Person, it will need to be handled in the client code. This 16 | * implementation will deserialize these future types as an instance of this class. 17 | * 18 | * @param description Information about the type that we received that is undefined in this version of 19 | * the client. 20 | */ 21 | 22 | final case class PersonUndefinedType( 23 | description: String 24 | ) extends Person 25 | 26 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/ScalaCaseClassesUnionInterfacesSpec.interfaceHasSameName.json: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by API Builder - https://www.apibuilder.io 3 | * Service version: 0.0.1 4 | */ 5 | package test.models { 6 | 7 | sealed trait Person extends _root_.scala.Product with _root_.scala.Serializable 8 | final case class User( 9 | id: String 10 | ) extends Person 11 | 12 | /** 13 | * Provides future compatibility in clients - in the future, when a type is added 14 | * to the union Person, it will need to be handled in the client code. This 15 | * implementation will deserialize these future types as an instance of this class. 16 | * 17 | * @param description Information about the type that we received that is undefined in this version of 18 | * the client. 19 | */ 20 | 21 | final case class PersonUndefinedType( 22 | description: String 23 | ) extends Person 24 | 25 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/ScalaCaseClassesUnionInterfacesSpec.interfaceHasSameNameAndFields.json: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by API Builder - https://www.apibuilder.io 3 | * Service version: 0.0.1 4 | */ 5 | package test.models { 6 | 7 | sealed trait Person extends _root_.scala.Product with _root_.scala.Serializable { 8 | def id: String 9 | 10 | } 11 | final case class User( 12 | override val id: String 13 | ) extends Person 14 | 15 | /** 16 | * Provides future compatibility in clients - in the future, when a type is added 17 | * to the union Person, it will need to be handled in the client code. This 18 | * implementation will deserialize these future types as an instance of this class. 19 | * 20 | * @param description Information about the type that we received that is undefined in this version of 21 | * the client. 22 | */ 23 | 24 | final case class PersonUndefinedType( 25 | description: String 26 | ) extends Person { 27 | override def id: String = ??? 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/collection-json-defaults-user-case-class.txt: -------------------------------------------------------------------------------- 1 | @deprecated("to be merged with members") 2 | final case class User( 3 | email: String, 4 | role: String = "admin", 5 | groups: Seq[String], 6 | @deprecated permissions: Seq[String] 7 | ) -------------------------------------------------------------------------------- /lib/src/test/resources/generators/collection-json-defaults-user-patch-case-class.txt: -------------------------------------------------------------------------------- 1 | final case class UserPatch( 2 | groups: _root_.scala.Option[Seq[String]] = None, 3 | permissions: Seq[String] = Nil, 4 | preferences: Seq[String] = scala.List("foo") 5 | ) -------------------------------------------------------------------------------- /lib/src/test/resources/generators/play-2-bindable-age-group.txt: -------------------------------------------------------------------------------- 1 | val ageGroupConverter: ApibuilderTypeConverter[io.apibuilder.reference.api.v0.models.AgeGroup] = new ApibuilderTypeConverter[io.apibuilder.reference.api.v0.models.AgeGroup] { 2 | override def convert(value: String): io.apibuilder.reference.api.v0.models.AgeGroup = io.apibuilder.reference.api.v0.models.AgeGroup(value) 3 | override def convert(value: io.apibuilder.reference.api.v0.models.AgeGroup): String = value.toString 4 | override def example: io.apibuilder.reference.api.v0.models.AgeGroup = io.apibuilder.reference.api.v0.models.AgeGroup.Youth 5 | override def validValues: Seq[io.apibuilder.reference.api.v0.models.AgeGroup] = io.apibuilder.reference.api.v0.models.AgeGroup.all 6 | } 7 | implicit def pathBindableAgeGroup(implicit stringBinder: QueryStringBindable[String]): PathBindable[io.apibuilder.reference.api.v0.models.AgeGroup] = ApibuilderPathBindable(ageGroupConverter) 8 | implicit def queryStringBindableAgeGroup(implicit stringBinder: QueryStringBindable[String]): QueryStringBindable[io.apibuilder.reference.api.v0.models.AgeGroup] = ApibuilderQueryStringBindable(ageGroupConverter) -------------------------------------------------------------------------------- /lib/src/test/resources/generators/play-2-json-spec-quality-healthcheck-readers.txt: -------------------------------------------------------------------------------- 1 | implicit def jsonReadsQualityHealthcheck: play.api.libs.json.Reads[com.gilt.quality.v0.models.Healthcheck] = { 2 | (__ \ "status").read[String].map { x => Healthcheck(status = x) } 3 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/play-2-json-spec-quality-healthcheck-writers.txt: -------------------------------------------------------------------------------- 1 | def jsObjectHealthcheck(obj: com.gilt.quality.v0.models.Healthcheck): play.api.libs.json.JsObject = { 2 | play.api.libs.json.Json.obj( 3 | "status" -> play.api.libs.json.JsString(obj.status) 4 | ) 5 | } 6 | 7 | implicit def jsonWritesQualityHealthcheck: play.api.libs.json.Writes[Healthcheck] = { 8 | (obj: com.gilt.quality.v0.models.Healthcheck) => { 9 | com.gilt.quality.v0.models.json.jsObjectHealthcheck(obj) 10 | } 11 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/play-2-json-spec-quality-plan-readers.txt: -------------------------------------------------------------------------------- 1 | implicit def jsonReadsQualityPlan: play.api.libs.json.Reads[com.gilt.quality.v0.models.Plan] = { 2 | for { 3 | id <- (__ \ "id").read[Long] 4 | incidentId <- (__ \ "incident_id").read[Long] 5 | body <- (__ \ "body").read[String] 6 | grade <- (__ \ "grade").readNullable[Int] 7 | createdAt <- (__ \ "created_at").read[_root_.org.joda.time.DateTime] 8 | } yield Plan(id, incidentId, body, grade, createdAt) 9 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/play-2-json-spec-quality-plan-writers.txt: -------------------------------------------------------------------------------- 1 | def jsObjectPlan(obj: com.gilt.quality.v0.models.Plan): play.api.libs.json.JsObject = { 2 | play.api.libs.json.Json.obj( 3 | "id" -> play.api.libs.json.JsNumber(obj.id), 4 | "incident_id" -> play.api.libs.json.JsNumber(obj.incidentId), 5 | "body" -> play.api.libs.json.JsString(obj.body), 6 | "created_at" -> play.api.libs.json.JsString(_root_.org.joda.time.format.ISODateTimeFormat.dateTime.print(obj.createdAt)) 7 | ) ++ (obj.grade match { 8 | case None => play.api.libs.json.Json.obj() 9 | case Some(x) => play.api.libs.json.Json.obj("grade" -> play.api.libs.json.JsNumber(x)) 10 | }) 11 | } 12 | 13 | implicit def jsonWritesQualityPlan: play.api.libs.json.Writes[Plan] = { 14 | (obj: com.gilt.quality.v0.models.Plan) => { 15 | com.gilt.quality.v0.models.json.jsObjectPlan(obj) 16 | } 17 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/play-2-route-util-reference-get-users.txt: -------------------------------------------------------------------------------- 1 | val queryParameters = Seq( 2 | guid.map("guid" -> _.toString), 3 | ageGroup.map("age_group" -> _.toString), 4 | email.map("email" -> _), 5 | Some("active" -> active.toString) 6 | ).flatten ++ 7 | organizationGuids.getOrElse(Nil).map("organization_guids" -> _.toString) 8 | -------------------------------------------------------------------------------- /lib/src/test/resources/generators/play2-client-generator-spec-errors-package-no-models.txt: -------------------------------------------------------------------------------- 1 | package errors { 2 | 3 | import test.apidoc.apidoctest.v0.models.json._ 4 | 5 | final case class UnitResponse(status: Int) extends Exception(s"HTTP $status") 6 | 7 | final case class FailedRequest(responseCode: Int, message: String, requestUri: Option[_root_.java.net.URI] = None) extends _root_.java.lang.Exception(s"HTTP $responseCode: $message") 8 | 9 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/play2-client-generator-spec-errors-package.txt: -------------------------------------------------------------------------------- 1 | package errors { 2 | 3 | import io.apibuilder.common.v0.models.json._ 4 | import io.apibuilder.generator.v0.models.json._ 5 | import io.apibuilder.spec.v0.models.json._ 6 | 7 | final case class ErrorsResponse( 8 | response: play.api.libs.ws.WSResponse, 9 | message: Option[String] = None 10 | ) extends Exception(message.getOrElse(s"${response.status}: ${response.body}")) { 11 | lazy val errors = _root_.test.apidoc.Client.parseJson("Seq[io.apibuilder.generator.v0.models.Error]", response, _.validate[Seq[io.apibuilder.generator.v0.models.Error]]) 12 | } 13 | 14 | final case class UnitResponse(status: Int) extends Exception(s"HTTP $status") 15 | 16 | final case class FailedRequest(responseCode: Int, message: String, requestUri: Option[_root_.java.net.URI] = None) extends _root_.java.lang.Exception(s"HTTP $responseCode: $message") 17 | 18 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/reference-spec-member-case-class.txt: -------------------------------------------------------------------------------- 1 | final case class Member( 2 | guid: _root_.java.util.UUID, 3 | organization: io.apibuilder.reference.api.v0.models.Organization, 4 | user: io.apibuilder.reference.api.v0.models.User, 5 | role: String 6 | ) -------------------------------------------------------------------------------- /lib/src/test/resources/generators/reference-spec-user-case-class.txt: -------------------------------------------------------------------------------- 1 | final case class User( 2 | guid: _root_.java.util.UUID, 3 | email: String, 4 | active: Boolean, 5 | ageGroup: io.apibuilder.reference.api.v0.models.AgeGroup, 6 | tags: _root_.scala.Option[Map[String, String]] = None 7 | ) -------------------------------------------------------------------------------- /lib/src/test/resources/generators/ruby-client-primitive-object-list.txt: -------------------------------------------------------------------------------- 1 | class Content 2 | 3 | attr_reader :data 4 | 5 | def initialize(incoming={}) 6 | opts = HttpClient::Helper.symbolize_keys(incoming) 7 | HttpClient::Preconditions.require_keys(opts, [:data], 'Content') 8 | @data = HttpClient::Preconditions.assert_class('data', opts.delete(:data), Array).map { |v| HttpClient::Preconditions.assert_class('data', HttpClient::Helper.to_object(v), Hash) } 9 | end 10 | 11 | def to_json 12 | JSON.dump(to_hash) 13 | end 14 | 15 | def copy(incoming={}) 16 | Content.new(to_hash.merge(HttpClient::Helper.symbolize_keys(incoming))) 17 | end 18 | 19 | def to_hash 20 | { 21 | :data => data 22 | } 23 | end 24 | 25 | end -------------------------------------------------------------------------------- /lib/src/test/resources/generators/ruby-client-primitive-object-map.txt: -------------------------------------------------------------------------------- 1 | class Content 2 | 3 | attr_reader :data 4 | 5 | def initialize(incoming={}) 6 | opts = HttpClient::Helper.symbolize_keys(incoming) 7 | HttpClient::Preconditions.require_keys(opts, [:data], 'Content') 8 | @data = HttpClient::Preconditions.assert_class('data', opts.delete(:data), Hash).inject({}) { |h, d| h[d[0]] = HttpClient::Preconditions.assert_class('data', HttpClient::Helper.to_object(d[1]), Hash); h } 9 | end 10 | 11 | def to_json 12 | JSON.dump(to_hash) 13 | end 14 | 15 | def copy(incoming={}) 16 | Content.new(to_hash.merge(HttpClient::Helper.symbolize_keys(incoming))) 17 | end 18 | 19 | def to_hash 20 | { 21 | :data => data 22 | } 23 | end 24 | 25 | end -------------------------------------------------------------------------------- /lib/src/test/resources/generators/ruby-client-primitive-object-response-list.txt: -------------------------------------------------------------------------------- 1 | r.map { |x| x } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/ruby-client-primitive-object-response-map.txt: -------------------------------------------------------------------------------- 1 | r.inject({}) { |hash, x| hash[x[0]] = x[1].nil? ? nil : x[1]; hash } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/ruby-client-primitive-object-response-singleton.txt: -------------------------------------------------------------------------------- 1 | r -------------------------------------------------------------------------------- /lib/src/test/resources/generators/ruby-client-primitive-object-singleton.txt: -------------------------------------------------------------------------------- 1 | class Content 2 | 3 | attr_reader :data 4 | 5 | def initialize(incoming={}) 6 | opts = HttpClient::Helper.symbolize_keys(incoming) 7 | HttpClient::Preconditions.require_keys(opts, [:data], 'Content') 8 | @data = HttpClient::Preconditions.assert_class('data', HttpClient::Helper.to_object(opts.delete(:data)), Hash) 9 | end 10 | 11 | def to_json 12 | JSON.dump(to_hash) 13 | end 14 | 15 | def copy(incoming={}) 16 | Content.new(to_hash.merge(HttpClient::Helper.symbolize_keys(incoming))) 17 | end 18 | 19 | def to_hash 20 | { 21 | :data => data 22 | } 23 | end 24 | 25 | end -------------------------------------------------------------------------------- /lib/src/test/resources/generators/scala-primitive-object-list.txt: -------------------------------------------------------------------------------- 1 | package test.apidoc.apidoctest.v0.models { 2 | 3 | final case class Content( 4 | data: Seq[_root_.play.api.libs.json.JsObject] 5 | ) 6 | 7 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/scala-primitive-object-map.txt: -------------------------------------------------------------------------------- 1 | package test.apidoc.apidoctest.v0.models { 2 | 3 | final case class Content( 4 | data: Map[String, _root_.play.api.libs.json.JsObject] 5 | ) 6 | 7 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/scala-primitive-object-response-list.txt: -------------------------------------------------------------------------------- 1 | object Contents extends Contents { 2 | override def get( 3 | requestHeaders: Seq[(String, String)] = Nil 4 | )(implicit ec: scala.concurrent.ExecutionContext): scala.concurrent.Future[Seq[_root_.play.api.libs.json.JsObject]] = { 5 | _executeRequest("GET", s"/contents/data", requestHeaders = requestHeaders).map { 6 | case r if r.status == 200 => _root_.test.apidoc.Client.parseJson("Seq[_root_.play.api.libs.json.JsObject]", r, _.validate[Seq[_root_.play.api.libs.json.JsObject]]) 7 | case r => throw test.apidoc.errors.FailedRequest(r.status, s"Unsupported response code[${r.status}]. Expected: 200") 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/scala-primitive-object-response-map.txt: -------------------------------------------------------------------------------- 1 | object Contents extends Contents { 2 | override def get( 3 | requestHeaders: Seq[(String, String)] = Nil 4 | )(implicit ec: scala.concurrent.ExecutionContext): scala.concurrent.Future[Map[String, _root_.play.api.libs.json.JsObject]] = { 5 | _executeRequest("GET", s"/contents/data", requestHeaders = requestHeaders).map { 6 | case r if r.status == 200 => _root_.test.apidoc.Client.parseJson("Map[String, _root_.play.api.libs.json.JsObject]", r, _.validate[Map[String, _root_.play.api.libs.json.JsObject]]) 7 | case r => throw test.apidoc.errors.FailedRequest(r.status, s"Unsupported response code[${r.status}]. Expected: 200") 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/scala-primitive-object-response-singleton.txt: -------------------------------------------------------------------------------- 1 | object Contents extends Contents { 2 | override def get( 3 | requestHeaders: Seq[(String, String)] = Nil 4 | )(implicit ec: scala.concurrent.ExecutionContext): scala.concurrent.Future[_root_.play.api.libs.json.JsObject] = { 5 | _executeRequest("GET", s"/contents/data", requestHeaders = requestHeaders).map { 6 | case r if r.status == 200 => _root_.test.apidoc.Client.parseJson("_root_.play.api.libs.json.JsObject", r, _.validate[_root_.play.api.libs.json.JsObject]) 7 | case r => throw test.apidoc.errors.FailedRequest(r.status, s"Unsupported response code[${r.status}]. Expected: 200") 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /lib/src/test/resources/generators/scala-primitive-object-singleton.txt: -------------------------------------------------------------------------------- 1 | package test.apidoc.apidoctest.v0.models { 2 | 3 | final case class Content( 4 | data: _root_.play.api.libs.json.JsObject 5 | ) 6 | 7 | } -------------------------------------------------------------------------------- /lib/src/test/resources/http4s/mock-client/http4s_022_date-time-instant.txt: -------------------------------------------------------------------------------- 1 | package io.gregor.time.types.v0.mock { 2 | 3 | class Client[F[_]: cats.Applicative] extends io.gregor.time.types.v0.interfaces.Client[F] { 4 | 5 | val baseUrl: org.http4s.Uri = org.http4s.Uri.unsafeFromString("http://mock.localhost") 6 | 7 | override def dateTimeModels: io.gregor.time.types.v0.DateTimeModels[F] = new MockDateTimeModels[F] 8 | 9 | } 10 | 11 | class MockDateTimeModels[F[_]: cats.Applicative] extends io.gregor.time.types.v0.DateTimeModels[F] { 12 | 13 | def postByPathDateAndPathTime( 14 | pathDate: _root_.java.time.LocalDate, 15 | pathTime: _root_.java.time.Instant, 16 | queryDate: _root_.java.time.LocalDate, 17 | queryTime: _root_.java.time.Instant, 18 | dateTimeModel: io.gregor.time.types.v0.models.DateTimeModel, 19 | requestHeaders: Seq[(String, String)] = Nil 20 | ): F[io.gregor.time.types.v0.models.DateTimeModel] = cats.Applicative[F].pure { 21 | io.gregor.time.types.v0.mock.Factories.makeDateTimeModel() 22 | } 23 | 24 | } 25 | 26 | object Factories { 27 | 28 | def randomString(length: Int = 24): String = { 29 | _root_.scala.util.Random.alphanumeric.take(length).mkString 30 | } 31 | 32 | def makeDateTimeModel(): io.gregor.time.types.v0.models.DateTimeModel = io.gregor.time.types.v0.models.DateTimeModel( 33 | dateTime = _root_.java.time.Instant.now, 34 | date = _root_.java.time.LocalDate.now 35 | ) 36 | 37 | def makeDateTimeUnion(): io.gregor.time.types.v0.models.DateTimeUnion = io.gregor.time.types.v0.models.DateTimeUnionInstant(_root_.java.time.Instant.now) 38 | 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /lib/src/test/resources/http4s/mock-client/http4s_022_date-time-joda.txt: -------------------------------------------------------------------------------- 1 | package io.gregor.time.types.v0.mock { 2 | 3 | class Client[F[_]: cats.Applicative] extends io.gregor.time.types.v0.interfaces.Client[F] { 4 | 5 | val baseUrl: org.http4s.Uri = org.http4s.Uri.unsafeFromString("http://mock.localhost") 6 | 7 | override def dateTimeModels: io.gregor.time.types.v0.DateTimeModels[F] = new MockDateTimeModels[F] 8 | 9 | } 10 | 11 | class MockDateTimeModels[F[_]: cats.Applicative] extends io.gregor.time.types.v0.DateTimeModels[F] { 12 | 13 | def postByPathDateAndPathTime( 14 | pathDate: _root_.org.joda.time.LocalDate, 15 | pathTime: _root_.org.joda.time.DateTime, 16 | queryDate: _root_.org.joda.time.LocalDate, 17 | queryTime: _root_.org.joda.time.DateTime, 18 | dateTimeModel: io.gregor.time.types.v0.models.DateTimeModel, 19 | requestHeaders: Seq[(String, String)] = Nil 20 | ): F[io.gregor.time.types.v0.models.DateTimeModel] = cats.Applicative[F].pure { 21 | io.gregor.time.types.v0.mock.Factories.makeDateTimeModel() 22 | } 23 | 24 | } 25 | 26 | object Factories { 27 | 28 | def randomString(length: Int = 24): String = { 29 | _root_.scala.util.Random.alphanumeric.take(length).mkString 30 | } 31 | 32 | def makeDateTimeModel(): io.gregor.time.types.v0.models.DateTimeModel = io.gregor.time.types.v0.models.DateTimeModel( 33 | dateTime = _root_.org.joda.time.DateTime.now, 34 | date = _root_.org.joda.time.LocalDate.now 35 | ) 36 | 37 | def makeDateTimeUnion(): io.gregor.time.types.v0.models.DateTimeUnion = io.gregor.time.types.v0.models.DateTimeUnionDateTime(_root_.org.joda.time.DateTime.now) 38 | 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /lib/src/test/resources/http4s/mock-client/http4s_022_date-time-offsetdatetime.txt: -------------------------------------------------------------------------------- 1 | package io.gregor.time.types.v0.mock { 2 | 3 | class Client[F[_]: cats.Applicative] extends io.gregor.time.types.v0.interfaces.Client[F] { 4 | 5 | val baseUrl: org.http4s.Uri = org.http4s.Uri.unsafeFromString("http://mock.localhost") 6 | 7 | override def dateTimeModels: io.gregor.time.types.v0.DateTimeModels[F] = new MockDateTimeModels[F] 8 | 9 | } 10 | 11 | class MockDateTimeModels[F[_]: cats.Applicative] extends io.gregor.time.types.v0.DateTimeModels[F] { 12 | 13 | def postByPathDateAndPathTime( 14 | pathDate: _root_.java.time.LocalDate, 15 | pathTime: _root_.java.time.OffsetDateTime, 16 | queryDate: _root_.java.time.LocalDate, 17 | queryTime: _root_.java.time.OffsetDateTime, 18 | dateTimeModel: io.gregor.time.types.v0.models.DateTimeModel, 19 | requestHeaders: Seq[(String, String)] = Nil 20 | ): F[io.gregor.time.types.v0.models.DateTimeModel] = cats.Applicative[F].pure { 21 | io.gregor.time.types.v0.mock.Factories.makeDateTimeModel() 22 | } 23 | 24 | } 25 | 26 | object Factories { 27 | 28 | def randomString(length: Int = 24): String = { 29 | _root_.scala.util.Random.alphanumeric.take(length).mkString 30 | } 31 | 32 | def makeDateTimeModel(): io.gregor.time.types.v0.models.DateTimeModel = io.gregor.time.types.v0.models.DateTimeModel( 33 | dateTime = _root_.java.time.OffsetDateTime.now, 34 | date = _root_.java.time.LocalDate.now 35 | ) 36 | 37 | def makeDateTimeUnion(): io.gregor.time.types.v0.models.DateTimeUnion = io.gregor.time.types.v0.models.DateTimeUnionOffsetDateTime(_root_.java.time.OffsetDateTime.now) 38 | 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /lib/src/test/resources/play2-json-spec-model-readers.txt: -------------------------------------------------------------------------------- 1 | for { 2 | requiredTags <- (__ \ "required_tags").read[Seq[String]] 3 | optionalTags <- (__ \ "optional_tags").readNullable[Seq[String]] 4 | data <- (__ \ "data").readNullable[Map[String, Long]] 5 | } yield Content(requiredTags, optionalTags, data) -------------------------------------------------------------------------------- /lib/src/test/resources/play2/mock-client/play27_date-time-instant.txt: -------------------------------------------------------------------------------- 1 | package io.gregor.time.types.v0.mock { 2 | 3 | trait Client extends io.gregor.time.types.v0.interfaces.Client { 4 | 5 | val baseUrl: String = "http://mock.localhost" 6 | 7 | override def dateTimeModels: io.gregor.time.types.v0.DateTimeModels = MockDateTimeModelsImpl 8 | 9 | } 10 | 11 | object MockDateTimeModelsImpl extends MockDateTimeModels 12 | 13 | trait MockDateTimeModels extends io.gregor.time.types.v0.DateTimeModels { 14 | 15 | def postByPathDateAndPathTime( 16 | pathDate: _root_.java.time.LocalDate, 17 | pathTime: _root_.java.time.Instant, 18 | queryDate: _root_.java.time.LocalDate, 19 | queryTime: _root_.java.time.Instant, 20 | dateTimeModel: io.gregor.time.types.v0.models.DateTimeModel, 21 | requestHeaders: Seq[(String, String)] = Nil 22 | )(implicit ec: scala.concurrent.ExecutionContext): scala.concurrent.Future[io.gregor.time.types.v0.models.DateTimeModel] = scala.concurrent.Future.successful { 23 | io.gregor.time.types.v0.mock.Factories.makeDateTimeModel() 24 | } 25 | 26 | } 27 | 28 | object Factories { 29 | 30 | def randomString(length: Int = 24): String = { 31 | _root_.scala.util.Random.alphanumeric.take(length).mkString 32 | } 33 | 34 | def makeDateTimeModel(): io.gregor.time.types.v0.models.DateTimeModel = io.gregor.time.types.v0.models.DateTimeModel( 35 | dateTime = _root_.java.time.Instant.now, 36 | date = _root_.java.time.LocalDate.now 37 | ) 38 | 39 | def makeDateTimeUnion(): io.gregor.time.types.v0.models.DateTimeUnion = io.gregor.time.types.v0.models.DateTimeUnionInstant(_root_.java.time.Instant.now) 40 | 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /lib/src/test/resources/play2/mock-client/play27_date-time-joda.txt: -------------------------------------------------------------------------------- 1 | package io.gregor.time.types.v0.mock { 2 | 3 | trait Client extends io.gregor.time.types.v0.interfaces.Client { 4 | 5 | val baseUrl: String = "http://mock.localhost" 6 | 7 | override def dateTimeModels: io.gregor.time.types.v0.DateTimeModels = MockDateTimeModelsImpl 8 | 9 | } 10 | 11 | object MockDateTimeModelsImpl extends MockDateTimeModels 12 | 13 | trait MockDateTimeModels extends io.gregor.time.types.v0.DateTimeModels { 14 | 15 | def postByPathDateAndPathTime( 16 | pathDate: _root_.org.joda.time.LocalDate, 17 | pathTime: _root_.org.joda.time.DateTime, 18 | queryDate: _root_.org.joda.time.LocalDate, 19 | queryTime: _root_.org.joda.time.DateTime, 20 | dateTimeModel: io.gregor.time.types.v0.models.DateTimeModel, 21 | requestHeaders: Seq[(String, String)] = Nil 22 | )(implicit ec: scala.concurrent.ExecutionContext): scala.concurrent.Future[io.gregor.time.types.v0.models.DateTimeModel] = scala.concurrent.Future.successful { 23 | io.gregor.time.types.v0.mock.Factories.makeDateTimeModel() 24 | } 25 | 26 | } 27 | 28 | object Factories { 29 | 30 | def randomString(length: Int = 24): String = { 31 | _root_.scala.util.Random.alphanumeric.take(length).mkString 32 | } 33 | 34 | def makeDateTimeModel(): io.gregor.time.types.v0.models.DateTimeModel = io.gregor.time.types.v0.models.DateTimeModel( 35 | dateTime = _root_.org.joda.time.DateTime.now, 36 | date = _root_.org.joda.time.LocalDate.now 37 | ) 38 | 39 | def makeDateTimeUnion(): io.gregor.time.types.v0.models.DateTimeUnion = io.gregor.time.types.v0.models.DateTimeUnionDateTime(_root_.org.joda.time.DateTime.now) 40 | 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /lib/src/test/resources/play2/mock-client/play27_date-time-offsetdatetime.txt: -------------------------------------------------------------------------------- 1 | package io.gregor.time.types.v0.mock { 2 | 3 | trait Client extends io.gregor.time.types.v0.interfaces.Client { 4 | 5 | val baseUrl: String = "http://mock.localhost" 6 | 7 | override def dateTimeModels: io.gregor.time.types.v0.DateTimeModels = MockDateTimeModelsImpl 8 | 9 | } 10 | 11 | object MockDateTimeModelsImpl extends MockDateTimeModels 12 | 13 | trait MockDateTimeModels extends io.gregor.time.types.v0.DateTimeModels { 14 | 15 | def postByPathDateAndPathTime( 16 | pathDate: _root_.java.time.LocalDate, 17 | pathTime: _root_.java.time.OffsetDateTime, 18 | queryDate: _root_.java.time.LocalDate, 19 | queryTime: _root_.java.time.OffsetDateTime, 20 | dateTimeModel: io.gregor.time.types.v0.models.DateTimeModel, 21 | requestHeaders: Seq[(String, String)] = Nil 22 | )(implicit ec: scala.concurrent.ExecutionContext): scala.concurrent.Future[io.gregor.time.types.v0.models.DateTimeModel] = scala.concurrent.Future.successful { 23 | io.gregor.time.types.v0.mock.Factories.makeDateTimeModel() 24 | } 25 | 26 | } 27 | 28 | object Factories { 29 | 30 | def randomString(length: Int = 24): String = { 31 | _root_.scala.util.Random.alphanumeric.take(length).mkString 32 | } 33 | 34 | def makeDateTimeModel(): io.gregor.time.types.v0.models.DateTimeModel = io.gregor.time.types.v0.models.DateTimeModel( 35 | dateTime = _root_.java.time.OffsetDateTime.now, 36 | date = _root_.java.time.LocalDate.now 37 | ) 38 | 39 | def makeDateTimeUnion(): io.gregor.time.types.v0.models.DateTimeUnion = io.gregor.time.types.v0.models.DateTimeUnionOffsetDateTime(_root_.java.time.OffsetDateTime.now) 40 | 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /lib/src/test/resources/play2/mock-client/play28_date-time-instant.txt: -------------------------------------------------------------------------------- 1 | package io.gregor.time.types.v0.mock { 2 | 3 | trait Client extends io.gregor.time.types.v0.interfaces.Client { 4 | 5 | val baseUrl: String = "http://mock.localhost" 6 | 7 | override def dateTimeModels: io.gregor.time.types.v0.DateTimeModels = MockDateTimeModelsImpl 8 | 9 | } 10 | 11 | object MockDateTimeModelsImpl extends MockDateTimeModels 12 | 13 | trait MockDateTimeModels extends io.gregor.time.types.v0.DateTimeModels { 14 | 15 | def postByPathDateAndPathTime( 16 | pathDate: _root_.java.time.LocalDate, 17 | pathTime: _root_.java.time.Instant, 18 | queryDate: _root_.java.time.LocalDate, 19 | queryTime: _root_.java.time.Instant, 20 | dateTimeModel: io.gregor.time.types.v0.models.DateTimeModel, 21 | requestHeaders: Seq[(String, String)] = Nil 22 | )(implicit ec: scala.concurrent.ExecutionContext): scala.concurrent.Future[io.gregor.time.types.v0.models.DateTimeModel] = scala.concurrent.Future.successful { 23 | io.gregor.time.types.v0.mock.Factories.makeDateTimeModel() 24 | } 25 | 26 | } 27 | 28 | object Factories { 29 | 30 | def randomString(length: Int = 24): String = { 31 | _root_.scala.util.Random.alphanumeric.take(length).mkString 32 | } 33 | 34 | def makeDateTimeModel(): io.gregor.time.types.v0.models.DateTimeModel = io.gregor.time.types.v0.models.DateTimeModel( 35 | dateTime = _root_.java.time.Instant.now, 36 | date = _root_.java.time.LocalDate.now 37 | ) 38 | 39 | def makeDateTimeUnion(): io.gregor.time.types.v0.models.DateTimeUnion = io.gregor.time.types.v0.models.DateTimeUnionInstant(_root_.java.time.Instant.now) 40 | 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /lib/src/test/resources/play2/mock-client/play28_date-time-joda.txt: -------------------------------------------------------------------------------- 1 | package io.gregor.time.types.v0.mock { 2 | 3 | trait Client extends io.gregor.time.types.v0.interfaces.Client { 4 | 5 | val baseUrl: String = "http://mock.localhost" 6 | 7 | override def dateTimeModels: io.gregor.time.types.v0.DateTimeModels = MockDateTimeModelsImpl 8 | 9 | } 10 | 11 | object MockDateTimeModelsImpl extends MockDateTimeModels 12 | 13 | trait MockDateTimeModels extends io.gregor.time.types.v0.DateTimeModels { 14 | 15 | def postByPathDateAndPathTime( 16 | pathDate: _root_.org.joda.time.LocalDate, 17 | pathTime: _root_.org.joda.time.DateTime, 18 | queryDate: _root_.org.joda.time.LocalDate, 19 | queryTime: _root_.org.joda.time.DateTime, 20 | dateTimeModel: io.gregor.time.types.v0.models.DateTimeModel, 21 | requestHeaders: Seq[(String, String)] = Nil 22 | )(implicit ec: scala.concurrent.ExecutionContext): scala.concurrent.Future[io.gregor.time.types.v0.models.DateTimeModel] = scala.concurrent.Future.successful { 23 | io.gregor.time.types.v0.mock.Factories.makeDateTimeModel() 24 | } 25 | 26 | } 27 | 28 | object Factories { 29 | 30 | def randomString(length: Int = 24): String = { 31 | _root_.scala.util.Random.alphanumeric.take(length).mkString 32 | } 33 | 34 | def makeDateTimeModel(): io.gregor.time.types.v0.models.DateTimeModel = io.gregor.time.types.v0.models.DateTimeModel( 35 | dateTime = _root_.org.joda.time.DateTime.now, 36 | date = _root_.org.joda.time.LocalDate.now 37 | ) 38 | 39 | def makeDateTimeUnion(): io.gregor.time.types.v0.models.DateTimeUnion = io.gregor.time.types.v0.models.DateTimeUnionDateTime(_root_.org.joda.time.DateTime.now) 40 | 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /lib/src/test/resources/play2/mock-client/play28_date-time-offsetdatetime.txt: -------------------------------------------------------------------------------- 1 | package io.gregor.time.types.v0.mock { 2 | 3 | trait Client extends io.gregor.time.types.v0.interfaces.Client { 4 | 5 | val baseUrl: String = "http://mock.localhost" 6 | 7 | override def dateTimeModels: io.gregor.time.types.v0.DateTimeModels = MockDateTimeModelsImpl 8 | 9 | } 10 | 11 | object MockDateTimeModelsImpl extends MockDateTimeModels 12 | 13 | trait MockDateTimeModels extends io.gregor.time.types.v0.DateTimeModels { 14 | 15 | def postByPathDateAndPathTime( 16 | pathDate: _root_.java.time.LocalDate, 17 | pathTime: _root_.java.time.OffsetDateTime, 18 | queryDate: _root_.java.time.LocalDate, 19 | queryTime: _root_.java.time.OffsetDateTime, 20 | dateTimeModel: io.gregor.time.types.v0.models.DateTimeModel, 21 | requestHeaders: Seq[(String, String)] = Nil 22 | )(implicit ec: scala.concurrent.ExecutionContext): scala.concurrent.Future[io.gregor.time.types.v0.models.DateTimeModel] = scala.concurrent.Future.successful { 23 | io.gregor.time.types.v0.mock.Factories.makeDateTimeModel() 24 | } 25 | 26 | } 27 | 28 | object Factories { 29 | 30 | def randomString(length: Int = 24): String = { 31 | _root_.scala.util.Random.alphanumeric.take(length).mkString 32 | } 33 | 34 | def makeDateTimeModel(): io.gregor.time.types.v0.models.DateTimeModel = io.gregor.time.types.v0.models.DateTimeModel( 35 | dateTime = _root_.java.time.OffsetDateTime.now, 36 | date = _root_.java.time.LocalDate.now 37 | ) 38 | 39 | def makeDateTimeUnion(): io.gregor.time.types.v0.models.DateTimeUnion = io.gregor.time.types.v0.models.DateTimeUnionOffsetDateTime(_root_.java.time.OffsetDateTime.now) 40 | 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /lib/src/test/resources/play2/route/date-time-instant.txt: -------------------------------------------------------------------------------- 1 | # Generated by API Builder - https://www.apibuilder.io 2 | # Service version: 0.0.1-dev 3 | 4 | POST /date_time_models/:path_date/:path_time controllers.DateTimeModels.postByPathDateAndPathTime(path_date: _root_.java.time.LocalDate, path_time: _root_.java.time.Instant, query_date: _root_.java.time.LocalDate, query_time: _root_.java.time.Instant) -------------------------------------------------------------------------------- /lib/src/test/resources/play2/route/date-time-joda.txt: -------------------------------------------------------------------------------- 1 | # Generated by API Builder - https://www.apibuilder.io 2 | # Service version: 0.0.1-dev 3 | 4 | POST /date_time_models/:path_date/:path_time controllers.DateTimeModels.postByPathDateAndPathTime(path_date: _root_.org.joda.time.LocalDate, path_time: _root_.org.joda.time.DateTime, query_date: _root_.org.joda.time.LocalDate, query_time: _root_.org.joda.time.DateTime) -------------------------------------------------------------------------------- /lib/src/test/resources/play2/route/date-time-offsetdatetime.txt: -------------------------------------------------------------------------------- 1 | # Generated by API Builder - https://www.apibuilder.io 2 | # Service version: 0.0.1-dev 3 | 4 | POST /date_time_models/:path_date/:path_time controllers.DateTimeModels.postByPathDateAndPathTime(path_date: _root_.java.time.LocalDate, path_time: _root_.java.time.OffsetDateTime, query_date: _root_.java.time.LocalDate, query_time: _root_.java.time.OffsetDateTime) -------------------------------------------------------------------------------- /lib/src/test/resources/ruby-gem-enums.txt: -------------------------------------------------------------------------------- 1 | class AgeGroup 2 | 3 | attr_reader :value 4 | 5 | def initialize(value) 6 | @value = HttpClient::Preconditions.assert_class('value', value, String) 7 | end 8 | 9 | # Returns the instance of AgeGroup for this value, creating a new instance for an unknown value 10 | def AgeGroup.apply(value) 11 | if value.instance_of?(AgeGroup) 12 | value 13 | else 14 | HttpClient::Preconditions.assert_class_or_nil('value', value, String) 15 | value.nil? ? nil : (from_string(value) || AgeGroup.new(value)) 16 | end 17 | end 18 | 19 | # Returns the instance of AgeGroup for this value, or nil if not found 20 | def AgeGroup.from_string(value) 21 | HttpClient::Preconditions.assert_class('value', value, String) 22 | AgeGroup.ALL.find { |v| v.value == value } 23 | end 24 | 25 | def AgeGroup.ALL 26 | @@all ||= [AgeGroup.thirties, AgeGroup.forties] 27 | end 28 | 29 | def AgeGroup.thirties 30 | @@_thirties ||= AgeGroup.new('Thirties') 31 | end 32 | 33 | def AgeGroup.forties 34 | @@_forties ||= AgeGroup.new('Forties') 35 | end 36 | 37 | def to_hash 38 | value 39 | end 40 | 41 | end -------------------------------------------------------------------------------- /lib/src/test/resources/scala-nested-union-models-case-classes.txt: -------------------------------------------------------------------------------- 1 | package test.apidoc.apidoctest.v0.models { 2 | 3 | sealed trait InnerType extends OuterType with SecondOuterType 4 | 5 | sealed trait OuterType extends _root_.scala.Product with _root_.scala.Serializable 6 | 7 | sealed trait SecondOuterType extends _root_.scala.Product with _root_.scala.Serializable 8 | final case class StringModel( 9 | name: String 10 | ) extends InnerType 11 | 12 | /** 13 | * Provides future compatibility in clients - in the future, when a type is added 14 | * to the union InnerType, it will need to be handled in the client code. This 15 | * implementation will deserialize these future types as an instance of this class. 16 | * 17 | * @param description Information about the type that we received that is undefined in this version of 18 | * the client. 19 | */ 20 | 21 | final case class InnerTypeUndefinedType( 22 | description: String 23 | ) extends InnerType 24 | 25 | /** 26 | * Provides future compatibility in clients - in the future, when a type is added 27 | * to the union OuterType, it will need to be handled in the client code. This 28 | * implementation will deserialize these future types as an instance of this class. 29 | * 30 | * @param description Information about the type that we received that is undefined in this version of 31 | * the client. 32 | */ 33 | 34 | final case class OuterTypeUndefinedType( 35 | description: String 36 | ) extends OuterType 37 | 38 | /** 39 | * Provides future compatibility in clients - in the future, when a type is added 40 | * to the union SecondOuterType, it will need to be handled in the client code. 41 | * This implementation will deserialize these future types as an instance of this 42 | * class. 43 | * 44 | * @param description Information about the type that we received that is undefined in this version of 45 | * the client. 46 | */ 47 | 48 | final case class SecondOuterTypeUndefinedType( 49 | description: String 50 | ) extends SecondOuterType 51 | 52 | } -------------------------------------------------------------------------------- /lib/src/test/resources/scala-nested-union-models-json-union-type-readers-inner-type.txt: -------------------------------------------------------------------------------- 1 | implicit def jsonReadsAPIBuilderTestInnerType: play.api.libs.json.Reads[test.apidoc.apidoctest.v0.models.InnerType] = { 2 | ( 3 | (__ \ "string_model").read(jsonReadsAPIBuilderTestStringModel).asInstanceOf[play.api.libs.json.Reads[InnerType]] 4 | orElse 5 | play.api.libs.json.Reads(jsValue => play.api.libs.json.JsSuccess(InnerTypeUndefinedType(jsValue.toString))).asInstanceOf[play.api.libs.json.Reads[InnerType]] 6 | ) 7 | } -------------------------------------------------------------------------------- /lib/src/test/resources/scala-nested-union-models-json-union-type-readers-outer-type.txt: -------------------------------------------------------------------------------- 1 | implicit def jsonReadsAPIBuilderTestOuterType: play.api.libs.json.Reads[test.apidoc.apidoctest.v0.models.OuterType] = { 2 | ( 3 | (__ \ "inner_type").read(jsonReadsAPIBuilderTestOuterType).asInstanceOf[play.api.libs.json.Reads[OuterType]] 4 | orElse 5 | play.api.libs.json.Reads(jsValue => play.api.libs.json.JsSuccess(OuterTypeUndefinedType(jsValue.toString))).asInstanceOf[play.api.libs.json.Reads[OuterType]] 6 | ) 7 | } -------------------------------------------------------------------------------- /lib/src/test/resources/scala-union-enums-json.txt: -------------------------------------------------------------------------------- 1 | implicit def jsonReadsAPIBuilderTestUserType: play.api.libs.json.Reads[test.apidoc.apidoctest.v0.models.UserType] = { 2 | ( 3 | (__ \ "member_type").read(jsonReadsAPIBuilderTestMemberType).asInstanceOf[play.api.libs.json.Reads[UserType]] 4 | orElse 5 | (__ \ "role_type").read(jsonReadsAPIBuilderTestRoleType).asInstanceOf[play.api.libs.json.Reads[UserType]] 6 | orElse 7 | play.api.libs.json.Reads(jsValue => play.api.libs.json.JsSuccess(UserTypeUndefinedType(jsValue.toString))).asInstanceOf[play.api.libs.json.Reads[UserType]] 8 | ) 9 | } 10 | 11 | def jsObjectUserType(obj: test.apidoc.apidoctest.v0.models.UserType): play.api.libs.json.JsObject = { 12 | obj match { 13 | case x: test.apidoc.apidoctest.v0.models.MemberType => play.api.libs.json.Json.obj("member_type" -> play.api.libs.json.JsString(x.toString)) 14 | case x: test.apidoc.apidoctest.v0.models.RoleType => play.api.libs.json.Json.obj("role_type" -> play.api.libs.json.JsString(x.toString)) 15 | case x: test.apidoc.apidoctest.v0.models.UserTypeUndefinedType => { 16 | scala.util.Try { 17 | // If we received a JSON object - echo it back. This is a workaround for a bug in 18 | // serialization for unions w/out discriminators where they sometimes have the 19 | // type wrapper and sometimes do not 20 | play.api.libs.json.Json.parse(x.description).asInstanceOf[play.api.libs.json.JsObject] 21 | } match { 22 | case scala.util.Success(o) => o 23 | case scala.util.Failure(_) => sys.error("The type[test.apidoc.apidoctest.v0.models.UserTypeUndefinedType] should never be serialized") 24 | } 25 | } 26 | } 27 | } 28 | implicit def jsonWritesAPIBuilderTestUserType: play.api.libs.json.Writes[UserType] = { 29 | (obj: test.apidoc.apidoctest.v0.models.UserType) => { 30 | test.apidoc.apidoctest.v0.models.json.jsObjectUserType(obj) 31 | } 32 | } -------------------------------------------------------------------------------- /lib/src/test/resources/scala-union-models-case-classes.txt: -------------------------------------------------------------------------------- 1 | package test.apidoc.apidoctest.v0.models { 2 | 3 | sealed trait User extends _root_.scala.Product with _root_.scala.Serializable 4 | final case class GuestUser( 5 | id: Long, 6 | email: String, 7 | name: _root_.scala.Option[String] = None, 8 | bar: String 9 | ) extends User 10 | 11 | final case class RegisteredUser( 12 | id: Long, 13 | email: String, 14 | name: _root_.scala.Option[String] = None, 15 | foo: String 16 | ) extends User 17 | 18 | /** 19 | * Provides future compatibility in clients - in the future, when a type is added 20 | * to the union User, it will need to be handled in the client code. This 21 | * implementation will deserialize these future types as an instance of this class. 22 | * 23 | * @param description Information about the type that we received that is undefined in this version of 24 | * the client. 25 | */ 26 | 27 | final case class UserUndefinedType( 28 | description: String 29 | ) extends User 30 | 31 | } -------------------------------------------------------------------------------- /lib/src/test/resources/scala-union-models-json-union-type-readers.txt: -------------------------------------------------------------------------------- 1 | implicit def jsonReadsAPIBuilderTestUser: play.api.libs.json.Reads[test.apidoc.apidoctest.v0.models.User] = { 2 | ( 3 | (__ \ "registered_user").read(jsonReadsAPIBuilderTestRegisteredUser).asInstanceOf[play.api.libs.json.Reads[User]] 4 | orElse 5 | (__ \ "guest_user").read(jsonReadsAPIBuilderTestGuestUser).asInstanceOf[play.api.libs.json.Reads[User]] 6 | orElse 7 | play.api.libs.json.Reads(jsValue => play.api.libs.json.JsSuccess(UserUndefinedType(jsValue.toString))).asInstanceOf[play.api.libs.json.Reads[User]] 8 | ) 9 | } -------------------------------------------------------------------------------- /lib/src/test/resources/scala-union-models-json-union-type-writers.txt: -------------------------------------------------------------------------------- 1 | def jsObjectUser(obj: test.apidoc.apidoctest.v0.models.User): play.api.libs.json.JsObject = { 2 | obj match { 3 | case x: test.apidoc.apidoctest.v0.models.RegisteredUser => play.api.libs.json.Json.obj("registered_user" -> test.apidoc.apidoctest.v0.models.json.jsObjectRegisteredUser(x)) 4 | case x: test.apidoc.apidoctest.v0.models.GuestUser => play.api.libs.json.Json.obj("guest_user" -> test.apidoc.apidoctest.v0.models.json.jsObjectGuestUser(x)) 5 | case x: test.apidoc.apidoctest.v0.models.UserUndefinedType => { 6 | scala.util.Try { 7 | // If we received a JSON object - echo it back. This is a workaround for a bug in 8 | // serialization for unions w/out discriminators where they sometimes have the 9 | // type wrapper and sometimes do not 10 | play.api.libs.json.Json.parse(x.description).asInstanceOf[play.api.libs.json.JsObject] 11 | } match { 12 | case scala.util.Success(o) => o 13 | case scala.util.Failure(_) => sys.error("The type[test.apidoc.apidoctest.v0.models.UserUndefinedType] should never be serialized") 14 | } 15 | } 16 | } 17 | } 18 | implicit def jsonWritesAPIBuilderTestUser: play.api.libs.json.Writes[User] = { 19 | (obj: test.apidoc.apidoctest.v0.models.User) => { 20 | test.apidoc.apidoctest.v0.models.json.jsObjectUser(obj) 21 | } 22 | } -------------------------------------------------------------------------------- /lib/src/test/scala/MethodsSpec.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import org.scalatest.funspec.AnyFunSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | class MethodsSpec extends AnyFunSpec with Matchers { 7 | 8 | it("isJsonDocumentMethod") { 9 | Methods.isJsonDocumentMethod("GET") should be(false) 10 | Methods.isJsonDocumentMethod("get") should be(false) 11 | Methods.isJsonDocumentMethod("DELETE") should be(false) 12 | Methods.isJsonDocumentMethod("delete") should be(false) 13 | Methods.isJsonDocumentMethod("POST") should be(true) 14 | Methods.isJsonDocumentMethod("post") should be(true) 15 | Methods.isJsonDocumentMethod("PUT") should be(true) 16 | Methods.isJsonDocumentMethod("put") should be(true) 17 | Methods.isJsonDocumentMethod("PATCH") should be(true) 18 | Methods.isJsonDocumentMethod("patch") should be(true) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /lib/src/test/scala/ReviewSpec.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import org.scalatest.funspec.AnyFunSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | class ReviewSpec extends AnyFunSpec with Matchers { 7 | 8 | it("fromString") { 9 | Review.fromString(Review.Accept.key) should be(Some(Review.Accept)) 10 | Review.fromString(Review.Accept.key.toUpperCase) should be(Some(Review.Accept)) 11 | Review.fromString(Review.Accept.key.toLowerCase) should be(Some(Review.Accept)) 12 | 13 | Review.fromString(Review.Decline.key) should be(Some(Review.Decline)) 14 | Review.fromString(Review.Decline.key.toUpperCase) should be(Some(Review.Decline)) 15 | Review.fromString(Review.Decline.key.toLowerCase) should be(Some(Review.Decline)) 16 | 17 | Review.fromString("other") should be(None) 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /lib/src/test/scala/RoleSpec.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import org.scalatest.funspec.AnyFunSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | class RoleSpec extends AnyFunSpec with Matchers { 7 | 8 | it("fromString") { 9 | Role.fromString(Role.Admin.key) should be(Some(Role.Admin)) 10 | Role.fromString(Role.Admin.key.toUpperCase) should be(Some(Role.Admin)) 11 | Role.fromString(Role.Admin.key.toLowerCase) should be(Some(Role.Admin)) 12 | 13 | Role.fromString(Role.Member.key) should be(Some(Role.Member)) 14 | Role.fromString(Role.Member.key.toUpperCase) should be(Some(Role.Member)) 15 | Role.fromString(Role.Member.key.toLowerCase) should be(Some(Role.Member)) 16 | 17 | Role.fromString("other") should be(None) 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /lib/src/test/scala/TestHelperSpec.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import scala.util.{Failure, Success, Try} 4 | import models.TestHelper 5 | import org.scalatest.exceptions.TestFailedException 6 | import org.scalatest.funspec.AnyFunSpec 7 | import org.scalatest.matchers.should.Matchers 8 | 9 | class TestHelperSpec extends AnyFunSpec with Matchers { 10 | 11 | it("All services are valid") { 12 | val errors = Seq( 13 | "apidoc-api.json", 14 | "apidoc-generator.json", 15 | "collection-json-defaults.json", 16 | "reference-service.json", 17 | "reference-with-imports.json", 18 | "response-with-unit-type.json" 19 | ).flatMap { file => 20 | Try( 21 | models.TestHelper.parseFile(s"/examples/$file") 22 | ) match { 23 | case Success(_) => None 24 | case Failure(ex) => Some(s"$file: $ex") 25 | } 26 | } 27 | 28 | errors should be(Nil) 29 | } 30 | 31 | describe("assertValidScalaSourceCode") { 32 | it("accepts valid Scala source code") { 33 | val code = """ 34 | |import java.util.Calendar 35 | |import scala.concurrent.Future 36 | | 37 | |trait MyTrait 38 | | 39 | |class Foobar extends MyTrait 40 | | 41 | """.stripMargin 42 | TestHelper.assertValidScalaSourceCode(code, None) 43 | } 44 | 45 | it("throws exception when source code is invalid") { 46 | val ex = intercept[TestFailedException] { TestHelper.assertValidScalaSourceCode("invalid Scala code", Some("Invalid.scala")) } 47 | ex.getMessage should startWith ("Not valid Scala source") 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/src/test/scala/UrlKeySpec.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | 5 | class UrlKeySpec extends AnyFlatSpec { 6 | 7 | behavior of "Url Key Generator" 8 | 9 | it should "leave good urls alone" in { 10 | assert("foo" === UrlKey.generate("foo")) 11 | assert("foo-bar" === UrlKey.generate("foo-bar")) 12 | } 13 | 14 | it should "leave numbers alone" in { 15 | assert("foo123" === UrlKey.generate("foo123")) 16 | } 17 | 18 | it should "lower case" in { 19 | assert("foo-bar" === UrlKey.generate("FOO-BAR")) 20 | } 21 | 22 | it should "trim" in { 23 | assert("foo-bar" === UrlKey.generate(" FOO-BAR ")) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /lib/src/test/scala/helpers/TestHelpers.scala: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import cats.data.Validated.{Invalid, Valid} 4 | import cats.data.ValidatedNec 5 | 6 | trait TestHelpers { 7 | 8 | def rightOrErrors[T](value: Either[_, T]): T = { 9 | value match { 10 | case Right(r) => r 11 | case Left(errors) => sys.error(s"Expected valid value but got: $errors") 12 | } 13 | } 14 | 15 | def leftOrErrors[T](value: Either[T, _]): T = { 16 | value match { 17 | case Right(_) => sys.error("Expected a left value") 18 | case Left(r) => r 19 | } 20 | } 21 | def expectValid[T](r: ValidatedNec[String, T]): T = { 22 | r match { 23 | case Invalid(e) => sys.error(s"Expected valid but got: ${e.toNonEmptyList.toList}") 24 | case Valid(r) => r 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /postman-generator/src/main/scala/examples/RandomStringGenerator.scala: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import java.util.UUID 4 | 5 | import scala.util.hashing.MurmurHash3 6 | 7 | trait RandomStringGenerator { 8 | def generate(seed: String): String 9 | } 10 | 11 | object UuidRandomStringGenerator extends RandomStringGenerator { 12 | 13 | override def generate(seed: String): String = { 14 | UUID.randomUUID().toString.toLowerCase.takeRight(12) 15 | } 16 | 17 | } 18 | 19 | object MurmurRandomStringGenerator extends RandomStringGenerator { 20 | override def generate(seed: String): String = { 21 | val hash = MurmurHash3.stringHash(seed).toString.takeRight(6) 22 | s"lorem_ipsum_$hash" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /postman-generator/src/main/scala/generator/Heuristics.scala: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import io.apibuilder.spec.v0.models.Resource 4 | 5 | object Heuristics { 6 | 7 | // TODO: unify heuristics mechanism with PostmanAttributes.PostmanVariableName type and its var name->reference logic 8 | case class PathVariable(name: String, postmanVarName: String) { 9 | def postmanVarRef: String = s"{{$postmanVarName}}" 10 | } 11 | 12 | val IdFieldHeuristicThreshold = 3 13 | 14 | def idFieldHeuristic(resource: Resource): Option[String] = { 15 | val pathsWithOperation = resource.operations.flatMap { operation => 16 | PathParamsFinder.find(operation.path) 17 | .map(param => (param, resource)) 18 | } 19 | pathsWithOperation 20 | .groupBy { case (param, _) => param } 21 | .view 22 | .mapValues(_.size) 23 | .filter(_._2 >= IdFieldHeuristicThreshold) 24 | .keys 25 | .headOption 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /postman-generator/src/main/scala/generator/PathParamsFinder.scala: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | object PathParamsFinder { 4 | 5 | private val regex = """\:(\w+)[\/]{0,1}""".r 6 | 7 | def find(path: String): Seq[String] = { 8 | regex 9 | .findAllIn(path) 10 | .map(str => regex.replaceAllIn(str, "$1")) 11 | .toList 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /postman-generator/src/main/scala/generator/PostmanGeneratorConstants.scala: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | object PostmanGeneratorConstants { 4 | val BaseUrl = "BASE_URL" 5 | val EntitiesSetup = "Entities Setup" 6 | val EntitiesCleanup = "Entities Cleanup" 7 | } 8 | -------------------------------------------------------------------------------- /postman-generator/src/main/scala/generator/PredefinedCollectionItems.scala: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import generator.Heuristics.PathVariable 4 | import io.apibuilder.postman.collection.v21.v0.models._ 5 | import io.apibuilder.postman.collection.v21.v0.{models => postman} 6 | 7 | object PredefinedCollectionItems { 8 | 9 | def addItemTests(item: postman.Item): postman.Item = { 10 | import Method._ 11 | 12 | val methodsToCoverWithTests = Seq(Get, Put, Post) 13 | val itemMethod = item.request.method.getOrElse(UNDEFINED) 14 | 15 | if (methodsToCoverWithTests.contains(itemMethod)) { 16 | val test = PredefinedCollectionItems.testEventResponseStatusOk( 17 | f"$itemMethod requests should return 2xx" 18 | ) 19 | item.copy(event = Option(item.event.toSeq.flatten :+ test)) 20 | } else { 21 | item 22 | } 23 | } 24 | 25 | def testEventResponseStatusOk(testTitle: String): Event = { 26 | Event( 27 | EventType.Test, 28 | Some(Script(Seq( 29 | s"""pm.test("$testTitle", function () {""", 30 | """ pm.response.to.be.success;""", 31 | """});""" 32 | ))) 33 | ) 34 | } 35 | 36 | def testPostStatusOk(testTitle: String, pathVariable: Option[PathVariable]): Event = { 37 | Event( 38 | EventType.Test, 39 | Some(Script { 40 | val test = Seq( 41 | s"""pm.test("$testTitle", function () {""", 42 | """ pm.response.to.be.success;""", 43 | """});""" 44 | ) 45 | 46 | val pathVariableSetup = pathVariable.map { pathVar => 47 | Seq( 48 | """var jsonData = JSON.parse(responseBody);""", 49 | s"""var id = jsonData["${pathVar.name}"];""", 50 | s"""if (id != null) pm.environment.set("${pathVar.postmanVarName}", id);""" 51 | ) 52 | }.getOrElse(Seq.empty) 53 | 54 | test ++ pathVariableSetup 55 | } 56 | )) 57 | } 58 | } -------------------------------------------------------------------------------- /postman-generator/src/main/scala/generator/Utils.scala: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import io.apibuilder.postman.collection.v21.v0.models.Description 4 | import io.apibuilder.postman.collection.v21.v0.{models => postman} 5 | 6 | object Utils { 7 | 8 | object Description { 9 | def apply(s: String): postman.Description = { 10 | postman.Description(content = Some(s), `type` = None) 11 | } 12 | } 13 | 14 | object Variable { 15 | def apply( 16 | key: String, 17 | value: String, 18 | `type`: String, 19 | id: Option[String] = None, 20 | name: Option[String] = None, 21 | description: Option[Description] = None, 22 | system: Option[Boolean] = None, 23 | disabled: Option[Boolean] = None 24 | ): postman.Variable = { 25 | postman.Variable( 26 | id = id, 27 | key = Some(key), 28 | value = Some(value), 29 | `type` = Some(`type`), 30 | name = name, 31 | description = description, 32 | system = system, 33 | disabled = disabled 34 | ) 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /postman-generator/src/main/scala/models/AttributeValueReader.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import io.postman.generator.attributes.v0.models.AttributeName 4 | import _root_.play.api.libs.json.{JsError, JsSuccess, JsValue, Reads} 5 | import _root_.play.api.Logging 6 | import io.apibuilder.spec.v0.models.Attribute 7 | 8 | import scala.reflect.{ClassTag, classTag} 9 | 10 | object AttributeValueReader extends Logging { 11 | 12 | def findAndReadFirst[A : Reads : ClassTag](attributes: Seq[Attribute], attributeName: AttributeName): Option[A] = { 13 | attributes.collectFirst { 14 | case attr if attr.name.equalsIgnoreCase(attributeName.toString) => 15 | tryRead[A](attributeName, attr.value) 16 | }.flatten 17 | } 18 | 19 | def tryRead[A : Reads : ClassTag](attributeName: AttributeName, json: JsValue): Option[A] = { 20 | val reads = implicitly[Reads[A]] 21 | reads.reads(json) match { 22 | case JsSuccess(entity, _) => 23 | Some(entity) 24 | case JsError(errors) => 25 | logger.warn(s"Attribute [$attributeName] value $json could not be read as ${classTag[A].runtimeClass.getName} / Errors: $errors") 26 | None 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /postman-generator/src/main/scala/models/attributes/PostmanAttributes.scala: -------------------------------------------------------------------------------- 1 | package models.attributes 2 | 3 | import io.apibuilder.spec.v0.models.Parameter 4 | import io.postman.generator.attributes.v0.models.ObjectReference 5 | 6 | object PostmanAttributes { 7 | 8 | /** 9 | * This class basically extends the auto-generated [[ObjectReference]] 10 | * it contains one additional field with Postman variable name created by the generator 11 | * 12 | * As original auto-generated [[ObjectReference]] class is 'final case class' 13 | * we need to keep [[ExtendedObjectReference]] up to date manually, because we can't 'extend' the parent for real. 14 | */ 15 | case class ExtendedObjectReference( 16 | relatedServiceNamespace: String, 17 | resourceType: String, 18 | operationMethod: io.apibuilder.spec.v0.models.Method, 19 | operationPath: String, 20 | identifierField: String, 21 | queryParams: Option[Map[String, String]], 22 | deleteOperationPath: Option[String], 23 | postmanVariableName: PostmanVariableName 24 | ) 25 | 26 | case class PostmanVariableName(name: String) { 27 | def reference: String = s"{{$name}}" 28 | } 29 | 30 | implicit class ObjectReferenceExtend(val objRef: ObjectReference) extends AnyVal { 31 | 32 | def toExtended: ExtendedObjectReference = ExtendedObjectReference( 33 | relatedServiceNamespace = objRef.relatedServiceNamespace, 34 | resourceType = objRef.resourceType, 35 | operationMethod = objRef.operationMethod, 36 | operationPath = objRef.operationPath, 37 | identifierField = objRef.identifierField, 38 | queryParams = objRef.queryParams, 39 | deleteOperationPath = objRef.deleteOperationPath, 40 | postmanVariableName = postmanVariableNameFrom(objRef) 41 | ) 42 | } 43 | 44 | def postmanVariableNameFrom(objReference: ObjectReference): PostmanVariableName = { 45 | import objReference._ 46 | val name = s"$resourceType#$identifierField" 47 | PostmanVariableName(name) 48 | } 49 | 50 | def postmanVariableNameFrom(parameter: Parameter): PostmanVariableName = { 51 | val name = parameter.name 52 | PostmanVariableName(name) 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /postman-generator/src/main/scala/models/operation/DependantOperations.scala: -------------------------------------------------------------------------------- 1 | package models.operation 2 | 3 | import io.apibuilder.spec.v0.models.Operation 4 | 5 | case class DependantOperations(referencedOperation: Operation, deleteOperationOpt: Option[Operation]) 6 | -------------------------------------------------------------------------------- /postman-generator/src/main/scala/models/service/ResolvedService.scala: -------------------------------------------------------------------------------- 1 | package models.service 2 | 3 | import io.apibuilder.spec.v0.models.{Resource, Service} 4 | 5 | case class ResolvedService(service: Service, serviceNamespaceToResources: Map[String, Seq[Resource]]) -------------------------------------------------------------------------------- /postman-generator/src/test/scala/generator/FileInputOutputGeneratorTests.scala: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | import models.TestHelper._ 5 | import org.scalatest.Assertion 6 | import play.api.libs.json._ 7 | import testUtils.TestPostmanCollectionGenerator 8 | 9 | import scala.io.Source 10 | import org.scalatest.wordspec.AnyWordSpec 11 | 12 | class FileInputOutputGeneratorTests extends AnyWordSpec { 13 | 14 | "Postman Generator" should { 15 | "successfully generate a postman collection from test-service-1.json" in { 16 | generateCollectionAndVerify("/apibuilder/test-service-1.json", "/postman/expected-1.json") 17 | } 18 | 19 | "successfully generate a postman collection from test-service-operation-deps.json" in { 20 | generateCollectionAndVerify("/apibuilder/test-service-operation-deps.json", "/postman/expected-operation-deps.json") 21 | } 22 | 23 | } 24 | 25 | private def generateCollectionAndVerify(servicePath: String, expectedCollectionPath: String): Assertion = { 26 | val resourceUrl = getClass.getResource(expectedCollectionPath) 27 | val expectedJson = Json.parse( 28 | Source.fromURL(resourceUrl).mkString 29 | ).as[JsValue] 30 | 31 | val inputFile = Source.fromURL(getClass.getResource(servicePath)).mkString 32 | val parsedService = service(inputFile) 33 | 34 | val invocationForm = InvocationForm(parsedService, importedServices = None) 35 | val result = TestPostmanCollectionGenerator.invoke(invocationForm) 36 | 37 | val files = result.getOrElse(fail("Generator invoke failure")) 38 | val str = files.head.contents 39 | val outputJsonCollection = Json.parse(str) 40 | 41 | outputJsonCollection shouldEqual expectedJson 42 | } 43 | } -------------------------------------------------------------------------------- /postman-generator/src/test/scala/generator/HeuristicsSpec.scala: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import io.apibuilder.spec.v0.models.{Method, Operation, Resource} 4 | import org.scalatest.wordspec.AnyWordSpec 5 | 6 | class HeuristicsSpec extends AnyWordSpec { 7 | 8 | "Postman Generator Heuristics" should { 9 | 10 | // Heuristics.idFieldHeuristic is not used anywhere, and I'm not sure this test is correct 11 | // both :organization and :key show up the same number of times, so how is it supposed to know which one is the id? 12 | "find an identifier param in a group of paths" ignore { 13 | val op1 = Operation(Method.Get, "/:organization/attributes/:key") 14 | val op2 = op1.copy(path = "/:organization/attributes/:key/path/some") 15 | val op3 = op2.copy(path = "/:organization/attributes/:key/path/some/other/path") 16 | 17 | val operations = Seq(op1, op2, op3) 18 | val resource = Resource("Resource", "some Resource", operations = operations) 19 | assert(Heuristics.idFieldHeuristic(resource).get == "key") 20 | 21 | assert(Heuristics.idFieldHeuristic(resource.copy(operations = operations.take(2))) == None) 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /postman-generator/src/test/scala/generator/PathParamsFinderSpec.scala: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import org.scalatest.matchers.should.Matchers 4 | import org.scalatest.wordspec.AnyWordSpec 5 | 6 | class PathParamsFinderSpec extends AnyWordSpec with Matchers { 7 | 8 | "PathParamsFinder" should { 9 | 10 | "find params in path" in { 11 | PathParamsFinder.find("/:organization/attributes/:key") shouldEqual List("organization", "key") 12 | PathParamsFinder.find("/:organization/attributes/:key/:abc") shouldEqual List("organization", "key", "abc") 13 | } 14 | 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /postman-generator/src/test/scala/testUtils/TestPostmanCollectionGenerator.scala: -------------------------------------------------------------------------------- 1 | package testUtils 2 | 3 | import examples.MurmurRandomStringGenerator 4 | import generator.PostmanCollectionGenerator 5 | 6 | object TestPostmanCollectionGenerator extends PostmanCollectionGenerator(MurmurRandomStringGenerator) 7 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.7.1 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // Comment to get more information during initialization 2 | logLevel := Level.Warn 3 | 4 | // The Typesafe repository 5 | resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" 6 | 7 | // Use the Play sbt plugin for Play projects 8 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.20") 9 | -------------------------------------------------------------------------------- /ruby-generator/src/main/scala/models/ApiBuilderComments.scala: -------------------------------------------------------------------------------- 1 | package ruby.models 2 | 3 | import lib.{AbstractApiBuilderComments, Constants} 4 | 5 | /** 6 | * @param version e.g. 1.2.3 Used to inject headers on the full version and the major version number 7 | */ 8 | case class ApiBuilderComments(override val version: String, override val userAgent: Option[String]) extends AbstractApiBuilderComments(version, userAgent) { 9 | 10 | def toRubyString: String = RubyUtil.textToComment(elements) 11 | 12 | } 13 | -------------------------------------------------------------------------------- /ruby-generator/src/main/scala/models/FeatureMigration.scala: -------------------------------------------------------------------------------- 1 | package ruby.models 2 | 3 | import lib.VersionTag 4 | 5 | case class FeatureMigration(serviceVersion: String) { 6 | 7 | private val version = VersionTag(serviceVersion) 8 | 9 | def hasImplicit404s: Boolean = { 10 | versionLessThanOrEqual("0.9.4") 11 | } 12 | 13 | private def versionLessThanOrEqual(value: String): Boolean = { 14 | version.compare(VersionTag(value)) <= 0 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /ruby-generator/src/main/scala/models/Headers.scala: -------------------------------------------------------------------------------- 1 | package ruby.models 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | import lib.VersionTag 5 | import lib.Text._ 6 | 7 | case class Headers( 8 | form: InvocationForm 9 | ) { 10 | 11 | private val versionMajor: Option[Int] = VersionTag(form.service.version).major 12 | 13 | private val VersionMajorName = "VersionMajor" 14 | private val VersionMajorHeaderName = "X-Apidoc-Version-Major" 15 | 16 | private val constants = Seq( 17 | form.service.baseUrl.map { url => ("BaseUrl", url) }, 18 | Some(("Namespace", form.service.namespace)), 19 | Some(("UserAgent", form.userAgent.getOrElse("apibuilder-play_2x_client-unknown"))), 20 | Some(("Version", form.service.version)), 21 | versionMajor.map { major => (VersionMajorName, major.toString) } 22 | ).flatten 23 | 24 | val rubyModuleConstants: String = { 25 | Seq( 26 | "module Constants", 27 | constants.map { pair => 28 | val name = RubyUtil.toConstant(pair._1) 29 | if (pair._1 == VersionMajorName) { 30 | s"$name = ${pair._2} unless defined?($name)" 31 | } else { 32 | s"$name = ${RubyUtil.wrapInQuotes(pair._2)} unless defined?(Constants::$name)" 33 | } 34 | }.mkString("\n").indentString(2), 35 | "end" 36 | ).mkString("\n\n") 37 | } 38 | 39 | val ruby: Seq[(String, String)] = Seq( 40 | Some("User-Agent" -> s"Constants::USER_AGENT"), 41 | Some("X-Apidoc-Version" -> s"Constants::VERSION"), 42 | versionMajor.map { _ => VersionMajorHeaderName -> s"Constants::VERSION_MAJOR" } 43 | ).flatten ++ form.service.headers.filter(_.default.isDefined).map { h => 44 | h.name -> RubyUtil.wrapInQuotes(h.default.get) 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /ruby-generator/src/main/scala/models/RubyPrimitiveWrapper.scala: -------------------------------------------------------------------------------- 1 | package ruby.models 2 | 3 | import lib.Datatype.Primitive 4 | 5 | import io.apibuilder.spec.v0.models.{Field, Model, Service, Union} 6 | 7 | private[models] object RubyPrimitiveWrapper { 8 | 9 | def className(union: Union, primitive: Primitive): String = { 10 | RubyUtil.toClassName(Seq(union.name, primitive.toString, "wrapper").mkString("_")) 11 | } 12 | 13 | } 14 | 15 | private[models] case class RubyPrimitiveWrapper(service: Service) { 16 | 17 | private val primitives: Seq[Primitive] = service.unions.flatMap(_.types).map(_.`type`).flatMap { name => 18 | Primitive(name).toOption 19 | }.distinct.sortBy(_.toString) 20 | 21 | case class Wrapper(model: Model, union: Union) 22 | 23 | def wrappers(): Seq[Wrapper] = { 24 | primitives.flatMap { p => 25 | service.unions.filter(_.types.map(_.`type`).contains(p.name)).map { union => 26 | val name = RubyPrimitiveWrapper.className(union, p) 27 | val model = Model( 28 | name = name, 29 | plural = s"${name}s", 30 | description = Some(s"Wrapper class to support the union type[${union.name}] datatype[${p.name}]"), 31 | fields = Seq( 32 | Field( 33 | name = "value", 34 | `type` = p.name, 35 | required = true 36 | ) 37 | ) 38 | ) 39 | Wrapper(model, union) 40 | } 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /ruby-generator/src/main/scala/models/Util.scala: -------------------------------------------------------------------------------- 1 | package ruby.models 2 | 3 | import io.apibuilder.spec.v0.models._ 4 | 5 | object Util { 6 | 7 | def responseCodeAsString(code: ResponseCode): String = { 8 | code match { 9 | case ResponseCodeInt(value) => value.toString 10 | case ResponseCodeOption.Default => ResponseCodeOption.Default.toString 11 | case ResponseCodeOption.UNDEFINED(value) => value 12 | case ResponseCodeUndefinedType(value) => value 13 | } 14 | } 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /ruby-generator/src/test/scala/models/ApiBuilderCommentsSpec.scala: -------------------------------------------------------------------------------- 1 | package ruby.models 2 | 3 | import org.scalatest.funspec.AnyFunSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | class ApiBuilderCommentsSpec extends AnyFunSpec with Matchers { 7 | 8 | it("with only version") { 9 | ApiBuilderComments("1.0", None).toRubyString should be(""" 10 | # Generated by API Builder - https://www.apibuilder.io 11 | # Service version: 1.0 12 | """.trim) 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /ruby-generator/src/test/scala/models/BuiltInTypesSpec.scala: -------------------------------------------------------------------------------- 1 | package ruby.models 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | import org.scalatest.funspec.AnyFunSpec 5 | import org.scalatest.matchers.should.Matchers 6 | 7 | class BuiltInTypesSpec extends AnyFunSpec with Matchers { 8 | 9 | private lazy val service = models.TestHelper.parseFile(s"/examples/built-in-types.json") 10 | 11 | it("generates built-in types") { 12 | RubyClientGenerator.invoke(InvocationForm(service = service)) match { 13 | case Left(errors) => fail(errors.mkString(", ")) 14 | case Right(sourceFiles) => { 15 | sourceFiles.size shouldBe 1 16 | models.TestHelper.assertEqualsFile("/generators/ruby-built-in-types.txt", sourceFiles.head.contents) 17 | } 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /ruby-generator/src/test/scala/models/ExampleUnionTypesSpec.scala: -------------------------------------------------------------------------------- 1 | package ruby.models 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | import org.scalatest.funspec.AnyFunSpec 5 | import org.scalatest.matchers.should.Matchers 6 | 7 | class ExampleUnionTypesSpec extends AnyFunSpec with Matchers { 8 | 9 | private lazy val service = models.TestHelper.parseFile(s"/examples/apidoc-example-union-types.json") 10 | 11 | it("generates expected code for ruby client") { 12 | RubyClientGenerator.invoke(InvocationForm(service = service)) match { 13 | case Left(errors) => fail(errors.mkString(", ")) 14 | case Right(sourceFiles) => { 15 | sourceFiles.size shouldBe 1 16 | models.TestHelper.assertEqualsFile("/example-union-types-ruby-client.txt", sourceFiles.head.contents) 17 | } 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /ruby-generator/src/test/scala/models/FeatureMigrationSpec.scala: -------------------------------------------------------------------------------- 1 | package ruby.models 2 | 3 | import org.scalatest.funspec.AnyFunSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | class FeatureMigrationSpec extends AnyFunSpec with Matchers { 7 | 8 | it("hasImplicit404s") { 9 | FeatureMigration("0.0.1").hasImplicit404s should be(true) 10 | FeatureMigration("0.9.4").hasImplicit404s should be(true) 11 | FeatureMigration("0.9.5").hasImplicit404s should be(false) 12 | FeatureMigration("1.0.0").hasImplicit404s should be(false) 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /ruby-generator/src/test/scala/models/ResponsesWithUnitTypeSpec.scala: -------------------------------------------------------------------------------- 1 | package ruby.models 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | import org.scalatest.funspec.AnyFunSpec 5 | import org.scalatest.matchers.should.Matchers 6 | 7 | class ResponsesWithUnitTypeSpec extends AnyFunSpec with Matchers { 8 | 9 | private lazy val service = models.TestHelper.parseFile(s"/examples/response-with-unit-type.json") 10 | 11 | it("parses response type") { 12 | RubyClientGenerator.invoke(InvocationForm(service = service)) match { 13 | case Left(errors) => fail(errors.mkString(", ")) 14 | case Right(sourceFiles) => { 15 | sourceFiles.size shouldBe 1 16 | models.TestHelper.assertEqualsFile("/example-response-with-unit-type.txt", sourceFiles.head.contents) 17 | } 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/ApiBuilderComments.scala: -------------------------------------------------------------------------------- 1 | package scala.models 2 | 3 | import lib.AbstractApiBuilderComments 4 | 5 | import scala.generator.ScalaUtil 6 | 7 | /** 8 | * @param version e.g. 1.2.3 Used to inject headers on the full version and the major version number 9 | */ 10 | case class ApiBuilderComments(override val version: String, override val userAgent: Option[String]) extends AbstractApiBuilderComments(version, userAgent) { 11 | 12 | def toJavaString: String = ScalaUtil.textToComment(elements) 13 | 14 | def forPlayRoutes: String = elements.map(s => s"# $s").mkString("\n") 15 | 16 | } 17 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/Headers.scala: -------------------------------------------------------------------------------- 1 | package scala.models 2 | 3 | import scala.generator.ScalaUtil 4 | import io.apibuilder.generator.v0.models.InvocationForm 5 | import lib.VersionTag 6 | import lib.Text._ 7 | 8 | case class Headers( 9 | form: InvocationForm 10 | ) { 11 | 12 | private val versionMajor: Option[Int] = VersionTag(form.service.version).major 13 | 14 | private val VersionMajorName = "VersionMajor" 15 | private val VersionMajorHeaderName = "X-Apidoc-Version-Major" 16 | 17 | private val constants = Seq( 18 | form.service.baseUrl.map { url => ("BaseUrl", url) }, 19 | Some(("Namespace", form.service.namespace)), 20 | Some(("UserAgent", form.userAgent.getOrElse("apibuilder-play_2x_client-unknown"))), 21 | Some(("Version", form.service.version)), 22 | versionMajor.map { major => (VersionMajorName, major.toString) } 23 | ).flatten 24 | 25 | val objectConstants: String = { 26 | Seq( 27 | "object Constants {", 28 | constants.map { pair => 29 | if (pair._1 == VersionMajorName) { 30 | s"val ${pair._1} = ${pair._2.trim}" 31 | } else { 32 | s"val ${pair._1} = ${ScalaUtil.wrapInQuotes(pair._2.trim)}" 33 | } 34 | }.mkString("\n").indentString(2), 35 | "}" 36 | ).mkString("\n\n") 37 | } 38 | 39 | val scala = Seq( 40 | Some("User-Agent" -> s"Constants.UserAgent"), 41 | Some("X-Apidoc-Version" -> s"Constants.Version"), 42 | versionMajor.map { _ => VersionMajorHeaderName -> s"Constants.VersionMajor.toString" } 43 | ).flatten ++ form.service.headers.filter(!_.default.isEmpty).map { h => 44 | (h.name -> ScalaUtil.wrapInQuotes(h.default.get)) 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/JsonImports.scala: -------------------------------------------------------------------------------- 1 | package scala.models 2 | 3 | import scala.generator.Namespaces 4 | import io.apibuilder.spec.v0.models.Service 5 | 6 | /** 7 | * Given a service, returns a list of all of the imports needed for 8 | * json serialization/deserialization/ 9 | */ 10 | object JsonImports { 11 | 12 | def apply(service: Service): Seq[String] = { 13 | ( 14 | Seq(s"import ${Namespaces(service.namespace).models}.json._") ++ 15 | service.imports.map { imp => 16 | s"import ${Namespaces(imp.namespace).models}.json._" 17 | } 18 | ).sorted 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/Play2StandaloneModels.scala: -------------------------------------------------------------------------------- 1 | package scala.models 2 | 3 | import io.apibuilder.generator.v0.models.{File, InvocationForm} 4 | import lib.generator.CodeGenerator 5 | 6 | object Play2Scala2StandaloneModelsJson extends PlayStandaloneModelsJson(ScalaVersion(2)) 7 | object Play2Scala3StandaloneModelsJson extends PlayStandaloneModelsJson(ScalaVersion(3)) 8 | 9 | case class PlayStandaloneModelsJson(version: ScalaVersion) extends CodeGenerator { 10 | 11 | override def invoke( 12 | form: InvocationForm 13 | ): Either[Seq[String], Seq[File]] = { 14 | Right(Play2Models(version).generateCode(form = form, addBindables = false, addHeader = true, useBuiltInImplicits = false)) 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/Util.scala: -------------------------------------------------------------------------------- 1 | package scala.models 2 | 3 | import io.apibuilder.spec.v0.models._ 4 | 5 | object Util { 6 | 7 | def responseCodeAsString(code: ResponseCode): String = { 8 | code match { 9 | case ResponseCodeInt(value) => value.toString 10 | case ResponseCodeOption.Default => ResponseCodeOption.Default.toString 11 | case ResponseCodeOption.UNDEFINED(value) => value 12 | case ResponseCodeUndefinedType(value) => value 13 | } 14 | } 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/generator/DiscriminatorValue.scala: -------------------------------------------------------------------------------- 1 | package scala.generator 2 | 3 | sealed trait DiscriminatorValue { 4 | def generateCode: String 5 | } 6 | 7 | object DiscriminatorValue { 8 | private def declaration(discriminatorField: ScalaField): String = { 9 | s"override val ${ScalaUtil.quoteNameIfKeyword(discriminatorField.field.name)}: ${discriminatorField.field.`type`} = ${discriminatorField.field.`type`}." 10 | } 11 | 12 | case class TypeModel(discriminatorField: ScalaField, unionType: ScalaUnionType) extends DiscriminatorValue { 13 | override def generateCode: String = { 14 | s"${declaration(discriminatorField)}${unionType.name}" 15 | } 16 | } 17 | case class Undefined(discriminatorField: ScalaField, wrapper: UnionTypeUndefinedModelWrapper) extends DiscriminatorValue { 18 | override def generateCode: String = { 19 | s"${declaration(discriminatorField)}UNDEFINED(${wrapper.descriptionField.name})" 20 | } 21 | } 22 | 23 | def generateCode(`enum`: ScalaEnum, unions: Seq[ScalaUnion]): Seq[String] = { 24 | unions.flatMap { u => 25 | generate(u, enum.name) 26 | }.distinct.map(_.generateCode) 27 | } 28 | 29 | def generateCode(model: ScalaModel, unions: Seq[ScalaUnion]): Seq[String] = { 30 | unions.flatMap { u => 31 | generate(u, model.name) 32 | }.distinct.map(_.generateCode) 33 | } 34 | 35 | private def generate(union: ScalaUnion, typeName: String): Option[DiscriminatorValue] = { 36 | union.discriminatorField.flatMap { d => 37 | union.types.find(_.name == typeName) match { 38 | case Some(t) => Some(DiscriminatorValue.TypeModel(d.field, t)) 39 | case None if typeName == union.undefinedType.model.name => Some( 40 | DiscriminatorValue.Undefined(d.field, union.undefinedType) 41 | ) 42 | case None => None 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/generator/Namespaces.scala: -------------------------------------------------------------------------------- 1 | package scala.generator 2 | 3 | import lib.generator.GeneratorUtil 4 | 5 | object Namespaces { 6 | 7 | val Parsers = "parsers" 8 | val Conversions = "conversions" 9 | 10 | def quote(ns: String): String = { 11 | ns.split("\\.").map(ScalaUtil.quoteNameIfKeyword).mkString(".") 12 | } 13 | 14 | } 15 | 16 | case class Namespaces(original: String) { 17 | 18 | val base: String = Namespaces.quote(original) 19 | 20 | val models: String = GeneratorUtil.fullyQualifiedInternalName(base, GeneratorUtil.ObjectType.Model) 21 | val enums: String = GeneratorUtil.fullyQualifiedInternalName(base, GeneratorUtil.ObjectType.Enum) 22 | val unions: String = GeneratorUtil.fullyQualifiedInternalName(base, GeneratorUtil.ObjectType.Union) 23 | 24 | val json: String = Seq(models, "json").mkString(".") 25 | 26 | val anorm: String = Seq(base, "anorm").mkString(".") 27 | val anormParsers: String = Seq(anorm, Namespaces.Parsers).mkString(".") 28 | val anormConversions: String = Seq(anorm, Namespaces.Conversions).mkString(".") 29 | val errors: String = Seq(base, "errors").mkString(".") 30 | 31 | val mock: String = Seq(base, "mock").mkString(".") 32 | val interfaces: String = Seq(base, "interfaces").mkString(".") 33 | 34 | val last: String = base.split("\\.").last 35 | 36 | def get(objectType: GeneratorUtil.ObjectType): String = { 37 | objectType match { 38 | case GeneratorUtil.ObjectType.Enum => enums 39 | case GeneratorUtil.ObjectType.Model => models 40 | case GeneratorUtil.ObjectType.Union => unions 41 | } 42 | } 43 | 44 | def importStatements(): Seq[String] = { 45 | Seq(s"import $models._") 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/generator/ScalaClientAuthClasses.scala: -------------------------------------------------------------------------------- 1 | package scala.generator 2 | 3 | object ScalaClientAuthClasses { 4 | 5 | def apply(): String = """ 6 | sealed trait Authorization extends _root_.scala.Product with _root_.scala.Serializable 7 | object Authorization { 8 | final case class Basic(username: String, password: Option[String] = None) extends Authorization 9 | } 10 | """.trim 11 | 12 | } 13 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/http4s/Http4sScalaClientCommon.scala: -------------------------------------------------------------------------------- 1 | package scala.models.http4s 2 | 3 | import scala.generator.{Namespaces, ScalaClientCommon, ScalaClientMethodConfig, ScalaClientMethodConfigs} 4 | import lib.Text._ 5 | 6 | object Http4sScalaClientCommon extends ScalaClientCommon { 7 | 8 | def mustGetHttp4sConfig(config: ScalaClientMethodConfig): ScalaClientMethodConfigs.Http4s = { 9 | config match { 10 | case cfg: ScalaClientMethodConfigs.Http4s => cfg 11 | case _: ScalaClientMethodConfigs.Ning | _: ScalaClientMethodConfigs.Play => sys.error(s"Unexpected config: ${config.getClass.getName}") 12 | } 13 | } 14 | 15 | override def makeClientObject( 16 | config: ScalaClientMethodConfig 17 | ): String = { 18 | val extraMethods = config.extraClientObjectMethods match { 19 | case Some(methods) => methods.indentString(2) + "\n" 20 | case _ => "" 21 | } 22 | 23 | val http4sConfig = mustGetHttp4sConfig(config) 24 | 25 | s"""object Client { 26 | | ${http4sConfig.asyncTypeImport} 27 | | 28 | |$extraMethods 29 | | def parseJson[${http4sConfig.asyncTypeParam(Some(http4sConfig.asyncTypeConstraint)).map(p => p+", ").getOrElse("")}T]( 30 | | className: String, 31 | | r: ${config.responseClass} 32 | | )(implicit decoder: io.circe.Decoder[T]): ${http4sConfig.asyncType}[T] = r.attemptAs[T].${http4sConfig.monadTransformerInvoke}.flatMap { 33 | | case ${http4sConfig.rightType}(value) => ${http4sConfig.asyncSuccessInvoke}(value) 34 | | case ${http4sConfig.leftType}(error) => ${http4sConfig.wrappedAsyncType(config.asyncTypeConstraint).getOrElse(http4sConfig.asyncType)}.${http4sConfig.asyncFailure}(new ${Namespaces(config.namespace).errors}.FailedRequest(r.${config.responseStatusMethod}, s"Invalid json for class[" + className + "]", None, error)) 35 | | } 36 | |}""".stripMargin.trim 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/http4s/ScalaService.scala: -------------------------------------------------------------------------------- 1 | package scala.models.http4s 2 | 3 | import io.apibuilder.spec.v0.models.Service 4 | 5 | import scala.models.Attributes 6 | 7 | class ScalaService(service: Service, config: Attributes = Attributes.Http4sDefaultConfig) extends scala.generator.ScalaService(service, config) 8 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/http4s/mock/Http4s018MockClientGenerator.scala: -------------------------------------------------------------------------------- 1 | package models.http4s.mock 2 | 3 | import scala.generator.{ScalaClientMethodConfig, ScalaResource, ScalaService} 4 | import scala.generator.mock.MockClientGenerator 5 | import lib.Text._ 6 | 7 | class Http4s018MockClientGenerator( 8 | ssd: ScalaService, 9 | userAgent: Option[String], 10 | config: ScalaClientMethodConfig 11 | ) extends MockClientGenerator(ssd, userAgent, config) { 12 | 13 | override def clientCode: String = { 14 | Seq( 15 | s"class Client[F[_]: cats.Applicative] extends ${ssd.namespaces.interfaces}.Client[F] {", 16 | s""" ${config.formatBaseUrl(Some("http://mock.localhost"))}""", 17 | ssd.resources.map { resource => 18 | s"override def ${generator.methodName(resource)}: ${ssd.namespaces.base}.${resource.plural}[F] = new Mock${resource.plural}[F]" 19 | }.mkString("\n").indentString(2), 20 | "}", 21 | ssd.resources.map { resource => 22 | generateMockResource(resource) 23 | }.mkString("\n\n") 24 | ).mkString("\n\n") 25 | } 26 | 27 | override def generateMockResource(resource: ScalaResource): String = { 28 | Seq( 29 | s"class Mock${resource.plural}[F[_]: cats.Applicative] extends ${ssd.namespaces.base}.${resource.plural}[F] {", 30 | generator.methods(resource).map { m => 31 | Seq( 32 | m.interface + s" = cats.Applicative[F].pure {", 33 | mockImplementation(m).indentString(2), 34 | "}" 35 | ).mkString("\n") 36 | }.mkString("\n\n").indentString(2), 37 | "}" 38 | ).mkString("\n\n") 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/http4s/mock/Http4s020MockClientGenerator.scala: -------------------------------------------------------------------------------- 1 | package models.http4s.mock 2 | 3 | import lib.Text._ 4 | 5 | import scala.generator.mock.MockClientGenerator 6 | import scala.generator.{ScalaClientMethodConfig, ScalaResource, ScalaService} 7 | 8 | class Http4s020MockClientGenerator( 9 | ssd: ScalaService, 10 | userAgent: Option[String], 11 | config: ScalaClientMethodConfig 12 | ) extends MockClientGenerator(ssd, userAgent, config) { 13 | 14 | override def clientCode: String = { 15 | Seq( 16 | s"class Client[F[_]: cats.Applicative] extends ${ssd.namespaces.interfaces}.Client[F] {", 17 | s""" ${config.formatBaseUrl(Some("http://mock.localhost"))}""", 18 | ssd.resources.map { resource => 19 | s"override def ${generator.methodName(resource)}: ${ssd.namespaces.base}.${resource.plural}[F] = new Mock${resource.plural}[F]" 20 | }.mkString("\n").indentString(2), 21 | "}", 22 | ssd.resources.map { resource => 23 | generateMockResource(resource) 24 | }.mkString("\n\n") 25 | ).mkString("\n\n") 26 | } 27 | 28 | override def generateMockResource(resource: ScalaResource): String = { 29 | Seq( 30 | s"class Mock${resource.plural}[F[_]: cats.Applicative] extends ${ssd.namespaces.base}.${resource.plural}[F] {", 31 | generator.methods(resource).map { m => 32 | Seq( 33 | m.interface + s" = cats.Applicative[F].pure {", 34 | mockImplementation(m).indentString(2), 35 | "}" 36 | ).mkString("\n") 37 | }.mkString("\n\n").indentString(2), 38 | "}" 39 | ).mkString("\n\n") 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/http4s/mock/Http4s022MockClientGenerator.scala: -------------------------------------------------------------------------------- 1 | package models.http4s.mock 2 | 3 | import lib.Text._ 4 | 5 | import scala.generator.mock.MockClientGenerator 6 | import scala.generator.{ScalaClientMethodConfig, ScalaResource, ScalaService} 7 | 8 | class Http4s022MockClientGenerator( 9 | ssd: ScalaService, 10 | userAgent: Option[String], 11 | config: ScalaClientMethodConfig 12 | ) extends MockClientGenerator(ssd, userAgent, config) { 13 | 14 | override def clientCode: String = { 15 | Seq( 16 | s"class Client[F[_]: cats.Applicative] extends ${ssd.namespaces.interfaces}.Client[F] {", 17 | s""" ${config.formatBaseUrl(Some("http://mock.localhost"))}""", 18 | ssd.resources.map { resource => 19 | s"override def ${generator.methodName(resource)}: ${ssd.namespaces.base}.${resource.plural}[F] = new Mock${resource.plural}[F]" 20 | }.mkString("\n").indentString(2), 21 | "}", 22 | ssd.resources.map { resource => 23 | generateMockResource(resource) 24 | }.mkString("\n\n") 25 | ).mkString("\n\n") 26 | } 27 | 28 | override def generateMockResource(resource: ScalaResource): String = { 29 | Seq( 30 | s"class Mock${resource.plural}[F[_]: cats.Applicative] extends ${ssd.namespaces.base}.${resource.plural}[F] {", 31 | generator.methods(resource).map { m => 32 | Seq( 33 | m.interface + s" = cats.Applicative[F].pure {", 34 | mockImplementation(m).indentString(2), 35 | "}" 36 | ).mkString("\n") 37 | }.mkString("\n\n").indentString(2), 38 | "}" 39 | ).mkString("\n\n") 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/ning/PathSegment.scala: -------------------------------------------------------------------------------- 1 | package scala.models.ning 2 | 3 | /** 4 | * This file has been modified from 5 | * https://github.com/playframework/playframework/blob/2.3.x/framework/src/play/src/main/scala/play/utils/UriEncoding.scala 6 | * 7 | * This file contains software that is licensed under the Apache 2 license. 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with 10 | * the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 11 | */ 12 | private[ning] object PathSegment { 13 | 14 | val definition = """ 15 | object PathSegment { 16 | // See https://github.com/playframework/playframework/blob/2.3.x/framework/src/play/src/main/scala/play/utils/UriEncoding.scala 17 | def encode(s: String, inputCharset: String): String = { 18 | val in = s.getBytes(inputCharset) 19 | val out = new java.io.ByteArrayOutputStream() 20 | for (b <- in) { 21 | val allowed = segmentChars.get(b & 0xFF) 22 | if (allowed) { 23 | out.write(b) 24 | } else { 25 | out.write('%') 26 | out.write(upperHex((b >> 4) & 0xF)) 27 | out.write(upperHex(b & 0xF)) 28 | } 29 | } 30 | out.toString("US-ASCII") 31 | } 32 | 33 | private def upperHex(x: Int): Int = { 34 | // Assume 0 <= x < 16 35 | if (x < 10) (x + '0') else (x - 10 + 'A') 36 | } 37 | 38 | private val segmentChars: java.util.BitSet = membershipTable(pchar) 39 | 40 | private def pchar: Seq[Char] = { 41 | val alphaDigit = for ((min, max) <- Seq(('a', 'z'), ('A', 'Z'), ('0', '9')); c <- min to max) yield c 42 | val unreserved = alphaDigit ++ Seq('-', '.', '_', '~') 43 | val subDelims = Seq('!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=') 44 | unreserved ++ subDelims ++ Seq(':', '@') 45 | } 46 | 47 | private def membershipTable(chars: Seq[Char]): java.util.BitSet = { 48 | val bits = new java.util.BitSet(256) 49 | for (c <- chars) { bits.set(c.toInt) } 50 | bits 51 | } 52 | } 53 | """.trim 54 | 55 | } 56 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/play/files/Client.scala: -------------------------------------------------------------------------------- 1 | package scala.models.play.files 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | 5 | import scala.models.Attributes 6 | 7 | object Client { 8 | 9 | def contents(form: InvocationForm): String = 10 | scala.models.Play2ClientGeneratorImpl( 11 | scala.models.Play26ClientGenerator.config(form), 12 | form, 13 | Attributes.PlayGen2DefaultConfig 14 | ) 15 | .client() 16 | 17 | } 18 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/play/files/MockClient.scala: -------------------------------------------------------------------------------- 1 | package scala.models.play.files 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | 5 | import scala.models.Attributes 6 | 7 | object MockClient { 8 | 9 | def contents(form: InvocationForm): String = { 10 | val scalaService = scala.generator.ScalaService(form.service, Attributes.PlayGen2DefaultConfig.withAttributes(form.attributes)) 11 | val config = scala.generator.ScalaClientMethodConfigs.Play26(scalaService.namespaces.base, Attributes.PlayDefaultConfig, None) 12 | 13 | new scala.generator.mock.MockClientGenerator(scalaService, form.userAgent, config).generateCode() 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/play/files/Models.scala: -------------------------------------------------------------------------------- 1 | package scala.models.play.files 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | 5 | import scala.models.Attributes 6 | 7 | object Models { 8 | 9 | def contents(form: InvocationForm): String = { 10 | val scalaService = scala.generator.ScalaService(form.service, Attributes.PlayGen2DefaultConfig.withAttributes(form.attributes)) 11 | scala.generator.ScalaCaseClasses 12 | .generateCode(scalaService, form.userAgent, false) 13 | .headOption 14 | .fold("")(_.contents) 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/play/files/ModelsBindables.scala: -------------------------------------------------------------------------------- 1 | package scala.models.play.files 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | 5 | import scala.models.Attributes 6 | 7 | object ModelsBindables { 8 | 9 | def contents(form: InvocationForm): String = { 10 | val scalaService = scala.generator.ScalaService(form.service, Attributes.PlayGen2DefaultConfig.withAttributes(form.attributes)) 11 | val bindables = scala.models.Play2Bindables(scalaService) 12 | .build() 13 | .split("\n") 14 | .drop(1) 15 | .dropRight(1) 16 | .mkString("\n") 17 | 18 | s""" 19 | package ${scalaService.namespaces.models} 20 | 21 | package object bindables { 22 | 23 | ${bindables} 24 | 25 | } 26 | """ 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/play/files/ModelsBodyParsers.scala: -------------------------------------------------------------------------------- 1 | package scala.models.play.files 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | 5 | import scala.models.Attributes 6 | 7 | object ModelsBodyParsers { 8 | 9 | val BadRequest = "_root_.play.api.mvc.Results.BadRequest" 10 | val BodyParser = "_root_.play.api.mvc.BodyParser" 11 | val ExecutionContext = "_root_.scala.concurrent.ExecutionContext" 12 | val JsError = "_root_.play.api.libs.json.JsError" 13 | val JsReads = "_root_.play.api.libs.json.Reads" 14 | val JsValue = "_root_.play.api.libs.json.JsValue" 15 | 16 | def bodyParser(): String = s""" 17 | private def bodyParser[A](parser: ${BodyParser}[${JsValue}])(implicit ec: ${ExecutionContext}, rds: ${JsReads}[A]): ${BodyParser}[A] = 18 | parser.validate(_.validate[A].asEither.left.map(e => ${BadRequest}(${JsError}.toJson(e)))) 19 | """ 20 | 21 | def bodyParser(`enum`: scala.generator.ScalaEnum): String = bodyParser(enum.name, enum.qualifiedName) 22 | def bodyParser(model: scala.generator.ScalaModel): String = bodyParser(model.name, model.qualifiedName) 23 | def bodyParser(union: scala.generator.ScalaUnion): String = bodyParser(union.name, union.qualifiedName) 24 | def bodyParser(suffix: String, tpe: String): String = s""" 25 | def bodyParser${suffix}(parser: ${BodyParser}[${JsValue}])(implicit ec: ${ExecutionContext}, rds: ${JsReads}[${tpe}]): ${BodyParser}[${tpe}] = 26 | bodyParser[${tpe}](parser) 27 | """ 28 | 29 | def contents(form: InvocationForm): String = { 30 | val scalaService = scala.generator.ScalaService(form.service, Attributes.PlayGen2DefaultConfig.withAttributes(form.attributes)) 31 | val bodyParsers = 32 | scalaService.enums.map(bodyParser) ++ 33 | scalaService.models.map(bodyParser) ++ 34 | scalaService.unions.map(bodyParser) 35 | 36 | s""" 37 | package ${scalaService.namespaces.models} 38 | 39 | package object bodyparsers { 40 | 41 | ${bodyParser()} 42 | 43 | ${bodyParsers.mkString("\n")} 44 | 45 | } 46 | """ 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/play/files/ModelsJson.scala: -------------------------------------------------------------------------------- 1 | package scala.models.play.files 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | 5 | import scala.models.{Attributes, DateTimeTypeConfig, DateTypeConfig, Play2ModelImplicits, Play2Models} 6 | 7 | object ModelsJson { 8 | 9 | private def implicits(ssd: scala.generator.ScalaService) = Seq(Play2ModelImplicits.play) ++ 10 | (ssd.attributes.dateTimeType match { 11 | case DateTimeTypeConfig.JodaDateTime => Seq(Play2ModelImplicits.jodaDateTime) 12 | case _ => Nil 13 | }) ++ 14 | (ssd.attributes.dateType match { 15 | case DateTypeConfig.JodaLocalDate => Seq(Play2ModelImplicits.jodaLocalDate) 16 | case _ => Nil 17 | }) 18 | 19 | def contents(form: InvocationForm, scala3Support: Boolean): String = { 20 | val scalaService = scala.generator.ScalaService(form.service, Attributes.PlayGen2DefaultConfig.withAttributes(form.attributes)) 21 | val imports = scala.models.JsonImports(form.service) 22 | val gen = scala.models.Play2Json(scalaService, scala3Support = scala3Support) 23 | 24 | s""" 25 | package ${scalaService.namespaces.models} 26 | 27 | package object json { 28 | 29 | import play.api.libs.json.{__, JsString, Writes} 30 | import play.api.libs.functional.syntax._ 31 | 32 | ${implicits(scalaService).mkString("\n")} 33 | 34 | ${imports.mkString("\n")} 35 | 36 | ${gen.generateEnums()} 37 | ${gen.generateModelsAndUnions()} 38 | 39 | } 40 | """ 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/models/play/files/Routes.scala: -------------------------------------------------------------------------------- 1 | package scala.models.play.files 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | 5 | import scala.models.Attributes 6 | 7 | object Routes { 8 | 9 | def contents(form: InvocationForm): String = { 10 | val header = scala.models.ApiBuilderComments(form.service.version, form.userAgent).forPlayRoutes + "\n" 11 | scala.models.Play2RouteGenerator(form, Attributes.PlayGen2DefaultConfig) 12 | .invoke() 13 | .toOption 14 | .flatMap(_.headOption) 15 | .fold("")(_.contents) 16 | .replaceAll(header, "") 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /scala-generator/src/main/scala/utils/ScalaFormatter.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | object ScalaFormatter { 4 | 5 | def format(code: String): Either[Throwable, String] = 6 | org.scalafmt.Scalafmt.format(code, org.scalafmt.config.ScalafmtConfig.default120) 7 | .toEither 8 | 9 | } 10 | -------------------------------------------------------------------------------- /scala-generator/src/test/scala/models/ApiBuilderCommentsSpec.scala: -------------------------------------------------------------------------------- 1 | package scala.models 2 | 3 | import org.scalatest.funspec.AnyFunSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | class ApiBuilderCommentsSpec extends AnyFunSpec with Matchers { 7 | 8 | it("with only version") { 9 | ApiBuilderComments("1.0", None).toJavaString should be(""" 10 | /** 11 | * Generated by API Builder - https://www.apibuilder.io 12 | * Service version: 1.0 13 | */ 14 | """.trim) 15 | 16 | ApiBuilderComments("1.0", None).forPlayRoutes should be(""" 17 | # Generated by API Builder - https://www.apibuilder.io 18 | # Service version: 1.0 19 | """.trim) 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /scala-generator/src/test/scala/models/ExampleUnionTypesSpec.scala: -------------------------------------------------------------------------------- 1 | package scala.models 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | import ning.Ning18ClientGenerator 5 | import org.scalatest.funspec.AnyFunSpec 6 | import org.scalatest.matchers.should.Matchers 7 | 8 | class ExampleUnionTypesSpec extends AnyFunSpec with Matchers { 9 | 10 | private lazy val service = models.TestHelper.parseFile(s"/examples/apidoc-example-union-types.json") 11 | 12 | it("generates expected code for play 2.3 client") { 13 | Play23ClientGenerator.invoke(InvocationForm(service = service)) match { 14 | case Left(errors) => fail(errors.mkString(", ")) 15 | case Right(sourceFiles) => { 16 | sourceFiles.size shouldBe 1 17 | models.TestHelper.assertEqualsFile("/example-union-types-play-23.txt", sourceFiles.head.contents) 18 | } 19 | } 20 | } 21 | 22 | it("generates expected code for play 2.9 client and scala 3") { 23 | lazy val service = models.TestHelper.parseFile(s"/examples/apidoc-example-union-types-primitives.json") 24 | 25 | Play29Scala3ClientGenerator.invoke(InvocationForm(service = service)) match { 26 | case Left(errors) => fail(errors.mkString(", ")) 27 | case Right(sourceFiles) => { 28 | sourceFiles.size shouldBe 1 29 | models.TestHelper.assertEqualsFile("/example-union-types-play-29-scala-3.txt", sourceFiles.head.contents) 30 | } 31 | } 32 | } 33 | 34 | it("generates expected code for ning client") { 35 | Ning18ClientGenerator.invoke(InvocationForm(service = service)) match { 36 | case Left(errors) => fail(errors.mkString(", ")) 37 | case Right(sourceFiles) => { 38 | sourceFiles.size shouldBe 1 39 | models.TestHelper.assertEqualsFile("/example-union-types-ning-client.txt", sourceFiles.head.contents) 40 | } 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /scala-generator/src/test/scala/models/ExampleUnionTypesWithDiscriminatorSpec.scala: -------------------------------------------------------------------------------- 1 | package scala.models 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | import org.scalatest.funspec.AnyFunSpec 5 | import org.scalatest.matchers.should.Matchers 6 | 7 | class ExampleUnionTypesWithDiscriminatorSpec extends AnyFunSpec with Matchers { 8 | 9 | private lazy val service = models.TestHelper.parseFile("/examples/apidoc-example-union-types-discriminator.json") 10 | 11 | it("generates expected code for play 2.4 client") { 12 | Play24ClientGenerator.invoke(InvocationForm(service = service)) match { 13 | case Left(errors) => fail(errors.mkString(", ")) 14 | case Right(sourceFiles) => { 15 | sourceFiles.size shouldBe 1 16 | models.TestHelper.assertEqualsFile("/union-types-discriminator-service-play-24.txt", sourceFiles.head.contents) 17 | } 18 | } 19 | } 20 | 21 | it("generates expected code for play 2.7 client") { 22 | Play27ClientGenerator.invoke(InvocationForm(service = service)) match { 23 | case Left(errors) => fail(errors.mkString(", ")) 24 | case Right(sourceFiles) => { 25 | sourceFiles.size shouldBe 1 26 | models.TestHelper.assertEqualsFile("/union-types-discriminator-service-play-27.txt", sourceFiles.head.contents) 27 | } 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /scala-generator/src/test/scala/models/JsonImportsSpec.scala: -------------------------------------------------------------------------------- 1 | package scala.models 2 | 3 | import org.scalatest.funspec.AnyFunSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | class JsonImportsSpec extends AnyFunSpec with Matchers { 7 | 8 | it("basic service") { 9 | JsonImports(models.TestHelper.referenceApiService) should be( 10 | Seq( 11 | "import io.apibuilder.reference.api.v0.models.json._" 12 | ) 13 | ) 14 | } 15 | 16 | it("includes imports") { 17 | JsonImports(models.TestHelper.generatorApiService) should be( 18 | Seq( 19 | "import io.apibuilder.common.v0.models.json._", 20 | "import io.apibuilder.generator.v0.models.json._", 21 | "import io.apibuilder.spec.v0.models.json._" 22 | ) 23 | ) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /scala-generator/src/test/scala/models/Play2StandaloneModelsJsonSpec.scala: -------------------------------------------------------------------------------- 1 | package scala.models 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | import org.scalatest.funspec.AnyFunSpec 5 | import org.scalatest.matchers.should.Matchers 6 | 7 | class Play2StandaloneModelsJsonSpec extends AnyFunSpec with Matchers { 8 | 9 | it("quality") { 10 | val quality = models.TestHelper.parseFile("/examples/quality.json") 11 | 12 | Play2Scala2StandaloneModelsJson.invoke(InvocationForm(quality)) match { 13 | case Left(errors) => fail(errors.mkString(", ")) 14 | case Right(files) => { 15 | files.map(_.name) should be (Seq("GiltQualityV0Models.scala")) 16 | val scalaSourceCode = files.head.contents 17 | models.TestHelper.assertValidScalaSourceCode(scalaSourceCode) 18 | models.TestHelper.assertEqualsFile( 19 | "/generators/play-2-standalone-json-spec-quality.txt", 20 | scalaSourceCode 21 | ) 22 | } 23 | } 24 | } 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /scala-generator/src/test/scala/models/UndefinedUnionWithInterfaceSpec.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import helpers.ServiceHelpers 4 | import io.apibuilder.spec.v0.models.Field 5 | import org.scalatest.funspec.AnyFunSpec 6 | import org.scalatest.matchers.should.Matchers 7 | 8 | import scala.generator.ScalaService 9 | 10 | class UndefinedUnionWithInterfaceSpec extends AnyFunSpec with Matchers with ServiceHelpers { 11 | 12 | private def build(fields: Seq[Field]): ScalaService = { 13 | ScalaService( 14 | makeService( 15 | interfaces = Seq( 16 | makeInterface(name = "user", fields = fields) 17 | ), 18 | unions = Seq( 19 | makeUnion( 20 | name = "user", 21 | interfaces = Seq("user"), 22 | types = Seq(makeUnionType("registered_user")), 23 | ) 24 | ), 25 | models = Seq( 26 | makeModel( 27 | name = "registered_user", 28 | ) 29 | ) 30 | ) 31 | ) 32 | } 33 | 34 | private def fields(service: ScalaService): Seq[String] = { 35 | service.unions.map(_.undefinedType).head.model.model.fields.map(_.name) 36 | } 37 | 38 | it("defaults to 'description'") { 39 | fields(build(Nil)) shouldBe Seq("description") 40 | } 41 | 42 | it("aliases description field") { 43 | fields(build(Seq( 44 | makeField("description", required = false), 45 | ))) shouldBe Seq("typeDescription") 46 | } 47 | 48 | it("aliases description and typeDescription field") { 49 | fields(build(Seq( 50 | makeField("description", required = false), 51 | makeField("type_description", required = false), 52 | ))) shouldBe Seq("type2Description") 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /scala-generator/src/test/scala/models/generator/NamespacesSpec.scala: -------------------------------------------------------------------------------- 1 | package scala.generator 2 | 3 | import org.scalatest.funspec.AnyFunSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | class NamespacesSpec extends AnyFunSpec with Matchers { 7 | 8 | it("quotes keywords") { 9 | val ns = Namespaces("io.apibuilder.example.union.type.v0") 10 | ns.base should be("io.apibuilder.example.union.`type`.v0") 11 | ns.enums should be ("io.apibuilder.example.union.`type`.v0.models") 12 | ns.models should be ("io.apibuilder.example.union.`type`.v0.models") 13 | ns.unions should be ("io.apibuilder.example.union.`type`.v0.models") 14 | ns.mock should be ("io.apibuilder.example.union.`type`.v0.mock") 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /scala-generator/src/test/scala/models/generator/ScalaCaseClassesInterfacesSpec.scala: -------------------------------------------------------------------------------- 1 | package models.generator 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | import io.apibuilder.spec.v0.models.Interface 5 | import models.TestHelper 6 | import org.scalatest.funspec.AnyFunSpec 7 | import org.scalatest.matchers.should.Matchers 8 | 9 | import scala.generator.{ScalaCaseClasses, ScalaService} 10 | 11 | class ScalaCaseClassesInterfacesSpec extends AnyFunSpec with Matchers with helpers.ServiceHelpers { 12 | 13 | private def build(interfaces: Seq[Interface]): ScalaService = { 14 | ScalaService( 15 | makeService(interfaces = interfaces) 16 | ) 17 | } 18 | 19 | it("interfaceWithNoFields") { 20 | val ssd = build( 21 | Seq(makeInterface( 22 | name = "person", 23 | fields = Nil, 24 | )) 25 | ) 26 | 27 | models.TestHelper.assertEqualsFile( 28 | "/generators/ScalaCaseClassesInterfacesSpec.interfaceWithNoFields.json", 29 | ScalaCaseClasses.generateTrait(ssd.interfaces.head) 30 | ) 31 | } 32 | 33 | it("interfaceWithSingleField") { 34 | val ssd = build( 35 | Seq(makeInterface( 36 | name = "person", 37 | fields = Seq(makeField(name = "first")), 38 | )) 39 | ) 40 | 41 | models.TestHelper.assertEqualsFile( 42 | "/generators/ScalaCaseClassesInterfacesSpec.interfaceWithSingleField.json", 43 | ScalaCaseClasses.generateTrait(ssd.interfaces.head) 44 | ) 45 | } 46 | 47 | it("interfacesWithDeprecation") { 48 | val service = models.TestHelper.parseFile(s"/examples/interfaces.json") 49 | ScalaCaseClasses.invoke(InvocationForm(service = service)) match { 50 | case Left(errors) => fail(errors.mkString(", ")) 51 | case Right(sourceFiles) => { 52 | sourceFiles.size shouldBe 1 53 | TestHelper.assertValidScalaSourceFiles(sourceFiles) 54 | models.TestHelper.assertEqualsFile( 55 | "/generators/scala-models-interfaces.txt", 56 | sourceFiles.head.contents 57 | ) 58 | } 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /scala-generator/src/test/scala/models/generator/ScalaEnumsSpec.scala: -------------------------------------------------------------------------------- 1 | package scala.generator 2 | 3 | import scala.models.Play2Json 4 | import org.scalatest.funspec.AnyFunSpec 5 | import org.scalatest.matchers.should.Matchers 6 | 7 | class ScalaEnumsSpec extends AnyFunSpec with Matchers { 8 | 9 | describe("for a model with 2 enum fields") { 10 | 11 | val json = models.TestHelper.buildJson(""" 12 | "imports": [], 13 | "headers": [], 14 | "info": [], 15 | "unions": [], 16 | "resources": [], 17 | "attributes": [], 18 | 19 | "enums": [ 20 | { 21 | "name": "age_group", 22 | "plural": "age_groups", 23 | "attributes": [], 24 | "values": [ 25 | { "name": "twenties", "attributes": [] }, 26 | { "name": "thirties", "attributes": [] } 27 | ] 28 | }, 29 | { 30 | "name": "genre", 31 | "plural": "genres", 32 | "attributes": [], 33 | "values": [ 34 | { "name": "Classical", "attributes": [] }, 35 | { "name": "Jazz", "attributes": [] } 36 | ] 37 | } 38 | ], 39 | 40 | "models": [ 41 | { 42 | "name": "user", 43 | "plural": "users", 44 | "attributes": [], 45 | "fields": [ 46 | { "name": "age_group", "type": "age_group", "required": true, "attributes": [] }, 47 | { "name": "music", "type": "genre", "required": true, "attributes": [] } 48 | ] 49 | } 50 | ] 51 | """) 52 | 53 | lazy val ssd = ScalaService(models.TestHelper.service(json)) 54 | 55 | it("generates valid models") { 56 | val enums = ssd.enums.map { ScalaEnums(ssd, _).build() }.mkString("\n\n") 57 | models.TestHelper.assertEqualsFile("/play2enums-example.txt", enums) 58 | } 59 | 60 | it("generates valid json conversions") { 61 | val jsonConversions = Play2Json(ssd, scala3Support = false).generateEnums() 62 | models.TestHelper.assertEqualsFile("/play2enums-json-example.txt", jsonConversions) 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /scala-generator/src/test/scala/models/generator/TargetSpec.scala: -------------------------------------------------------------------------------- 1 | package scala.generator 2 | 3 | import org.scalatest.funspec.AnyFunSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | class TargetSpec extends AnyFunSpec with Matchers { 7 | 8 | private lazy val service = models.TestHelper.generatorApiService 9 | 10 | it("Has a field named target") { 11 | service.models.find(_.name == "generator").get.fields.find(_.name == "key").getOrElse { 12 | sys.error("Cannot find generator.key field") 13 | } 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /scala-generator/src/test/scala/models/play/Helpers.scala: -------------------------------------------------------------------------------- 1 | package scala.models.play 2 | 3 | import io.apibuilder.spec.v0.models.{Apidoc, Application, Info, Organization, Service} 4 | import org.scalatest.Assertion 5 | import org.scalatest.matchers.should.Matchers 6 | 7 | object Helpers extends Matchers { 8 | def removeAllExtraWhiteSpaces(a: String): String = a.replaceAll("\\s+", " ").trim 9 | 10 | def compareWithoutWhiteSpaces(a: String, b: String): Assertion = 11 | removeAllExtraWhiteSpaces(a) should be(removeAllExtraWhiteSpaces(b)) 12 | 13 | def basicService(namespace: String): Service = Service( 14 | apidoc = Some(Apidoc("0")), 15 | name = "name", 16 | organization = Organization("foo"), 17 | application = Application("bar"), 18 | namespace = namespace, 19 | version = "1", 20 | baseUrl = None, 21 | description = None, 22 | info = Info(), 23 | headers = Nil, 24 | imports = Nil, 25 | enums = Nil, 26 | interfaces = Nil, 27 | unions = Nil, 28 | models = Nil, 29 | resources = Nil, 30 | attributes = Nil, 31 | annotations = Nil, 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /scala-generator/src/test/scala/models/play/Play26EnvelopeGeneratorSpec.scala: -------------------------------------------------------------------------------- 1 | package models.play 2 | 3 | import io.apibuilder.generator.v0.models.{Attribute, InvocationForm} 4 | import models.TestHelper.assertValidScalaSourceCode 5 | import org.scalatest.matchers.should.Matchers 6 | import org.scalatest.funspec.AnyFunSpec 7 | 8 | import scala.models.Play26EnvelopeClientGenerator 9 | 10 | class Play26EnvelopeGeneratorSpec extends AnyFunSpec with Matchers { 11 | 12 | it("generates response envelope") { 13 | val form = InvocationForm( 14 | models.TestHelper.dateTimeService, 15 | Seq(Attribute("response", "envelope")), 16 | None 17 | ) 18 | val Right(files) = Play26EnvelopeClientGenerator.invoke(form) 19 | assertValidScalaSourceCode(files(0).contents) 20 | models.TestHelper.assertEqualsFile(s"/generators/play-26-envelope.txt", files(0).contents) 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /scala-generator/src/test/scala/models/play/Play26GeneratorSpec.scala: -------------------------------------------------------------------------------- 1 | package scala.models.play 2 | 3 | import io.apibuilder.generator.v0.models.InvocationForm 4 | import io.apibuilder.generator.v0.models.gens._ 5 | import org.scalatest.funspec.AnyFunSpec 6 | import org.scalatest.matchers.should.Matchers 7 | import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks 8 | 9 | import scala.models.play.Helpers.compareWithoutWhiteSpaces 10 | 11 | class Play26GeneratorSpec extends AnyFunSpec with Matchers with ScalaCheckPropertyChecks { 12 | 13 | private implicit val scalacheckConfig: PropertyCheckConfiguration = generatorDrivenConfig.copy(sizeRange = 10) 14 | 15 | it("prependHeader should prepend a header") { 16 | forAll { (header: String, contents: String, form: InvocationForm) => 17 | val expected = s""" 18 | ${header} 19 | 20 | ${contents} 21 | """ 22 | 23 | val result = Play26Generator.prependHeader(contents, form, _ => header) 24 | compareWithoutWhiteSpaces(result, expected) 25 | } 26 | } 27 | 28 | it("file should wrap generator.ServiceFileNames.toFile") { 29 | forAll { (form: InvocationForm, suffix: String, contents: String, extension: Option[String]) => 30 | val expected = generator.ServiceFileNames.toFile(form.service.namespace, form.service.organization.key, form.service.application.key, form.service.version, suffix, contents, extension) 31 | val result = Play26Generator.file(form, suffix, contents, extension) 32 | 33 | result should be(expected) 34 | } 35 | } 36 | 37 | it("formatRoutes should format routes") { 38 | forAll { (lines: List[String], spacesCount: Byte) => 39 | val spaces = " " * spacesCount.toInt 40 | val contents = lines.mkString(spaces, s"${spaces}\n${spaces}", spaces) 41 | val expected = lines.mkString("\n").trim 42 | val result = Play26Generator.formatRoutes(contents) 43 | 44 | result should be(expected) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /scala-generator/src/test/scala/utils/ScalaFormatterSpec.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import org.scalatest.funspec.AnyFunSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | class ScalaFormatterSpec extends AnyFunSpec with Matchers { 7 | 8 | it("ScalaFormatter should format valid scala code") { 9 | val contents = "case class Foo(bar: String)" 10 | val result = ScalaFormatter.format(contents) 11 | 12 | result should be(Symbol("right")) 13 | } 14 | 15 | it("ScalaFormatter should fail to format invalid scala code") { 16 | val contents = "Foo Bar" 17 | val result = ScalaFormatter.format(contents) 18 | 19 | result should be(Symbol("left")) 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /scripts/update-examples.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def fetch(name) 4 | puts "" 5 | puts "Fetching #{name}" 6 | tmp = "/tmp/apidoc-generator.update.#{name}.tmp" 7 | url = "http://apidoc.me/bryzek/#{name}/latest/service.json" 8 | run "curl --silent '#{url}' | jq . > #{tmp}" 9 | yield tmp 10 | end 11 | 12 | def run(cmd) 13 | puts " " + cmd 14 | system(cmd) 15 | end 16 | 17 | def fetch_and_copy(name, target) 18 | fetch(name) do |path| 19 | run("cp #{path} #{target}") 20 | end 21 | end 22 | 23 | def fetch_and_copy_to_examples(name) 24 | fetch(name) do |path| 25 | run("cp #{path} lib/src/test/resources/examples/#{name}.json") 26 | end 27 | end 28 | 29 | fetch_and_copy_to_examples("apidoc-api") 30 | fetch_and_copy_to_examples("apidoc-generator") 31 | fetch_and_copy_to_examples("apidoc-example-union-types") 32 | fetch_and_copy_to_examples("apidoc-example-union-types-discriminator") 33 | 34 | fetch("apidoc-reference-api") do |path| 35 | run("cp #{path} reference-api/service.json") 36 | run("cp #{path} lib/src/test/resources/examples/reference-service.json") 37 | end 38 | --------------------------------------------------------------------------------