diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 8cb9527..097195e 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -8,12 +8,12 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'zulu' - java-version: 11 + java-version: 17 cache: 'gradle' - name: Build run: ./gradlew build - name: Upload Unsigned Module - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v5 with: name: ignition-extensions-unsigned path: build/Ignition-Extensions.unsigned.modl diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 70629ed..e096668 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/setup-java@v4 with: distribution: 'zulu' - java-version: 11 + java-version: 17 cache: 'gradle' - name: Deserialize signing certs run: | diff --git a/.gitignore b/.gitignore index 1a00f40..df59032 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .gradle/ local.properties +/common/.jython_cache diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts new file mode 100644 index 0000000..d418805 --- /dev/null +++ b/build-logic/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + `kotlin-dsl` apply false +} \ No newline at end of file diff --git a/build-logic/conventions/build.gradle.kts b/build-logic/conventions/build.gradle.kts new file mode 100644 index 0000000..46f9c3f --- /dev/null +++ b/build-logic/conventions/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + `kotlin-dsl` +} \ No newline at end of file diff --git a/build-logic/conventions/src/main/kotlin/imdc/build/base.gradle.kts b/build-logic/conventions/src/main/kotlin/imdc/build/base.gradle.kts new file mode 100644 index 0000000..9c9c3d3 --- /dev/null +++ b/build-logic/conventions/src/main/kotlin/imdc/build/base.gradle.kts @@ -0,0 +1,15 @@ +package imdc.build + + +group = "org.imdc.extensions" + +plugins { + base +} + +repositories { + mavenCentral() + gradlePluginPortal() + maven(url = "https://nexus.inductiveautomation.com/repository/public/") + maven(url = "https://nexus.inductiveautomation.com/repository/inductiveautomation-releases/") +} diff --git a/build-logic/conventions/src/main/kotlin/imdc/build/ignition-module-scope.gradle.kts b/build-logic/conventions/src/main/kotlin/imdc/build/ignition-module-scope.gradle.kts new file mode 100644 index 0000000..6dbe6c7 --- /dev/null +++ b/build-logic/conventions/src/main/kotlin/imdc/build/ignition-module-scope.gradle.kts @@ -0,0 +1,47 @@ +package imdc.build + +import libs +import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension + +plugins { + id("imdc.build.base") + alias(libs.plugins.kotlin) + `java-library` + + alias(libs.plugins.spotless) +} + +version = project.parent?.version ?: "0.0.0-SNAPSHOT" + +val jvmLanguageVersion = libs.versions.java.map { JavaLanguageVersion.of(it) } + +configure { + jvmToolchain { + languageVersion = jvmLanguageVersion + } +} + +configure { + toolchain { + languageVersion = jvmLanguageVersion + } +} + +spotless { + ratchetFrom("HEAD") + format("misc") { + target("*.gradle", ".gitattributes", ".gitignore") + trimTrailingWhitespace() + endWithNewline() + } + java { + palantirJavaFormat() + formatAnnotations() + } + kotlin { + ktlint() + } + kotlinGradle { + ktlint() + } +} diff --git a/build-logic/conventions/src/main/kotlin/imdc/test/junit-tests.gradle.kts b/build-logic/conventions/src/main/kotlin/imdc/test/junit-tests.gradle.kts new file mode 100644 index 0000000..e4986c9 --- /dev/null +++ b/build-logic/conventions/src/main/kotlin/imdc/test/junit-tests.gradle.kts @@ -0,0 +1,8 @@ +package imdc.test + +tasks { + withType { + useJUnitPlatform() + jvmArgs = listOf("--add-opens", "java.base/java.io=ALL-UNNAMED") + } +} diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 0000000..6522961 --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,19 @@ +rootProject.name = "build-logic" + +plugins { + id("dev.panuszewski.typesafe-conventions") version "0.10.0" +} + +dependencyResolutionManagement { + repositories { + mavenCentral() + gradlePluginPortal() + maven(url = "https://nexus.inductiveautomation.com/repository/public/") + maven(url = "https://nexus.inductiveautomation.com/repository/inductiveautomation-releases/") + maven(url = "https://nexus.inductiveautomation.com/repository/inductiveautomation-snapshots/") + } +} + +include( + "conventions", +) \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index d8bf480..278c1cf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,20 +1,7 @@ plugins { - alias(libs.plugins.kotlin) alias(libs.plugins.modl) } -allprojects { - repositories { - mavenCentral() - maven("https://nexus.inductiveautomation.com/repository/public") - } -} - -subprojects { - // cascade version, which will be set at command line in CI, down to subprojects - version = rootProject.version -} - ignitionModule { name = "Ignition Extensions" fileName = "Ignition-Extensions.modl" diff --git a/client/build.gradle.kts b/client/build.gradle.kts index 59ea172..d69cfb2 100644 --- a/client/build.gradle.kts +++ b/client/build.gradle.kts @@ -1,10 +1,5 @@ plugins { - `java-library` - kotlin("jvm") -} - -kotlin { - jvmToolchain(libs.versions.java.map(String::toInt).get()) + id("imdc.build.ignition-module-scope") } dependencies { diff --git a/client/src/main/kotlin/org/imdc/extensions/client/ClientProjectExtensions.kt b/client/src/main/kotlin/org/imdc/extensions/client/ClientProjectExtensions.kt index 828b638..f85d88e 100644 --- a/client/src/main/kotlin/org/imdc/extensions/client/ClientProjectExtensions.kt +++ b/client/src/main/kotlin/org/imdc/extensions/client/ClientProjectExtensions.kt @@ -1,13 +1,13 @@ package org.imdc.extensions.client import com.inductiveautomation.ignition.client.model.ClientContext -import com.inductiveautomation.ignition.common.project.Project -import com.inductiveautomation.ignition.common.script.hints.ScriptFunction +import com.inductiveautomation.ignition.common.resourcecollection.ResourceCollection +import com.inductiveautomation.ignition.common.script.hints.JythonElement import org.imdc.extensions.common.ProjectExtensions class ClientProjectExtensions(private val context: ClientContext) : ProjectExtensions { - @ScriptFunction(docBundlePrefix = "ClientProjectExtensions") - override fun getProject(): Project { + @JythonElement(docBundlePrefix = "ClientProjectExtensions") + override fun getProject(): ResourceCollection { return requireNotNull(context.project) } } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 8df1fca..2fdee14 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,10 +1,6 @@ plugins { - `java-library` - kotlin("jvm") -} - -kotlin { - jvmToolchain(libs.versions.java.map(String::toInt).get()) + id("imdc.build.ignition-module-scope") + id("imdc.test.junit-tests") } dependencies { @@ -13,10 +9,3 @@ dependencies { testImplementation(libs.bundles.kotest) testImplementation(libs.mockk) } - -tasks { - withType { - useJUnitPlatform() - jvmArgs = listOf("--add-opens", "java.base/java.io=ALL-UNNAMED") - } -} diff --git a/common/src/main/java/org/imdc/extensions/common/UtilitiesExtensions.java b/common/src/main/java/org/imdc/extensions/common/UtilitiesExtensions.java index c6b66e6..8d068a4 100644 --- a/common/src/main/java/org/imdc/extensions/common/UtilitiesExtensions.java +++ b/common/src/main/java/org/imdc/extensions/common/UtilitiesExtensions.java @@ -21,7 +21,7 @@ import com.inductiveautomation.ignition.common.script.PyArgParser; import com.inductiveautomation.ignition.common.script.ScriptContext; import com.inductiveautomation.ignition.common.script.builtin.KeywordArgs; -import com.inductiveautomation.ignition.common.script.hints.ScriptFunction; +import com.inductiveautomation.ignition.common.script.hints.JythonElement; import com.inductiveautomation.ignition.common.tags.model.TagPath; import com.inductiveautomation.ignition.common.tags.paths.parser.TagPathParser; import org.apache.commons.lang3.tuple.Pair; @@ -41,13 +41,13 @@ public UtilitiesExtensions(CommonContext context) { this.context = context; } - @ScriptFunction(docBundlePrefix = "UtilitiesExtensions") + @JythonElement(docBundlePrefix = "UtilitiesExtensions") @UnsafeExtension public CommonContext getContext() { return context; } - @ScriptFunction(docBundlePrefix = "UtilitiesExtensions") + @JythonElement(docBundlePrefix = "UtilitiesExtensions") @KeywordArgs(names = {"object"}, types = {PyObject.class}) public PyObject deepCopy(PyObject[] args, String[] keywords) { PyArgParser parsedArgs = PyArgParser.parseArgs(args, keywords, this.getClass(), "deepCopy"); @@ -76,7 +76,7 @@ private static PyObject recursiveConvert(@NotNull PyObject object) { } } - @ScriptFunction(docBundlePrefix = "UtilitiesExtensions") + @JythonElement(docBundlePrefix = "UtilitiesExtensions") @KeywordArgs(names = {"expression"}, types = {String.class}) public QualifiedValue evalExpression(PyObject[] args, String[] keywords) throws Exception { if (args.length == 0) { @@ -116,7 +116,7 @@ public Expression createBoundExpression(String reference) throws RuntimeExceptio return new ConstantExpression(keywords.get(reference)); } else { try { - TagPath path = TagPathParser.parse(ScriptContext.defaultTagProvider(), reference); + TagPath path = TagPathParser.parse(ScriptContext.getDefaultTagProvider().orElseThrow(), reference); var tagValues = context.getTagManager().readAsync(List.of(path)).get(30, TimeUnit.SECONDS); return new ConstantExpression(tagValues.get(0).getValue()); } catch (IOException | InterruptedException | ExecutionException | TimeoutException e) { @@ -131,7 +131,7 @@ public FunctionFactory getFunctionFactory() { } } - @ScriptFunction(docBundlePrefix = "UtilitiesExtensions") + @JythonElement(docBundlePrefix = "UtilitiesExtensions") public UUID getUUID4() { return UUID.randomUUID(); } diff --git a/common/src/main/kotlin/org/imdc/extensions/common/DatasetExtensions.kt b/common/src/main/kotlin/org/imdc/extensions/common/DatasetExtensions.kt index 5b31d60..d1e35fe 100644 --- a/common/src/main/kotlin/org/imdc/extensions/common/DatasetExtensions.kt +++ b/common/src/main/kotlin/org/imdc/extensions/common/DatasetExtensions.kt @@ -6,7 +6,7 @@ import com.inductiveautomation.ignition.common.TypeUtilities import com.inductiveautomation.ignition.common.script.PyArgParser import com.inductiveautomation.ignition.common.script.builtin.KeywordArgs import com.inductiveautomation.ignition.common.script.hints.ScriptArg -import com.inductiveautomation.ignition.common.script.hints.ScriptFunction +import com.inductiveautomation.ignition.common.script.hints.JythonElement import com.inductiveautomation.ignition.common.util.DatasetBuilder import com.inductiveautomation.ignition.common.xmlserialization.ClassNameResolver import org.apache.poi.ss.usermodel.Cell @@ -35,7 +35,7 @@ import kotlin.streams.asSequence object DatasetExtensions { @Suppress("unused") - @ScriptFunction(docBundlePrefix = "DatasetExtensions") + @JythonElement(docBundlePrefix = "DatasetExtensions") @KeywordArgs( names = ["dataset", "mapper", "preserveColumnTypes"], types = [Dataset::class, PyFunction::class, Boolean::class], @@ -74,7 +74,7 @@ object DatasetExtensions { } @Suppress("unused") - @ScriptFunction(docBundlePrefix = "DatasetExtensions") + @JythonElement(docBundlePrefix = "DatasetExtensions") @KeywordArgs( names = ["dataset", "filter"], types = [Dataset::class, PyFunction::class], @@ -122,7 +122,7 @@ object DatasetExtensions { } @Suppress("unused") - @ScriptFunction(docBundlePrefix = "DatasetExtensions") + @JythonElement(docBundlePrefix = "DatasetExtensions") @KeywordArgs( names = ["dataset", "output", "includeTypes"], types = [Dataset::class, Appendable::class, Boolean::class], @@ -221,7 +221,7 @@ object DatasetExtensions { } @Suppress("unused") - @ScriptFunction(docBundlePrefix = "DatasetExtensions") + @JythonElement(docBundlePrefix = "DatasetExtensions") @KeywordArgs( names = ["dataset", "filterNull"], types = [Dataset::class, Boolean::class], @@ -253,7 +253,7 @@ object DatasetExtensions { } @Suppress("unused") - @ScriptFunction(docBundlePrefix = "DatasetExtensions") + @JythonElement(docBundlePrefix = "DatasetExtensions") @KeywordArgs( names = ["input", "headerRow", "sheetNumber", "firstRow", "lastRow", "firstColumn", "lastColumn", "typeOverrides"], types = [ByteArray::class, Int::class, Int::class, Int::class, Int::class, Int::class, Int::class, PyStringMap::class], @@ -412,7 +412,7 @@ object DatasetExtensions { } @Suppress("unused") - @ScriptFunction(docBundlePrefix = "DatasetExtensions") + @JythonElement(docBundlePrefix = "DatasetExtensions") fun equals( @ScriptArg("dataset1") ds1: Dataset, @ScriptArg("dataset2") ds2: Dataset, @@ -421,7 +421,7 @@ object DatasetExtensions { } @Suppress("unused") - @ScriptFunction(docBundlePrefix = "DatasetExtensions") + @JythonElement(docBundlePrefix = "DatasetExtensions") fun valuesEqual( @ScriptArg("dataset1") ds1: Dataset, @ScriptArg("dataset2") ds2: Dataset, @@ -440,7 +440,7 @@ object DatasetExtensions { } @Suppress("unused") - @ScriptFunction(docBundlePrefix = "DatasetExtensions") + @JythonElement(docBundlePrefix = "DatasetExtensions") @JvmOverloads fun columnsEqual( @ScriptArg("dataset1") ds1: Dataset, @@ -473,7 +473,7 @@ object DatasetExtensions { } } - @ScriptFunction(docBundlePrefix = "DatasetExtensions") + @JythonElement(docBundlePrefix = "DatasetExtensions") @KeywordArgs( names = ["**columns"], types = [KeywordArgs::class], diff --git a/common/src/main/kotlin/org/imdc/extensions/common/ExtensionDocProvider.kt b/common/src/main/kotlin/org/imdc/extensions/common/ExtensionDocProvider.kt index 85bcb02..0f7a960 100644 --- a/common/src/main/kotlin/org/imdc/extensions/common/ExtensionDocProvider.kt +++ b/common/src/main/kotlin/org/imdc/extensions/common/ExtensionDocProvider.kt @@ -2,6 +2,7 @@ package org.imdc.extensions.common import com.inductiveautomation.ignition.common.script.hints.PropertiesFileDocProvider import com.inductiveautomation.ignition.common.script.hints.ScriptFunctionDocProvider +import com.inductiveautomation.ignition.common.script.typing.CompletionDescriptor import java.lang.reflect.Method private val propertiesFileDocProvider = PropertiesFileDocProvider() @@ -13,20 +14,23 @@ private val WARNING = """ """.trimIndent() object ExtensionDocProvider : ScriptFunctionDocProvider by propertiesFileDocProvider { - override fun getMethodDescription(path: String, method: Method): String { - val methodDescription: String? = propertiesFileDocProvider.getMethodDescription(path, method) + override fun getMethodDescriptor(path: String, method: Method): CompletionDescriptor.Method? { + val base = propertiesFileDocProvider.getMethodDescriptor(path, method) + val unsafeAnnotation = method.getAnnotation() - return buildString { - if (unsafeAnnotation != null) { + return if (unsafeAnnotation != null) { + base?.copy(description = buildString { append("") append(WARNING) if (unsafeAnnotation.note.isNotEmpty()) { append("
").append(unsafeAnnotation.note) } append("


") - } - append(methodDescription.orEmpty()) + append(base.description.orEmpty()) + }) + } else { + base } } } diff --git a/common/src/main/kotlin/org/imdc/extensions/common/ProjectExtensions.kt b/common/src/main/kotlin/org/imdc/extensions/common/ProjectExtensions.kt index d229648..65e955b 100644 --- a/common/src/main/kotlin/org/imdc/extensions/common/ProjectExtensions.kt +++ b/common/src/main/kotlin/org/imdc/extensions/common/ProjectExtensions.kt @@ -1,7 +1,8 @@ package org.imdc.extensions.common -import com.inductiveautomation.ignition.common.project.Project +import com.inductiveautomation.ignition.common.resourcecollection.ResourceCollection + interface ProjectExtensions { - fun getProject(): Project + fun getProject(): ResourceCollection } diff --git a/common/src/main/kotlin/org/imdc/extensions/common/TagExtensions.kt b/common/src/main/kotlin/org/imdc/extensions/common/TagExtensions.kt index 589e9be..453014a 100644 --- a/common/src/main/kotlin/org/imdc/extensions/common/TagExtensions.kt +++ b/common/src/main/kotlin/org/imdc/extensions/common/TagExtensions.kt @@ -5,16 +5,17 @@ import com.inductiveautomation.ignition.common.config.PyTagList import com.inductiveautomation.ignition.common.script.PyArgParser import com.inductiveautomation.ignition.common.script.ScriptContext import com.inductiveautomation.ignition.common.script.builtin.KeywordArgs -import com.inductiveautomation.ignition.common.script.hints.ScriptFunction +import com.inductiveautomation.ignition.common.script.hints.JythonElement import com.inductiveautomation.ignition.common.tags.config.TagConfigurationModel import com.inductiveautomation.ignition.common.tags.model.TagPath import com.inductiveautomation.ignition.common.tags.paths.parser.TagPathParser import org.python.core.PyDictionary import org.python.core.PyObject +import kotlin.jvm.optionals.getOrNull abstract class TagExtensions { @UnsafeExtension - @ScriptFunction(docBundlePrefix = "TagExtensions") + @JythonElement(docBundlePrefix = "TagExtensions") @KeywordArgs( names = ["basePath", "recursive"], types = [String::class, Boolean::class], @@ -36,9 +37,11 @@ abstract class TagExtensions { } protected open fun parseTagPath(path: String): TagPath { - val parsed = TagPathParser.parse(ScriptContext.defaultTagProvider(), path) - if (TagPathParser.isRelativePath(parsed) && ScriptContext.relativeTagPathRoot() != null) { - return TagPathParser.derelativize(parsed, ScriptContext.relativeTagPathRoot()) + val parsed = TagPathParser.parse(ScriptContext.getDefaultTagProvider().orElseThrow(), path) + ScriptContext.getRelativeTagPathRoot().getOrNull()?.let { tagPathRoot -> + if(TagPathParser.isRelativePath(parsed)) { + return TagPathParser.derelativize(parsed, tagPathRoot) + } } return parsed } diff --git a/designer/build.gradle.kts b/designer/build.gradle.kts index 4e658fb..850c947 100644 --- a/designer/build.gradle.kts +++ b/designer/build.gradle.kts @@ -1,10 +1,5 @@ plugins { - `java-library` - kotlin("jvm") -} - -kotlin { - jvmToolchain(libs.versions.java.map(String::toInt).get()) + id("imdc.build.ignition-module-scope") } dependencies { diff --git a/designer/src/main/kotlin/org/imdc/extensions/designer/DesignerProjectExtensions.kt b/designer/src/main/kotlin/org/imdc/extensions/designer/DesignerProjectExtensions.kt index df22708..37c5f1a 100644 --- a/designer/src/main/kotlin/org/imdc/extensions/designer/DesignerProjectExtensions.kt +++ b/designer/src/main/kotlin/org/imdc/extensions/designer/DesignerProjectExtensions.kt @@ -1,6 +1,7 @@ package org.imdc.extensions.designer -import com.inductiveautomation.ignition.common.script.hints.ScriptFunction +import com.inductiveautomation.ignition.common.gui.progress.DummyTaskProgressListener +import com.inductiveautomation.ignition.common.script.hints.JythonElement import com.inductiveautomation.ignition.designer.IgnitionDesigner import com.inductiveautomation.ignition.designer.model.DesignerContext import com.inductiveautomation.ignition.designer.project.DesignableProject @@ -9,19 +10,20 @@ import org.imdc.extensions.common.ProjectExtensions import org.imdc.extensions.common.UnsafeExtension class DesignerProjectExtensions(private val context: DesignerContext) : ProjectExtensions { - @ScriptFunction(docBundlePrefix = "DesignerProjectExtensions") + @JythonElement(docBundlePrefix = "DesignerProjectExtensions") @UnsafeExtension override fun getProject(): DesignableProject { return requireNotNull(context.project) } - @ScriptFunction(docBundlePrefix = "DesignerProjectExtensions") + @JythonElement(docBundlePrefix = "DesignerProjectExtensions") @UnsafeExtension fun save() { MethodUtils.invokeMethod( context.frame, true, // forceAccess "handleSave", + DummyTaskProgressListener(), // pl (ProgressListener) false, // saveAs null, // newName false, // commitOnly @@ -30,7 +32,7 @@ class DesignerProjectExtensions(private val context: DesignerContext) : ProjectE ) } - @ScriptFunction(docBundlePrefix = "DesignerProjectExtensions") + @JythonElement(docBundlePrefix = "DesignerProjectExtensions") @UnsafeExtension fun update() { (context.frame as IgnitionDesigner).updateProject() diff --git a/docker-compose.yml b/docker-compose.yml index 79350cf..d2ed05b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: gateway: - image: inductiveautomation/ignition:8.1.37 + image: inductiveautomation/ignition:8.3.0 ports: - 18088:8088 - 18000:8000 diff --git a/gateway/build.gradle.kts b/gateway/build.gradle.kts index 1783cc1..8c8d8b4 100644 --- a/gateway/build.gradle.kts +++ b/gateway/build.gradle.kts @@ -1,10 +1,5 @@ plugins { - `java-library` - kotlin("jvm") -} - -kotlin { - jvmToolchain(libs.versions.java.map(String::toInt).get()) + id("imdc.build.ignition-module-scope") } dependencies { diff --git a/gateway/src/main/kotlin/org/imdc/extensions/gateway/GatewayProjectExtensions.kt b/gateway/src/main/kotlin/org/imdc/extensions/gateway/GatewayProjectExtensions.kt index 69950a2..0ffea3f 100644 --- a/gateway/src/main/kotlin/org/imdc/extensions/gateway/GatewayProjectExtensions.kt +++ b/gateway/src/main/kotlin/org/imdc/extensions/gateway/GatewayProjectExtensions.kt @@ -1,24 +1,28 @@ package org.imdc.extensions.gateway -import com.inductiveautomation.ignition.common.project.RuntimeProject +import com.inductiveautomation.ignition.common.resourcecollection.RuntimeResourceCollection import com.inductiveautomation.ignition.common.script.ScriptContext +import com.inductiveautomation.ignition.common.script.hints.JythonElement import com.inductiveautomation.ignition.common.script.hints.ScriptArg -import com.inductiveautomation.ignition.common.script.hints.ScriptFunction import com.inductiveautomation.ignition.gateway.model.GatewayContext import org.imdc.extensions.common.ProjectExtensions import org.python.core.Py class GatewayProjectExtensions(private val context: GatewayContext) : ProjectExtensions { - @ScriptFunction(docBundlePrefix = "GatewayProjectExtensions") - override fun getProject(): RuntimeProject { - val defaultProject = ScriptContext.defaultProject() ?: throw Py.EnvironmentError("No context project populated") - return requireNotNull(getProject(defaultProject)) { "No such project $defaultProject" } + @JythonElement(docBundlePrefix = "GatewayProjectExtensions") + override fun getProject(): RuntimeResourceCollection { + val defaultProject = ScriptContext.getDefaultProject() + .orElseThrow { Py.EnvironmentError("No context project populated") } + + return requireNotNull(getProject(defaultProject)) { + "No such project $defaultProject" + } } - @ScriptFunction(docBundlePrefix = "GatewayProjectExtensions") + @JythonElement(docBundlePrefix = "GatewayProjectExtensions") fun getProject( @ScriptArg("project", optional = true) project: String, - ): RuntimeProject? { - return context.projectManager.getProject(project).orElse(null) + ): RuntimeResourceCollection? { + return context.projectManager.find(project).orElse(null) } } diff --git a/gateway/src/main/kotlin/org/imdc/extensions/gateway/HistoryServlet.kt b/gateway/src/main/kotlin/org/imdc/extensions/gateway/HistoryServlet.kt index 725639f..31346ee 100644 --- a/gateway/src/main/kotlin/org/imdc/extensions/gateway/HistoryServlet.kt +++ b/gateway/src/main/kotlin/org/imdc/extensions/gateway/HistoryServlet.kt @@ -8,7 +8,15 @@ import com.inductiveautomation.ignition.common.sqltags.history.AggregationMode import com.inductiveautomation.ignition.common.sqltags.history.BasicTagHistoryQueryParams import com.inductiveautomation.ignition.common.sqltags.history.ReturnFormat import com.inductiveautomation.ignition.common.util.LoggerEx +import com.inductiveautomation.ignition.gateway.dataroutes.AccessControlStrategy +import com.inductiveautomation.ignition.gateway.dataroutes.PermissionType +import com.inductiveautomation.ignition.gateway.dataroutes.PermissionType.getStrategies +import com.inductiveautomation.ignition.gateway.dataroutes.RequestContext +import com.inductiveautomation.ignition.gateway.dataroutes.RouteAccess import com.inductiveautomation.ignition.gateway.model.GatewayContext +import jakarta.servlet.http.HttpServlet +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse import org.apache.http.entity.ContentType import java.io.PrintWriter import java.text.SimpleDateFormat @@ -16,9 +24,6 @@ import java.time.Instant import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit import java.util.Date -import javax.servlet.http.HttpServlet -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse class HistoryServlet : HttpServlet() { private lateinit var context: GatewayContext @@ -27,7 +32,15 @@ class HistoryServlet : HttpServlet() { context = servletContext.getAttribute(GatewayContext.SERVLET_CONTEXT_KEY) as GatewayContext } + private val strategies = PermissionType.getStrategies(PermissionType.READ) + override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) { + val requestContext = RequestContext(req, req.servletPath) + val routeAccess = AccessControlStrategy.or(strategies).canAccess(requestContext) + if (routeAccess != RouteAccess.GRANTED) { + resp.sendError(HttpServletResponse.SC_FORBIDDEN) + return + } resp.contentType = ContentType.APPLICATION_JSON.toString() resp.writer.use { writer -> val historyQuery: BasicTagHistoryQueryParams = diff --git a/gradle.properties b/gradle.properties index 6b09920..1920a3b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,3 @@ version=0.0.1-SNAPSHOT + +kotlin.stdlib.default.dependency=false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0a9f094..a69fcd9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,13 +1,13 @@ [versions] -java = "11" -kotlin = "1.9.22" +java = "17" +kotlin = "1.9.20" kotest = "5.8.0" -ignition = "8.1.0" +ignition = "8.3.0" [plugins] kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -modl = { id = "io.ia.sdk.modl", version = "0.1.1" } -ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "12.1.0" } +modl = { id = "io.ia.sdk.modl", version = "0.5.0" } +spotless = { id = "com.diffplug.spotless", version = "8.0.0" } [libraries] ignition-common = { group = "com.inductiveautomation.ignitionsdk", name = "ignition-common", version.ref = "ignition" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a80b22c..b82aa23 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle.kts b/settings.gradle.kts index 43734e1..4ea0b47 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,9 +9,14 @@ pluginManagement { rootProject.name = "ignition-extensions" +includeBuild("build-logic") include( "common", "gateway", "designer", "client", ) + +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +}