Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data binding #3

Merged
merged 4 commits into from
Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-Build-${{ hashFiles('gradlew.bat', 'src/dotnet/*/*.csproj', 'src/dotnet/*.props', 'gradle-wrapper.properties') }}
- run: ./gradlew :rdgen --no-daemon
- run: ./gradlew :buildPlugin --no-daemon
- uses: actions/upload-artifact@v3
if: always()
Expand All @@ -38,4 +39,5 @@ jobs:
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-Test-${{ hashFiles('gradlew.bat', 'src/dotnet/*/*.csproj', 'src/dotnet/*.props', 'gradle-wrapper.properties') }}
- run: ./gradlew :rdgen --no-daemon
- run: ./gradlew :testDotNet --no-daemon
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ output
.gradle
.tmp

# Generated files
**/*.Generated.*

# User-specific files
*.suo
*.user
Expand Down
14 changes: 14 additions & 0 deletions protocol/src/main/kotlin/model/rider/ProtocolModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package model.rider

import com.jetbrains.rd.generator.nova.Ext
import com.jetbrains.rd.generator.nova.PredefinedType
import com.jetbrains.rd.generator.nova.call
import com.jetbrains.rd.generator.nova.property
import com.jetbrains.rider.model.nova.ide.SolutionModel

object ProtocolModel : Ext(SolutionModel.Solution) {
init {
property("cliVersion", PredefinedType.string)
call("getCliVersion", PredefinedType.void, PredefinedType.string)
}
}
1 change: 1 addition & 0 deletions src/dotnet/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project>

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>Latest</LangVersion>
<NoWarn>MSB3277</NoWarn>
<NoPackageAnalysis>true</NoPackageAnalysis>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

Expand All @@ -10,7 +9,6 @@
<PackageReference Include="JetBrains.ReSharper.SDK.Tests" Version="$(SdkVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
<PackageReference Update="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
</ItemGroup>

<ItemGroup>
Expand Down
6 changes: 6 additions & 0 deletions src/dotnet/ReSharperPlugin.ODataCliUi/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace ReSharperPlugin.ODataCliUi;

public static class Constants
{
public const string ODataCliPackageId = "microsoft.odata.cli";
}
57 changes: 57 additions & 0 deletions src/dotnet/ReSharperPlugin.ODataCliUi/PluginHost.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using JetBrains.Core;
using JetBrains.Lifetimes;
using JetBrains.ProjectModel;
using JetBrains.ProjectModel.NuGet.DotNetTools;
using JetBrains.Rd.Tasks;
using JetBrains.ReSharper.Feature.Services.Protocol;
using JetBrains.Rider.Model;
using System;
using System.Linq;

namespace ReSharperPlugin.ODataCliUi;

[SolutionComponent]
public sealed class PluginHost : IDisposable
{
private readonly Tracker _tracker;
private readonly ProtocolModel _protocolModel;

public PluginHost(ISolution solution, Tracker tracker)
{
_protocolModel = solution.GetProtocolSolution().GetProtocolModel();
_protocolModel.GetCliVersion.SetSync(GetCliVersion);

_tracker = tracker;
tracker.DotNetToolCacheChanged += OnDotNetToolCacheChanged;
tracker.Start();
}

private string GetCliVersion(Lifetime lifetime, Unit unit) => _protocolModel.CliVersion.Value;

private void OnDotNetToolCacheChanged(DotNetToolCache cache)
{
var localTool = cache.ToolLocalCache.GetAllLocalTools().FirstOrDefault(t => t.PackageId == Constants.ODataCliPackageId);
if (localTool is not null)
{
_protocolModel.CliVersion.Value = $"Local, {localTool.Version}";
return;
}

var globalTool = cache.ToolGlobalCache.GetGlobalTool(Constants.ODataCliPackageId)?.FirstOrDefault();
if (globalTool is not null)
{
_protocolModel.CliVersion.Value = $"Global, {globalTool.Version}";
return;
}

_protocolModel.CliVersion.Value = "Not installed";
}

public void Dispose()
{
if (_tracker is not null)
{
_tracker.DotNetToolCacheChanged -= OnDotNetToolCacheChanged;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>ReSharperPlugin.ODataCliUi</AssemblyName>
<RootNamespace>$(AssemblyName)</RootNamespace>
<IsPackable>false</IsPackable>
Expand Down
36 changes: 36 additions & 0 deletions src/dotnet/ReSharperPlugin.ODataCliUi/Tracker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using JetBrains.Lifetimes;
using JetBrains.ProjectModel;
using JetBrains.ProjectModel.NuGet.DotNetTools;
using JetBrains.Threading;
using System;

namespace ReSharperPlugin.ODataCliUi;

[SolutionComponent]
public class Tracker
{
private readonly JetFastSemiReenterableRWLock _lock = new();
private readonly Lifetime _lifetime;
private readonly SolutionDotnetToolsTracker _dotnetToolsTracker;

public event Action<DotNetToolCache> DotNetToolCacheChanged;

public Tracker(Lifetime lifetime, SolutionDotnetToolsTracker dotnetToolsTracker)
{
_lifetime = lifetime;
_dotnetToolsTracker = dotnetToolsTracker;
}

public void Start()
{
_dotnetToolsTracker.DotNetToolCache.Change.Advise(_lifetime, args =>
{
if (!args.HasNew || args.New is null)
return;

using var _ = _lock.UsingWriteLock();
var cache = args.New;
DotNetToolCacheChanged?.Invoke(cache);
});
}
}
6 changes: 6 additions & 0 deletions src/dotnet/ReSharperPlugin.ODataCliUi/ZoneMarker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using JetBrains.Application.BuildScript.Application.Zones;

namespace ReSharperPlugin.ODataCliUi;

[ZoneMarker]
public class ZoneMarker;
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
package com.jetbrains.rider.plugins.odatacliui
package com.jetbrains.rider.plugins.odatacliui.actions

import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.project.Project
import com.intellij.openapi.rd.util.launchOnUi
import com.intellij.openapi.rd.util.lifetime
import com.jetbrains.rd.ide.model.protocolModel
import com.jetbrains.rider.plugins.odatacliui.dialogs.CliDialog
import com.jetbrains.rider.plugins.odatacliui.extensions.entityForAction
import com.jetbrains.rider.plugins.odatacliui.models.CliDialogModel
import com.jetbrains.rider.projectView.solution
import com.jetbrains.rider.projectView.workspace.isProject
import com.jetbrains.rider.projectView.workspace.isWebReferenceFolder

class OpenCliDialogAction : AnAction() {
override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return

val cliVersion = getCliVersion(project)
val dialogModel = CliDialogModel(cliVersion)

project.lifetime.launchOnUi {
val dialog = CliDialog()
val dialog = CliDialog(dialogModel)
dialog.show()
}
}
Expand All @@ -24,4 +33,9 @@ class OpenCliDialogAction : AnAction() {
val entity = e.entityForAction
e.presentation.isVisible = entity.isWebReferenceFolder() || entity.isProject()
}

private fun getCliVersion(project: Project): String {
return project.solution.protocolModel.cliVersion.valueOrNull
?: project.solution.protocolModel.getCliVersion.sync(Unit)
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package com.jetbrains.rider.plugins.odatacliui
package com.jetbrains.rider.plugins.odatacliui.dialogs

import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.DialogPanel
import com.intellij.ui.components.JBTabbedPane
import com.intellij.ui.dsl.builder.*
import com.jetbrains.rider.plugins.odatacliui.models.CliDialogModel
import javax.swing.JComponent

@Suppress("UnstableApiUsage")
class CliDialog : DialogWrapper(false) {
class CliDialog(private val model: CliDialogModel) : DialogWrapper(false) {
init {
title = "OData Cli UI"
init()
Expand All @@ -23,18 +24,20 @@ class CliDialog : DialogWrapper(false) {

return panel {
row {
label("Global, 1.2.0")
label(model.cliVersion)
.label("OData Cli Version:")
.comment("Not installed? Follow <a href='https://learn.microsoft.com/en-us/odata/odatacli/getting-started#install'>instruction</a>")
}.bottomGap(BottomGap.MEDIUM)
row("Service name:") {
textField()
.align(AlignX.FILL)
.bindText(model.serviceName)
}
row("Metadata source:") {
textFieldWithBrowseButton(fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileDescriptor("xml"))
.align(AlignX.FILL)
.comment("The URI of the metadata document. The value must be set to a valid service document URI or a local file path", Int.MAX_VALUE)
.bindText(model.metadataSource)
}.bottomGap(BottomGap.SMALL)
row {
cell(tabbedPane)
Expand All @@ -50,46 +53,55 @@ class CliDialog : DialogWrapper(false) {
textField()
.align(AlignX.FILL)
.comment("The name of the generated file. If not provided, then the default name 'Reference.cs' is used", Int.MAX_VALUE)
.bindText(model.fileName)
}
row("--namespace-prefix") {
textField()
.align(AlignX.FILL)
.comment("The namespace of the client code generated. <b>Example: NorthWindService.Client</b> or it could be a name related to the OData endpoint that you are generating code for", Int.MAX_VALUE)
.bindText(model.namespacePrefix)
}
row("--excluded-operation-imports") {
textField()
.align(AlignX.FILL)
.comment("Comma-separated list of the names of operation imports to exclude from the generated code. <b>Example: ExcludedOperationImport1, ExcludedOperationImport2</b>", Int.MAX_VALUE)
.bindText(model.excludedOperationImports)
}
row("--excluded-bound-operations") {
textField()
.align(AlignX.FILL)
.comment("Comma-separated list of the names of bound operations to exclude from the generated code. <b>Example: BoundOperation1, BoundOperation2</b>", Int.MAX_VALUE)
.bindText(model.excludedBoundOperations)
}
row("--excluded-schema-types") {
textField()
.align(AlignX.FILL)
.comment("Comma-separated list of the names of entity types to exclude from the generated code. <b>Example: EntityType1, EntityType2, EntityType3</b>", Int.MAX_VALUE)
.bindText(model.excludedSchemaTypes)
}
row {
checkBox("--internal")
.align(AlignX.FILL)
.comment("Applies the internal class modifier on generated classes instead of public thereby making them invisible outside the assembly", Int.MAX_VALUE)
.bindSelected(model.internal)
}
row {
checkBox("--multiple-files")
.align(AlignX.FILL)
.comment("Split the generated classes into separate files instead of generating all the code in a single file", Int.MAX_VALUE)
.bindSelected(model.multipleFiles)
}
row {
checkBox("--ignore-unexpected-elements")
.align(AlignX.FILL)
.comment("This flag indicates whether to ignore unexpected elements and attributes in the metadata document and generate the client code if any", Int.MAX_VALUE)
.bindSelected(model.ignoreUnexpectedElements)
}
row {
checkBox("--upper-camel-case")
.align(AlignX.FILL)
.comment("Transforms names (class and property names) to upper camel-case so that to better conform with C# naming conventions", Int.MAX_VALUE)
.bindSelected(model.upperCamelCase)
}
}

Expand All @@ -98,11 +110,13 @@ class CliDialog : DialogWrapper(false) {
textField()
.align(AlignX.FILL)
.comment("Headers that will get sent along with the request when fetching the metadata document from the service. <b>Format: Header1:HeaderValue, Header2:HeaderValue</b>", Int.MAX_VALUE)
.bindText(model.customHeaders)
}
row("--proxy") {
textField()
.align(AlignX.FILL)
.comment("Proxy settings. <b>Format: domain\\\\user:password@SERVER:PORT</b>", Int.MAX_VALUE)
.bindText(model.proxy)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.jetbrains.rider.plugins.odatacliui.models

class CliDialogModel(val cliVersion: String) {
val serviceName = MutableProperty("")
val metadataSource = MutableProperty("")

val fileName = MutableProperty("")
val namespacePrefix = MutableProperty("")
val excludedOperationImports = MutableProperty("")
val excludedBoundOperations = MutableProperty("")
val excludedSchemaTypes = MutableProperty("")
val internal = MutableProperty(false)
val multipleFiles = MutableProperty(false)
val ignoreUnexpectedElements = MutableProperty(false)
val upperCamelCase = MutableProperty(true)

val customHeaders = MutableProperty("")
val proxy = MutableProperty("")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.jetbrains.rider.plugins.odatacliui.models

import com.intellij.ui.dsl.builder.MutableProperty

class MutableProperty<T>(defaultValue: T) : MutableProperty<T> {
private var internalValue = defaultValue

override fun get(): T = internalValue

override fun set(value: T) {
internalValue = value
}
}
2 changes: 1 addition & 1 deletion src/rider/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</description>

<actions>
<action class="com.jetbrains.rider.plugins.odatacliui.OpenCliDialogAction" id="odatacliui.OpenCliDialogAction" text="OData Reference...">
<action class="com.jetbrains.rider.plugins.odatacliui.actions.OpenCliDialogAction" id="odatacliui.OpenCliDialogAction" text="OData Reference...">
<add-to-group group-id="SolutionViewAddGroup.SpecialSection" anchor="after" relative-to-action="AddWebReferenceAction" />
<add-to-group group-id="SolutionExplorerPopupMenuForReferenceFolder" anchor="after" relative-to-action="AddWebReferenceAction" />
</action>
Expand Down
Loading