diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 77fd0c5..87ec22d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,8 +30,13 @@ jobs: - name: Test run: dotnet test src/fdc3-dotnet.sln --no-build --verbosity normal --collect:"XPlat Code Coverage" --configuration Release - - name: Pack - run: dotnet pack src/fdc3-dotnet.sln --no-build --configuration Release --output packages + - name: Pack Fdc3 + run: + dotnet pack src/Fdc3/MorganStanley.Fdc3.csproj --no-build --configuration Release --output packages + + - name: Pack Fdc3.Json + run: + dotnet pack src/Fdc3.Json/MorganStanley.Fdc3.Json.csproj --no-build --configuration Release --output packages - name: Upload uses: actions/upload-artifact@v3 diff --git a/src/Examples/WpfFdc3/Fdc3/Channel.cs b/src/Examples/WpfFdc3/Fdc3/Channel.cs new file mode 100644 index 0000000..fde4a8c --- /dev/null +++ b/src/Examples/WpfFdc3/Fdc3/Channel.cs @@ -0,0 +1,63 @@ +/* + * Morgan Stanley makes this available to you under the Apache License, + * Version 2.0 (the "License"). You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0. + * + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Unless required by applicable law or agreed + * to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +using MorganStanley.Fdc3; +using MorganStanley.Fdc3.Context; +using Prism.Events; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace WpfFdc3.Fdc3 +{ + public class Channel : IChannel + { + private readonly IEventAggregator _eventAggregator; + private readonly Dictionary _lastContexts = new Dictionary(); + private IContext? _lastContext; + + public Channel(string id, string type) + { + this.Id = id; + this.Type = type; + _eventAggregator = new EventAggregator(); + } + + public string Id { get; } + + public string Type { get; } + + public IDisplayMetadata? DisplayMetadata => throw new System.NotImplementedException(); + + public Task AddContextListener(string? contextType, ContextHandler handler) where T : IContext + { + return Task.Run(() => { + return new Listener(_eventAggregator, contextType, handler); + }); + } + + public Task Broadcast(IContext context) + { + return Task.Run(() => + { + _lastContexts[context.Type] = _lastContext = context; + _eventAggregator.GetEvent().Publish(context); + }); + } + + public Task GetCurrentContext(string? contextType) + { + return Task.Run(() => (contextType != null) ? _lastContexts[contextType] : _lastContext); + } + } +} diff --git a/src/Examples/WpfFdc3/Fdc3/ContextEvent.cs b/src/Examples/WpfFdc3/Fdc3/ContextEvent.cs new file mode 100644 index 0000000..f9e9738 --- /dev/null +++ b/src/Examples/WpfFdc3/Fdc3/ContextEvent.cs @@ -0,0 +1,23 @@ +/* + * Morgan Stanley makes this available to you under the Apache License, + * Version 2.0 (the "License"). You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0. + * + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Unless required by applicable law or agreed + * to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +using MorganStanley.Fdc3.Context; +using Prism.Events; + +namespace WpfFdc3.Fdc3 +{ + internal class ContextEvent : PubSubEvent + { + } +} diff --git a/src/Examples/WpfFdc3/Fdc3/DesktopAgent.cs b/src/Examples/WpfFdc3/Fdc3/DesktopAgent.cs index 16aec42..d46662a 100644 --- a/src/Examples/WpfFdc3/Fdc3/DesktopAgent.cs +++ b/src/Examples/WpfFdc3/Fdc3/DesktopAgent.cs @@ -16,15 +16,24 @@ using MorganStanley.Fdc3.Context; using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; namespace WpfFdc3.Fdc3 { internal class DesktopAgent : IDesktopAgent { + private readonly List _channels = new List(); + private IChannel? _currentChannel; + + public DesktopAgent() + { + _channels.Add(new Channel("global", "app")); + } + public Task AddContextListener(string? contextType, ContextHandler handler) where T : IContext { - throw new NotImplementedException(); + return _currentChannel?.AddContextListener(contextType, handler); } public Task AddIntentListener(string intent, IntentHandler handler) where T : IContext @@ -34,7 +43,12 @@ public Task AddIntentListener(string intent, IntentHandler hand public Task Broadcast(IContext context) { - throw new NotImplementedException(); + if (_currentChannel != null) + { + return Task.Run(() => _currentChannel.Broadcast(context)); + } + + throw new Exception("Not joined to a channel"); } public Task CreatePrivateChannel() @@ -64,7 +78,7 @@ public Task GetAppMetadata(IAppIdentifier app) public Task GetCurrentChannel() { - throw new NotImplementedException(); + return Task.Run(() => _currentChannel); } @@ -80,22 +94,43 @@ public Task GetInfo() public Task GetOrCreateChannel(string channelId) { - throw new NotImplementedException(); + return Task.Run(() => + { + IChannel channel = _channels.First(channel => channel.Id == channelId); + if (channel == null) + { + channel = new Channel(channelId, "app"); + _channels.Add(channel); + } + + return channel; + }); } public Task> GetUserChannels() { - throw new NotImplementedException(); + return Task.Run>(() => _channels ); } public Task JoinUserChannel(string channelId) { - throw new NotImplementedException(); + return Task.Run(() => + { + IChannel channel = _channels.First(channel => channel.Id == channelId); + if (channel != null) + { + _currentChannel = channel; + } + else + { + throw new Exception(ChannelError.NoChannelFound); + } + }); } public Task LeaveCurrentChannel() { - throw new NotImplementedException(); + return Task.Run(() => _currentChannel = null); } public Task Open(IAppIdentifier app, IContext? context = null) diff --git a/src/Examples/WpfFdc3/Fdc3/Listener.cs b/src/Examples/WpfFdc3/Fdc3/Listener.cs new file mode 100644 index 0000000..a934481 --- /dev/null +++ b/src/Examples/WpfFdc3/Fdc3/Listener.cs @@ -0,0 +1,45 @@ +/* + * Morgan Stanley makes this available to you under the Apache License, + * Version 2.0 (the "License"). You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0. + * + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. Unless required by applicable law or agreed + * to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +using MorganStanley.Fdc3; +using MorganStanley.Fdc3.Context; +using Prism.Events; + +namespace WpfFdc3.Fdc3 +{ + internal class Listener : IListener where T : IContext + { + private IEventAggregator _eventAggregator; + private System.Action _callback; + + internal Listener(IEventAggregator eventAggregator, string? contextType, ContextHandler handler) + { + _eventAggregator = eventAggregator; + _callback = context => + { + if (contextType == context.Type) + { + handler((T)context, null); + } + }; + + _eventAggregator.GetEvent().Subscribe(_callback); + } + + public void Unsubscribe() + { + _eventAggregator.GetEvent().Unsubscribe(_callback); + } + } +} diff --git a/src/Examples/WpfFdc3/ViewModels/WorkbenchViewModel.cs b/src/Examples/WpfFdc3/ViewModels/WorkbenchViewModel.cs index def767f..ebf7ec1 100644 --- a/src/Examples/WpfFdc3/ViewModels/WorkbenchViewModel.cs +++ b/src/Examples/WpfFdc3/ViewModels/WorkbenchViewModel.cs @@ -14,21 +14,115 @@ */ using MorganStanley.Fdc3; +using MorganStanley.Fdc3.Context; +using MorganStanley.Fdc3.Json.Serialization; +using Newtonsoft.Json; +using Prism.Commands; +using System.Collections.Generic; +using System.ComponentModel; +using System.Windows.Input; namespace WpfFdc3.ViewModels { - public class WorkbenchViewModel + public class WorkbenchViewModel : INotifyPropertyChanged { - private readonly IDesktopAgent DesktopAgent; + private readonly IDesktopAgent _desktopAgent; + private IListener? _listener; + private ICommand? _joinChannelCommand; + private ICommand? _addContextListenerCommand; + private ICommand? _broadcastContextCommand; + private Fdc3JsonSerializerSettings SerializerSettings = new Fdc3JsonSerializerSettings() { Formatting = Formatting.Indented }; public WorkbenchViewModel(IDesktopAgent desktopAgent) { - this.DesktopAgent = desktopAgent; + _desktopAgent = desktopAgent; } public IImplementationMetadata? ImplementionMetadata { - get { return this.DesktopAgent?.GetInfo()?.Result; } + get { return _desktopAgent?.GetInfo()?.Result; } } + + public IEnumerable? AvailableChannels + { + get { return _desktopAgent?.GetUserChannels()?.Result; } + } + + public IChannel? SelectedChannel { get; set; } + + public ICommand JoinChannelCommand + { + get + { + return _joinChannelCommand ?? (_joinChannelCommand = new DelegateCommand(() => + { + if (this.SelectedChannel != null) + { + _desktopAgent.JoinUserChannel(this.SelectedChannel.Id); + } + })); + } + } + + public IEnumerable AvailableContexts + { + get + { + return new IContext[] + { + new Contact(new ContactID() {Email="jane.doe@mail.com"}, "Jane Doe"), + new Instrument(new InstrumentID() {Ticker = "MSFT", RIC="MSFT.OQ", ISIN="US5949181045"}), + new Position(200000, new Instrument(new InstrumentID() {Ticker = "AAPL"})) + }; + } + } + + public IContext? SelectedContextListener { get; set; } + + public string? LastContextMessage { get; private set; } + + public ICommand AddContextListenerCommand + { + get + { + return _addContextListenerCommand ?? (_addContextListenerCommand = new DelegateCommand(async () => + { + if (this.SelectedContextListener != null) + { + _listener?.Unsubscribe(); + + _listener = await _desktopAgent.AddContextListener(this.SelectedContextListener.Type, (context, contextMetadata) => + { + this.LastContextMessage = JsonConvert.SerializeObject(context, this.SerializerSettings); + this.OnPropertyChanged("LastContextMessage"); + }); + } + })); + } + } + + public IContext? SelectedContext { get; set; } + + + public ICommand BroadcastContextCommand + { + get + { + return _broadcastContextCommand ?? (_broadcastContextCommand = new DelegateCommand(() => + { + if (this.SelectedContext != null) + { + _desktopAgent.Broadcast(this.SelectedContext); + } + })); + } + } + + protected void OnPropertyChanged(string property) + { + this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); + } + + public event PropertyChangedEventHandler? PropertyChanged; } } diff --git a/src/Examples/WpfFdc3/Views/Workbench.xaml b/src/Examples/WpfFdc3/Views/Workbench.xaml index 49b634d..0c02479 100644 --- a/src/Examples/WpfFdc3/Views/Workbench.xaml +++ b/src/Examples/WpfFdc3/Views/Workbench.xaml @@ -68,11 +68,54 @@ - + + + + + + + Join Channel + + + + + + + + + + Broadcast Context + + + + + + + + + + Add Context Listener + + + + + + + + + + + - + + + + + + + diff --git a/src/Examples/WpfFdc3/WpfFdc3.csproj b/src/Examples/WpfFdc3/WpfFdc3.csproj index 2632f02..585ee88 100644 --- a/src/Examples/WpfFdc3/WpfFdc3.csproj +++ b/src/Examples/WpfFdc3/WpfFdc3.csproj @@ -11,6 +11,10 @@ + + + +