├── .gitignore ├── LICENSE ├── NOTICE ├── README.md ├── build.gradle ├── docs ├── CNAME ├── gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ ├── gradle-wrapper.properties │ │ │ └── gradle-wrapper.properties.kotlin-dsl │ ├── gradlew │ └── gradlew.bat ├── index.html ├── jquery.js ├── kotlin.js ├── ktor-generator.js ├── ktor_logo_box.svg ├── lib │ ├── bootstrap.bundle.min.js │ ├── bootstrap.min.css │ ├── jquery-3.3.1.min.js │ └── require.min.js ├── maven │ ├── mvnw │ ├── mvnw.cmd │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── output.js ├── static │ └── ktor_logo.svg ├── styles.css └── swagger │ └── SwaggerUtils.kt.txt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── ktor-generator-website ├── build.gradle ├── resources │ ├── index.html │ ├── ktor_logo_box.svg │ ├── lib │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.min.css │ │ ├── jquery-3.3.1.min.js │ │ └── require.min.js │ └── styles.css ├── src │ └── io │ │ └── ktor │ │ └── start │ │ ├── JsUtils.kt │ │ ├── Main.kt │ │ └── MainEvents.kt └── test │ └── SampleTest.kt ├── ktor-generator ├── .gitignore ├── build.gradle ├── package.json └── src │ ├── commonMain │ ├── kotlin │ │ └── io │ │ │ ├── dahgan │ │ │ └── Yaml.kt │ │ │ └── ktor │ │ │ └── start │ │ │ ├── BuildInfo.kt │ │ │ ├── Feature.kt │ │ │ ├── KtorEngine.kt │ │ │ ├── ProjectTypes.kt │ │ │ ├── Repos.kt │ │ │ ├── Versions.kt │ │ │ ├── features │ │ │ ├── all.kt │ │ │ ├── client │ │ │ │ ├── ClientEngines.kt │ │ │ │ └── ClientFeatures.kt │ │ │ ├── server │ │ │ │ ├── AuthBasicFeature.kt │ │ │ │ ├── AuthDigestFeature.kt │ │ │ │ ├── AuthFeature.kt │ │ │ │ ├── AuthJwtFeature.kt │ │ │ │ ├── AuthLdapFeature.kt │ │ │ │ ├── AuthOauthFeature.kt │ │ │ │ ├── AutoHeadResponseFeature.kt │ │ │ │ ├── CORSFeature.kt │ │ │ │ ├── CachingHeadersFeature.kt │ │ │ │ ├── CallLoggingFeature.kt │ │ │ │ ├── CompressionFeature.kt │ │ │ │ ├── ConditionalHeadersFeature.kt │ │ │ │ ├── ContentNegotiationFeature.kt │ │ │ │ ├── CssDslFeature.kt │ │ │ │ ├── DataConversionFeature.kt │ │ │ │ ├── DefaultHeadersFeature.kt │ │ │ │ ├── ForwardedHeaderSupportFeature.kt │ │ │ │ ├── FreemarkerFeature.kt │ │ │ │ ├── HSTSFeature.kt │ │ │ │ ├── HtmlDslFeature.kt │ │ │ │ ├── HttpsRedirectFeature.kt │ │ │ │ ├── JsonGsonFeature.kt │ │ │ │ ├── JsonJacksonFeature.kt │ │ │ │ ├── LocationsFeature.kt │ │ │ │ ├── MetricsFeature.kt │ │ │ │ ├── MustacheFeature.kt │ │ │ │ ├── PartialContentFeature.kt │ │ │ │ ├── RoutingFeature.kt │ │ │ │ ├── SessionsFeature.kt │ │ │ │ ├── ShutdownUrlFeature.kt │ │ │ │ ├── StaticContentFeature.kt │ │ │ │ ├── StatusPagesFeature.kt │ │ │ │ ├── ThymeleafFeature.kt │ │ │ │ ├── VelocityFeature.kt │ │ │ │ ├── WebjarsFeature.kt │ │ │ │ └── WebsocketsFeature.kt │ │ │ └── sockets │ │ │ │ ├── RawSocketsFeature.kt │ │ │ │ └── RawSocketsTlsFeature.kt │ │ │ ├── http │ │ │ └── HttpStatusCodes.kt │ │ │ ├── project │ │ │ ├── ApplicationConf.kt │ │ │ ├── ApplicationKt.kt │ │ │ ├── ApplicationTestKt.kt │ │ │ ├── BuildFiles.kt │ │ │ ├── BuildFilesGradle.kt │ │ │ ├── BuildFilesMaven.kt │ │ │ ├── GitIgnoreFile.kt │ │ │ ├── LogBackXml.kt │ │ │ └── Properties.kt │ │ │ ├── swagger │ │ │ ├── ContentType.kt │ │ │ ├── JsonSchema.kt │ │ │ ├── SwaggerGenerator.kt │ │ │ ├── SwaggerGeneratorBase.kt │ │ │ ├── SwaggerGeneratorCommon.kt │ │ │ ├── SwaggerGeneratorInterface.kt │ │ │ ├── SwaggerGeneratorRaw.kt │ │ │ ├── SwaggerModel.kt │ │ │ └── SwaggerModelExt.kt │ │ │ └── util │ │ │ ├── DateTime.kt │ │ │ ├── Dynamic.kt │ │ │ ├── FileMode.kt │ │ │ ├── FormUrlEncoded.kt │ │ │ ├── Json.kt │ │ │ ├── MetaListIterator.kt │ │ │ ├── Stack.kt │ │ │ ├── StrReader.kt │ │ │ ├── Template.kt │ │ │ ├── baos.kt │ │ │ ├── buildList.kt │ │ │ ├── charset.kt │ │ │ ├── crc32.kt │ │ │ ├── ext.kt │ │ │ ├── extra.kt │ │ │ ├── hex.kt │ │ │ ├── id.kt │ │ │ ├── indenter.kt │ │ │ ├── mvndependency.kt │ │ │ ├── quote.kt │ │ │ ├── semver.kt │ │ │ └── zip.kt │ └── resources │ │ ├── gradle │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ ├── gradle-wrapper.properties │ │ │ │ └── gradle-wrapper.properties.kotlin-dsl │ │ ├── gradlew │ │ └── gradlew.bat │ │ ├── maven │ │ ├── mvnw │ │ ├── mvnw.cmd │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ │ ├── static │ │ └── ktor_logo.svg │ │ └── swagger │ │ └── SwaggerUtils.kt.txt │ ├── commonTest │ └── kotlin │ │ └── io │ │ └── ktor │ │ └── start │ │ └── util │ │ ├── MyCommonTest.kt │ │ └── SemVerTest.kt │ ├── jsMain │ └── kotlin │ │ └── io │ │ └── ktor │ │ └── start │ │ └── util │ │ └── DateTimeJs.kt │ ├── jsTest │ └── kotlin │ │ └── io │ │ └── ktor │ │ └── start │ │ └── MyJsTest.kt │ ├── jvmMain │ └── kotlin │ │ └── io │ │ └── ktor │ │ └── start │ │ └── util │ │ └── DateTimeJvm.kt │ └── jvmTest │ ├── kotlin │ └── io │ │ └── ktor │ │ ├── start │ │ ├── GenerationTest.kt │ │ ├── IntegrationTests.kt │ │ ├── SwaggerGenerationTest.kt │ │ ├── TestTools.kt │ │ ├── Tools.kt │ │ ├── WriteToFolderTest.kt │ │ └── swagger │ │ │ ├── GenerationSpike.kt │ │ │ ├── JsonSchemaTest.kt │ │ │ ├── ModelTest.kt │ │ │ └── YamlSchemaTest.kt │ │ └── util │ │ ├── IdTest.kt │ │ ├── IndenterTest.kt │ │ └── JsonTest.kt │ └── resources │ ├── V30 │ ├── api-with-examples.yaml │ ├── callback-example.yaml │ ├── link-example.yaml │ ├── petstore-expanded.yaml │ ├── petstore.yaml │ └── uspto.yaml │ ├── empty.json │ ├── openapi.yaml │ ├── small-petstore3.json │ ├── swagger.json │ ├── swagger.yaml │ └── uspto.json ├── ktor-intellij-plugin ├── build.gradle ├── resources │ ├── META-INF │ │ └── plugin.xml │ └── icons │ │ ├── ktor-icon.png │ │ └── ktor-icon16.png ├── src │ └── io │ │ └── ktor │ │ └── start │ │ └── intellij │ │ ├── FqNames.kt │ │ ├── KtorArtifactWizardStep.kt │ │ ├── KtorModuleBuilder.kt │ │ ├── KtorModuleConfig.kt │ │ ├── KtorModuleType.kt │ │ ├── KtorModuleWizardStep.kt │ │ ├── RoutingLineMarker.kt │ │ ├── locations │ │ ├── AddClassParameterQuickFix.kt │ │ ├── AddOuterClassParameterQuickFix.kt │ │ ├── CompletionInsertHandler.kt │ │ ├── ConvertToClassQuickFix.kt │ │ ├── ElementManipulators.kt │ │ ├── LocationPatternBraceMatcher.kt │ │ ├── LocationsLineMarker.kt │ │ ├── LocationsParametersFindUsagesHandlerFactory.kt │ │ ├── LocationsRefactoringsSupportProvider.kt │ │ ├── LocationsReferenceContributor.kt │ │ ├── MakeParameterOptionalQuickFix.kt │ │ ├── MissingOwnerClassReferenceInspection.kt │ │ ├── ParameterNameAnnotator.kt │ │ ├── ParameterNameInPlaceRenameHandler.kt │ │ ├── ParameterNameInspection.kt │ │ ├── ParameterRenameProcessor.kt │ │ ├── ParameterUsagesSearcher.kt │ │ ├── Parser.kt │ │ ├── ParserToPsi.kt │ │ ├── PatternFileTypeFactory.kt │ │ ├── PatternFindUsagesProvider.kt │ │ ├── PatternLexer.kt │ │ ├── PatternParameterBackReference.kt │ │ ├── PatternParameterReference.kt │ │ ├── PatternParser.kt │ │ ├── PatternSyntaxHighlighter.kt │ │ ├── PropertyNamesCompletionContributor.kt │ │ └── QuickFixActions.kt │ │ └── util │ │ ├── UIExt.kt │ │ └── Utils.kt ├── test │ └── io │ │ └── ktor │ │ └── start │ │ └── intellij │ │ └── util │ │ ├── ParsingTest.kt │ │ └── PathInfoTest.kt └── testresources │ ├── Smoke.locationspattern │ ├── Smoke.txt │ └── swagger.json ├── settings.gradle └── synchronize_versions.kt /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .gradle 3 | *.iml 4 | *.iws 5 | *.ipr 6 | *.DS_Store 7 | /build 8 | /docs/META-INF 9 | /docs/output 10 | /ktor-generator/build 11 | /ktor-generator/common/build 12 | /ktor-generator/js/build 13 | /ktor-generator/jvm/build 14 | /ktor-generator-website/build 15 | /ktor-intellij-plugin/build 16 | package-lock.json 17 | 18 | *.js.map 19 | *.meta.js 20 | 21 | local.properties -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | YAML Parsing 18 | https://github.com/kareez/dahgan 19 | 20 | Released under Apache 2.0: 21 | https://github.com/kareez/dahgan/blob/dee63dcf0c26097e9078b79770cd6dd1799fde0d/LICENSE 22 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | ========================================================================= 2 | == NOTICE file corresponding to the section 4 d of == 3 | == the Apache License, Version 2.0, == 4 | == in this case for the ktor-init-tools. == 5 | ========================================================================= 6 | 7 | ktor-init-tools. 8 | Copyright 2018 JetBrains s.r.o and respective authors and developers 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ktor-init-tools 2 | 3 | A set of tools for creating Ktor projects. 4 | 5 | This includes: 6 | * A MPP library shared for Ktor project generation. 7 | * A Website for generating Ktor projects as ZIP files client-side *(Kotlin-JS)*. 8 | * A IntelliJ plugin generating Ktor projects *(Kotlin-JVM)*. 9 | 10 | The code generation features: 11 | * Maven and Gradle support including wrappers. 12 | * Backend engine selection. 13 | * Ktor version selection. 14 | * GroupId/ArtifactId/Version definition. 15 | * Feature selection with sample code generation. 16 | * Code generation from swagger models. 17 | * Easy to modify and add new things. 18 | 19 | ## Project Generation Website 20 | 21 | The website is generated in the `/docs` folder as plain static files. 22 | You can serve it locally with any webserver like `hs docs/`. 23 | Once pushed, it will be available in its domain via github's pages. 24 | 25 | You can compile it continuously on change with the following command: 26 | 27 | ```bash 28 | ./gradlew -t :ktor-generator-website:buildAndCopy 29 | ``` 30 | 31 | ## IntelliJ plugin 32 | 33 | To run an IDE with the plugin for development: 34 | 35 | ```bash 36 | ./gradlew :ktor-intellij-plugin:runIde 37 | ``` 38 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.web_dir = 'docs' 3 | 4 | repositories { 5 | mavenLocal() 6 | maven { url "https://plugins.gradle.org/m2/" } 7 | mavenCentral() 8 | } 9 | dependencies { 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | classpath 'com.moowork.gradle:gradle-node-plugin:1.2.0' 12 | } 13 | } 14 | 15 | plugins { 16 | id 'kotlin-multiplatform' version '1.3.61' 17 | } 18 | 19 | group 'io.ktor.start' 20 | version '1.0-SNAPSHOT' 21 | 22 | apply plugin: 'idea' 23 | 24 | allprojects { 25 | repositories { 26 | mavenLocal() 27 | mavenCentral() 28 | maven { url "https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven" } 29 | maven { url "https://plugins.gradle.org/m2/" } 30 | } 31 | } 32 | 33 | idea { 34 | module { 35 | excludeDirs = [".gradle", ".idea", "build", "gradle", "docs", "resources/lib"].collect { new File(it) } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | start.ktor.io -------------------------------------------------------------------------------- /docs/gradle/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktorio/ktor-init-tools/790e8b389ae9aed630eea72a73181a3a5b725977/docs/gradle/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /docs/gradle/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /docs/gradle/gradle/wrapper/gradle-wrapper.properties.kotlin-dsl: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /docs/gradle/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /docs/jquery.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) 3 | define(['exports', 'kotlin'], factory); 4 | else if (typeof exports === 'object') 5 | factory(module.exports, require('kotlin')); 6 | else { 7 | if (typeof kotlin === 'undefined') { 8 | throw new Error("Error loading module 'jquery'. Its dependency 'kotlin' was not found. Please, check whether 'kotlin' is loaded prior to 'jquery'."); 9 | } 10 | root.jquery = factory(typeof jquery === 'undefined' ? {} : jquery, kotlin); 11 | } 12 | }(this, function (_, Kotlin) { 13 | 'use strict'; 14 | Kotlin.defineModule('jquery', _); 15 | return _; 16 | })); 17 | 18 | //# sourceMappingURL=jquery.js.map 19 | -------------------------------------------------------------------------------- /docs/ktor_logo_box.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/maven/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktorio/ktor-init-tools/790e8b389ae9aed630eea72a73181a3a5b725977/docs/maven/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /docs/maven/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip -------------------------------------------------------------------------------- /docs/static/ktor_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /docs/styles.css: -------------------------------------------------------------------------------- 1 | .loading { 2 | color: #aaa; 3 | } 4 | 5 | .loaded { 6 | display: none; 7 | } 8 | 9 | label.artifact { 10 | display: block; 11 | padding: 0 8px; 12 | } 13 | 14 | .artifact-group { 15 | padding: 3px 8px; 16 | font: 18px Arial; 17 | background: #aaa; 18 | color: white; 19 | } 20 | 21 | label.artifact .subtitle { 22 | } 23 | 24 | .artifact .subtitle { 25 | font-size: 0.75em; 26 | color: #777; 27 | } 28 | 29 | .artifact .artifact-name { 30 | font-size: 0.75em; 31 | color: #aaa; 32 | } 33 | 34 | .artifact:hover { 35 | background: #fafafa; 36 | } 37 | 38 | .artifact.active { 39 | background: #f7f7f7; 40 | } 41 | 42 | .dependencies { 43 | border: 1px solid #ddd; 44 | } 45 | 46 | h2 { 47 | font-size: 1.7em; 48 | } 49 | 50 | label.selected { 51 | background: #e7e7ff !important; 52 | } 53 | 54 | label.indeterminate { 55 | background: #ffe7ff !important; 56 | color: #777; 57 | } 58 | 59 | label.artifact { 60 | margin: 0; 61 | } 62 | 63 | .dependencies h2 { 64 | margin: 0; 65 | } 66 | 67 | .dependencies { 68 | overflow:auto; 69 | margin-top: 8px; 70 | border-radius:6px; 71 | 72 | min-height: 100px; 73 | height: 287px; /* Fallback for older browsers */ 74 | height: calc(100vh - 240px); 75 | } 76 | 77 | @media(max-width: 576px) { 78 | .dependencies { 79 | height: 20vh !important; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=1.5.4 2 | kotlin_version=1.3.70 3 | kotlin.code.style=official 4 | kotlinx_coroutines_version=1.3.4 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktorio/ktor-init-tools/790e8b389ae9aed630eea72a73181a3a5b725977/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /ktor-generator-website/resources/ktor_logo_box.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ktor-generator-website/resources/styles.css: -------------------------------------------------------------------------------- 1 | .loading { 2 | color: #aaa; 3 | } 4 | 5 | .loaded { 6 | display: none; 7 | } 8 | 9 | label.artifact { 10 | display: block; 11 | padding: 0 8px; 12 | } 13 | 14 | .artifact-group { 15 | padding: 3px 8px; 16 | font: 18px Arial; 17 | background: #aaa; 18 | color: white; 19 | } 20 | 21 | label.artifact .subtitle { 22 | } 23 | 24 | .artifact .subtitle { 25 | font-size: 0.75em; 26 | color: #777; 27 | } 28 | 29 | .artifact .artifact-name { 30 | font-size: 0.75em; 31 | color: #aaa; 32 | } 33 | 34 | .artifact:hover { 35 | background: #fafafa; 36 | } 37 | 38 | .artifact.active { 39 | background: #f7f7f7; 40 | } 41 | 42 | .dependencies { 43 | border: 1px solid #ddd; 44 | } 45 | 46 | h2 { 47 | font-size: 1.7em; 48 | } 49 | 50 | label.selected { 51 | background: #e7e7ff !important; 52 | } 53 | 54 | label.indeterminate { 55 | background: #ffe7ff !important; 56 | color: #777; 57 | } 58 | 59 | label.artifact { 60 | margin: 0; 61 | } 62 | 63 | .dependencies h2 { 64 | margin: 0; 65 | } 66 | 67 | .dependencies { 68 | overflow:auto; 69 | margin-top: 8px; 70 | border-radius:6px; 71 | 72 | min-height: 100px; 73 | height: 287px; /* Fallback for older browsers */ 74 | height: calc(100vh - 240px); 75 | } 76 | 77 | @media(max-width: 576px) { 78 | .dependencies { 79 | height: 20vh !important; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /ktor-generator-website/src/io/ktor/start/MainEvents.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start 2 | 3 | import js.externals.jquery.* 4 | import org.w3c.dom.* 5 | import org.w3c.dom.events.* 6 | import kotlin.browser.* 7 | 8 | fun registerKeyboardUsability() { 9 | document.addEventListener("keydown", { be -> 10 | val e = be as KeyboardEvent 11 | val active = document.activeElement 12 | for (filterId in listOf("dependency-filter-client", "dependency-filter-server")) { 13 | val dependencyFilter = document.getElementById(filterId) 14 | 15 | if ((active?.tagName != "INPUT") && (active?.tagName != "TEXTAREA") && (e.key in listOf("f", "s", "c"))) { 16 | if ( 17 | ((e.key === "f" || e.key === "s") && filterId.contains("server")) || (e.key === "c" && filterId.contains("client")) 18 | ) { 19 | dependencyFilter.asDynamic()?.focus() 20 | e.preventDefault() 21 | } 22 | } 23 | 24 | if (active === dependencyFilter) { 25 | val current = jq(".artifact.active:visible") 26 | when (e.key) { 27 | "Escape" -> { 28 | dependencyFilter.asDynamic()?.blur() 29 | e.preventDefault() 30 | } 31 | "Enter" -> { 32 | val checkbox = current.find("[type=checkbox]") 33 | checkbox.prop("checked", !(checkbox.prop("checked").asDynamic())) 34 | e.preventDefault() 35 | } 36 | "ArrowUp", "ArrowDown" -> { 37 | val up = e.key === "ArrowUp" 38 | val count = current.length 39 | var next: JQuery 40 | if (count == 0) { 41 | next = jq(".artifact:visible").first() 42 | } else { 43 | next = current 44 | 45 | do { 46 | next = if (up) next.prev(".artifact") else next.next(".artifact") 47 | } while (next.length.toInt() >= 1 && !next.`is`(":visible")) 48 | 49 | if (next.length == 0) { 50 | next = if (up) jq(".artifact:visible").last() else jq(".artifact:visible").first() 51 | } 52 | } 53 | 54 | next.addClass("active") 55 | current.removeClass("active") 56 | 57 | if (next.length.toInt() >= 1) { 58 | next[0]?.scrollIntoView( 59 | jsObject( 60 | "behavior" to "instant", 61 | "block" to "center", 62 | "inline" to "center" 63 | ) 64 | ) 65 | } 66 | 67 | e.preventDefault() 68 | } 69 | } 70 | } 71 | } 72 | }) 73 | } 74 | -------------------------------------------------------------------------------- /ktor-generator-website/test/SampleTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import kotlin.test.* 19 | 20 | class SampleTest { 21 | @Test 22 | fun name() { 23 | assertEquals(true, true) 24 | } 25 | } -------------------------------------------------------------------------------- /ktor-generator/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /ktor-generator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "mocha": "^8.3.2", 4 | "mocha-junit-reporter": "^2.0.0", 5 | "mocha-multi-reporters": "^1.5.1", 6 | "mocha-simple-html-reporter": "^2.0.0", 7 | "mochawesome": "^6.2.2", 8 | "source-map-support": "^0.5.19" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/BuildInfo.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start 19 | 20 | import io.ktor.start.swagger.* 21 | import io.ktor.start.util.* 22 | 23 | data class BuildInfo( 24 | val includeWrapper: Boolean = false, 25 | val projectType: ProjectType = ProjectType.Gradle, 26 | val ktorVersion: KtorVersion = Versions.LAST, 27 | val artifactName: String = "example", 28 | val artifactGroup: String = "com.example", 29 | val artifactVersion: String = "0.0.1-SNAPSHOT", 30 | val ktorEngine: KtorEngine = KtorEngine.Netty, 31 | val generateFeatureSample: Boolean = true, 32 | //val swaggerGenKind: SwaggerGenerator.Kind = SwaggerGenerator.Kind.INTERFACE, 33 | val swaggerGenKind: SwaggerGenerator.Kind = SwaggerGenerator.Kind.RAW, 34 | val fetch: suspend (path: String) -> ByteArray = { TODO("Must set fetch") } 35 | ) : BlockBuilder.Config() { 36 | val is100OrGreater = ktorVersion.semVersion >= SemVer("1.0.0") 37 | 38 | val developmentPackage = "io.ktor.server.${ktorEngine.id}" 39 | val developmentEngineFQ = when { 40 | is100OrGreater -> "$developmentPackage.EngineMain" 41 | else -> "$developmentPackage.DevelopmentEngine" 42 | } 43 | val kotlinVersion get() = ktorVersion.semKotlinVersion 44 | 45 | override fun transform(data: ByteArray, charset: Charset?): ByteArray { 46 | if (charset == null) return data 47 | val content = data.toString(charset) 48 | return if (is100OrGreater) { 49 | content 50 | .replace("kotlin.coroutines.experimental.", "kotlin.coroutines.") 51 | .replace("kotlinx.coroutines.experimental.", "kotlinx.coroutines.") 52 | .replace("// kotlinx.coroutines-1.0.0: // ", "") 53 | } else { 54 | content 55 | }.toByteArray(charset) 56 | } 57 | } 58 | 59 | typealias BuildInfoBlock = Block 60 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/Feature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start 19 | 20 | import io.ktor.start.project.* 21 | import io.ktor.start.util.* 22 | 23 | interface FileContainer { 24 | fun add(name: String, content: ByteArray, mode: FileMode = FileMode("644")) 25 | } 26 | 27 | fun FileContainer.add(name: String, content: String, charset: Charset = UTF8, mode: FileMode = FileMode("644")) { 28 | add(name, content.toByteArray(charset), mode = mode) 29 | } 30 | 31 | abstract class ServerFeature(vararg deps: Block) : Feature(*deps) 32 | 33 | abstract class ClientFeature(vararg deps: Block) : Feature(*deps) 34 | 35 | abstract class Feature(vararg deps: Block) : Block(*deps) { 36 | val featureDeps get() = blockDeps.filterIsInstance() 37 | 38 | open val group: String = "Features" 39 | open val tags = listOf() 40 | open val since: KtorVersion? = null 41 | 42 | open val repos: List = Repos.mavenCentral 43 | abstract val id: String 44 | open val artifacts: List by lazy { listOf("io.ktor:$id:\$ktor_version") } 45 | open val testArtifacts: List by lazy { listOf() } 46 | abstract val title: String 47 | abstract val description: String 48 | open val documentation: String? = null 49 | 50 | final override fun BlockBuilder.render(info: BuildInfo) { 51 | for (repo in repos) { 52 | addMavenRepository(repo) 53 | } 54 | for (artifact in artifacts) { 55 | addCompileDependency(MvnArtifact(artifact)) 56 | } 57 | for (artifact in testArtifacts) { 58 | addTestDependency(MvnArtifact(artifact)) 59 | } 60 | if (info.generateFeatureSample) { 61 | renderFeature(info) 62 | } 63 | } 64 | 65 | open fun BlockBuilder.renderFeature(info: BuildInfo) { 66 | } 67 | 68 | override fun toString(): String = "Feature($id)" 69 | } 70 | 71 | class FeatureSet(features: Iterable) { 72 | val direct = features.toSet() 73 | val all = direct.flatMap { it.getAllDependantBlocks().filterIsInstance() }.toSet() 74 | val transitive = all - direct 75 | } 76 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/KtorEngine.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start 19 | 20 | enum class KtorEngine(val id: String) { 21 | Netty("netty"), 22 | Jetty("jetty"), 23 | Tomcat("tomcat"), 24 | CIO("cio"); 25 | 26 | companion object { 27 | val BY_ID = values().associateBy { it.id } 28 | val BY_NAME = values().associateBy { it.name } 29 | val BY = BY_ID + BY_NAME 30 | 31 | operator fun invoke(name: String) = BY[name] ?: error("Unknown engine $name") 32 | } 33 | } -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/ProjectTypes.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start 19 | 20 | enum class ProjectType(val id: String) { 21 | Gradle("gradle"), 22 | GradleKotlinDsl("gradle-kotlin-dsl"), 23 | Maven("maven"); 24 | 25 | companion object { 26 | val BY_ID = values().associateBy { it.id } 27 | val BY_NAME = values().associateBy { it.name } 28 | val BY = BY_ID + BY_NAME 29 | 30 | operator fun invoke(name: String): ProjectType = BY[name] ?: error("Unknown project type $name") 31 | } 32 | } -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/Repos.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start 19 | 20 | object Repos { 21 | val local = listOf("local") 22 | val mavenCentral = listOf("mavenCentral") 23 | val kotlin_js_wrappers = listOf("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-js-wrappers") 24 | } 25 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/all.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.features.sockets.* 22 | import io.ktor.start.features.client.* 23 | import io.ktor.start.features.server.* 24 | 25 | val ALL_SERVER_FEATURES by lazy { ALL_FEATURES.filterIsInstance() } 26 | val ALL_CLIENT_FEATURES by lazy { ALL_FEATURES.filterIsInstance() } 27 | 28 | val ALL_FEATURES: List = listOf( 29 | // Client Features 30 | CoreClientEngine, 31 | ApacheClientEngine, 32 | CioClientEngine, 33 | JettyClientEngine, 34 | MockClientEngine, 35 | HttpTimeoutClientFeature, 36 | AuthBasicClientFeature, 37 | GsonClientFeature, 38 | WebSocketClientFeature, 39 | LoggingClientFeature, 40 | UserAgentClientFeature, 41 | 42 | // Server Features 43 | HtmlDslFeature, 44 | CssDslFeature, 45 | FreemarkerFeature, 46 | VelocityFeature, 47 | MustacheFeature, 48 | ThymeleafFeature, 49 | StaticContentFeature, 50 | AuthBasicFeature, 51 | AuthDigestFeature, 52 | AuthJwtFeature, 53 | AuthLdapFeature, 54 | AuthOauthFeature, 55 | AuthFeature, 56 | JsonGsonFeature, 57 | JsonJacksonFeature, 58 | LocationsFeature, 59 | MetricsFeature, 60 | SessionsFeature, 61 | CompressionFeature, 62 | CachingHeadersFeature, 63 | CallLoggingFeature, 64 | ConditionalHeadersFeature, 65 | CORSFeature, 66 | AutoHeadResponseFeature, 67 | DataConversionFeature, 68 | DefaultHeadersFeature, 69 | ForwardedHeaderSupportFeature, 70 | HSTSFeature, 71 | StatusPagesFeature, 72 | RoutingFeature, 73 | WebjarsFeature, 74 | ContentNegotiationFeature, 75 | HttpsRedirectFeature, 76 | ShutdownUrlFeature, 77 | WebsocketsFeature, 78 | RawSocketsFeature, 79 | PartialContentFeature, 80 | RawSocketsTlsFeature 81 | ) 82 | 83 | val ALL_FEATURES_BY_ID = ALL_FEATURES.associateBy { it.id } 84 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/AuthBasicFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object AuthBasicFeature : ServerFeature(ApplicationKt, AuthFeature, RoutingFeature) { 25 | override val group: String = "Authentication" 26 | override val artifacts = listOf("io.ktor:ktor-auth:\$ktor_version") 27 | override val id = "auth-basic" 28 | override val title = "Authentication Basic" 29 | override val description = "Handle Basic authentication" 30 | override val documentation = "https://ktor.io/docs/basic.html" 31 | 32 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 33 | addImport("io.ktor.auth.*") 34 | addAuthProvider { 35 | "basic(\"myBasicAuth\")" { 36 | +"realm = \"Ktor Server\"" 37 | +"validate { if (it.name == \"test\" && it.password == \"password\") UserIdPrincipal(it.name) else null }" 38 | } 39 | } 40 | addRoute { 41 | "authenticate(\"myBasicAuth\")" { 42 | "get(\"/protected/route/basic\")" { 43 | +"val principal = call.principal()!!" 44 | +"call.respondText(\"Hello \${principal.name}\")" 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/AuthDigestFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object AuthDigestFeature : ServerFeature(ApplicationKt, AuthFeature, RoutingFeature) { 25 | override val group: String = "Authentication" 26 | override val artifacts = listOf("io.ktor:ktor-auth:\$ktor_version") 27 | override val id = "auth-digest" 28 | override val title = "Authentication Digest" 29 | override val description = "Handle Digest authentication" 30 | override val documentation = "https://ktor.io/docs/digest.html" 31 | 32 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 33 | addImport("io.ktor.util.*") 34 | addAuthProvider { 35 | +"val myRealm = \"MyRealm\"" 36 | +"val usersInMyRealmToHA1: Map = mapOf(" 37 | indent { 38 | +"// pass=\"test\", HA1=MD5(\"test:MyRealm:pass\")=\"fb12475e62dedc5c2744d98eb73b8877\"" 39 | +"\"test\" to hex(\"fb12475e62dedc5c2744d98eb73b8877\")" 40 | } 41 | +")" 42 | +"digest(\"myDigestAuth\")" { 43 | lineNoOpen("userNameRealmPasswordDigestProvider = { userName, realm ->") { 44 | +"usersInMyRealmToHA1[userName]" 45 | } 46 | } 47 | } 48 | addRoute { 49 | "authenticate(\"myDigestAuth\")" { 50 | "get(\"/protected/route/digest\")" { 51 | +"val principal = call.principal()!!" 52 | +"call.respondText(\"Hello \${principal.name}\")" 53 | } 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/AuthFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object AuthFeature : ServerFeature(ApplicationKt, RoutingFeature) { 25 | override val group: String = "Authentication" 26 | override val artifacts = listOf("io.ktor:ktor-auth:\$ktor_version") 27 | override val id = "auth" 28 | override val title = "Authentication" 29 | override val description = "Handle Basic and Digest HTTP Auth, Form authentication and OAuth 1a and 2" 30 | override val documentation = "https://ktor.io/docs/features-authentication.html" 31 | 32 | val BLOCK = newSlot("BLOCK") 33 | 34 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 35 | addImport("io.ktor.auth.*") 36 | addFeatureInstall { 37 | "install(Authentication)" { 38 | block(BLOCK) 39 | } 40 | } 41 | } 42 | } 43 | 44 | fun BlockBuilder.addAuthProvider(callback: Indenter.() -> Unit) { 45 | appendSeparated(AuthFeature.BLOCK) { 46 | callback() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/AuthJwtFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | 23 | object AuthJwtFeature : ServerFeature(ApplicationKt, AuthFeature) { 24 | override val group: String = "Authentication" 25 | override val artifacts = listOf("io.ktor:ktor-auth-jwt:\$ktor_version") 26 | override val id = "auth-jwt" 27 | override val title = "Authentication JWT" 28 | override val description = "Handle JWT authentication" 29 | override val documentation = "https://ktor.io/docs/jwt.html" 30 | } 31 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/AuthLdapFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | 23 | object AuthLdapFeature : ServerFeature(ApplicationKt, AuthFeature) { 24 | override val group: String = "Authentication" 25 | override val artifacts = listOf("io.ktor:ktor-auth-ldap:\$ktor_version") 26 | override val id = "auth-ldap" 27 | override val title = "Authentication LDAP" 28 | override val description = "Handle LDAP authentication" 29 | override val documentation = "https://ktor.io/docs/ldap.html" 30 | } 31 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/AuthOauthFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | 23 | object AuthOauthFeature : ServerFeature(ApplicationKt, AuthFeature) { 24 | override val group: String = "Authentication" 25 | override val artifacts = listOf("io.ktor:ktor-auth:\$ktor_version") 26 | override val id = "auth-oauth" 27 | override val title = "Authentication OAuth" 28 | override val description = "Handle OAuth authentication" 29 | override val documentation = "https://ktor.io/docs/authentication-oauth.html" 30 | } 31 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/AutoHeadResponseFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object AutoHeadResponseFeature : ServerFeature(ApplicationKt) { 25 | override val artifacts = listOf("io.ktor:ktor-server-core:\$ktor_version") 26 | override val id = "caching-headers" 27 | override val title = "CachingHeaders" 28 | override val description = "Send the headers Cache-Control and Expires used by clients and proxies to cache requests" 29 | override val documentation = "https://ktor.io/docs/caching-headers.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.features.*") 33 | addImport("io.ktor.http.*") 34 | addImport("io.ktor.http.content.*") 35 | addImport("io.ktor.util.date.*") 36 | addFeatureInstall { 37 | "install(CachingHeaders)" { 38 | +"options { outgoingContent ->" 39 | indent { 40 | "when (outgoingContent.contentType?.withoutParameters())" { 41 | +"ContentType.Text.CSS -> CachingOptions(CacheControl.MaxAge(maxAgeSeconds = 24 * 60 * 60), expires = null as? GMTDate?)" 42 | +"else -> null" 43 | } 44 | } 45 | +"}" 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/CORSFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object CORSFeature : ServerFeature(ApplicationKt) { 25 | override val artifacts = listOf("io.ktor:ktor-server-core:\$ktor_version") 26 | override val id = "cors" 27 | override val title = "CORS" 28 | override val description = "Enable Cross-Origin Resource Sharing (CORS)" 29 | override val documentation = "https://ktor.io/docs/cors.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.features.*") 33 | addFeatureInstall { 34 | "install(CORS)" { 35 | +"method(HttpMethod.Options)" 36 | +"method(HttpMethod.Put)" 37 | +"method(HttpMethod.Delete)" 38 | +"method(HttpMethod.Patch)" 39 | +"header(HttpHeaders.Authorization)" 40 | +"header(\"MyCustomHeader\")" 41 | +"allowCredentials = true" 42 | +"anyHost() // @TODO: Don't do this in production if possible. Try to limit it." 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/CachingHeadersFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object CachingHeadersFeature : ServerFeature(ApplicationKt) { 25 | override val artifacts = listOf("io.ktor:ktor-server-core:\$ktor_version") 26 | override val id = "auto-head-response" 27 | override val title = "AutoHeadResponse" 28 | override val description = "Provide responses to HEAD requests for existing routes that have the GET verb defined" 29 | override val documentation = "https://ktor.io/docs/autoheadresponse.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.features.*") 33 | addFeatureInstall { 34 | +"install(AutoHeadResponse)" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/CallLoggingFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object CallLoggingFeature : ServerFeature(ApplicationKt) { 25 | override val artifacts = listOf("io.ktor:ktor-server-core:\$ktor_version") 26 | override val id = "call-logging" 27 | override val title = "CallLogging" 28 | override val description = "Logs client requests" 29 | override val documentation = "https://ktor.io/docs/call-logging.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.features.*") 33 | addImport("org.slf4j.event.*") 34 | addFeatureInstall { 35 | "install(CallLogging)" { 36 | +"level = Level.INFO" 37 | +"filter { call -> call.request.path().startsWith(\"/\") }" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/CompressionFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object CompressionFeature : ServerFeature(ApplicationKt) { 25 | override val artifacts = listOf("io.ktor:ktor-server-core:\$ktor_version") 26 | override val id = "compression" 27 | override val title = "Compression" 28 | override val description = "Compress outgoing content using gzip, deflate or custom encoder and thus reduce the size of the response" 29 | override val documentation = "https://ktor.io/docs/compression.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.features.*") 33 | addFeatureInstall { 34 | "install(Compression)" { 35 | "gzip" { 36 | +"priority = 1.0" 37 | } 38 | "deflate" { 39 | +"priority = 10.0" 40 | +"minimumSize(1024) // condition" 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/ConditionalHeadersFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object ConditionalHeadersFeature : ServerFeature(ApplicationKt) { 25 | override val artifacts = listOf("io.ktor:ktor-server-core:\$ktor_version") 26 | override val id = "conditional-headers" 27 | override val title = "ConditionalHeaders" 28 | override val description = "Avoids sending content if the client already has the same content using ETag or LastModified" 29 | override val documentation = "https://ktor.io/docs/conditional-headers.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.features.*") 33 | addFeatureInstall { 34 | +"install(ConditionalHeaders)" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/ContentNegotiationFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object ContentNegotiationFeature : ServerFeature(ApplicationKt) { 25 | override val group: String = "Content Negotiation" 26 | 27 | override val artifacts = listOf("io.ktor:ktor-server-core:\$ktor_version") 28 | override val id = "content-negotiation" 29 | override val title = "ContentNegotiation" 30 | override val description = "Provides automatic content conversion according to Content-Type and Accept headers." 31 | override val documentation = "https://ktor.io/docs/content-negotiation.html" 32 | 33 | val BLOCK = newSlot("BLOCK") 34 | 35 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 36 | addFeatureInstall { 37 | "install(ContentNegotiation)" { 38 | block(BLOCK) 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/CssDslFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object CssDslFeature : ServerFeature(ApplicationKt, RoutingFeature, HtmlDslFeature) { 25 | override val group: String = "Templating" 26 | override val repos = Repos.mavenCentral + Repos.kotlin_js_wrappers 27 | override val artifacts = listOf("org.jetbrains:kotlin-css-jvm:1.0.0-pre.31-kotlin-1.2.41") 28 | override val id = "css-dsl" 29 | override val title = "CSS DSL" 30 | override val description = "Generate CSS using Kotlin code" 31 | override val documentation = "https://github.com/JetBrains/kotlin-wrappers/tree/master/kotlin-css" 32 | 33 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 34 | addImport("kotlinx.html.*") 35 | addImport("kotlinx.css.*") 36 | addImport("io.ktor.http.*") 37 | addRoute { 38 | "get(\"/styles.css\")" { 39 | "call.respondCss" { 40 | "body" { 41 | +"backgroundColor = Color.red" 42 | } 43 | "p" { 44 | +"fontSize = 2.em" 45 | } 46 | "rule(\"p.myclass\")" { 47 | +"color = Color.blue" 48 | } 49 | } 50 | } 51 | } 52 | addExtensionMethods { 53 | "fun FlowOrMetaDataContent.styleCss(builder: CSSBuilder.() -> Unit)" { 54 | "style(type = ContentType.Text.CSS.toString())" { 55 | +"+CSSBuilder().apply(builder).toString()" 56 | } 57 | } 58 | } 59 | addExtensionMethods { 60 | "fun CommonAttributeGroupFacade.style(builder: CSSBuilder.() -> Unit)" { 61 | +"this.style = CSSBuilder().apply(builder).toString().trim()" 62 | } 63 | } 64 | addExtensionMethods { 65 | "suspend inline fun ApplicationCall.respondCss(builder: CSSBuilder.() -> Unit)" { 66 | +"this.respondText(CSSBuilder().apply(builder).toString(), ContentType.Text.CSS)" 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/DataConversionFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object DataConversionFeature : ServerFeature(ApplicationKt) { 25 | override val artifacts = listOf("io.ktor:ktor-server-core:\$ktor_version") 26 | override val id = "data-conversion" 27 | override val title = "DataConversion" 28 | override val description = "Allows to serialize and deserialize a list of values (used by the Locations feature)" 29 | override val documentation = "https://ktor.io/docs/data-conversion.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.features.*") 33 | addFeatureInstall { 34 | +"install(DataConversion)" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/DefaultHeadersFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object DefaultHeadersFeature : ServerFeature(ApplicationKt) { 25 | override val artifacts = listOf("io.ktor:ktor-server-core:\$ktor_version") 26 | override val id = "default-headers" 27 | override val title = "DefaultHeaders" 28 | override val description = "This feature adds a default set of headers to HTTP responses" 29 | override val documentation = "https://ktor.io/docs/default-headers.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.features.*") 33 | addFeatureInstall { 34 | "install(DefaultHeaders)" { 35 | +"header(\"X-Engine\", \"Ktor\") // will send this header with each response" 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/ForwardedHeaderSupportFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object ForwardedHeaderSupportFeature : ServerFeature(ApplicationKt) { 25 | override val artifacts = listOf("io.ktor:ktor-server-core:\$ktor_version") 26 | override val id = "forwarded-header-support" 27 | override val title = "ForwardedHeaderSupport" 28 | override val description = "This feature allows you to handle reverse proxy headers to get information about the original request when it’s behind a proxy." 29 | override val documentation = "https://ktor.io/docs/forward-headers.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.features.*") 33 | addFeatureInstall { 34 | +"install(ForwardedHeaderSupport) // WARNING: for security, do not include this if not behind a reverse proxy" 35 | +"install(XForwardedHeaderSupport) // WARNING: for security, do not include this if not behind a reverse proxy" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/FreemarkerFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object FreemarkerFeature : ServerFeature(ApplicationKt, RoutingFeature) { 25 | override val group: String = "Templating" 26 | override val artifacts = listOf("io.ktor:ktor-freemarker:\$ktor_version") 27 | override val id = "freemarker" 28 | override val title = "Freemarker" 29 | override val description = "Serve HTML content using Apache's FreeMarker template engine" 30 | override val documentation = "https://ktor.io/docs/freemarker.html" 31 | 32 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 33 | addImport("freemarker.cache.*") 34 | addImport("io.ktor.freemarker.*") 35 | addApplicationClasses { 36 | +"data class IndexData(val items: List)" 37 | } 38 | addFeatureInstall { 39 | "install(FreeMarker)" { 40 | +"templateLoader = ClassTemplateLoader(this::class.java.classLoader, \"templates\")" 41 | } 42 | } 43 | addRoute { 44 | "get(\"/html-freemarker\")" { 45 | +"call.respond(FreeMarkerContent(\"index.ftl\", mapOf(\"data\" to IndexData(listOf(1, 2, 3))), \"\"))" 46 | } 47 | } 48 | fileText("resources/templates/index.ftl") { 49 | +"<#-- @ftlvariable name=\"data\" type=\"${info.artifactGroup}.IndexData\" -->" 50 | +"" 51 | indent { 52 | +"" 53 | indent { 54 | +"
    " 55 | +"<#list data.items as item>" 56 | indent { 57 | +"
  • \${item}
  • " 58 | } 59 | +"" 60 | +"
" 61 | } 62 | +"" 63 | } 64 | +"" 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/HSTSFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object HSTSFeature : ServerFeature(ApplicationKt) { 25 | override val artifacts = listOf("io.ktor:ktor-server-core:\$ktor_version") 26 | override val id = "hsts" 27 | override val title = "HSTS" 28 | override val description = "Enable HTTP Strict Transport Security (HSTS)" 29 | override val documentation = "https://ktor.io/docs/hsts.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.features.*") 33 | addFeatureInstall { 34 | "install(HSTS)" { 35 | +"includeSubDomains = true" 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/HtmlDslFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object HtmlDslFeature : ServerFeature(ApplicationKt, RoutingFeature) { 25 | override val group: String = "Templating" 26 | override val repos = Repos.mavenCentral 27 | override val artifacts = listOf("io.ktor:ktor-html-builder:\$ktor_version") 28 | override val id = "html-dsl" 29 | override val title = "HTML DSL" 30 | override val description = "Generate HTML using Kotlin code like a pure-core template engine" 31 | override val documentation = "https://ktor.io/docs/html-dsl.html" 32 | 33 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 34 | addImport("io.ktor.html.*") 35 | addImport("kotlinx.html.*") 36 | addRoute { 37 | "get(\"/html-dsl\")" { 38 | "call.respondHtml" { 39 | "body" { 40 | +"h1 { +\"HTML\" }" 41 | "ul" { 42 | "for (n in 1..10)" { 43 | +"li { +\"\$n\" }" 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/HttpsRedirectFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object HttpsRedirectFeature : ServerFeature(ApplicationKt) { 25 | override val artifacts = listOf("io.ktor:ktor-server-core:\$ktor_version") 26 | override val id = "https-redirect" 27 | override val title = "HttpsRedirect" 28 | override val description = "All the affected HTTP calls perform a redirect to its HTTPS counterpart before processing the call" 29 | override val documentation = "https://ktor.io/docs/https-redirect.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.features.*") 33 | addFeatureInstall { 34 | +"// https://ktor.io/servers/features/https-redirect.html#testing" 35 | +"if (!testing)" { 36 | +"install(HttpsRedirect)" { 37 | +"// The port to redirect to. By default 443, the default HTTPS port." 38 | +"sslPort = 443" 39 | +"// 301 Moved Permanently, or 302 Found redirect." 40 | +"permanentRedirect = true" 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/JsonGsonFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object JsonGsonFeature : ServerFeature(ApplicationKt, 25 | ContentNegotiationFeature, 26 | RoutingFeature 27 | ) { 28 | override val group: String = "Content Negotiation" 29 | 30 | override val artifacts = listOf("io.ktor:ktor-gson:\$ktor_version") 31 | override val id = "ktor-gson" 32 | override val title = "GSON" 33 | override val description = "Handles JSON serialization using GSON library" 34 | override val documentation = "https://ktor.io/docs/content-negotiation-gson.html" 35 | 36 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 37 | addImport("io.ktor.gson.*") 38 | addImport("io.ktor.features.*") 39 | appendSeparated(ContentNegotiationFeature.BLOCK) { 40 | "gson" { 41 | } 42 | } 43 | addRoute { 44 | "get(\"/json/gson\")" { 45 | +"call.respond(mapOf(\"hello\" to \"world\"))" 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/JsonJacksonFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object JsonJacksonFeature : ServerFeature(ApplicationKt, 25 | ContentNegotiationFeature, 26 | RoutingFeature 27 | ) { 28 | override val group: String = "Content Negotiation" 29 | 30 | override val artifacts = listOf("io.ktor:ktor-jackson:\$ktor_version") 31 | override val id = "ktor-jackson" 32 | override val title = "Jackson" 33 | override val description = "Handles JSON serialization using Jackson library" 34 | override val documentation = "https://ktor.io/docs/content-negotiation-jackson.html" 35 | 36 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 37 | addImport("com.fasterxml.jackson.databind.*") 38 | addImport("io.ktor.jackson.*") 39 | addImport("io.ktor.features.*") 40 | appendSeparated(ContentNegotiationFeature.BLOCK) { 41 | "jackson" { 42 | +"enable(SerializationFeature.INDENT_OUTPUT)" 43 | } 44 | } 45 | addRoute { 46 | "get(\"/json/jackson\")" { 47 | +"call.respond(mapOf(\"hello\" to \"world\"))" 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/LocationsFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object LocationsFeature : ServerFeature(ApplicationKt, RoutingFeature) { 25 | override val artifacts = listOf("io.ktor:ktor-locations:\$ktor_version") 26 | override val id = "ktor-locations" 27 | override val title = "Locations" 28 | override val description = "Allows to define route locations in a typed way" 29 | override val documentation = "https://ktor.io/docs/features-locations.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.locations.*") 33 | 34 | addApplicationClasses { 35 | SEPARATOR { 36 | +"@Location(\"/location/{name}\")" 37 | +"class MyLocation(val name: String, val arg1: Int = 42, val arg2: String = \"default\")" 38 | } 39 | SEPARATOR { 40 | +"@Location(\"/type/{name}\") data class Type(val name: String)" { 41 | SEPARATOR { 42 | +"@Location(\"/edit\")" 43 | +"data class Edit(val type: Type)" 44 | } 45 | SEPARATOR { 46 | +"@Location(\"/list/{page}\")" 47 | +"data class List(val type: Type, val page: Int)" 48 | } 49 | } 50 | } 51 | } 52 | addFeatureInstall { 53 | +"install(Locations)" { 54 | } 55 | } 56 | addRoute { 57 | +"get" { 58 | +"call.respondText(\"Location: name=\${it.name}, arg1=\${it.arg1}, arg2=\${it.arg2}\")" 59 | } 60 | 61 | +"// Register nested routes" 62 | +"get" { 63 | +"call.respondText(\"Inside \$it\")" 64 | } 65 | +"get" { 66 | +"call.respondText(\"Inside \$it\")" 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/MetricsFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | 23 | object MetricsFeature : ServerFeature(ApplicationKt) { 24 | override val artifacts = listOf("io.ktor:ktor-metrics:\$ktor_version") 25 | override val id = "ktor-metrics" 26 | override val title = "Metrics" 27 | override val description = "Adds supports for monitoring several metrics" 28 | override val documentation = "https://ktor.io/docs/features-metrics.html" 29 | } 30 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/MustacheFeature.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.features.server 2 | 3 | import io.ktor.start.BuildInfo 4 | import io.ktor.start.Repos 5 | import io.ktor.start.ServerFeature 6 | import io.ktor.start.project.ApplicationKt 7 | import io.ktor.start.project.addApplicationClasses 8 | import io.ktor.start.project.addFeatureInstall 9 | import io.ktor.start.project.addImport 10 | import io.ktor.start.util.BlockBuilder 11 | 12 | object MustacheFeature : ServerFeature(ApplicationKt, RoutingFeature) { 13 | override val group: String = "Templating" 14 | override val artifacts = listOf("io.ktor:ktor-mustache:\$ktor_version") 15 | override val id = "mustache" 16 | override val title = "Mustache" 17 | override val description = "Serve HTML content using Mustache template engine" 18 | override val documentation = "https://ktor.io/docs/mustache.html" 19 | 20 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 21 | addImport("com.github.mustachejava.DefaultMustacheFactory") 22 | addImport("io.ktor.mustache.Mustache") 23 | addImport("io.ktor.mustache.MustacheContent") 24 | addApplicationClasses { 25 | +"data class MustacheUser(val id: Int, val name: String)" 26 | } 27 | addFeatureInstall { 28 | "install(Mustache)" { 29 | +"mustacheFactory = DefaultMustacheFactory(\"templates/mustache\")" 30 | } 31 | } 32 | addRoute { 33 | "get(\"/html-mustache\")" { 34 | +"call.respond(MustacheContent(\"index.hbs\", mapOf(\"user\" to MustacheUser(1, \"user1\"))))" 35 | } 36 | } 37 | fileText("resources/templates/mustache/index.hbs") { 38 | +"" 39 | +"" 40 | +"

