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

Metadata cleanup. Approval. Synchronization. #169

Merged
merged 53 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
03f0a0a
Pick up NodeSetModel 1.0.3
MarkusHorstmann Nov 30, 2022
f3c67c4
Special versioning semantics for core UA nodesets
MarkusHorstmann Nov 30, 2022
fcf4c43
Upload: properly match nodeset with legacy hash
MarkusHorstmann Nov 30, 2022
1bf11ca
GraphQL: enabled SubTypes query
MarkusHorstmann Nov 30, 2022
c044bb4
Whitespace
MarkusHorstmann Dec 1, 2022
59733bc
Test: Add OPC Core 1.05 nodeset
MarkusHorstmann Dec 1, 2022
4d57c09
NamespaceMetadata, Category and Organization tables. Search performance.
MarkusHorstmann Dec 17, 2022
15707c8
Legacy Metadata migration
MarkusHorstmann Dec 17, 2022
92ff699
Database migrations
MarkusHorstmann Dec 17, 2022
fd6bef8
Approval mechanism
MarkusHorstmann Dec 17, 2022
93269f8
CloudLibrary synchronization, download and upload
MarkusHorstmann Dec 17, 2022
a5b5dbd
Database file store option (development only)
MarkusHorstmann Dec 17, 2022
70930d8
File storage: make directory configurable
MarkusHorstmann Dec 17, 2022
5677a6f
Adjust to latest NodeSet utilities
MarkusHorstmann Dec 17, 2022
52ec39e
Client: mark some methods obsolete, code cleanup
MarkusHorstmann Dec 17, 2022
8b1bd1a
Tests: upload tests for versioning. Cleanup.
MarkusHorstmann Dec 17, 2022
412f45a
GraphQl schema: baseline (Prod 11-17-2022)
MarkusHorstmann Dec 17, 2022
f30e260
GraphQL schema: update
MarkusHorstmann Dec 17, 2022
73dd42d
Merge main
MarkusHorstmann Dec 17, 2022
fe3518f
Whitespace
MarkusHorstmann Dec 17, 2022
3c790a4
Fix build
MarkusHorstmann Dec 17, 2022
a4a8110
Test: adjust exprcted counts
MarkusHorstmann Dec 17, 2022
c2120f8
Test: one more adjustment
MarkusHorstmann Dec 17, 2022
e93d390
Client: GetNodeSets - allow exclusion of metadata, totalcount, depend…
MarkusHorstmann Dec 21, 2022
d5916a9
Code review feedback
MarkusHorstmann Jan 16, 2023
4e4b238
Client: pick up GraphQL.Query.Build 2.0.1 (datetime support etc.)
MarkusHorstmann Feb 15, 2023
506f7e5
GraphQL Client: change nodeSet(nodesetURI) parameter to modelUri (bre…
MarkusHorstmann Feb 15, 2023
e00e210
GraphQL: add nodeSet(modelUri) parameter (naming consistency), keep n…
MarkusHorstmann Feb 16, 2023
f055f51
Test: adjust to nodeSet(modelUri) breaking change in Client
MarkusHorstmann Feb 16, 2023
32f0252
GraphQL: move to HotChocolate 13.0.2
MarkusHorstmann Feb 16, 2023
bab9657
UserService: add ASP.Net roles as user claims
MarkusHorstmann Feb 16, 2023
bd9251f
Indexer: properly index unapproved nodesets
MarkusHorstmann Feb 16, 2023
3b7c5dc
Internal rename: nodesetUri to modelUri
MarkusHorstmann Feb 16, 2023
4a81fe2
Approval: add/remove additional properties, canceled/delete status, r…
MarkusHorstmann Feb 16, 2023
52b6a63
#170: Cannot upload any NodeSet2.xml via UA-CloudViewer
MarkusHorstmann Feb 16, 2023
57d7083
REST/GraphQL Allow retrieval/download of unapproved namespaces by ide…
MarkusHorstmann Feb 16, 2023
31b8cb6
Test: add approval test
MarkusHorstmann Feb 16, 2023
a26e423
Add policies to admin and approval
MarkusHorstmann Feb 16, 2023
70dfbe6
Client: add approval method
MarkusHorstmann Feb 16, 2023
9660b8e
Client: change License from Enum to String (breaking change)
MarkusHorstmann Feb 16, 2023
fc1a867
Sample, readme, sync minor updates
MarkusHorstmann Feb 16, 2023
30cd5e4
Whitespace
MarkusHorstmann Feb 16, 2023
073bdbf
Whitespace
MarkusHorstmann Feb 16, 2023
d3e96cd
Missing Test nodeset
MarkusHorstmann Feb 16, 2023
53ac836
Move to NodeSetModel 1.0.7
MarkusHorstmann Feb 16, 2023
80f3d01
Enable ASP.Net roles
MarkusHorstmann Feb 16, 2023
a4fd0c9
GraphQL: return unapproved nodeset if requested by identifier
MarkusHorstmann Feb 16, 2023
e02cbd2
Test: approve after upload
MarkusHorstmann Feb 17, 2023
33e619b
#159: GraphQL: ModellingRule should follow UK English spelling
MarkusHorstmann Feb 17, 2023
02334aa
#157: GraphQL: Add support for more easily querying all referenced no…
MarkusHorstmann Feb 17, 2023
43e9882
Allow blob deletion also release builds
MarkusHorstmann Feb 21, 2023
46adfe6
Upload namespace: overwrite orphaned blobs
MarkusHorstmann Feb 21, 2023
72b8494
Move file stores to FileStorage directory
MarkusHorstmann Feb 21, 2023
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
6 changes: 6 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ jobs:
- name: Restore dependencies CloudLibClientTests
run: dotnet restore
working-directory: Tests/CloudLibClientTests
- name: Restore dependencies CloudLibSync
working-directory: CloudLibSync
run: dotnet restore
- name: Restore dependencies CloudLibSyncAzureFunction
working-directory: CloudLibSyncAzureFunction
run: dotnet restore
- name: Build
run: dotnet build UA-CloudLibrary.sln --configuration Release --no-restore
- name: Test
Expand Down
350 changes: 350 additions & 0 deletions CloudLibSync/CloudLibSync.cs

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions CloudLibSync/CloudLibSync.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Opc.Ua.CloudLib.Client\Opc.Ua.Cloud.Library.Client.csproj" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Core" Version="1.4.370.12" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta3.22114.1" />
<PackageReference Include="System.CommandLine.NamingConventionBinder" Version="2.0.0-beta3.22114.1" />
</ItemGroup>

</Project>
73 changes: 73 additions & 0 deletions CloudLibSync/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using Microsoft.Extensions.Logging;
using Opc.Ua.CloudLib.Sync;

class Program : ILogger
{
public static Task<int> Main(string[] args)
{
return new Program().MainAsync(args);
}

public async Task<int> MainAsync(string[] args)
{
var downloadCommand = new Command("download", "Downloads all nodesets and their metadata from a Cloud Library to a local directory.")
{
new Argument<string>("sourceUrl") {},
new Argument<string>("sourceUserName") {},
new Argument<string>("sourcePassword") {},
new Option<string>("--localDir", () => "Downloads") { },
new Option<string>("--nodeSetXmlDir", "If specified the node sets without their metadata (XML only) will be written to this directory.") { },
};
downloadCommand.Handler = CommandHandler.Create(new CloudLibSync(this).DownloadAsync);

var syncCommand = new Command("sync", "Downloads all nodests and their metadata from a Cloud Library (source) and uploads it to another Cloud Library (target).")
{
new Argument<string>("sourceUrl") {},
new Argument<string>("sourceUserName") {},
new Argument<string>("sourcePassword") { },
new Argument<string>("targetUrl") { },
new Argument<string>("targetUserName") {},
new Argument<string>("targetPassword") {},
};
syncCommand.Handler = CommandHandler.Create(new CloudLibSync(this).SynchronizeAsync);


var uploadCommand = new Command("upload", "Uploads nodesets and their metadata from a local directory to a cloud library.")
{
new Argument<string>("targetUrl") {},
new Argument<string>("targetUserName") {},
new Argument<string>("targetPassword") {},
new Option<string>("--localDir", () => "Downloads") {},
new Option<string>("--fileName", "If specified, uploads only this nodeset file. Otherwise all files in --localDir are uploaded.") {},
};
uploadCommand.Handler = CommandHandler.Create(new CloudLibSync(this).UploadAsync);

var root = new RootCommand()
{
syncCommand,
downloadCommand,
uploadCommand,
};

await root.InvokeAsync(args);

return 0;
}

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
Console.WriteLine(formatter(state, exception));
}

public bool IsEnabled(LogLevel logLevel)
{
return true;
}

public IDisposable BeginScope<TState>(TState state)
{
return new MemoryStream();
}
}
22 changes: 22 additions & 0 deletions CloudLibSyncAzureFunction/CloudLibSyncAzureFunction.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CloudLibSync\CloudLibSync.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
66 changes: 66 additions & 0 deletions CloudLibSyncAzureFunction/CloudSyncFunction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Opc.Ua.Cloud.Library.Client;
using Opc.Ua.CloudLib.Sync;