Hello, {{user.name}}

" 41 | +"" 42 | +"" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/PartialContentFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object PartialContentFeature : ServerFeature(ApplicationKt) { 25 | override val artifacts = listOf("io.ktor:ktor-server-core:\$ktor_version") 26 | override val id = "partial-content" 27 | override val title = "PartialContent" 28 | override val description = "Handles requests with the Range header. " + 29 | "Generating Accept-Ranges and the Content-Range headers and slicing the served content when required." 30 | override val documentation = "https://ktor.io/docs/partial-content.html" 31 | 32 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 33 | addImport("io.ktor.features.*") 34 | addFeatureInstall { 35 | "install(PartialContent)" { 36 | +"// Maximum number of ranges that will be accepted from a HTTP request." 37 | +"// If the HTTP request specifies more ranges, they will all be merged into a single range." 38 | +"maxRangeCount = 10" 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/RoutingFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object RoutingFeature : ServerFeature(ApplicationKt, ApplicationTestKt) { 25 | override val artifacts = listOf("io.ktor:ktor-server-core:\$ktor_version") 26 | override val id = "routing" 27 | override val title = "Routing" 28 | override val description = "Allows to define structured routes and associated handlers." 29 | override val documentation = "https://ktor.io/docs/routing.html" 30 | val BLOCK = newSlot("BLOCK") 31 | 32 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 33 | addImport("io.ktor.routing.*") 34 | addImport("io.ktor.http.*") 35 | appendSeparated(ApplicationKt.MODULE_POST) { 36 | "routing" { 37 | "get(\"/\")" { 38 | +"call.respondText(\"HELLO WORLD!\", contentType = ContentType.Text.Plain)" 39 | } 40 | block(BLOCK) 41 | } 42 | } 43 | 44 | addTestMethod("testRoot") { 45 | +"withTestApplication({ module(testing = true) })" { 46 | +"handleRequest(HttpMethod.Get, \"/\").apply" { 47 | +"assertEquals(HttpStatusCode.OK, response.status())" 48 | +"assertEquals(\"HELLO WORLD!\", response.content)" 49 | } 50 | } 51 | } 52 | } 53 | } 54 | 55 | fun BlockBuilder.addRoute(callback: Indenter.() -> Unit) { 56 | appendSeparated(RoutingFeature.BLOCK) { 57 | callback() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/SessionsFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object SessionsFeature : ServerFeature(ApplicationKt, RoutingFeature) { 25 | override val artifacts = listOf( 26 | "io.ktor:ktor-server-sessions:\$ktor_version" // Only required for Cache and Directory storages 27 | ) 28 | override val id = "ktor-sessions" 29 | override val title = "Sessions" 30 | override val description = "Adds supports for sessions: with the payload in the client or the server" 31 | override val documentation = "https://ktor.io/docs/sessions.html" 32 | 33 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 34 | addImport("io.ktor.sessions.*") 35 | addApplicationClasses { 36 | +"data class MySession(val count: Int = 0)" 37 | } 38 | addFeatureInstall { 39 | +"install(Sessions)" { 40 | +"cookie(\"MY_SESSION\")" { 41 | +"cookie.extensions[\"SameSite\"] = \"lax\"" 42 | } 43 | } 44 | } 45 | addRoute { 46 | +"get(\"/session/increment\")" { 47 | +"val session = call.sessions.get() ?: MySession()" 48 | +"call.sessions.set(session.copy(count = session.count + 1))" 49 | +"call.respondText(\"Counter is \${session.count}. Refresh to increment.\")" 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/ShutdownUrlFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object ShutdownUrlFeature : ServerFeature(ApplicationKt, ApplicationConf) { 25 | override val artifacts = listOf("io.ktor:ktor-server-host-common:\$ktor_version") 26 | override val id = "shutdown-url" 27 | override val title = "Shutdown URL" 28 | override val description = "This feature enables a URL that when accessed, shutdowns the server." 29 | override val documentation = "https://ktor.io/docs/shutdown-url.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.server.engine.*") 33 | addHoconDeployment { 34 | +"shutdown.url = \"/ktor/application/shutdown\"" 35 | } 36 | addFeatureInstall { 37 | "install(ShutDownUrl.ApplicationCallFeature)" { 38 | +"// The URL that will be intercepted (you can also use the application.conf's ktor.deployment.shutdown.url key)" 39 | +"shutDownUrl = \"/ktor/application/shutdown\"" 40 | +"// A function that will be executed to get the exit code of the process" 41 | +"exitCodeSupplier = { 0 } // ApplicationCall.() -> Int" 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/StaticContentFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object StaticContentFeature : ServerFeature(ApplicationKt, RoutingFeature) { 25 | override val artifacts = listOf("io.ktor:ktor-server-host-common:\$ktor_version") 26 | override val id = "static-content" 27 | override val title = "Static Content" 28 | override val description = "Serves static files from defined locations." 29 | override val documentation = "https://ktor.io/docs/static-content.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.content.*") 33 | addImport("io.ktor.http.content.*") 34 | addRoute { 35 | +"// Static feature. Try to access `/static/ktor_logo.svg`" 36 | "static(\"/static\")" { 37 | +"resources(\"static\")" 38 | } 39 | } 40 | fileBinary("resources/static/ktor_logo.svg") { 41 | info.fetch("static/ktor_logo.svg") 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/StatusPagesFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object StatusPagesFeature : ServerFeature(ApplicationKt, RoutingFeature) { 25 | override val artifacts = listOf("io.ktor:ktor-server-host-common:\$ktor_version") 26 | override val id = "status-pages" 27 | override val title = "Status Pages" 28 | override val description = "Allow to respond to thrown exceptions." 29 | override val documentation = "https://ktor.io/docs/status-pages.html" 30 | 31 | val CUSTOM_STATUS_PAGES = newSlot("CUSTOM_STATUS_PAGES") 32 | 33 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 34 | addImport("io.ktor.features.*") 35 | addApplicationClasses { 36 | +"class AuthenticationException : RuntimeException()" 37 | +"class AuthorizationException : RuntimeException()" 38 | } 39 | addRoute { 40 | "install(StatusPages)" { 41 | lineNoOpen("exception { cause ->") { 42 | +"call.respond(HttpStatusCode.Unauthorized)" 43 | } 44 | lineNoOpen("exception { cause ->") { 45 | +"call.respond(HttpStatusCode.Forbidden)" 46 | } 47 | SEPARATOR { 48 | block(CUSTOM_STATUS_PAGES) 49 | } 50 | } 51 | } 52 | } 53 | } 54 | 55 | fun BlockBuilder.addCustomStatusPage(callback: Indenter.() -> Unit) { 56 | appendSeparated(StatusPagesFeature.CUSTOM_STATUS_PAGES) { 57 | callback() 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/ThymeleafFeature.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.features.server 2 | 3 | import io.ktor.start.BuildInfo 4 | import io.ktor.start.Repos 5 | import io.ktor.start.ServerFeature 6 | import io.ktor.start.project.ApplicationKt 7 | import io.ktor.start.project.addApplicationClasses 8 | import io.ktor.start.project.addFeatureInstall 9 | import io.ktor.start.project.addImport 10 | import io.ktor.start.util.BlockBuilder 11 | 12 | object ThymeleafFeature : ServerFeature(ApplicationKt, RoutingFeature) { 13 | override val group: String = "Templating" 14 | override val artifacts = listOf("io.ktor:ktor-thymeleaf:\$ktor_version") 15 | override val id = "thymeleaf" 16 | override val title = "Thymeleaf" 17 | override val description = "Serve HTML content using Thymeleaf template engine" 18 | override val documentation = "https://ktor.io/docs/thymeleaf.html" 19 | 20 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 21 | addImport("io.ktor.thymeleaf.Thymeleaf") 22 | addImport("io.ktor.thymeleaf.ThymeleafContent") 23 | addImport("org.thymeleaf.templateresolver.ClassLoaderTemplateResolver") 24 | addApplicationClasses { 25 | +"data class ThymeleafUser(val id: Int, val name: String)" 26 | } 27 | addFeatureInstall { 28 | "install(Thymeleaf)" { 29 | +"setTemplateResolver(ClassLoaderTemplateResolver().apply {" 30 | indent { 31 | +"prefix = \"templates/thymeleaf/\"" 32 | +"suffix = \".html\"" 33 | +"characterEncoding = \"utf-8\"" 34 | } 35 | +"})" 36 | } 37 | } 38 | addRoute { 39 | "get(\"/html-thymeleaf\")" { 40 | +"call.respond(ThymeleafContent(\"index\", mapOf(\"user\" to ThymeleafUser(1, \"user1\"))))" 41 | } 42 | } 43 | fileText("resources/templates/thymeleaf/index.html") { 44 | +"" 45 | +"" 46 | +"" 47 | +"" 48 | +"Title" 49 | +"" 50 | +"" 51 | +"" 52 | +"" 53 | +"" 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/VelocityFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | 23 | object VelocityFeature : ServerFeature(ApplicationKt) { 24 | override val group: String = "Templating" 25 | override val artifacts = listOf("io.ktor:ktor-velocity:\$ktor_version") 26 | override val id = "velocity" 27 | override val title = "Velocity" 28 | override val description = "Serve HTML content using Apache's Velocity template engine" 29 | override val documentation = "https://ktor.io/docs/velocity.html" 30 | } 31 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/WebjarsFeature.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.features.server 2 | 3 | import io.ktor.start.* 4 | import io.ktor.start.project.* 5 | import io.ktor.start.util.* 6 | 7 | object WebjarsFeature : ServerFeature(ApplicationKt, RoutingFeature) { 8 | override val since = Versions.V094 9 | override val artifacts = listOf( 10 | "io.ktor:ktor-webjars:\$ktor_version", 11 | "org.webjars:jquery:3.2.1" // sample 12 | ) 13 | override val id = "webjars" 14 | override val title = "Webjars" 15 | override val description = "Allows you to package your assets such as javascript libraries and css as part of your uber-jar." 16 | override val documentation = "https://ktor.io/docs/webjars.html" 17 | 18 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 19 | addImport("io.ktor.webjars.*") 20 | addImport("java.time.*") 21 | 22 | addFeatureInstall { 23 | +"install(Webjars)" { 24 | +"path = \"/webjars\" //defaults to /webjars" 25 | +"zone = ZoneId.systemDefault() //defaults to ZoneId.systemDefault()" 26 | } 27 | } 28 | addRoute { 29 | +"get(\"/webjars\")" { 30 | +"call.respondText(\"\", ContentType.Text.Html)" 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/server/WebsocketsFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.server 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object WebsocketsFeature : ServerFeature(ApplicationKt, RoutingFeature) { 25 | override val artifacts = listOf("io.ktor:ktor-websockets:\$ktor_version") 26 | override val id = "ktor-websockets" 27 | override val title = "WebSockets" 28 | override val description = "Adds WebSockets support for bidirectional communication with the client" 29 | override val documentation = "https://ktor.io/docs/clients-websockets.html" 30 | 31 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 32 | addImport("io.ktor.websocket.*") 33 | addImport("io.ktor.http.cio.websocket.*") 34 | addImport("java.time.*") 35 | 36 | addFeatureInstall { 37 | +"install(io.ktor.websocket.WebSockets)" { 38 | +"pingPeriod = Duration.ofSeconds(15)" 39 | +"timeout = Duration.ofSeconds(15)" 40 | +"maxFrameSize = Long.MAX_VALUE" 41 | +"masking = false" 42 | } 43 | } 44 | 45 | addRoute { 46 | +"webSocket(\"/myws/echo\")" { 47 | +"send(Frame.Text(\"Hi from server\"))" 48 | +"while (true)" { 49 | +"val frame = incoming.receive()" 50 | +"if (frame is Frame.Text)" { 51 | +"send(Frame.Text(\"Client said: \" + frame.readText()))" 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/features/sockets/RawSocketsTlsFeature.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.features.sockets 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.project.* 22 | import io.ktor.start.util.* 23 | 24 | object RawSocketsTlsFeature : ServerFeature(ApplicationKt, RawSocketsFeature) { 25 | override val group = "Sockets" 26 | 27 | override val id = "ktor-network-tls" 28 | override val title = "Raw Secure SSL/TLS Sockets" 29 | override val description = 30 | "Adds Raw Socket support for listening and connecting to tcp and udp sockets with secure sockets" 31 | override val documentation = "https://ktor.io/docs/servers-raw-sockets.html#secure" 32 | 33 | override fun BlockBuilder.renderFeature(info: BuildInfo) { 34 | addImport("io.ktor.network.tls.*") 35 | addImport("io.ktor.utils.io.core.*") 36 | addImport("kotlinx.coroutines.*") 37 | 38 | //replace(RawSocketsFeature.SERVER_SOCKET) { 39 | // +"val serverSocket = aSocket(selectorManager).tcp().bind(port = DefaultPort).tls()" 40 | //} 41 | //replace(RawSocketsFeature.CLIENT_SOCKET) { 42 | // +"val socket = aSocket(selectorManager).tcp().connect(\"127.0.0.1\", port = DefaultPort).tls()" 43 | //} 44 | 45 | if (info.ktorVersion >= Versions.V130) { 46 | addImport("io.ktor.utils.io.*") 47 | } else { 48 | addImport("kotlinx.io.core.*") 49 | } 50 | 51 | fileText("src/TlsRawSocket.kt") { 52 | +"package ${info.artifactGroup}" 53 | putImports(applicationKtImports) 54 | SEPARATOR { 55 | +"object TlsRawSocket" { 56 | +"@JvmStatic" 57 | +"fun main(args: Array)" { 58 | +"runBlocking" { 59 | +"val selectorManager = ActorSelectorManager(Dispatchers.IO)" 60 | +"val socket = aSocket(selectorManager).tcp().connect(\"www.google.com\", port = 443).tls(coroutineContext = coroutineContext)" 61 | +"val write = socket.openWriteChannel()" 62 | +"val EOL = \"\\r\\n\"" 63 | +"write.writeStringUtf8(\"GET / HTTP/1.1\${EOL}Host: www.google.com\${EOL}Connection: close\${EOL}\${EOL}\")" 64 | +"write.flush()" 65 | +"println(socket.openReadChannel().readRemaining().readBytes().toString(Charsets.UTF_8))" 66 | } 67 | } 68 | } 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/http/HttpStatusCodes.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.http 2 | 3 | data class FreeHttpStatusCode(val code: Int, val description: String) 4 | 5 | enum class HttpStatusCode(val code: Int, val description: String) { 6 | Continue(100, "Continue"), 7 | SwitchingProtocols(101, "Switching Protocols"), 8 | Processing(102, "Processing"), 9 | 10 | OK(200, "OK"), 11 | Created(201, "Created"), 12 | Accepted(202, "Accepted"), 13 | NonAuthoritativeInformation(203, "Non-Authoritative Information"), 14 | NoContent(204, "No Content"), 15 | ResetContent(205, "Reset Content"), 16 | PartialContent(206, "Partial Content"), 17 | 18 | MultipleChoices(300, "Multiple Choices"), 19 | MovedPermanently(301, "Moved Permanently"), 20 | Found(302, "Found"), 21 | SeeOther(303, "See Other"), 22 | NotModified(304, "Not Modified"), 23 | UseProxy(305, "Use Proxy"), 24 | SwitchProxy(306, "Switch Proxy"), 25 | TemporaryRedirect(307, "Temporary Redirect"), 26 | PermanentRedirect(308, "Permanent Redirect"), 27 | 28 | BadRequest(400, "Bad Request"), 29 | Unauthorized(401, "Unauthorized"), 30 | PaymentRequired(402, "Payment Required"), 31 | Forbidden(403, "Forbidden"), 32 | NotFound(404, "Not Found"), 33 | MethodNotAllowed(405, "Method Not Allowed"), 34 | NotAcceptable(406, "Not Acceptable"), 35 | ProxyAuthenticationRequired(407, "Proxy Authentication Required"), 36 | RequestTimeout(408, "Request Timeout"), 37 | Conflict(409, "Conflict"), 38 | Gone(410, "Gone"), 39 | LengthRequired(411, "Length Required"), 40 | PreconditionFailed(412, "Precondition Failed"), 41 | PayloadTooLarge(413, "Payload Too Large"), 42 | RequestURITooLong(414, "Request-URI Too Long"), 43 | 44 | UnsupportedMediaType(415, "Unsupported Media Type"), 45 | RequestedRangeNotSatisfiable(416, "Requested Range Not Satisfiable"), 46 | ExceptionFailed(417, "Exception Failed"), 47 | UpgradeRequired(426, "Upgrade Required"), 48 | TooManyRequests(429, "Too Many Requests"), 49 | RequestHeaderFieldTooLarge(431, "Request Header Fields Too Large"), 50 | 51 | InternalServerError(500, "Internal Server Error"), 52 | NotImplemented(501, "Not Implemented"), 53 | BadGateway(502, "Bad Gateway"), 54 | ServiceUnavailable(503, "Service Unavailable"), 55 | GatewayTimeout(504, "Gateway Timeout"), 56 | VersionNotSupported(505, "HTTP Version Not Supported"), 57 | VariantAlsoNegotiates(506, "Variant Also Negotiates"); 58 | 59 | val free = FreeHttpStatusCode(code, description) 60 | 61 | companion object { 62 | val byCode = values().associateBy { it.code } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/project/ApplicationConf.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.project 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.util.* 22 | 23 | object ApplicationConf : BuildInfoBlock() { 24 | val KTOR = newSlot("KTOR") 25 | val DEPLOYMENT = newSlot("DEPLOYMENT") 26 | val TOP = newSlot("CONF") 27 | 28 | override fun BlockBuilder.render(info: BuildInfo) { 29 | fileText("resources/application.conf") { 30 | "ktor" { 31 | "deployment" { 32 | +"port = 8080" 33 | +"port = \${?PORT}" 34 | block(DEPLOYMENT) 35 | } 36 | 37 | "application" { 38 | +"modules = [ ${info.artifactGroup}.ApplicationKt.module ]" 39 | } 40 | 41 | block(KTOR) 42 | } 43 | block(TOP) 44 | } 45 | } 46 | } 47 | 48 | fun BlockBuilder.addHoconDeployment(block: Indenter.() -> Unit) { 49 | appendSeparated(ApplicationConf.DEPLOYMENT) { 50 | block() 51 | } 52 | } 53 | 54 | fun BlockBuilder.addHoconKtor(block: Indenter.() -> Unit) { 55 | appendSeparated(ApplicationConf.KTOR) { 56 | block() 57 | } 58 | } 59 | 60 | fun BlockBuilder.addHoconTop(block: Indenter.() -> Unit) { 61 | appendSeparated(ApplicationConf.TOP) { 62 | block() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/project/ApplicationTestKt.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.project 2 | 3 | import io.ktor.start.* 4 | import io.ktor.start.util.* 5 | 6 | object ApplicationTestKt : BuildInfoBlock(ApplicationKt) { 7 | val TESTS = newSlot("TESTS") 8 | 9 | override fun BlockBuilder.render(info: BuildInfo) { 10 | addTestImport("kotlin.test.*") 11 | addTestImport("io.ktor.server.testing.*") 12 | 13 | fileText("test/ApplicationTest.kt") { 14 | +"package ${info.artifactGroup}" 15 | SEPARATOR { 16 | linedeferred { 17 | for (import in applicationKtImports + applicationTestKtImports) { 18 | +"import $import" 19 | } 20 | } 21 | } 22 | SEPARATOR { 23 | "class ApplicationTest" { 24 | block(TESTS) 25 | } 26 | } 27 | } 28 | } 29 | } 30 | 31 | val BlockBuilder.applicationTestKtImports: LinkedHashSet by Extra.PropertyThis { LinkedHashSet() } 32 | 33 | fun BlockBuilder.addTestImport(import: String) { 34 | applicationTestKtImports += import 35 | } 36 | 37 | fun BlockBuilder.addTestMethod(name: String, block: Indenter.() -> Unit) { 38 | appendSeparated(ApplicationTestKt.TESTS) { 39 | +"@Test" 40 | "fun $name()" { 41 | block() 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/project/BuildFiles.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.project 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.util.* 22 | 23 | object BuildFiles : BuildInfoBlock() { 24 | override fun BlockBuilder.render(info: BuildInfo) { 25 | when (info.projectType) { 26 | ProjectType.Gradle -> BuildFilesGradle(kotlin = false).apply { render(info) } 27 | ProjectType.GradleKotlinDsl -> BuildFilesGradle(kotlin = true).apply { render(info) } 28 | ProjectType.Maven -> BuildFilesMaven.apply { render(info) } 29 | } 30 | 31 | addMavenRepository(Repos.local) 32 | addMavenRepository(Repos.mavenCentral) 33 | addCompileDependency(MvnArtifact("org.jetbrains.kotlin:kotlin-stdlib-jdk8:\$kotlin_version")) 34 | 35 | addCompileDependency(MvnArtifact("io.ktor:ktor-server-${info.ktorEngine.id}:\$ktor_version")) 36 | addCompileDependency(MvnArtifact("ch.qos.logback:logback-classic:\$logback_version")) 37 | addTestDependency(MvnArtifact("io.ktor:ktor-server-tests:\$ktor_version")) 38 | } 39 | } 40 | 41 | internal fun BlockBuilder.getAllReposToInclude(info: BuildInfo) = this.reposToInclude + info.ktorVersion.extraRepos.toSet() 42 | 43 | internal val BlockBuilder.reposToInclude: LinkedHashSet by Extra.PropertyThis { LinkedHashSet() } 44 | internal val BlockBuilder.compileDependencies: LinkedHashSet by Extra.PropertyThis { LinkedHashSet() } 45 | internal val BlockBuilder.testDependencies: LinkedHashSet by Extra.PropertyThis { LinkedHashSet() } 46 | 47 | fun BlockBuilder.addMavenRepository(repository: String) { 48 | reposToInclude += repository 49 | } 50 | 51 | fun BlockBuilder.addMavenRepository(repos: List) { 52 | reposToInclude += repos 53 | } 54 | 55 | fun BlockBuilder.addCompileDependency(dependency: MvnArtifact) { 56 | compileDependencies += dependency 57 | } 58 | 59 | fun BlockBuilder.addTestDependency(dependency: MvnArtifact) { 60 | testDependencies += dependency 61 | } 62 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/project/GitIgnoreFile.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.project 2 | 3 | import io.ktor.start.* 4 | import io.ktor.start.util.* 5 | 6 | object GitIgnoreFile : BuildInfoBlock() { 7 | override fun BlockBuilder.render(info: BuildInfo) { 8 | fileText(".gitignore") { 9 | +"/.gradle" 10 | +"/.idea" 11 | +"/out" 12 | +"/build" 13 | +"*.iml" 14 | +"*.ipr" 15 | +"*.iws" 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/project/LogBackXml.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.project 2 | 3 | import io.ktor.start.* 4 | import io.ktor.start.util.* 5 | 6 | internal object LogBackXml : BuildInfoBlock() { 7 | override fun BlockBuilder.render(info: BuildInfo) { 8 | fileText("resources/logback.xml") { 9 | xml { 10 | "" { 11 | "" { 12 | "" { 13 | +"%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" 14 | } 15 | } 16 | "" { 17 | +"" 18 | } 19 | +"" 20 | +"" 21 | } 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/project/Properties.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.project 2 | 3 | import io.ktor.start.* 4 | 5 | object Properties { 6 | fun getProperties(info: BuildInfo): Map { 7 | return HashMap().apply { 8 | this["kotlin.code.style"] = "official" 9 | this["kotlin_version"] = info.kotlinVersion.toString() 10 | this["ktor_version"] = info.ktorVersion.toString() 11 | this["logback_version"] = "1.2.1" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/swagger/ContentType.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.swagger 2 | 3 | data class ContentType(val str: String) { 4 | companion object { 5 | val ApplicationJson = ContentType("application/json") 6 | val ApplicationXWwwFormUrlencoded = ContentType("application/x-www-form-urlencoded") 7 | } 8 | 9 | override fun toString(): String = str 10 | } -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/swagger/SwaggerGenerator.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.swagger 2 | 3 | import io.ktor.start.BuildInfo 4 | import io.ktor.start.features.server.addCustomStatusPage 5 | import io.ktor.start.project.* 6 | import io.ktor.start.util.* 7 | 8 | class SwaggerGenerator( 9 | val model: SwaggerModel, 10 | val generationKind: Kind 11 | ) : Block(*model.buildDepsFromModel().toTypedArray()) { 12 | enum class Kind { INTERFACE, RAW } 13 | 14 | override fun BlockBuilder.render(info: BuildInfo) { 15 | addImport("kotlin.reflect.*") // For KClass 16 | addImport("java.util.*") // For Date 17 | 18 | addCustomStatusPage { 19 | "exception"(suffix = " cause ->") { 20 | +"call.respond(cause.code, cause.description)" 21 | } 22 | } 23 | 24 | val arguments = SwaggerArguments(buildList { 25 | if (model.securityDefinitions.isNotEmpty()) { 26 | SwaggerGeneratorCommon.apply { 27 | addAll(generateJwt(model)) 28 | } 29 | } 30 | }) 31 | 32 | when (generationKind) { 33 | Kind.RAW -> { 34 | SwaggerGeneratorRaw.apply { 35 | registerRoutes(info, model, arguments) 36 | fileSwaggerDtos("src/${model.info.className}.kt", info, model) 37 | fileSwaggerBackendHandler("src/${model.info.classNameServer}.kt", info, model, arguments) 38 | fileSwaggerFrontendHandler("src/${model.info.classNameClient}.kt", info, model) 39 | } 40 | } 41 | Kind.INTERFACE -> { 42 | SwaggerGeneratorInterface.apply { 43 | registerRoutes(info, model, arguments) 44 | fileSwaggerCommonInterface("src/${model.info.className}.kt", info, model) 45 | fileSwaggerBackendHandler("src/${model.info.classNameServer}.kt", info, model, arguments) 46 | fileSwaggerFrontendHandler("src/${model.info.classNameClient}.kt", info, model) 47 | } 48 | } 49 | } 50 | 51 | SwaggerGeneratorCommon.apply { 52 | fileSwaggerBackendTests("test/${model.info.classNameServerTest}.kt", info, model) 53 | filesHttpApi( 54 | "api.http", 55 | "http-client.env.json", 56 | if (model.filename.endsWith(".json")) "api.json" else "api.yaml", 57 | info, 58 | model 59 | ) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/DateTime.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.util 19 | 20 | expect class DateTime 21 | 22 | expect fun NewDateTime(): DateTime 23 | expect fun NewDateTime(time: Long): DateTime 24 | expect val DateTime.time: Long 25 | 26 | expect val DateTime.fullYear: Int 27 | expect val DateTime.month: Int 28 | expect val DateTime.date: Int 29 | expect val DateTime.hours: Int 30 | expect val DateTime.minutes: Int 31 | expect val DateTime.seconds: Int 32 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/FileMode.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.util 2 | 3 | data class FileMode(val mode: Int) { 4 | constructor(octalMode: String) : this(octalMode.toInt(8)) 5 | 6 | val isUserExecutable: Boolean 7 | get() = ((mode ushr 6) and 1) != 0 8 | } 9 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/FormUrlEncoded.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.util 2 | 3 | fun String.encodeURIComponent(): String { 4 | return buildString { 5 | for (v in this@encodeURIComponent) { 6 | when (v) { 7 | '&', ' ', '=', '/' -> { 8 | append('%') 9 | append(v.toInt().toString(16).padStart(2, '0')) 10 | } 11 | else -> append(v) 12 | } 13 | } 14 | } 15 | } 16 | 17 | //fun List>.formUrlEncode(): String = 18 | // joinToString("&") { it.first.encodeURIComponent() + "=" + it.second.encodeURIComponent() } 19 | 20 | 21 | // @TODO: Encode 22 | fun String.formUrlDecode(): Map> = this.split('&') 23 | .map { val (key, value) = it.split('=', limit = 2) + listOf(""); key to value } 24 | .groupBy { it.first } 25 | .map { it.key to it.value.map { it.second } } 26 | .toMap() 27 | 28 | fun List>.formUrlEncode(): String = 29 | this.joinToString("&") { 30 | when { 31 | it.second.isNotEmpty() -> "${it.first.encodeURIComponent()}=${it.second.encodeURIComponent()}" 32 | else -> it.first.encodeURIComponent() 33 | } 34 | } 35 | 36 | fun Map>.formUrlEncode(): String = 37 | entries.flatMap { entry -> entry.value.map { entry.key to it } }.formUrlEncode() 38 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/MetaListIterator.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.util 2 | 3 | class MetaListIterable(val items: List) : Iterable, T>> { 4 | override fun iterator() = 5 | items.withIndex().map { IteratorStepInfo(it.index, items.size, it.value) to it.value }.iterator() 6 | } 7 | 8 | val Iterable.metaIter get() = MetaListIterable(this.toList()) 9 | 10 | data class IteratorStepInfo( 11 | val index0: Int, 12 | val length: Int, 13 | val item: T 14 | ) { 15 | val isFirst get() = index0 == 0 16 | val isLast get() = index0 >= length - 1 17 | } 18 | 19 | val IteratorStepInfo.optComma get() = if (this.isLast) "" else "," 20 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/Stack.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.util 2 | 3 | class Stack { 4 | private val items = arrayListOf() 5 | 6 | val size get() = items.size 7 | fun pop(): T = items.removeAt(items.size - 1) 8 | fun peek(): T = items[items.size - 1] 9 | fun push(value: T) = run { items.add(value) } 10 | } -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/StrReader.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.util 2 | 3 | import kotlin.math.* 4 | 5 | 6 | class StrReader(val str: String, val file: String = "file", var pos: Int = 0) { 7 | val length: Int = this.str.length 8 | val hasMore: Boolean get() = (this.pos < this.str.length) 9 | 10 | inline fun slice(action: () -> Unit): String? { 11 | val start = this.pos 12 | action() 13 | val end = this.pos 14 | return if (end > start) this.slice(start, end) else null 15 | } 16 | 17 | fun slice(start: Int, end: Int): String = this.str.substring(start, end) 18 | fun peek(count: Int): String = substr(this.pos, count) 19 | fun peekChar(): Char = if (hasMore) this.str[this.pos] else '\u0000' 20 | fun read(count: Int): String = this.peek(count).apply { skip(count) } 21 | inline fun skipWhile(filter: (Char) -> Boolean) = run { while (hasMore && filter(this.peekChar())) this.readChar() } 22 | 23 | inline fun readWhile(filter: (Char) -> Boolean) = this.slice { skipWhile(filter) } ?: "" 24 | fun unread(count: Int = 1) = this.apply { this.pos -= count; } 25 | fun readChar(): Char = if (hasMore) this.str[this.pos++] else '\u0000' 26 | fun read(): Char = if (hasMore) this.str[this.pos++] else '\u0000' 27 | 28 | 29 | fun readExpect(expected: String): String { 30 | val readed = this.read(expected.length) 31 | if (readed != expected) throw IllegalArgumentException("Expected '$expected' but found '$readed' at $pos") 32 | return readed 33 | } 34 | 35 | fun expect(expected: Char) = readExpect("$expected") 36 | fun skip(count: Int = 1) = this.apply { this.pos += count; } 37 | private fun substr(pos: Int, length: Int): String { 38 | return this.str.substring(min(pos, this.length), min(pos + length, this.length)) 39 | } 40 | 41 | fun skipSpaces() = this.apply { this.skipWhile { it.isWhitespace() } } 42 | 43 | fun tryRead(str: String): Boolean { 44 | if (peek(str.length) == str) { 45 | skip(str.length) 46 | return true 47 | } 48 | return false 49 | } 50 | } 51 | 52 | fun StrReader.readStringLit(reportErrors: Boolean = true): String { 53 | val out = StringBuilder() 54 | val quotec = read() 55 | when (quotec) { 56 | '"', '\'' -> Unit 57 | else -> error("Invalid string literal") 58 | } 59 | var closed = false 60 | while (hasMore) { 61 | val c = read() 62 | if (c == '\\') { 63 | val cc = read() 64 | out.append( 65 | when (cc) { 66 | '\\' -> '\\'; '/' -> '/'; '\'' -> '\''; '"' -> '"' 67 | 'b' -> '\b'; 'f' -> '\u000c'; 'n' -> '\n'; 'r' -> '\r'; 't' -> '\t' 68 | 'u' -> read(4).toInt(0x10).toChar() 69 | else -> Json.invalidJson("Invalid char '$cc'") 70 | } 71 | ) 72 | } else if (c == quotec) { 73 | closed = true 74 | break 75 | } else { 76 | out.append(c) 77 | } 78 | } 79 | if (!closed && reportErrors) { 80 | throw RuntimeException("String literal not closed! '${this.str}'") 81 | } 82 | return out.toString() 83 | } 84 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/baos.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.util 19 | 20 | import kotlin.math.* 21 | 22 | class ByteArrayOutputStream { 23 | private var pos = 0 24 | private var data = ByteArray(1024) 25 | val size get() = pos 26 | 27 | private fun ensure(count: Int): ByteArrayOutputStream { 28 | if (pos + count > data.size) { 29 | data = data.copyOf(max(pos + count, data.size * 2)) 30 | } 31 | return this 32 | } 33 | 34 | private inline fun byte(v: Number) = run { data[pos++] = v.toByte() } 35 | fun u8(v: Int) = ensure(1).apply { byte(v) } 36 | fun u16_le(v: Int) = ensure(2).apply { byte(v shr 0); byte(v shr 8) } 37 | fun u32_le(v: Int) = ensure(4).apply { byte(v shr 0); byte(v shr 8); byte(v shr 16); byte(v shr 24) } 38 | fun bytes(data: ByteArray) = ensure(data.size).apply { for (n in 0 until data.size) byte(data[n]) } 39 | 40 | fun toByteArray(): ByteArray { 41 | return data.copyOf(pos) 42 | } 43 | 44 | inline fun build(builder: ByteArrayOutputStream.() -> Unit): ByteArray { 45 | builder(this) 46 | return toByteArray() 47 | } 48 | } 49 | 50 | inline fun buildByteArray(builder: ByteArrayOutputStream.() -> Unit): ByteArray = ByteArrayOutputStream().build(builder) 51 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/buildList.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.util 2 | 3 | inline fun buildList(callback: MutableList.() -> Unit): List = ArrayList().apply(callback) -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/crc32.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.util 19 | 20 | object CRC32 { 21 | val table = IntArray(256).apply { 22 | val POLY: Int = 0xEDB88320.toInt() 23 | for (n in 0 until 256) { 24 | var c = n 25 | for (k in 0 until 8) { 26 | c = if ((c and 1) != 0) { 27 | POLY xor (c ushr 1) 28 | } else { 29 | c ushr 1 30 | } 31 | } 32 | this[n] = c 33 | } 34 | } 35 | 36 | fun update(initial: Int, u: ByteArray): Int { 37 | var crc = initial xor (-1) 38 | for (i in 0 until u.size) { 39 | crc = table[(crc xor u[i].toInt()) and 0xFF] xor (crc ushr 8) 40 | } 41 | return crc xor (-1) 42 | } 43 | } 44 | 45 | fun ByteArray.crc32(): Int = CRC32.update(0, this) 46 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/ext.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.util 19 | 20 | val String.octal get() = toInt(8) -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/extra.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.util 19 | 20 | import kotlin.reflect.* 21 | 22 | 23 | interface Extra { 24 | var extra: LinkedHashMap? 25 | 26 | class Mixin(override var extra: LinkedHashMap? = null) : Extra 27 | 28 | @Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") 29 | 30 | class Property(val name: String? = null, val defaultGen: () -> T) { 31 | inline operator fun getValue(thisRef: Extra, property: KProperty<*>): T { 32 | val res = (thisRef.extra?.get(name ?: property.name) as T?) 33 | if (res == null) { 34 | val r = defaultGen() 35 | setValue(thisRef, property, r) 36 | return r 37 | } 38 | return res 39 | } 40 | 41 | inline operator fun setValue(thisRef: Extra, property: KProperty<*>, value: T): Unit = run { 42 | //beforeSet(value) 43 | thisRef.setExtra(name ?: property.name, value as Any?) 44 | //afterSet(value) 45 | } 46 | } 47 | 48 | class PropertyThis(val name: String? = null, val defaultGen: T2.() -> T) { 49 | inline operator fun getValue(thisRef: T2, property: KProperty<*>): T { 50 | val res = (thisRef.extra?.get(name ?: property.name) as T?) 51 | if (res == null) { 52 | val r = defaultGen(thisRef) 53 | setValue(thisRef, property, r) 54 | return r 55 | } 56 | return res 57 | } 58 | 59 | inline operator fun setValue(thisRef: T2, property: KProperty<*>, value: T): Unit = run { 60 | //beforeSet(value) 61 | if (thisRef.extra == null) thisRef.extra = LinkedHashMap() 62 | thisRef.extra?.set(name ?: property.name, value as Any?) 63 | //afterSet(value) 64 | } 65 | } 66 | } 67 | 68 | fun Extra.getExtraTyped(name: String): T? = extra?.get(name) as T? 69 | fun Extra.getExtra(name: String): Any? = extra?.get(name) 70 | fun Extra.setExtra(name: String, value: Any?): Unit { 71 | if (extra == null) extra = LinkedHashMap() 72 | extra?.set(name, value) 73 | } 74 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/hex.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.util 2 | 3 | object Hex { 4 | private val DIGITS = "0123456789ABCDEF" 5 | private val DIGITS_UPPER = DIGITS.toUpperCase() 6 | private val DIGITS_LOWER = DIGITS.toLowerCase() 7 | 8 | fun decodeChar(c: Char): Int = when (c) { 9 | in '0'..'9' -> c - '0' 10 | in 'a'..'f' -> c - 'a' + 10 11 | in 'A'..'F' -> c - 'A' + 10 12 | else -> -1 13 | } 14 | 15 | fun encodeCharLower(v: Int): Char = DIGITS_LOWER[v] 16 | fun encodeCharUpper(v: Int): Char = DIGITS_UPPER[v] 17 | 18 | fun isHexDigit(c: Char): Boolean = decodeChar(c) >= 0 19 | 20 | fun decode(str: String): ByteArray { 21 | val out = ByteArray((str.length + 1) / 2) 22 | var opos = 0 23 | var nibbles = 0 24 | var value = 0 25 | for (c in str) { 26 | val vv = decodeChar(c) 27 | if (vv >= 0) { 28 | value = (value shl 4) or vv 29 | nibbles++ 30 | } 31 | if (nibbles == 2) { 32 | out[opos++] = value.toByte() 33 | nibbles = 0 34 | value = 0 35 | } 36 | } 37 | return if (opos != out.size) out.copyOf(opos) else out 38 | } 39 | 40 | fun encodeLower(src: ByteArray): String = encodeBase(src, DIGITS_LOWER) 41 | fun encodeUpper(src: ByteArray): String = encodeBase(src, DIGITS_UPPER) 42 | 43 | private fun encodeBase(data: ByteArray, digits: String = DIGITS): String { 44 | val out = StringBuilder(data.size * 2) 45 | for (n in data.indices) { 46 | val v = data[n].toInt() and 0xFF 47 | out.append(digits[(v ushr 4) and 0xF]) 48 | out.append(digits[(v ushr 0) and 0xF]) 49 | } 50 | return out.toString() 51 | } 52 | } 53 | 54 | val List.unhexIgnoreSpaces get() = joinToString("").unhexIgnoreSpaces 55 | val String.unhexIgnoreSpaces get() = this.replace(" ", "").unhex 56 | val String.unhex get() = Hex.decode(this) 57 | val ByteArray.hex get() = Hex.encodeLower(this) 58 | 59 | val Int.hex: String get() = "0x$shex" 60 | val Int.shex: String 61 | get() { 62 | var out = "" 63 | for (n in 0 until 8) { 64 | val v = (this ushr ((7 - n) * 4)) and 0xF 65 | out += Hex.encodeCharUpper(v) 66 | } 67 | return out 68 | } 69 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/id.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.util 2 | 3 | object ID { 4 | fun normalizeMethodName(str: List): String = normalizeMethodName(str.joinToString("/")) 5 | fun normalizeClassName(str: List): String = normalizeClassName(str.joinToString("/")) 6 | 7 | fun normalizeMethodName(str: String): String = normalize(str).decapitalize() 8 | fun normalizeClassName(str: String): String = normalize(str).capitalize() 9 | fun normalize(str: String): String = Regex("\\w+").findAll(str).joinToString("") { it.value.capitalize() } 10 | } -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/mvndependency.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.util 19 | 20 | data class MvnArtifact(val dependency: String) { 21 | val parts = dependency.split(":") 22 | val group get() = parts.getOrNull(0) 23 | val name get() = parts.getOrNull(1) 24 | val version get() = parts.getOrNull(2) 25 | } 26 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/quote.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.util 2 | 3 | fun String.stripLineBreaks() = this.split('\n').joinToString(" ") 4 | 5 | fun String.escape(): String { 6 | val out = StringBuilder() 7 | for (n in 0 until this.length) { 8 | val c = this[n] 9 | when (c) { 10 | '\\' -> out.append("\\\\") 11 | '"' -> out.append("\\\"") 12 | '\n' -> out.append("\\n") 13 | '\r' -> out.append("\\r") 14 | '\t' -> out.append("\\t") 15 | else -> if (c in ' ' until '\u007f') { 16 | out.append(c) 17 | } else { 18 | out.append("\\u" + c.toInt().toString(16).padStart(4, '0')) 19 | } 20 | } 21 | } 22 | return out.toString() 23 | } 24 | 25 | fun String.unescape(): String { 26 | val out = StringBuilder() 27 | var n = 0 28 | while (n < this.length) { 29 | val c = this[n++] 30 | when (c) { 31 | '\\' -> { 32 | val c2 = this[n++] 33 | when (c2) { 34 | '\\' -> out.append('\\') 35 | '"' -> out.append('\"') 36 | 'n' -> out.append('\n') 37 | 'r' -> out.append('\r') 38 | 't' -> out.append('\t') 39 | 'u' -> { 40 | val chars = this.substring(n, n + 4) 41 | n += 4 42 | out.append(chars.toInt(16).toChar()) 43 | } 44 | else -> { 45 | out.append("\\$c2") 46 | } 47 | } 48 | } 49 | else -> out.append(c) 50 | } 51 | } 52 | return out.toString() 53 | } 54 | 55 | fun String?.quote(): String = if (this != null) "\"${this.escape()}\"" else "null" 56 | 57 | fun String.isQuoted(): Boolean = this.startsWith('"') && this.endsWith('"') 58 | 59 | fun String.unquote(): String = if (isQuoted()) { 60 | this.substring(1, this.length - 1).unescape() 61 | } else { 62 | this 63 | } 64 | 65 | val Any?.kquoteLit: String get() { 66 | return when (this) { 67 | null -> "null" 68 | is Number -> "$this" 69 | is String -> this.quote() 70 | is Pair<*, *> -> this.first.kquoteLit + " to " + this.second.kquoteLit 71 | is List<*> -> "listOf(" + this.joinToString(", ") { it.kquoteLit } + ")" 72 | is Set<*> -> "setOf(" + this.joinToString(", ") { it.kquoteLit } + ")" 73 | is Map<*, *> -> 74 | "mapOf(" + this.entries.joinToString(", ") { it.key.kquoteLit + " to " + it.value.kquoteLit } + ")" 75 | is Regex -> "Regex(" + this.pattern.quote() + ")" 76 | else -> "" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/kotlin/io/ktor/start/util/semver.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.util 19 | 20 | class SemVer(val version: String) : Comparable { 21 | private val parts1 = version.split('-', limit = 2) 22 | private val parts2 = parts1.first().split('.') 23 | 24 | val major = parts2.getOrNull(0)?.toIntOrNull() ?: 0 25 | val minor = parts2.getOrNull(1)?.toIntOrNull() ?: 0 26 | val patch = parts2.getOrNull(2)?.toIntOrNull() ?: 0 27 | val info = parts1.getOrNull(1) ?: "" 28 | 29 | override fun compareTo(other: SemVer): Int { 30 | return this.major.compareTo(other.major).takeIf { it != 0 } 31 | ?: this.minor.compareTo(other.minor).takeIf { it != 0 } 32 | ?: this.patch.compareTo(other.patch).takeIf { it != 0 } 33 | ?: this.info.compareTo(other.info).takeIf { it != 0 } 34 | ?: 0 35 | } 36 | 37 | override fun toString(): String = version 38 | } -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/resources/gradle/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktorio/ktor-init-tools/790e8b389ae9aed630eea72a73181a3a5b725977/ktor-generator/src/commonMain/resources/gradle/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/resources/gradle/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/resources/gradle/gradle/wrapper/gradle-wrapper.properties.kotlin-dsl: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/resources/gradle/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/resources/maven/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktorio/ktor-init-tools/790e8b389ae9aed630eea72a73181a3a5b725977/ktor-generator/src/commonMain/resources/maven/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/resources/maven/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip -------------------------------------------------------------------------------- /ktor-generator/src/commonMain/resources/static/ktor_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ktor-generator/src/commonTest/kotlin/io/ktor/start/util/MyCommonTest.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.util 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class MyCommonTest { 7 | @Test 8 | fun commonTest() { 9 | assertEquals(1, 1) 10 | } 11 | } -------------------------------------------------------------------------------- /ktor-generator/src/commonTest/kotlin/io/ktor/start/util/SemVerTest.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.util 2 | 3 | import kotlin.test.* 4 | 5 | class SemVerTest { 6 | @Test 7 | fun simple() { 8 | assertEquals( 9 | "0.9.4, 0.9.5, 1.0.0, 1.0.0-alpha-2", 10 | listOf(SemVer("1.0.0"), SemVer("0.9.4"), SemVer("1.0.0-alpha-2"), SemVer("0.9.5")).sorted().joinToString(", ") 11 | ) 12 | 13 | assertEquals(+1, SemVer("1.0.0").compareTo(SemVer("0.9.5"))) 14 | } 15 | } -------------------------------------------------------------------------------- /ktor-generator/src/jsMain/kotlin/io/ktor/start/util/DateTimeJs.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.util 19 | 20 | import kotlin.js.* 21 | 22 | actual typealias DateTime = Date 23 | 24 | actual fun NewDateTime(): DateTime = Date() 25 | actual fun NewDateTime(time: Long): DateTime = Date(time) 26 | 27 | actual val DateTime.time: Long get() = (this as Date).getTime().toLong() 28 | 29 | actual val DateTime.fullYear: Int get() = (this as Date).getFullYear() 30 | actual val DateTime.month: Int get() = (this as Date).getMonth() 31 | actual val DateTime.date: Int get() = (this as Date).getDate() 32 | actual val DateTime.hours: Int get() = (this as Date).getHours() 33 | actual val DateTime.minutes: Int get() = (this as Date).getMinutes() 34 | actual val DateTime.seconds: Int get() = (this as Date).getSeconds() 35 | -------------------------------------------------------------------------------- /ktor-generator/src/jsTest/kotlin/io/ktor/start/MyJsTest.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertEquals 5 | 6 | class MyJsTest { 7 | @Test 8 | fun test1() { 9 | assertEquals(1, 1) 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /ktor-generator/src/jvmMain/kotlin/io/ktor/start/util/DateTimeJvm.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.util 19 | 20 | import java.util.* 21 | 22 | actual typealias DateTime = Date 23 | 24 | actual fun NewDateTime(): DateTime = Date() 25 | actual fun NewDateTime(time: Long): DateTime = Date(time) 26 | 27 | actual val DateTime.time: Long get() = (this as Date).time 28 | 29 | actual val DateTime.fullYear: Int get() = (this as Date).year 30 | actual val DateTime.month: Int get() = (this as Date).month 31 | actual val DateTime.date: Int get() = (this as Date).date 32 | actual val DateTime.hours: Int get() = (this as Date).hours 33 | actual val DateTime.minutes: Int get() = (this as Date).minutes 34 | actual val DateTime.seconds: Int get() = (this as Date).seconds 35 | -------------------------------------------------------------------------------- /ktor-generator/src/jvmTest/kotlin/io/ktor/start/TestTools.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start 2 | 3 | fun getResourceBytes(name: String) = 4 | null 5 | ?: SwaggerGenerationTest::class.java.getResourceAsStream(name)?.readBytes() 6 | ?: SwaggerGenerationTest::class.java.getResourceAsStream("/$name")?.readBytes() 7 | ?: ClassLoader.getSystemClassLoader().getResourceAsStream(name)?.readBytes() 8 | ?: ClassLoader.getSystemClassLoader().getResourceAsStream("/$name")?.readBytes() 9 | 10 | fun getResourceString(name: String) = 11 | getResourceBytes(name)?.toString(Charsets.UTF_8) 12 | 13 | -------------------------------------------------------------------------------- /ktor-generator/src/jvmTest/kotlin/io/ktor/start/Tools.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start 2 | 3 | import java.io.File 4 | import java.nio.charset.Charset 5 | 6 | fun File.updateTextIfExists(charset: Charset = Charsets.UTF_8, update: (String) -> String) { 7 | if (exists()) updateText(charset, update) 8 | } 9 | 10 | fun File.updateText(charset: Charset = Charsets.UTF_8, update: (String) -> String) { 11 | val oldContent = this.readText(charset) 12 | val newContent = update(oldContent) 13 | this.writeText(newContent, charset) 14 | } 15 | 16 | operator fun File.get(child: String) = File(this, child) 17 | -------------------------------------------------------------------------------- /ktor-generator/src/jvmTest/kotlin/io/ktor/start/WriteToFolderTest.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start 2 | 3 | import io.ktor.start.util.* 4 | import java.io.* 5 | 6 | fun Map.writeToFolder(folder: File, print: Boolean = false) { 7 | for (fileResult in this.values) { 8 | if (print && fileResult.charset != null) { 9 | println("${fileResult.name}:") 10 | println(fileResult.string) 11 | println() 12 | } 13 | val file = File(folder, fileResult.name) 14 | file.parentFile.mkdirs() 15 | file.writeBytes(fileResult.data) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ktor-generator/src/jvmTest/kotlin/io/ktor/start/swagger/GenerationSpike.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.swagger 2 | 3 | import io.ktor.start.* 4 | import io.ktor.start.util.* 5 | import kotlinx.coroutines.* 6 | import java.io.* 7 | 8 | object GenerationSpike { 9 | @JvmStatic 10 | fun main(args: Array) { 11 | val testProjectRoot = File("/tmp/swagger-gen") 12 | 13 | runBlocking { 14 | val swaggerJson = getResourceString("/swagger.json") 15 | 16 | val model = SwaggerModel.parseJson(swaggerJson) 17 | generate(info.copy( 18 | ktorVersion = Versions.LAST 19 | ), SwaggerGenerator(model, info.swaggerGenKind)).writeToFolder(testProjectRoot) 20 | //println("RESULT: ${result.tasks.joinToString(", ") { it.path }}") 21 | } 22 | } 23 | 24 | val info = BuildInfo( 25 | includeWrapper = false, 26 | projectType = ProjectType.Gradle, 27 | ktorVersion = Versions.LAST, 28 | artifactName = "example1", 29 | artifactGroup = "com.example", 30 | artifactVersion = "0.1.0-SNAPSHOT", 31 | ktorEngine = KtorEngine.Netty, 32 | swaggerGenKind = SwaggerGenerator.Kind.RAW, 33 | fetch = { getResourceBytes(it) } 34 | ) 35 | 36 | fun getResourceBytes(name: String): ByteArray { 37 | //println(File("ktor-generator/src/commonMain/resources/$name").absoluteFile) 38 | return null 39 | // @TODO: Hacks because bad import 40 | ?: File("ktor-generator/src/jvmTest/resources/$name").takeIf { it.exists() }?.readBytes() 41 | ?: File("ktor-generator/src/jvmMain/resources/$name").takeIf { it.exists() }?.readBytes() 42 | ?: File("ktor-generator/src/commonMain/resources/$name").takeIf { it.exists() }?.readBytes() 43 | ?: GenerationSpike::class.java.getResourceAsStream(name)?.readBytes() 44 | ?: GenerationSpike::class.java.getResourceAsStream("/$name")?.readBytes() 45 | ?: ClassLoader.getSystemClassLoader().getResourceAsStream(name)?.readBytes() 46 | ?: ClassLoader.getSystemClassLoader().getResourceAsStream("/$name")?.readBytes() 47 | ?: error("Can't find resources '$name'") 48 | } 49 | 50 | fun getResourceString(name: String) = 51 | getResourceBytes(name)?.toString(Charsets.UTF_8) 52 | 53 | fun Map.writeToFolder(folder: File, print: Boolean = false) { 54 | for (fileResult in this.values) { 55 | if (print && fileResult.charset != null) { 56 | println("${fileResult.name}:") 57 | println(fileResult.string) 58 | println() 59 | } 60 | val file = File(folder, fileResult.name) 61 | file.parentFile.mkdirs() 62 | file.writeBytes(fileResult.data) 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /ktor-generator/src/jvmTest/kotlin/io/ktor/start/swagger/JsonSchemaTest.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.swagger 2 | 3 | import org.junit.Test 4 | import kotlin.test.* 5 | 6 | class JsonSchemaTest { 7 | @Test 8 | fun name() { 9 | assertEquals("it <= 100", test(mapOf("maximum" to 100))) 10 | assertEquals("it < 100", test(mapOf("maximum" to 100, "exclusiveMaximum" to true))) 11 | assertEquals("it in 10 .. 100", test(mapOf("minimum" to 10, "maximum" to 100))) 12 | assertEquals("it in 10 until 100", test(mapOf("minimum" to 10, "maximum" to 100, "exclusiveMaximum" to true))) 13 | assertEquals("it > 10 && it < 100", test(mapOf("minimum" to 10, "exclusiveMinimum" to true, "maximum" to 100, "exclusiveMaximum" to true))) 14 | assertEquals("it > 10 && it <= 100", test(mapOf("minimum" to 10, "exclusiveMinimum" to true, "maximum" to 100, "exclusiveMaximum" to false))) 15 | } 16 | 17 | @Test 18 | fun length() { 19 | assertEquals("it.length >= 4", test(mapOf("minLength" to 4))) 20 | assertEquals("it.length <= 16", test(mapOf("maxLength" to 16))) 21 | assertEquals("it.length in 4..16", test(mapOf("minLength" to 4, "maxLength" to 16))) 22 | } 23 | 24 | private inline fun test(map: Map) = JsonRule.parse(map).toKotlin("it", T::class) 25 | } 26 | -------------------------------------------------------------------------------- /ktor-generator/src/jvmTest/kotlin/io/ktor/start/swagger/ModelTest.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.swagger 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | 6 | class ModelTest { 7 | @Test 8 | fun `support default value for float type`() { 9 | assertEquals(0.0f, SwaggerModel.FloatType.toDefaultUntyped()) 10 | assertEquals(42.0f, SwaggerModel.FloatType.toDefaultUntyped(emptyList(), 42.0)) 11 | } 12 | } -------------------------------------------------------------------------------- /ktor-generator/src/jvmTest/kotlin/io/ktor/start/swagger/YamlSchemaTest.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.swagger 2 | 3 | import io.ktor.start.util.* 4 | import kotlin.test.* 5 | 6 | class YamlSchemaTest { 7 | @Test 8 | fun openAPITest() { 9 | val openAPIStream = YamlSchemaTest::javaClass.javaClass.classLoader 10 | .getResourceAsStream("openapi.yaml")!! 11 | .readBytes() 12 | .toString(UTF8) 13 | 14 | val model = SwaggerModel.parseJsonOrYaml(openAPIStream, filename = "openapi.yaml") 15 | 16 | val windowHierarchyChildDef = model.definitions["WindowHierarchyChild"] 17 | ?: error("WindowHierarchyChild does not exist") 18 | val expectedOptions = listOf("id", "parent", "text", "package", "checkable", "clickable", "index", 19 | "contentDescription", "focusable", "resourceId", "enabled", "password", "longClickable", "longText", 20 | "clazz", "scrollable", "selected", "checked", "focused", "bounds", "children") 21 | 22 | for (requiredOption in expectedOptions) { 23 | assertTrue { windowHierarchyChildDef.props.contains(requiredOption) } 24 | } 25 | 26 | val prop = windowHierarchyChildDef.props["children"] 27 | assertEquals("List", prop!!.type.toString()) 28 | } 29 | } -------------------------------------------------------------------------------- /ktor-generator/src/jvmTest/kotlin/io/ktor/util/IdTest.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.util 2 | 3 | import io.ktor.start.util.* 4 | import org.junit.Test 5 | import kotlin.test.* 6 | 7 | class IdTest { 8 | @Test 9 | fun name() { 10 | assertEquals("helloWorld", ID.normalizeMethodName("hello-world")) 11 | assertEquals("pathToHello", ID.normalizeMethodName("/path/to/{hello}")) 12 | } 13 | } -------------------------------------------------------------------------------- /ktor-generator/src/jvmTest/kotlin/io/ktor/util/IndenterTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.util 19 | 20 | import io.ktor.start.util.* 21 | import org.junit.Test 22 | import kotlin.test.* 23 | 24 | class IndenterTest { 25 | @Test 26 | fun name() { 27 | val indenter = Indenter { 28 | +"HELLO" 29 | +"" 30 | +"WORLD" 31 | } 32 | assertEquals("HELLO\n\nWORLD\n", indenter.toString()) 33 | } 34 | 35 | @Test 36 | fun testEmptyLineOnce1() { 37 | val indenter = Indenter { 38 | +"HELLO" 39 | EMPTY_LINE_ONCE() 40 | +"WORLD" 41 | } 42 | assertEquals("HELLO\n\nWORLD\n", indenter.toString()) 43 | } 44 | 45 | @Test 46 | fun testEmptyLineOnce2() { 47 | val indenter = Indenter { 48 | +"HELLO" 49 | EMPTY_LINE_ONCE() 50 | EMPTY_LINE_ONCE() 51 | +"WORLD" 52 | } 53 | assertEquals("HELLO\n\nWORLD\n", indenter.toString()) 54 | } 55 | 56 | @Test 57 | fun testBlock() { 58 | val indenter = Indenter { 59 | "hello" { 60 | +"world" 61 | } 62 | } 63 | assertEquals("hello {\n\tworld\n}\n", indenter.toString()) 64 | } 65 | } -------------------------------------------------------------------------------- /ktor-generator/src/jvmTest/kotlin/io/ktor/util/JsonTest.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.util 2 | 3 | import io.ktor.start.util.* 4 | import org.junit.Test 5 | import kotlin.test.* 6 | 7 | class JsonTest { 8 | @Test 9 | fun testPrettyEmpty() { 10 | assertEquals("{}", Json.encodePrettyUntyped(mapOf()).trim()) 11 | assertEquals("[]", Json.encodePrettyUntyped(listOf()).trim()) 12 | assertEquals("{\n\t\"hello\": []\n}", Json.encodePrettyUntyped(mapOf("hello" to listOf())).trim()) 13 | } 14 | } -------------------------------------------------------------------------------- /ktor-generator/src/jvmTest/resources/V30/callback-example.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | info: 3 | title: Callback Example 4 | version: 1.0.0 5 | paths: 6 | /streams: 7 | post: 8 | description: subscribes a client to receive out-of-band data 9 | parameters: 10 | - name: callbackUrl 11 | in: query 12 | required: true 13 | description: | 14 | the location where data will be sent. Must be network accessible 15 | by the source server 16 | schema: 17 | type: string 18 | format: uri 19 | example: https://tonys-server.com 20 | responses: 21 | '201': 22 | description: subscription successfully created 23 | content: 24 | application/json: 25 | schema: 26 | description: subscription information 27 | required: 28 | - subscriptionId 29 | properties: 30 | subscriptionId: 31 | description: this unique identifier allows management of the subscription 32 | type: string 33 | example: 2531329f-fb09-4ef7-887e-84e648214436 34 | callbacks: 35 | # the name `onData` is a convenience locator 36 | onData: 37 | # when data is sent, it will be sent to the `callbackUrl` provided 38 | # when making the subscription PLUS the suffix `/data` 39 | '{$request.query.callbackUrl}/data': 40 | post: 41 | requestBody: 42 | description: subscription payload 43 | content: 44 | application/json: 45 | schema: 46 | properties: 47 | timestamp: 48 | type: string 49 | format: date-time 50 | userData: 51 | type: string 52 | responses: 53 | '202': 54 | description: | 55 | Your server implementation should return this HTTP status code 56 | if the data was received successfully 57 | '204': 58 | description: | 59 | Your server should return this HTTP status code if no longer interested 60 | in further updates 61 | -------------------------------------------------------------------------------- /ktor-generator/src/jvmTest/resources/V30/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pets: 11 | get: 12 | summary: List all pets 13 | operationId: listPets 14 | tags: 15 | - pets 16 | parameters: 17 | - name: limit 18 | in: query 19 | description: How many items to return at one time (max 100) 20 | required: false 21 | schema: 22 | type: integer 23 | format: int32 24 | responses: 25 | '200': 26 | description: A paged array of pets 27 | headers: 28 | x-next: 29 | description: A link to the next page of responses 30 | schema: 31 | type: string 32 | content: 33 | application/json: 34 | schema: 35 | $ref: "#/components/schemas/Pets" 36 | default: 37 | description: unexpected error 38 | content: 39 | application/json: 40 | schema: 41 | $ref: "#/components/schemas/Error" 42 | post: 43 | summary: Create a pet 44 | operationId: createPets 45 | tags: 46 | - pets 47 | responses: 48 | '201': 49 | description: Null response 50 | default: 51 | description: unexpected error 52 | content: 53 | application/json: 54 | schema: 55 | $ref: "#/components/schemas/Error" 56 | /pets/{petId}: 57 | get: 58 | summary: Info for a specific pet 59 | operationId: showPetById 60 | tags: 61 | - pets 62 | parameters: 63 | - name: petId 64 | in: path 65 | required: true 66 | description: The id of the pet to retrieve 67 | schema: 68 | type: string 69 | responses: 70 | '200': 71 | description: Expected response to a valid request 72 | content: 73 | application/json: 74 | schema: 75 | $ref: "#/components/schemas/Pets" 76 | default: 77 | description: unexpected error 78 | content: 79 | application/json: 80 | schema: 81 | $ref: "#/components/schemas/Error" 82 | components: 83 | schemas: 84 | Pet: 85 | required: 86 | - id 87 | - name 88 | properties: 89 | id: 90 | type: integer 91 | format: int64 92 | name: 93 | type: string 94 | tag: 95 | type: string 96 | Pets: 97 | type: array 98 | items: 99 | $ref: "#/components/schemas/Pet" 100 | Error: 101 | required: 102 | - code 103 | - message 104 | properties: 105 | code: 106 | type: integer 107 | format: int32 108 | message: 109 | type: string 110 | -------------------------------------------------------------------------------- /ktor-generator/src/jvmTest/resources/empty.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /ktor-intellij-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "org.jetbrains.intellij" version "0.4.16" 3 | } 4 | 5 | apply plugin: "kotlin" 6 | apply plugin: "idea" 7 | 8 | sourceSets { 9 | main.kotlin.srcDirs += "src" 10 | main.resources.srcDirs += "resources" 11 | test.kotlin.srcDirs += "test" 12 | test.resources.srcDirs += "testresources" 13 | } 14 | 15 | dependencies { 16 | //compile project(":ktor-generator") 17 | compile project(path: ':ktor-generator', configuration: 'jvmRuntimeElements') 18 | } 19 | 20 | def intellijBaseVersion = "2018.3.6" 21 | //def intellijBaseVersion = "2019.3.3" 22 | def intellijVersion = "IC-$intellijBaseVersion" 23 | //def intellijVersion = "IU-$intellijBaseVersion" 24 | 25 | intellij { 26 | version intellijVersion 27 | //version 'LATEST-EAP-SNAPSHOT' 28 | updateSinceUntilBuild false 29 | plugins = [ 30 | 'maven', 'gradle', 'Kotlin'//, 'java' 31 | ] 32 | pluginName 'ktor' 33 | downloadSources true 34 | } 35 | 36 | publishPlugin { 37 | if (rootProject.properties.jetbrainsUsername != null) { 38 | username rootProject.properties.jetbrainsUsername 39 | password rootProject.properties.jetbrainsPassword 40 | } 41 | } 42 | 43 | runIde { 44 | maxHeapSize = "2g" 45 | } 46 | 47 | repositories { 48 | maven { url "https://plugins.gradle.org/m2/" } 49 | mavenCentral() 50 | } 51 | 52 | [compileKotlin, compileTestKotlin].each { 53 | sourceCompatibility = JavaVersion.VERSION_1_8 54 | targetCompatibility = JavaVersion.VERSION_1_8 55 | 56 | it.kotlinOptions { 57 | freeCompilerArgs = ["-XXLanguage:+InlineClasses"] 58 | apiVersion = "1.3" 59 | languageVersion = "1.3" 60 | jvmTarget = "1.8" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/resources/icons/ktor-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktorio/ktor-init-tools/790e8b389ae9aed630eea72a73181a3a5b725977/ktor-intellij-plugin/resources/icons/ktor-icon.png -------------------------------------------------------------------------------- /ktor-intellij-plugin/resources/icons/ktor-icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktorio/ktor-init-tools/790e8b389ae9aed630eea72a73181a3a5b725977/ktor-intellij-plugin/resources/icons/ktor-icon16.png -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/FqNames.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij 2 | 3 | import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall 4 | import org.jetbrains.kotlin.psi.KtAnnotationEntry 5 | import org.jetbrains.kotlin.psi.KtCallExpression 6 | import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull 7 | import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode 8 | import org.jetbrains.kotlinx.serialization.compiler.resolve.toClassDescriptor 9 | 10 | internal const val KTOR_LOCATIONS_ANNOTATION_FQNAME = "io.ktor.locations.Location" 11 | internal const val KTOR_ROUTING_FQNAME = "io.ktor.routing.routing" 12 | 13 | internal fun KtAnnotationEntry.isLocation(): Boolean = resolveToCall(BodyResolveMode.PARTIAL) 14 | ?.candidateDescriptor 15 | ?.returnType 16 | ?.toClassDescriptor 17 | ?.fqNameOrNull() 18 | ?.asString() == KTOR_LOCATIONS_ANNOTATION_FQNAME 19 | 20 | internal fun KtCallExpression.isRouteingInvocation(): Boolean = resolveToCall(BodyResolveMode.PARTIAL) 21 | ?.candidateDescriptor 22 | ?.fqNameOrNull() 23 | ?.asString() == KTOR_ROUTING_FQNAME 24 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/KtorModuleConfig.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.intellij 19 | 20 | import io.ktor.start.* 21 | import io.ktor.start.swagger.* 22 | 23 | class KtorModuleConfig { 24 | var artifactGroup = "com.example" 25 | var artifactId = "example" 26 | var artifactVersion = "0.0.1" 27 | var projectType = ProjectType.Gradle 28 | var featuresToInstall = listOf() 29 | var ktorVersion = Versions.LAST 30 | var wrapper = true 31 | var engine = KtorEngine.Netty 32 | var swaggerModules = listOf() 33 | var swaggerGenKind: SwaggerGenerator.Kind = SwaggerGenerator.Kind.RAW 34 | } 35 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/KtorModuleType.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package io.ktor.start.intellij 19 | 20 | import com.intellij.openapi.module.* 21 | import com.intellij.openapi.util.* 22 | import javax.swing.* 23 | 24 | class KtorModuleType : ModuleType("ktor") { 25 | companion object { 26 | val NAME = "Ktor" 27 | val DESCRIPTION = "Ktor Quickstart" 28 | val KTOR_BIG_ICON by lazy { IconLoader.getIcon("/icons/ktor-icon.png") } 29 | val KTOR_ICON by lazy { IconLoader.getIcon("/icons/ktor-icon16.png") } 30 | val INSTANCE = KtorModuleType() 31 | } 32 | 33 | override fun createModuleBuilder(): KtorModuleBuilder = KtorModuleBuilder() 34 | override fun getName(): String = NAME 35 | override fun getDescription(): String = DESCRIPTION 36 | override fun getNodeIcon(isOpened: Boolean): Icon = KTOR_ICON 37 | override fun getIcon(): Icon = KTOR_BIG_ICON 38 | } 39 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/RoutingLineMarker.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij 2 | 3 | import com.intellij.codeInsight.daemon.LineMarkerInfo 4 | import com.intellij.codeInsight.daemon.LineMarkerProvider 5 | import com.intellij.icons.AllIcons 6 | import com.intellij.openapi.editor.markup.GutterIconRenderer 7 | import com.intellij.psi.PsiElement 8 | import com.intellij.psi.impl.source.tree.LeafPsiElement 9 | import com.intellij.psi.util.parentOfType 10 | import org.jetbrains.kotlin.idea.editor.fixers.range 11 | import org.jetbrains.kotlin.lexer.KtTokens 12 | import org.jetbrains.kotlin.psi.KtCallExpression 13 | 14 | class RoutingLineMarker : LineMarkerProvider { 15 | override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? { 16 | return null 17 | } 18 | 19 | override fun collectSlowLineMarkers( 20 | elements: MutableList, 21 | result: MutableCollection> 22 | ) { 23 | for (element in elements) { 24 | if (element is LeafPsiElement 25 | && element.elementType == KtTokens.IDENTIFIER 26 | && element.text == "routing" 27 | ) { 28 | val callExpression = element.parentOfType() 29 | if (callExpression != null && callExpression.isRouteingInvocation()) { 30 | result.add(LineMarkerInfo( 31 | element, element.range, AllIcons.Nodes.Weblistener, 32 | 11, null, null, 33 | GutterIconRenderer.Alignment.LEFT 34 | )) 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/AddClassParameterQuickFix.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.codeInspection.* 4 | import com.intellij.openapi.project.* 5 | import org.jetbrains.kotlin.psi.* 6 | 7 | class AddClassParameterQuickFix( 8 | private val parameterName: String, 9 | private val optional: Boolean 10 | ) : LocalQuickFix { 11 | override fun getFamilyName(): String { 12 | return "Add path parameter to constructor." 13 | } 14 | 15 | override fun applyFix(project: Project, descriptor: ProblemDescriptor) { 16 | val classElement = findClassOrObject(project, descriptor.psiElement) as? KtClass 17 | ?: error("No kotlin class found") 18 | 19 | addClassParameter(project, classElement, parameterName, optional) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/AddOuterClassParameterQuickFix.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.codeInspection.* 4 | import com.intellij.openapi.project.* 5 | import com.intellij.psi.util.* 6 | import org.jetbrains.kotlin.idea.core.* 7 | import org.jetbrains.kotlin.psi.* 8 | import org.jetbrains.kotlin.util.capitalizeDecapitalize.* 9 | 10 | class AddOuterClassParameterQuickFix( 11 | private val simpleClassName: String, 12 | private val convert: Boolean 13 | ) : LocalQuickFix { 14 | override fun getFamilyName(): String = when (convert) { 15 | false -> "Add constructor parameter for outer class" 16 | true -> "Convert object to class and add parameter" 17 | } 18 | 19 | override fun applyFix(project: Project, descriptor: ProblemDescriptor) { 20 | var classElement = descriptor.psiElement.parentOfType() 21 | ?: error("Unable to lookup class element for ${descriptor.psiElement}") 22 | val factory = KtPsiFactory(project) 23 | 24 | if (classElement is KtObjectDeclaration) { 25 | classElement = convertToClass(project, classElement) 26 | } 27 | 28 | if (classElement is KtClass) { 29 | classElement.createPrimaryConstructorIfAbsent() 30 | val parametersList = classElement.createPrimaryConstructorParameterListIfAbsent() 31 | val existingNames = parametersList.parameters.mapNotNull { it.name }.toSet() 32 | val name = KotlinNameSuggester.suggestNameByName(simpleClassName.decapitalizeAsciiOnly()) { suggestion -> 33 | suggestion !in existingNames 34 | } 35 | 36 | val text = buildString { 37 | append("val ") 38 | append(name) 39 | append(": ") 40 | append(simpleClassName) 41 | } 42 | 43 | parametersList.addParameter(factory.createParameter(text)) 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/CompletionInsertHandler.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.codeInsight.completion.* 4 | import com.intellij.codeInsight.lookup.* 5 | import com.intellij.psi.util.* 6 | 7 | object CompletionInsertHandler : InsertHandler { 8 | override fun handleInsert(context: InsertionContext, item: LookupElement) { 9 | val text = item.lookupString 10 | 11 | val element = PsiTreeUtil.findElementOfClassAtOffset( 12 | context.file, 13 | context.tailOffset - 1, LocationsPatternPsiElement::class.java, false 14 | ) ?: return 15 | 16 | context.document.replaceString( 17 | element.node.startOffset, 18 | element.node.startOffset + element.node.textLength, text 19 | ) 20 | 21 | context.commitDocument() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/ConvertToClassQuickFix.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.codeInspection.* 4 | import com.intellij.openapi.project.* 5 | import org.jetbrains.kotlin.psi.* 6 | 7 | class ConvertToClassQuickFix( 8 | private val parameterName: String, 9 | private val optional: Boolean 10 | ) : LocalQuickFix { 11 | override fun getFamilyName(): String = "Convert locations object to class" 12 | 13 | override fun applyFix(project: Project, descriptor: ProblemDescriptor) { 14 | val objectElement = findClassOrObject(project, descriptor.psiElement) 15 | as? KtObjectDeclaration 16 | ?: error("Kotlin object not found.") 17 | 18 | val newClass = convertToClass(project, objectElement) 19 | addClassParameter(project, newClass, parameterName, optional) 20 | } 21 | } -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/ElementManipulators.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.openapi.util.TextRange 4 | import com.intellij.psi.AbstractElementManipulator 5 | import com.intellij.psi.PsiFileFactory 6 | import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType 7 | 8 | 9 | class ManipulatorImpl : AbstractElementManipulator() { 10 | override fun handleContentChange( 11 | element: LocationsPatternPsiElement.ParameterNameElement, 12 | range: TextRange, 13 | newContent: String? 14 | ): LocationsPatternPsiElement.ParameterNameElement? { 15 | if (newContent == null) return null 16 | 17 | val dummy = PsiFileFactory.getInstance(element.project) 18 | .createFileFromText(LocationsPatternLanguage, "{$newContent}") 19 | 20 | val newElement = 21 | dummy.findDescendantOfType() ?: return null 22 | 23 | return element.replace(newElement) as LocationsPatternPsiElement.ParameterNameElement 24 | } 25 | } 26 | 27 | class SubstitutionManipulatorImpl : AbstractElementManipulator() { 28 | override fun handleContentChange( 29 | element: LocationsPatternPsiElement.SubstitutionElement, 30 | range: TextRange, 31 | newContent: String? 32 | ): LocationsPatternPsiElement.SubstitutionElement? { 33 | if (newContent == null) return null 34 | val parameterElement = element.parameterNameElement ?: return null 35 | 36 | val dummy = PsiFileFactory.getInstance(element.project) 37 | .createFileFromText(LocationsPatternLanguage, "{$newContent}") 38 | 39 | val newElement = 40 | dummy.findDescendantOfType() ?: return null 41 | 42 | parameterElement.replace(newElement) 43 | return element 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/LocationPatternBraceMatcher.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.lang.* 4 | import com.intellij.psi.* 5 | import com.intellij.psi.tree.* 6 | import com.intellij.psi.util.* 7 | import org.jetbrains.kotlin.psi.psiUtil.* 8 | 9 | class LocationPatternBraceMatcher : PairedBraceMatcher { 10 | override fun getCodeConstructStart(file: PsiFile?, openingBraceOffset: Int): Int { 11 | if (file == null) return 0 12 | 13 | return file.findElementAt(openingBraceOffset)?.parentOfType() 14 | ?.startOffset 15 | ?: openingBraceOffset 16 | } 17 | 18 | override fun getPairs(): Array { 19 | return arrayOf(BracePair(PatternLexer.TOKEN_SUB_OPEN, PatternLexer.TOKEN_SUB_CLOSE, false)) 20 | } 21 | 22 | override fun isPairedBracesAllowedBeforeType(lbraceType: IElementType, contextType: IElementType?): Boolean { 23 | return true 24 | } 25 | } -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/LocationsLineMarker.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.codeInsight.daemon.LineMarkerInfo 4 | import com.intellij.codeInsight.daemon.LineMarkerProvider 5 | import com.intellij.icons.AllIcons 6 | import com.intellij.openapi.editor.markup.GutterIconRenderer 7 | import com.intellij.psi.PsiElement 8 | import com.intellij.psi.impl.source.tree.LeafPsiElement 9 | import com.intellij.psi.util.parentOfType 10 | import io.ktor.start.intellij.isLocation 11 | import org.jetbrains.kotlin.idea.editor.fixers.range 12 | import org.jetbrains.kotlin.lexer.KtTokens 13 | import org.jetbrains.kotlin.psi.KtClassOrObject 14 | 15 | class LocationsLineMarker : LineMarkerProvider { 16 | override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? { 17 | return null 18 | } 19 | 20 | override fun collectSlowLineMarkers( 21 | elements: MutableList, 22 | result: MutableCollection> 23 | ) { 24 | elements.filterIsInstance() 25 | .filter { it.elementType == KtTokens.IDENTIFIER } 26 | .mapNotNull { it.parentOfType()?.let { found -> Pair(it, found) } } 27 | .filter { it.second.annotationEntries.isNotEmpty() && it.second.nameIdentifier == it.first } 28 | .mapNotNullTo(result) { (element, classElement) -> 29 | val annotation = classElement.annotationEntries.firstOrNull { it.isLocation() } 30 | 31 | if (annotation != null) { 32 | LineMarkerInfo( 33 | element, element.range, AllIcons.Javaee.WebService, 34 | 11, null, null, 35 | GutterIconRenderer.Alignment.LEFT 36 | ) 37 | } else { 38 | null 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/LocationsParametersFindUsagesHandlerFactory.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.find.findUsages.FindUsagesHandler 4 | import com.intellij.find.findUsages.FindUsagesHandlerFactory 5 | import com.intellij.psi.PsiElement 6 | import com.intellij.psi.util.parentOfType 7 | 8 | class LocationsParametersFindUsagesHandlerFactory : FindUsagesHandlerFactory() { 9 | override fun canFindUsages(element: PsiElement): Boolean { 10 | return element is LocationsPatternPsiElement.ParameterNameElement || 11 | element is LocationsPatternPsiElement.SubstitutionElement 12 | } 13 | 14 | override fun createFindUsagesHandler(element: PsiElement, forHighlightUsages: Boolean): FindUsagesHandler? { 15 | return when (element) { 16 | is LocationsPatternPsiElement.ParameterNameElement -> DefaultFindUsagesHandler(element.parentOfType()!!) 17 | is LocationsPatternPsiElement.SubstitutionElement -> DefaultFindUsagesHandler(element) 18 | else -> null 19 | } 20 | } 21 | 22 | private class DefaultFindUsagesHandler( 23 | private val substitution: LocationsPatternPsiElement.SubstitutionElement 24 | ) : FindUsagesHandler(substitution) { 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/LocationsRefactoringsSupportProvider.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.lang.refactoring.RefactoringSupportProvider 4 | import com.intellij.psi.PsiElement 5 | import com.intellij.psi.util.parentOfType 6 | import com.intellij.refactoring.RefactoringActionHandler 7 | import org.jetbrains.kotlin.psi.KtNamedDeclaration 8 | 9 | class LocationsRefactoringsSupportProvider : RefactoringSupportProvider() { 10 | override fun isSafeDeleteAvailable(element: PsiElement): Boolean { 11 | return true 12 | } 13 | 14 | override fun isMemberInplaceRenameAvailable(element: PsiElement, context: PsiElement?): Boolean { 15 | return context != null && 16 | (context.parentOfType() != null || 17 | context.language == LocationsPatternLanguage) && 18 | (element is LocationsPatternPsiElement.SubstitutionElement || 19 | element is LocationsPatternPsiElement.ParameterNameElement) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/MakeParameterOptionalQuickFix.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.codeInspection.* 4 | import com.intellij.lang.injection.* 5 | import com.intellij.openapi.project.* 6 | import com.intellij.psi.util.* 7 | import org.jetbrains.kotlin.idea.core.* 8 | import org.jetbrains.kotlin.idea.inspections.* 9 | import org.jetbrains.kotlin.psi.* 10 | import org.jetbrains.kotlin.psi.psiUtil.* 11 | 12 | class MakeParameterOptionalQuickFix : LocalQuickFix { 13 | override fun getFamilyName(): String = "Make parameter nullable" 14 | 15 | override fun applyFix(project: Project, descriptor: ProblemDescriptor) { 16 | val element = InjectedLanguageManager.getInstance(project) 17 | .getInjectionHost(descriptor.psiElement) 18 | ?: error("Unable to find injection host for element") 19 | 20 | val parameterNameElement = descriptor.psiElement 21 | .parentOfType() 22 | ?.getChildOfType() 23 | ?: error("Unable to find parameter name element") 24 | 25 | val propertyName = parameterNameElement.text.trim() 26 | val classElement = element.parentOfType() ?: error("Unable to find class element") 27 | val property = classElement.findPropertyByName(propertyName) 28 | ?: error("Unable to find property with name $propertyName") 29 | 30 | if (property is KtParameter) { 31 | val factory = KtPsiFactory(project) 32 | val oldType = property.typeReference ?: error("No type for property $propertyName") 33 | val newType = factory.createType(oldType.text + "?") 34 | oldType.replace(newType) 35 | property.setDefaultValue(factory.createExpression("null")) 36 | property.defaultValue?.endOffset?.let { endOffset -> 37 | property.findExistingEditor()?.moveCaret(endOffset) 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/MissingOwnerClassReferenceInspection.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.codeInspection.* 4 | import com.intellij.psi.* 5 | import io.ktor.start.intellij.isLocation 6 | import org.jetbrains.kotlin.idea.search.usagesSearch.* 7 | import org.jetbrains.kotlin.psi.* 8 | import org.jetbrains.kotlin.psi.psiUtil.* 9 | import org.jetbrains.kotlin.resolve.descriptorUtil.* 10 | 11 | class MissingOwnerClassReferenceInspection : LocalInspectionTool() { 12 | override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { 13 | return object : PsiElementVisitor() { 14 | override fun visitElement(element: PsiElement?) { 15 | if (element is KtClassOrObject) { 16 | processClassOrObject(element, holder) 17 | } 18 | 19 | super.visitElement(element) 20 | } 21 | } 22 | } 23 | 24 | private fun processClassOrObject(classElement: KtClassOrObject, holder: ProblemsHolder) { 25 | if (!classElement.isLocation()) return 26 | val parentClass = classElement.getParentOfType(true) ?: return 27 | if (!parentClass.isLocation()) return 28 | 29 | if (parentClass is KtObjectDeclaration && classElement is KtObjectDeclaration) { 30 | holder.registerProblem( 31 | classElement.getObjectKeyword() ?: classElement.navigationElement, 32 | "Nesting objects is no longer recommended. Convert the inner object into a class.", 33 | ProblemHighlightType.ERROR, 34 | AddOuterClassParameterQuickFix( 35 | parentClass.fqName?.shortName()?.asString() 36 | ?: parentClass.name?.substringAfterLast(".") ?: "?", 37 | convert = true 38 | ) 39 | ) 40 | } else if (classElement.primaryConstructorParameters.none { it.refersToClass(parentClass) }) { 41 | holder.registerProblem( 42 | classElement.nameIdentifier ?: classElement.navigationElement, 43 | "There is no property referencing outer class.", 44 | AddOuterClassParameterQuickFix( 45 | parentClass.fqName?.shortName()?.asString() 46 | ?: parentClass.name?.substringAfterLast(".") ?: "?", 47 | convert = false 48 | ) 49 | ) 50 | } 51 | } 52 | 53 | private fun KtClassOrObject.isLocation() = annotationEntries.any { it.isLocation() } 54 | 55 | private fun KtParameter.refersToClass(other: KtClassOrObject): Boolean { 56 | return descriptor?.type?.constructor?.declarationDescriptor?.fqNameOrNull() == other.fqName 57 | } 58 | } -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/ParameterNameAnnotator.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.lang.annotation.* 4 | import com.intellij.psi.* 5 | import org.jetbrains.kotlin.psi.KtAnnotationEntry 6 | 7 | class ParameterNameAnnotator : Annotator { 8 | override fun annotate(element: PsiElement, holder: AnnotationHolder) { 9 | if (element is LocationsPatternPsiElement.ParameterNameElement) { 10 | holder.createInfoAnnotation(element, null) 11 | .apply { 12 | tooltip = "Named path parameter" 13 | textAttributes = PatternSyntaxHighlighter.ParameterName 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/ParameterNameInPlaceRenameHandler.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.openapi.editor.Editor 4 | import com.intellij.psi.PsiElement 5 | import com.intellij.psi.PsiNameIdentifierOwner 6 | import com.intellij.psi.PsiNamedElement 7 | import com.intellij.psi.util.parentOfType 8 | import com.intellij.refactoring.rename.inplace.MemberInplaceRenameHandler 9 | import com.intellij.refactoring.rename.inplace.MemberInplaceRenamer 10 | import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer 11 | 12 | class ParameterNameInPlaceRenameHandler : MemberInplaceRenameHandler() { 13 | override fun createMemberRenamer( 14 | element: PsiElement, 15 | elementToRename: PsiNameIdentifierOwner, 16 | editor: Editor 17 | ): MemberInplaceRenamer { 18 | return Renamer(elementToRename, element, editor) 19 | } 20 | 21 | override fun createRenamer(elementToRename: PsiElement, editor: Editor): VariableInplaceRenamer? { 22 | elementToRename.parentOfType()?.let { 23 | return Renamer(it, null, editor) 24 | } 25 | 26 | return super.createRenamer(elementToRename, editor) 27 | } 28 | 29 | private class Renamer(elementToRename: PsiNamedElement, substituted: PsiElement?, editor: Editor) : 30 | MemberInplaceRenamer(elementToRename, substituted, editor) { 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/ParameterUsagesSearcher.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.psi.PsiElement 4 | import com.intellij.psi.PsiReference 5 | import com.intellij.psi.search.searches.DefinitionsScopedSearch 6 | import com.intellij.psi.search.searches.ReferencesSearch 7 | import com.intellij.util.ProcessingContext 8 | import com.intellij.util.Processor 9 | import com.intellij.util.QueryExecutor 10 | import org.jetbrains.kotlin.idea.util.application.runReadAction 11 | import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType 12 | 13 | class ParameterUsagesSearcher : QueryExecutor { 14 | override fun execute( 15 | queryParameters: DefinitionsScopedSearch.SearchParameters, 16 | consumer: Processor 17 | ): Boolean { 18 | val searchElement = queryParameters.element 19 | if (searchElement !is LocationsPatternPsiElement) { 20 | return true 21 | } 22 | 23 | var cancelled = false 24 | runReadAction { 25 | searchElement.collectDescendantsOfType() 26 | .forEach { element -> 27 | LocationsBackReferenceProvider().getReferencesByElement(element.firstChild, ProcessingContext()) 28 | .map { it.element }.forEach { target -> 29 | if (!consumer.process(target)) { 30 | cancelled = true 31 | return@runReadAction 32 | } 33 | } 34 | } 35 | } 36 | 37 | return !cancelled 38 | } 39 | } 40 | 41 | class ParametersReferencesSearcher : QueryExecutor { 42 | override fun execute( 43 | queryParameters: ReferencesSearch.SearchParameters, 44 | consumer: Processor 45 | ): Boolean { 46 | val searchElement = queryParameters.elementToSearch 47 | if (searchElement !is LocationsPatternPsiElement) { 48 | return true 49 | } 50 | 51 | var cancelled = false 52 | runReadAction { 53 | searchElement.collectDescendantsOfType() 54 | .forEach { element -> 55 | LocationsBackReferenceProvider().getReferencesByElement(element.firstChild, ProcessingContext()) 56 | .forEach { target -> 57 | if (!consumer.process(target)) { 58 | cancelled = true 59 | return@runReadAction 60 | } 61 | } 62 | } 63 | } 64 | 65 | return !cancelled 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/PatternFileTypeFactory.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.openapi.fileTypes.* 4 | 5 | class PatternFileTypeFactory : FileTypeFactory() { 6 | override fun createFileTypes(consumer: FileTypeConsumer) { 7 | consumer.consume(PatternFileType) 8 | } 9 | } -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/PatternFindUsagesProvider.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.lang.cacheBuilder.DefaultWordsScanner 4 | import com.intellij.lang.cacheBuilder.WordOccurrence 5 | import com.intellij.lang.cacheBuilder.WordsScanner 6 | import com.intellij.lang.findUsages.* 7 | import com.intellij.psi.* 8 | import com.intellij.psi.tree.TokenSet 9 | import com.intellij.util.Processor 10 | 11 | class PatternFindUsagesProvider : EmptyFindUsagesProvider() { 12 | override fun getWordsScanner(): WordsScanner? { 13 | return object : DefaultWordsScanner( 14 | PatternLexer(), 15 | TokenSet.create(PatternLexer.TOKEN_COMPONENT), 16 | TokenSet.EMPTY, 17 | TokenSet.EMPTY 18 | ) {} 19 | } 20 | 21 | override fun canFindUsagesFor(psiElement: PsiElement): Boolean { 22 | return psiElement is LocationsPatternPsiElement.ParameterNameElement 23 | || psiElement is LocationsPatternPsiElement.SubstitutionElement 24 | } 25 | 26 | override fun getType(element: PsiElement): String { 27 | return when (element) { 28 | is LocationsPatternPsiElement.ParameterNameElement -> "parameter" 29 | is LocationsPatternPsiElement.SubstitutionElement -> "parameter" 30 | else -> "" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/PatternParameterBackReference.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.openapi.util.TextRange 4 | import com.intellij.psi.* 5 | import com.intellij.util.IncorrectOperationException 6 | 7 | internal class PatternParameterBackReference( 8 | private val componentElement: LocationsPatternPsiElement.ParameterNameElement, 9 | private val references: PsiElement 10 | ): PsiReferenceBase(references), PsiPolyVariantReference { 11 | override fun resolve(): PsiElement? { 12 | return references 13 | } 14 | 15 | override fun multiResolve(incompleteCode: Boolean): Array { 16 | return arrayOf(PsiElementResolveResult(references)) 17 | } 18 | 19 | override fun getRangeInElement(): TextRange { 20 | return TextRange(0, element.textLength) 21 | } 22 | 23 | override fun handleElementRename(newElementName: String): PsiElement { 24 | val result = ManipulatorImpl().handleContentChange(componentElement, newElementName) 25 | ?: throw IncorrectOperationException("Failed to rename location parameter") 26 | 27 | return result 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/PatternParameterReference.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.lang.injection.* 4 | import com.intellij.openapi.util.* 5 | import com.intellij.psi.* 6 | import com.intellij.psi.util.* 7 | import org.jetbrains.kotlin.psi.* 8 | import org.jetbrains.kotlin.psi.psiUtil.* 9 | 10 | class PatternParameterReference( 11 | element: PsiNameIdentifierOwner, 12 | range: TextRange 13 | ) : PsiReferenceBase(element, range), PsiPolyVariantReference { 14 | override fun multiResolve(incompleteCode: Boolean): Array { 15 | val propertyName = element.name ?: return emptyArray() 16 | val classElement = element.parentOfType() ?: return emptyArray() 17 | 18 | val manager = InjectedLanguageManager.getInstance(element.project) ?: return emptyArray() 19 | 20 | return classElement.annotationEntries.flatMap { entry -> 21 | val file = entry.valueArguments.singleOrNull() 22 | ?.getArgumentExpression() 23 | ?.findDescendantOfType() 24 | ?.let { manager.getInjectedPsiFiles(it)?.singleOrNull()?.first as? PatternFileImpl } 25 | 26 | file?.parameterNames?.filter { it.text == propertyName } 27 | ?.mapNotNull { it.parentOfType() } 28 | .orEmpty() 29 | }.map { PsiElementResolveResult(it) }.toTypedArray() 30 | } 31 | 32 | override fun resolve(): PsiElement? { 33 | return multiResolve(false).singleOrNull()?.element 34 | } 35 | 36 | override fun isReferenceTo(element: PsiElement): Boolean { 37 | return super.isReferenceTo(element) 38 | } 39 | 40 | override fun getRangeInElement(): TextRange { 41 | val nameIdentifier = element.nameIdentifier ?: return TextRange(0, element.textLength) 42 | val startInside = nameIdentifier.getStartOffsetIn(element) 43 | val length = nameIdentifier.textLength 44 | 45 | if (startInside < 0 || length > element.textLength) { 46 | return TextRange(0, element.textLength) 47 | } 48 | 49 | return TextRange.from(startInside, length) 50 | } 51 | 52 | override fun handleElementRename(newElementName: String): PsiElement { 53 | return super.handleElementRename(newElementName) 54 | } 55 | } -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/PatternSyntaxHighlighter.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.lexer.* 4 | import com.intellij.openapi.editor.* 5 | import com.intellij.openapi.editor.colors.* 6 | import com.intellij.openapi.editor.colors.TextAttributesKey.* 7 | import com.intellij.openapi.fileTypes.* 8 | import com.intellij.openapi.project.* 9 | import com.intellij.openapi.vfs.* 10 | import com.intellij.psi.tree.* 11 | 12 | class PatternSyntaxHighlighter : SyntaxHighlighterBase() { 13 | override fun getTokenHighlights(tokenType: IElementType?): Array { 14 | return when (tokenType) { 15 | PatternLexer.TOKEN_COMPONENT -> arrayOf(DefaultLanguageHighlighterColors.CONSTANT) 16 | PatternLexer.TOKEN_SUB_OPEN, PatternLexer.TOKEN_SUB_CLOSE -> arrayOf(ParameterBraces) 17 | else -> emptyArray() 18 | } 19 | } 20 | 21 | override fun getHighlightingLexer(): Lexer { 22 | return PatternLexer() 23 | } 24 | 25 | companion object : SyntaxHighlighterFactory() { 26 | val ParameterName: TextAttributesKey = 27 | createTextAttributesKey("PARAMETER_NAME", DefaultLanguageHighlighterColors.IDENTIFIER) 28 | 29 | val ParameterBraces: TextAttributesKey = 30 | createTextAttributesKey("PARAMETER_BRACES", DefaultLanguageHighlighterColors.BRACES) 31 | 32 | override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?): SyntaxHighlighter { 33 | return PatternSyntaxHighlighter() 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /ktor-intellij-plugin/src/io/ktor/start/intellij/locations/QuickFixActions.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.locations 2 | 3 | import com.intellij.lang.injection.* 4 | import com.intellij.openapi.project.* 5 | import com.intellij.psi.* 6 | import com.intellij.psi.util.* 7 | import org.jetbrains.kotlin.idea.inspections.* 8 | import org.jetbrains.kotlin.psi.* 9 | import org.jetbrains.kotlin.psi.psiUtil.* 10 | 11 | internal fun findClassOrObject(project: Project, psiElement: PsiElement): KtClassOrObject? { 12 | return InjectedLanguageManager.getInstance(project) 13 | .getInjectionHost(psiElement) 14 | ?.parentOfType() 15 | } 16 | 17 | internal fun convertToClass( 18 | project: Project, 19 | objectElement: KtObjectDeclaration 20 | ): KtClass { 21 | val objectKeyword = objectElement.getObjectKeyword()!! 22 | val editor = objectElement.findExistingEditor()!! 23 | 24 | val startOffset = objectKeyword.startOffset 25 | editor.document.replaceString( 26 | startOffset, objectKeyword.endOffset, "class" 27 | ) 28 | 29 | PsiDocumentManager.getInstance(project).commitDocument(editor.document) 30 | val newElement = PsiDocumentManager.getInstance(project) 31 | .getPsiFile(editor.document)?.findElementAt(startOffset + 1) 32 | ?: error("Unable to lookup new class keyword element") 33 | 34 | return newElement.parentOfType() ?: error("Unable to lookup new class element") 35 | } 36 | 37 | internal fun addClassParameter( 38 | project: Project, 39 | classElement: KtClass, 40 | parameterName: String, 41 | optional: Boolean 42 | ) { 43 | classElement.createPrimaryConstructorIfAbsent() 44 | val parametersList = classElement.createPrimaryConstructorParameterListIfAbsent() 45 | 46 | val allVars = parametersList.parameters.isNotEmpty() && parametersList.parameters.all { it.isMutable } 47 | 48 | val text = buildString { 49 | if (allVars) { 50 | append("var ") 51 | } else { 52 | append("val ") 53 | } 54 | 55 | append(parameterName) 56 | append(": String") 57 | if (optional) { 58 | append("?") 59 | } 60 | } 61 | 62 | parametersList.addParameter(KtPsiFactory(project).createParameter(text)) 63 | } -------------------------------------------------------------------------------- /ktor-intellij-plugin/test/io/ktor/start/intellij/util/ParsingTest.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.util 2 | 3 | import com.intellij.testFramework.* 4 | import io.ktor.start.intellij.locations.PatternParserDefinition 5 | import org.junit.* 6 | 7 | class ParsingTest : ParsingTestCase("", "locationspattern", 8 | PatternParserDefinition() 9 | ) { 10 | 11 | @Test 12 | fun testSmoke() { 13 | doTest(true) 14 | } 15 | 16 | override fun skipSpaces(): Boolean { 17 | return false 18 | } 19 | 20 | override fun includeRanges(): Boolean { 21 | return true 22 | } 23 | 24 | override fun getTestDataPath(): String { 25 | return "testresources" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ktor-intellij-plugin/test/io/ktor/start/intellij/util/PathInfoTest.kt: -------------------------------------------------------------------------------- 1 | package io.ktor.start.intellij.util 2 | 3 | import org.junit.* 4 | import kotlin.test.* 5 | 6 | class PathInfoTest { 7 | @Test 8 | fun checks() { 9 | PathInfo("").let { info -> 10 | assertEquals(null, info.parent) 11 | assertEquals("", info.name) 12 | assertEquals("", info.path) 13 | } 14 | 15 | PathInfo("/").let { info -> 16 | assertEquals("", info.parent) 17 | assertEquals("", info.name) 18 | assertEquals("/", info.path) 19 | } 20 | 21 | PathInfo("test").let { info -> 22 | assertEquals(null, info.parent) 23 | assertEquals("test", info.name) 24 | assertEquals("test", info.path) 25 | } 26 | 27 | PathInfo("hello/world").let { info -> 28 | assertEquals("hello", info.parent) 29 | assertEquals("world", info.name) 30 | assertEquals("hello/world", info.path) 31 | } 32 | 33 | PathInfo("hello/world/42").let { info -> 34 | assertEquals("hello/world", info.parent) 35 | assertEquals("42", info.name) 36 | assertEquals("hello/world/42", info.path) 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /ktor-intellij-plugin/testresources/Smoke.locationspattern: -------------------------------------------------------------------------------- 1 | /a/b/c/{param}/{optional?}/{ellipsis...} -------------------------------------------------------------------------------- /ktor-intellij-plugin/testresources/Smoke.txt: -------------------------------------------------------------------------------- 1 | FILE(0,40) 2 | SlashElement(TOKEN_SLASH)(0,1) 3 | PsiElement(TOKEN_SLASH)('/')(0,1) 4 | ComponentElement(TOKEN_COMPONENT)(1,2) 5 | PsiElement(TOKEN_COMPONENT)('a')(1,2) 6 | SlashElement(TOKEN_SLASH)(2,3) 7 | PsiElement(TOKEN_SLASH)('/')(2,3) 8 | ComponentElement(TOKEN_COMPONENT)(3,4) 9 | PsiElement(TOKEN_COMPONENT)('b')(3,4) 10 | SlashElement(TOKEN_SLASH)(4,5) 11 | PsiElement(TOKEN_SLASH)('/')(4,5) 12 | ComponentElement(TOKEN_COMPONENT)(5,6) 13 | PsiElement(TOKEN_COMPONENT)('c')(5,6) 14 | SlashElement(TOKEN_SLASH)(6,7) 15 | PsiElement(TOKEN_SLASH)('/')(6,7) 16 | SubstitutionElement(TOKEN_SUBSTITUTION)(7,14) 17 | OtherElement(TOKEN_SUB_OPEN)(7,8) 18 | PsiElement(TOKEN_SUB_OPEN)('{')(7,8) 19 | ParameterNameElement(TOKEN_COMPONENT)(8,13) 20 | PsiElement(TOKEN_COMPONENT)('param')(8,13) 21 | OtherElement(TOKEN_SUB_CLOSE)(13,14) 22 | PsiElement(TOKEN_SUB_CLOSE)('}')(13,14) 23 | SlashElement(TOKEN_SLASH)(14,15) 24 | PsiElement(TOKEN_SLASH)('/')(14,15) 25 | SubstitutionElement(TOKEN_SUBSTITUTION)(15,26) 26 | OtherElement(TOKEN_SUB_OPEN)(15,16) 27 | PsiElement(TOKEN_SUB_OPEN)('{')(15,16) 28 | ParameterNameElement(TOKEN_COMPONENT)(16,24) 29 | PsiElement(TOKEN_COMPONENT)('optional')(16,24) 30 | VarArgElement(TOKEN_OPTIONAL)(24,25) 31 | PsiElement(TOKEN_OPTIONAL)('?')(24,25) 32 | OtherElement(TOKEN_SUB_CLOSE)(25,26) 33 | PsiElement(TOKEN_SUB_CLOSE)('}')(25,26) 34 | SlashElement(TOKEN_SLASH)(26,27) 35 | PsiElement(TOKEN_SLASH)('/')(26,27) 36 | SubstitutionElement(TOKEN_SUBSTITUTION)(27,40) 37 | OtherElement(TOKEN_SUB_OPEN)(27,28) 38 | PsiElement(TOKEN_SUB_OPEN)('{')(27,28) 39 | ParameterNameElement(TOKEN_COMPONENT)(28,36) 40 | PsiElement(TOKEN_COMPONENT)('ellipsis')(28,36) 41 | VarArgElement(TOKEN_ELLIPSIS)(36,39) 42 | PsiElement(TOKEN_ELLIPSIS)('...')(36,39) 43 | OtherElement(TOKEN_SUB_CLOSE)(39,40) 44 | PsiElement(TOKEN_SUB_CLOSE)('}')(39,40) 45 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | resolutionStrategy { 3 | eachPlugin { 4 | if (requested.id.id == "kotlin-multiplatform") { 5 | useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") 6 | } 7 | } 8 | } 9 | 10 | repositories { 11 | mavenLocal() 12 | mavenCentral() 13 | maven { url 'https://plugins.gradle.org/m2/' } 14 | } 15 | } 16 | 17 | rootProject.name = 'ktor-init-tools' 18 | 19 | enableFeaturePreview('GRADLE_METADATA') 20 | 21 | include "ktor-intellij-plugin" 22 | include "ktor-generator-website" 23 | include "ktor-generator" 24 | -------------------------------------------------------------------------------- /synchronize_versions.kt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env kscript 2 | 3 | import java.io.* 4 | import java.nio.charset.* 5 | import java.util.* 6 | 7 | fun main(args: Array) { 8 | val properties = Properties().apply { load(File("gradle.properties").readText().reader()) } 9 | val version = properties["version"] 10 | 11 | File("ktor-intellij-plugin/resources/META-INF/plugin.xml").updateFile(save = true) { 12 | it.replace(Regex("(.*?)"), "$version") 13 | } 14 | 15 | File("ktor-generator-website/resources/index.html").updateFile(save = true) { 16 | it.replace(Regex("""Ktor Project Generator \((.*?)\)"""), "Ktor Project Generator ($version)") 17 | } 18 | } 19 | 20 | fun File.updateFile(save: Boolean, charset: Charset = Charsets.UTF_8, callback: (String) -> String) { 21 | print("Processing $this...") 22 | val oldText = this.readText(charset) 23 | val newText = callback(oldText) 24 | if (save) { 25 | if (oldText != newText) { 26 | this.writeText(newText, charset) 27 | println("Saved") 28 | } else { 29 | println("Same content") 30 | } 31 | } else { 32 | println("Not saved") 33 | } 34 | } --------------------------------------------------------------------------------