[assembly: FunctionsStartup(typeof(CloudLibSyncAzureFunction.CloudSyncFunctionStartup))]

namespace CloudLibSyncAzureFunction
{

public class CloudLibSyncOptions
{
public UACloudLibClient.Options Source { get; set; }
public UACloudLibClient.Options Target { get; set; }
}

public class CloudSyncFunctionStartup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services
.AddOptions<CloudLibSyncOptions>()
.Configure<IConfiguration>((settings, configuration) => {
configuration.GetSection("CloudLibrarySync").Bind(settings);
});
}
}

public class CloudSyncFunction
{
public CloudSyncFunction(IOptions<CloudLibSyncOptions> options)
{
if (options == null || options.Value == null)
{
throw new ArgumentNullException(nameof(CloudLibSyncOptions));
}
if (options.Value.Source == null)
{
throw new ArgumentNullException(nameof(options.Value.Source));
}
if (options.Value.Target == null)
{
throw new ArgumentNullException(nameof(options.Value.Target));
}
_options = options.Value;
}
readonly CloudLibSyncOptions _options;

[FunctionName("CloudSyncFunction")]
public async Task Run([TimerTrigger("0 0 5 * * *", RunOnStartup = true)] TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
await new CloudLibSync(log).SynchronizeAsync(
_options.Source.EndPoint, _options.Source.Username, _options.Source.Password,
_options.Target.EndPoint, _options.Target.Username, _options.Target.Password).ConfigureAwait(false);
}
}
}

11 changes: 11 additions & 0 deletions CloudLibSyncAzureFunction/Properties/serviceDependencies.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"dependencies": {
"appInsights1": {
"type": "appInsights"
},
"storage1": {
"type": "storage",
"connectionId": "AzureWebJobsStorage"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"dependencies": {
"appInsights1": {
"type": "appInsights.sdk"
},
"storage1": {
"type": "storage.emulator",
"connectionId": "AzureWebJobsStorage"
}
}
}
12 changes: 12 additions & 0 deletions CloudLibSyncAzureFunction/host.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "2.0",
"functionTimeout": "00:10:00",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
}
}
7 changes: 7 additions & 0 deletions CloudLibSyncAzureFunction/local.settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
21 changes: 21 additions & 0 deletions CloudLibSyncAzureFunction/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
To configure the CloudLib Sync function, credentials for the source and target cloud libraries need to be provided.

For cloud hosting, add them in in the Azure portal.

For local development, add them to the local.settings.json:

```json
{
"Values": {
// ...
"CloudLibrarySync:Source:Url": "https://uacloudlibrary.opcfoundation.org/",
"CloudLibrarySync:Source:Username": "<yourUsername>",
"CloudLibrarySync:Source:Password": "<yourPassword>",
"CloudLibrarySync:Target:Url": "https://localhost:5001/",
"CloudLibrarySync:Target:Username": "<yourOtherUsername>",
"CloudLibrarySync:Target:Password": "<yourOtherPassword>"
}
}
```

tail -f LogFiles/Application/Functions/Function/CloudSyncFunction/*
46 changes: 46 additions & 0 deletions Opc.Ua.CloudLib.Client/GraphQlExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* ========================================================================
* Copyright (c) 2005-2021 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* The complete license agreement can be found here:
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/

namespace Opc.Ua.Cloud.Library.Client
{
using System;
using GraphQL.Query.Builder;

static class GraphQlExtensions
{
public static IQuery<TSource> AddFields<TSource>(this IQuery<TSource> This, Func<IQuery<TSource>, IQuery<TSource>> addFields, bool skip = false)
{
if (skip)
{
return This;
}
return addFields(This);
}
}
}
Loading