├── .gitattributes ├── .github └── workflows │ ├── main.yml │ ├── pr.yml │ └── release.yml ├── .gitignore ├── Abp.Push.sln ├── LICENSE ├── NuGet.Config ├── build.cake ├── build.ps1 ├── common.props ├── nupkg └── pack.ps1 ├── readme.md ├── src ├── Abp.Push.Common │ ├── Abp.Push.Common.csproj │ ├── Abp.Push.Common.nuspec │ └── Push │ │ ├── AbpPushCommonModule.cs │ │ ├── AbpPushConsts.cs │ │ ├── AbpPushSettingNames.cs │ │ ├── AbpPushSettingProvider.cs │ │ ├── Configuration │ │ ├── AbpPushConfiguration.cs │ │ ├── AbpPushDeviceStoreConfiguration.cs │ │ ├── AbpPushModuleConfigurationExtensions.cs │ │ ├── AbpPushRequestStoreConfiguration.cs │ │ ├── AbpPushStoreConfiguration.cs │ │ ├── IAbpPushConfiguration.cs │ │ ├── IAbpPushDeviceStoreConfiguration.cs │ │ ├── IAbpPushRequestStoreConfiguration.cs │ │ └── IAbpPushStoreConfiguration.cs │ │ ├── Devices │ │ ├── AbpPushDevice.cs │ │ ├── AbpPushDeviceManager.cs │ │ ├── AbpPushDeviceManagerExtensions.cs │ │ ├── DeviceIdentifier.cs │ │ ├── DeviceIdentifierExtensions.cs │ │ ├── DevicePlatformInfo.cs │ │ ├── IDeviceIdentifier.cs │ │ ├── IHasDeviceInfo.cs │ │ ├── IPushDeviceStore.cs │ │ ├── PushDeviceData.cs │ │ ├── PushDeviceExtensions.cs │ │ └── PushDeviceInfo.cs │ │ ├── IPushDefinition.cs │ │ ├── IPushDefinitionContext.cs │ │ ├── IPushDefinitionManager.cs │ │ ├── Localization │ │ ├── AbpPush.xml │ │ └── AbpPushLocalizationConfigurer.cs │ │ ├── Providers │ │ ├── AbpPushProviderManager.cs │ │ ├── IPushApiClient.cs │ │ ├── IPushProviderManager.cs │ │ ├── IPushServiceProvider.cs │ │ ├── PushApiClientBase.cs │ │ ├── PushPayload.cs │ │ ├── PushServiceProviderBase.cs │ │ └── ServiceProviderInfo.cs │ │ ├── PushDefinition.cs │ │ ├── PushDefinitionContext.cs │ │ ├── PushDefinitionManager.cs │ │ ├── PushDefinitionManagerExtensions.cs │ │ ├── PushDefinitionProvider.cs │ │ └── Requests │ │ ├── AbpPushRequestDistributor.cs │ │ ├── AbpPushRequestPublisher.cs │ │ ├── AbpPushRequestSubscriptionManager.cs │ │ ├── IPushRequestDistributor.cs │ │ ├── IPushRequestPublisher.cs │ │ ├── IPushRequestStore.cs │ │ ├── IPushRequestSubscriptionManager.cs │ │ ├── NullPushRequestStore.cs │ │ ├── PushRequest.cs │ │ ├── PushRequestData.cs │ │ ├── PushRequestDistributionJob.cs │ │ ├── PushRequestDistributionJobArgs.cs │ │ ├── PushRequestEntityData.cs │ │ ├── PushRequestPriority.cs │ │ ├── PushRequestPublisherExtensions.cs │ │ ├── PushRequestSubscription.cs │ │ └── PushRequestSubscriptionManagerExtensions.cs ├── Abp.Push.EntityFrameworkCore │ ├── Abp.Push.EntityFrameworkCore.csproj │ ├── Abp.Push.EntityFrameworkCore.nuspec │ └── Push │ │ └── EntityFrameworkCore │ │ ├── AbpPushEntityFrameworkCoreConfigurationExtensions.cs │ │ ├── AbpPushEntityFrameworkCoreModule.cs │ │ └── IAbpPushDbContext.cs └── Abp.Push │ ├── Abp.Push.csproj │ ├── Abp.Push.nuspec │ └── Push │ ├── AbpPushModule.cs │ ├── Configuration │ └── AbpPushModuleConfigurationExtensions.cs │ ├── Devices │ └── AbpPersistentPushDeviceStore.cs │ └── Requests │ ├── AbpInMemoryPushRequestStore.cs │ └── AbpPersistentPushRequestStore.cs ├── test └── Abp.Push.Tests │ ├── Abp.Push.Tests.csproj │ ├── Push │ ├── PushRequestDistributor_Tests.cs │ └── PushRequestPublisher_Tests.cs │ └── TestBaseWithLocalIocManager.cs └── tools ├── gitlink └── GitLink.exe └── nuget └── nuget.exe /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | timeout-minutes: 15 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Setup .NET 14 | uses: actions/setup-dotnet@v1 15 | with: 16 | dotnet-version: 5.0.x 17 | - name: Restore dependencies 18 | run: dotnet restore 19 | - name: Build 20 | run: dotnet build --no-restore 21 | - name: Test 22 | run: dotnet test --no-build --verbosity normal 23 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: PR Tests 2 | 3 | on: 4 | pull_request: 5 | branches: [ main ] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | timeout-minutes: 15 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Setup .NET 14 | uses: actions/setup-dotnet@v1 15 | with: 16 | dotnet-version: 5.0.x 17 | - name: Restore dependencies 18 | run: dotnet restore 19 | - name: Build 20 | run: dotnet build --no-restore 21 | - name: Test 22 | run: dotnet test --no-build --verbosity normal 23 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Nuget Releases 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v[0-9]+.[0-9]+.[0-9]+" 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 15 12 | steps: 13 | - name: Checkout from git 14 | uses: actions/checkout@v2 15 | - name: Verify commit exists in origin/main 16 | run: | 17 | git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/* 18 | git branch --remote --contains | grep origin/main 19 | - name: Set VERSION variable from tag 20 | run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV 21 | - name: Setup .NET 22 | uses: actions/setup-dotnet@v1 23 | with: 24 | dotnet-version: 5.0.x 25 | - name: Restore dependencies 26 | run: dotnet restore 27 | - name: Build 28 | run: dotnet build --configuration Release /p:Version=${VERSION} 29 | - name: Test 30 | run: dotnet test --configuration Release /p:Version=${VERSION} --no-build 31 | - name: Pack 32 | run: dotnet pack --configuration Release /p:Version=${VERSION} --no-build --output . 33 | - name: Push 34 | run: | 35 | dotnet nuget push src/Abp.Push.Common/Abp.Push.Common.nupkg --source https://nuget.pkg.github.com/acraven/index.json --api-key ${GITHUB_TOKEN} 36 | dotnet nuget push src/Abp.Push/Abp.Push.nupkg --source https://nuget.pkg.github.com/acraven/index.json --api-key ${GITHUB_TOKEN} 37 | dotnet nuget push src/Abp.Push.EntityFrameworkCore/Abp.Push.EntityFrameworkCore.nupkg --source https://nuget.pkg.github.com/acraven/index.json --api-key ${GITHUB_TOKEN} 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # Logs 6 | Logs/ 7 | 8 | # Generated files 9 | project.lock.json 10 | .vs/ 11 | 12 | # Nuget packages 13 | nupkg/*.nupkg 14 | nupkg/push.bat 15 | 16 | # Profile images 17 | ProfileImages/ 18 | 19 | # mstest test results 20 | TestResults 21 | 22 | ## Ignore Visual Studio temporary files, build results, and 23 | ## files generated by popular Visual Studio add-ons. 24 | 25 | # User-specific files 26 | *.suo 27 | *.user 28 | *.sln.docstates 29 | 30 | # Build results 31 | [Dd]ebug/ 32 | [Rr]elease/ 33 | x64/ 34 | *_i.c 35 | *_p.c 36 | *.ilk 37 | *.meta 38 | *.obj 39 | *.pch 40 | *.pdb 41 | *.pgc 42 | *.pgd 43 | *.rsp 44 | *.sbr 45 | *.tlb 46 | *.tli 47 | *.tlh 48 | *.tmp 49 | *.log 50 | *.vspscc 51 | *.vssscc 52 | .builds 53 | 54 | # Visual C++ cache files 55 | ipch/ 56 | *.aps 57 | *.ncb 58 | *.opensdf 59 | *.sdf 60 | 61 | # Visual Studio profiler 62 | *.psess 63 | *.vsp 64 | *.vspx 65 | 66 | # JetBrain 67 | .idea/ 68 | 69 | # Guidance Automation Toolkit 70 | *.gpState 71 | 72 | # ReSharper is a .NET coding add-in 73 | _ReSharper* 74 | 75 | # NCrunch 76 | *.ncrunch* 77 | .*crunch*.local.xml 78 | 79 | # Installshield output folder 80 | [Ee]xpress 81 | 82 | # DocProject is a documentation generator add-in 83 | DocProject/buildhelp/ 84 | DocProject/Help/*.HxT 85 | DocProject/Help/*.HxC 86 | DocProject/Help/*.hhc 87 | DocProject/Help/*.hhk 88 | DocProject/Help/*.hhp 89 | DocProject/Help/Html2 90 | DocProject/Help/html 91 | 92 | # Click-Once directory 93 | publish 94 | 95 | # Publish Web Output 96 | *.Publish.xml 97 | 98 | # NuGet Packages Directory 99 | packages 100 | 101 | # Windows Azure Build Output 102 | csx 103 | *.build.csdef 104 | 105 | # Windows Store app package directory 106 | AppPackages/ 107 | 108 | # Others 109 | [Bb]in 110 | [Oo]bj 111 | sql 112 | TestResults 113 | [Tt]est[Rr]esult* 114 | *.Cache 115 | ClientBin 116 | [Ss]tyle[Cc]op.* 117 | ~$* 118 | *.dbmdl 119 | Generated_Code #added for RIA/Silverlight projects 120 | 121 | # Backup & report files from converting an old project file to a newer 122 | # Visual Studio version. Backup files are not needed, because we have git ;-) 123 | _UpgradeReport_Files/ 124 | Backup*/ 125 | UpgradeLog*.XML 126 | src/.vs/config/applicationhost.config 127 | 128 | # GitLink 129 | !GitLink.exe 130 | !nuget.exe 131 | !SQLite.Interop.dll 132 | -------------------------------------------------------------------------------- /Abp.Push.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2010 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{CDCAEAFD-A61C-4525-98F5-E8F7227FE7B3}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0ACDB552-6577-497F-8DC5-F38941B5D395}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abp.Push.Common", "src\Abp.Push.Common\Abp.Push.Common.csproj", "{B4F6E73F-D573-461C-A4A6-8F838C50859F}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abp.Push", "src\Abp.Push\Abp.Push.csproj", "{62081BD6-0FAF-4EF1-BCE4-619AC948C7AF}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution-items", "solution-items", "{0A8896A1-EC2E-44D8-BD7E-34D2E46A16CF}" 15 | ProjectSection(SolutionItems) = preProject 16 | .gitattributes = .gitattributes 17 | .gitignore = .gitignore 18 | appveyor.yml = appveyor.yml 19 | build.cake = build.cake 20 | build.ps1 = build.ps1 21 | common.props = common.props 22 | LICENSE = LICENSE 23 | README.md = README.md 24 | EndProjectSection 25 | EndProject 26 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abp.Push.Tests", "test\Abp.Push.Tests\Abp.Push.Tests.csproj", "{935CBB37-42F7-4100-91BD-425FC2E1A72B}" 27 | EndProject 28 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abp.Push.EntityFrameworkCore", "src\Abp.Push.EntityFrameworkCore\Abp.Push.EntityFrameworkCore.csproj", "{ADBFFA10-DF24-41DA-BC78-25F9F10942F3}" 29 | EndProject 30 | Global 31 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 32 | Debug|Any CPU = Debug|Any CPU 33 | Release|Any CPU = Release|Any CPU 34 | EndGlobalSection 35 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 36 | {B4F6E73F-D573-461C-A4A6-8F838C50859F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {B4F6E73F-D573-461C-A4A6-8F838C50859F}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {B4F6E73F-D573-461C-A4A6-8F838C50859F}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {B4F6E73F-D573-461C-A4A6-8F838C50859F}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {62081BD6-0FAF-4EF1-BCE4-619AC948C7AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {62081BD6-0FAF-4EF1-BCE4-619AC948C7AF}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {62081BD6-0FAF-4EF1-BCE4-619AC948C7AF}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {62081BD6-0FAF-4EF1-BCE4-619AC948C7AF}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {935CBB37-42F7-4100-91BD-425FC2E1A72B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {935CBB37-42F7-4100-91BD-425FC2E1A72B}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {935CBB37-42F7-4100-91BD-425FC2E1A72B}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {935CBB37-42F7-4100-91BD-425FC2E1A72B}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {ADBFFA10-DF24-41DA-BC78-25F9F10942F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {ADBFFA10-DF24-41DA-BC78-25F9F10942F3}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {ADBFFA10-DF24-41DA-BC78-25F9F10942F3}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {ADBFFA10-DF24-41DA-BC78-25F9F10942F3}.Release|Any CPU.Build.0 = Release|Any CPU 52 | EndGlobalSection 53 | GlobalSection(SolutionProperties) = preSolution 54 | HideSolutionNode = FALSE 55 | EndGlobalSection 56 | GlobalSection(NestedProjects) = preSolution 57 | {B4F6E73F-D573-461C-A4A6-8F838C50859F} = {CDCAEAFD-A61C-4525-98F5-E8F7227FE7B3} 58 | {62081BD6-0FAF-4EF1-BCE4-619AC948C7AF} = {CDCAEAFD-A61C-4525-98F5-E8F7227FE7B3} 59 | {935CBB37-42F7-4100-91BD-425FC2E1A72B} = {0ACDB552-6577-497F-8DC5-F38941B5D395} 60 | {ADBFFA10-DF24-41DA-BC78-25F9F10942F3} = {CDCAEAFD-A61C-4525-98F5-E8F7227FE7B3} 61 | EndGlobalSection 62 | GlobalSection(ExtensibilityGlobals) = postSolution 63 | SolutionGuid = {B2C237ED-C779-4CDF-96E9-CFD415CF820E} 64 | EndGlobalSection 65 | GlobalSection(MonoDevelopProperties) = preSolution 66 | Policies = $0 67 | $0.TextStylePolicy = $1 68 | $1.FileWidth = 80 69 | $1.TabsToSpaces = True 70 | $1.scope = text/x-csharp 71 | $0.CSharpFormattingPolicy = $2 72 | $2.scope = text/x-csharp 73 | EndGlobalSection 74 | EndGlobal 75 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2019 ryancyq 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build.cake: -------------------------------------------------------------------------------- 1 | #tool nuget:?package=vswhere 2 | #tool "nuget:?package=xunit.runner.console&version=2.3.0-beta5-build3769" 3 | 4 | ////////////////////////////////////////////////////////////////////// 5 | // ARGUMENTS 6 | ////////////////////////////////////////////////////////////////////// 7 | 8 | var projectName = "Abp.Push"; 9 | var solution = "./" + projectName + ".sln"; 10 | 11 | var target = Argument("target", "Default"); 12 | var configuration = Argument("configuration", "Release"); 13 | var toolpath = Argument("toolpath", @"tools"); 14 | var branch = Argument("branch", EnvironmentVariable("APPVEYOR_REPO_BRANCH")); 15 | 16 | var vsLatest = VSWhereLatest(); 17 | var msBuildPathX64 = (vsLatest==null) 18 | ? null 19 | : vsLatest.CombineWithFilePath("./MSBuild/15.0/Bin/amd64/MSBuild.exe"); 20 | 21 | var testProjects = new List> 22 | { 23 | new Tuple("Abp.Push.Tests", new[] { "net461", "netcoreapp2.1" }) 24 | }; 25 | 26 | 27 | 28 | var nugetPath = toolpath + "/nuget.exe"; 29 | 30 | ////////////////////////////////////////////////////////////////////// 31 | // TASKS 32 | ////////////////////////////////////////////////////////////////////// 33 | 34 | Task("Clean") 35 | .Does(() => 36 | { 37 | Information("Current Branch is:" + branch); 38 | CleanDirectories("./src/**/bin"); 39 | CleanDirectories("./src/**/obj"); 40 | CleanDirectories("./test/**/bin"); 41 | CleanDirectories("./test/**/obj"); 42 | }); 43 | 44 | Task("Restore-NuGet-Packages") 45 | .IsDependentOn("Clean") 46 | .Does(() => 47 | { 48 | NuGetRestore(solution); 49 | DotNetCoreRestore(solution); 50 | }); 51 | 52 | Task("Build") 53 | .IsDependentOn("Restore-NuGet-Packages") 54 | .Does(() => 55 | { 56 | MSBuild(solution, new MSBuildSettings(){Configuration = configuration, ToolPath = msBuildPathX64} 57 | .WithProperty("SourceLinkCreate","true")); 58 | }); 59 | 60 | Task("Run-Unit-Tests") 61 | .IsDependentOn("Build") 62 | .DoesForEach(testProjects, testProject => 63 | { 64 | foreach (string targetFramework in testProject.Item2) 65 | { 66 | Information($"Test execution started for target frameowork: {targetFramework}..."); 67 | var testProj = GetFiles($"./test/**/*{testProject.Item1}.csproj").First(); 68 | DotNetCoreTest(testProj.FullPath, new DotNetCoreTestSettings { Configuration = "Release", Framework = targetFramework }); 69 | } 70 | }) 71 | .DeferOnError(); 72 | 73 | 74 | ////////////////////////////////////////////////////////////////////// 75 | // TASK TARGETS 76 | ////////////////////////////////////////////////////////////////////// 77 | 78 | Task("Default") 79 | .IsDependentOn("Build") 80 | .IsDependentOn("Run-Unit-Tests"); 81 | 82 | ////////////////////////////////////////////////////////////////////// 83 | // EXECUTION 84 | ////////////////////////////////////////////////////////////////////// 85 | 86 | RunTarget(target); 87 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This is a Powershell script to bootstrap a Cake build. 4 | .DESCRIPTION 5 | This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) 6 | and execute your Cake build script with the parameters you provide. 7 | .PARAMETER Target 8 | The build script target to run. 9 | .PARAMETER Configuration 10 | The build configuration to use. 11 | .PARAMETER Verbosity 12 | Specifies the amount of information to be displayed. 13 | .PARAMETER WhatIf 14 | Performs a dry run of the build script. 15 | No tasks will be executed. 16 | .PARAMETER ScriptArgs 17 | Remaining arguments are added here. 18 | .LINK 19 | https://cakebuild.net 20 | #> 21 | 22 | [CmdletBinding()] 23 | Param( 24 | [string]$Target = "Default", 25 | [ValidateSet("Release", "Debug")] 26 | [string]$Configuration = "Release", 27 | [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] 28 | [string]$Verbosity = "Verbose", 29 | [switch]$WhatIf, 30 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] 31 | [string[]]$ScriptArgs 32 | ) 33 | 34 | $CakeVersion = "0.23.0" 35 | $DotNetChannel = "Current"; 36 | $DotNetVersion = "2.1.302"; 37 | $DotNetInstallerUri = "https://dot.net/v1/dotnet-install.ps1"; 38 | $NugetUrl = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" 39 | 40 | # Temporarily skip verification and opt-in to new in-proc NuGet 41 | $ENV:CAKE_SETTINGS_SKIPVERIFICATION='true' 42 | $ENV:CAKE_NUGET_USEINPROCESSCLIENT='true' 43 | 44 | # Make sure tools folder exists 45 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 46 | $ToolPath = Join-Path $PSScriptRoot "tools" 47 | if (!(Test-Path $ToolPath)) { 48 | Write-Verbose "Creating tools directory..." 49 | New-Item -Path $ToolPath -Type directory | out-null 50 | } 51 | 52 | ########################################################################### 53 | # INSTALL .NET CORE CLI 54 | ########################################################################### 55 | 56 | Function Remove-PathVariable([string]$VariableToRemove) 57 | { 58 | $path = [Environment]::GetEnvironmentVariable("PATH", "User") 59 | if ($path -ne $null) 60 | { 61 | $newItems = $path.Split(';', [StringSplitOptions]::RemoveEmptyEntries) | Where-Object { "$($_)" -inotlike $VariableToRemove } 62 | [Environment]::SetEnvironmentVariable("PATH", [System.String]::Join(';', $newItems), "User") 63 | } 64 | 65 | $path = [Environment]::GetEnvironmentVariable("PATH", "Process") 66 | if ($path -ne $null) 67 | { 68 | $newItems = $path.Split(';', [StringSplitOptions]::RemoveEmptyEntries) | Where-Object { "$($_)" -inotlike $VariableToRemove } 69 | [Environment]::SetEnvironmentVariable("PATH", [System.String]::Join(';', $newItems), "Process") 70 | } 71 | } 72 | 73 | # Get .NET Core CLI path if installed. 74 | $FoundDotNetCliVersion = $null; 75 | if (Get-Command dotnet -ErrorAction SilentlyContinue) { 76 | $FoundDotNetCliVersion = dotnet --version; 77 | } 78 | 79 | if($FoundDotNetCliVersion -ne $DotNetVersion) { 80 | $InstallPath = Join-Path $PSScriptRoot ".dotnet" 81 | if (!(Test-Path $InstallPath)) { 82 | mkdir -Force $InstallPath | Out-Null; 83 | } 84 | (New-Object System.Net.WebClient).DownloadFile($DotNetInstallerUri, "$InstallPath\dotnet-install.ps1"); 85 | & $InstallPath\dotnet-install.ps1 -Channel $DotNetChannel -Version $DotNetVersion -InstallDir $InstallPath; 86 | 87 | Remove-PathVariable "$InstallPath" 88 | $env:PATH = "$InstallPath;$env:PATH" 89 | } 90 | 91 | $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 92 | $env:DOTNET_CLI_TELEMETRY_OPTOUT=1 93 | 94 | ########################################################################### 95 | # INSTALL NUGET 96 | ########################################################################### 97 | 98 | # Make sure nuget.exe exists. 99 | $NugetPath = Join-Path $ToolPath "nuget.exe" 100 | if (!(Test-Path $NugetPath)) { 101 | Write-Host "Downloading NuGet.exe..." 102 | (New-Object System.Net.WebClient).DownloadFile($NugetUrl, $NugetPath); 103 | } 104 | 105 | ########################################################################### 106 | # INSTALL CAKE 107 | ########################################################################### 108 | 109 | # Make sure Cake has been installed. 110 | $CakePath = Join-Path $ToolPath "Cake.$CakeVersion/Cake.exe" 111 | if (!(Test-Path $CakePath)) { 112 | Write-Host "Installing Cake..." 113 | Invoke-Expression "&`"$NugetPath`" install Cake -Version $CakeVersion -OutputDirectory `"$ToolPath`"" | Out-Null; 114 | if ($LASTEXITCODE -ne 0) { 115 | Throw "An error occured while restoring Cake from NuGet." 116 | } 117 | } 118 | 119 | ########################################################################### 120 | # RUN BUILD SCRIPT 121 | ########################################################################### 122 | 123 | # Build the argument list. 124 | $Arguments = @{ 125 | target=$Target; 126 | configuration=$Configuration; 127 | verbosity=$Verbosity; 128 | dryrun=$WhatIf; 129 | }.GetEnumerator() | %{"--{0}=`"{1}`"" -f $_.key, $_.value }; 130 | 131 | # Start Cake 132 | Write-Host "Running build script..." 133 | Invoke-Expression "& `"$CakePath`" `"build.cake`" $Arguments $ScriptArgs" 134 | exit $LASTEXITCODE -------------------------------------------------------------------------------- /common.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | $(NoWarn);CS1591 5 | 6 | https://github.com/ryancyq/abp-push 7 | MIT 8 | git 9 | https://github.com/ryancyq/abp-push 10 | 11 | 12 | 13 | bin\$(Platform)\$(Configuration)\ 14 | ..\..\nupkg\ 15 | 16 | 17 | 18 | True 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | true 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /nupkg/pack.ps1: -------------------------------------------------------------------------------- 1 | # Paths 2 | $packFolder = (Get-Item -Path "./" -Verbose).FullName 3 | $slnPath = Join-Path $packFolder "../" 4 | $srcPath = Join-Path $slnPath "src" 5 | 6 | # List of projects 7 | $projects = ( 8 | "Abp.Push.Common", 9 | "Abp.Push", 10 | "Abp.Push.EntityFrameworkCore" 11 | ) 12 | 13 | # Rebuild solution 14 | Set-Location $slnPath 15 | & dotnet restore 16 | 17 | # Copy all nuget packages to the pack folder 18 | foreach($project in $projects) { 19 | 20 | $projectFolder = Join-Path $srcPath $project 21 | 22 | # Create nuget pack 23 | Set-Location $projectFolder 24 | Remove-Item -Recurse (Join-Path $projectFolder "bin/Release") 25 | & dotnet msbuild /p:Configuration=Release /p:SourceLinkCreate=true 26 | & dotnet msbuild /t:pack /p:Configuration=Release /p:SourceLinkCreate=true /p:IncludeReferencedProjects=true 27 | 28 | # Copy nuget package 29 | $projectPackPath = Join-Path $projectFolder ("/bin/Release/" + $project + ".*.nupkg") 30 | Move-Item $projectPackPath $packFolder 31 | 32 | } 33 | 34 | # Go back to the pack folder 35 | Set-Location $packFolder -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Abp Push Notification 2 | A push notification system built base on Abp Framework. 3 | 4 | ![Build Status](https://github.com/ryancyq/abp-push/actions/workflows/main.yml/badge.svg) 5 | 6 | ## Nuget Packages 7 | |Package|Status| 8 | |---|---| 9 | |Abp.Push.Common|[![NuGet](https://img.shields.io/nuget/vpre/abp.push.common.svg)](https://www.nuget.org/packages/Abp.Push.Common)| 10 | |Abp.Push|[![NuGet](https://img.shields.io/nuget/vpre/abp.push.svg)](https://www.nuget.org/packages/Abp.Push)| 11 | |Abp.Push.EntityFrameworkCore|[![NuGet](https://img.shields.io/nuget/vpre/abp.push.entityframeworkcore.svg)](https://www.nuget.org/packages/Abp.Push.EntityFrameworkCore)| 12 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Abp.Push.Common.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | netstandard2.0 7 | Abp Push.Common 8 | Abp.Push.Common 9 | Abp.Push.Common 10 | Abp 11 | false 12 | false 13 | false 14 | false 15 | false 16 | false 17 | true 18 | False 19 | Library 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Abp.Push.Common.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Abp.Push.Common 5 | $version$ 6 | $title$ 7 | Ryancyq 8 | Ryancyq 9 | true 10 | Abp Push Common Module 11 | Summary of changes made in this release of the package. 12 | Copyright 2019 13 | netstandard2.0 asp.net mvc abp aspnetboilerplate 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/AbpPushCommonModule.cs: -------------------------------------------------------------------------------- 1 | using Abp.Dependency; 2 | using Abp.Modules; 3 | using Abp.Push.Localization; 4 | using Abp.Push.Requests; 5 | using Abp.Reflection.Extensions; 6 | 7 | namespace Abp.Push 8 | { 9 | [DependsOn(typeof(AbpKernelModule))] 10 | public class AbpPushCommonModule : AbpModule 11 | { 12 | /// 13 | public override void PreInitialize() 14 | { 15 | //Configure settings 16 | Configuration.Settings.Providers.Add(); 17 | 18 | //Configure localizations 19 | AbpPushLocalizationConfigurer.Configure(Configuration.Localization); 20 | } 21 | 22 | /// 23 | public override void Initialize() 24 | { 25 | IocManager.RegisterAssemblyByConvention(typeof(AbpPushCommonModule).GetAssembly()); 26 | } 27 | 28 | public override void PostInitialize() 29 | { 30 | IocManager.RegisterIfNot(DependencyLifeStyle.Singleton); 31 | 32 | IocManager.Resolve().Initialize(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/AbpPushConsts.cs: -------------------------------------------------------------------------------- 1 | namespace Abp.Push 2 | { 3 | public static class AbpPushConsts 4 | { 5 | public const string LocalizationSourceName = "AbpPush"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/AbpPushSettingNames.cs: -------------------------------------------------------------------------------- 1 | namespace Abp.Push 2 | { 3 | /// 4 | /// Pre-defined setting names for push system. 5 | /// 6 | public static class AbpPushSettingNames 7 | { 8 | /// 9 | /// A top-level switch to enable/disable receiving push for a user. 10 | /// 11 | public const string Receive = "Abp.Push.Receive"; 12 | } 13 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/AbpPushSettingProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Abp.Configuration; 3 | using Abp.Localization; 4 | 5 | namespace Abp.Push 6 | { 7 | internal class AbpPushSettingProvider : SettingProvider 8 | { 9 | public override IEnumerable GetSettingDefinitions(SettingDefinitionProviderContext context) 10 | { 11 | return new[] 12 | { 13 | new SettingDefinition( 14 | AbpPushSettingNames.Receive, 15 | "true", 16 | L("AbpPushSettingNames.Receive"), 17 | scopes: SettingScopes.User, 18 | isVisibleToClients: true) 19 | }; 20 | } 21 | 22 | protected virtual LocalizableString L(string name) 23 | { 24 | return new LocalizableString(name, AbpPushConsts.LocalizationSourceName); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Configuration/AbpPushConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Abp.Collections; 3 | using Abp.Configuration.Startup; 4 | using Abp.Dependency; 5 | using Abp.Push.Devices; 6 | using Abp.Push.Providers; 7 | 8 | namespace Abp.Push.Configuration 9 | { 10 | internal class AbpPushConfiguration : IAbpPushConfiguration, ISingletonDependency 11 | { 12 | public IAbpStartupConfiguration AbpConfiguration { get; private set; } 13 | 14 | public IAbpPushStoreConfiguration StoreConfiguration { get; private set; } 15 | 16 | public ITypeList Providers { get; private set; } 17 | 18 | public List ServiceProviders { get; private set; } 19 | 20 | public List DevicePlatforms { get; private set; } 21 | 22 | public int MaxUserCountForForegroundDistribution { get; set; } 23 | 24 | public AbpPushConfiguration( 25 | IAbpStartupConfiguration abpConfiguration, 26 | AbpPushStoreConfiguration pushStoreConfiguration 27 | ) 28 | { 29 | AbpConfiguration = abpConfiguration; 30 | StoreConfiguration = pushStoreConfiguration; 31 | Providers = new TypeList(); 32 | ServiceProviders = new List(); 33 | DevicePlatforms = new List(); 34 | MaxUserCountForForegroundDistribution = 5; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Configuration/AbpPushDeviceStoreConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Abp.Configuration.Startup; 2 | using Abp.Dependency; 3 | 4 | namespace Abp.Push.Configuration 5 | { 6 | internal class AbpPushDeviceStoreConfiguration : IAbpPushDeviceStoreConfiguration, ISingletonDependency 7 | { 8 | public IAbpStartupConfiguration AbpConfiguration { get; private set; } 9 | 10 | public AbpPushDeviceStoreConfiguration(IAbpStartupConfiguration abpConfiguration) 11 | { 12 | AbpConfiguration = abpConfiguration; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Configuration/AbpPushModuleConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Abp.Configuration.Startup; 2 | 3 | namespace Abp.Push.Configuration 4 | { 5 | public static class AbpPushConfigurationExtensions 6 | { 7 | /// 8 | /// Used to configure ABP Push module. 9 | /// 10 | public static IAbpPushConfiguration AbpPush(this IModuleConfigurations configurations) 11 | { 12 | return configurations.AbpConfiguration.Get(); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Configuration/AbpPushRequestStoreConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Abp.Configuration.Startup; 2 | using Abp.Dependency; 3 | 4 | namespace Abp.Push.Configuration 5 | { 6 | internal class AbpPushRequestStoreConfiguration : IAbpPushRequestStoreConfiguration, ISingletonDependency 7 | { 8 | public IAbpStartupConfiguration AbpConfiguration { get; private set; } 9 | 10 | public AbpPushRequestStoreConfiguration(IAbpStartupConfiguration abpConfiguration) 11 | { 12 | AbpConfiguration = abpConfiguration; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Configuration/AbpPushStoreConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Abp.Dependency; 2 | 3 | namespace Abp.Push.Configuration 4 | { 5 | internal class AbpPushStoreConfiguration : IAbpPushStoreConfiguration, ISingletonDependency 6 | { 7 | public IAbpPushDeviceStoreConfiguration DeviceStore { get; private set; } 8 | 9 | public IAbpPushRequestStoreConfiguration RequestStore { get; private set; } 10 | 11 | public AbpPushStoreConfiguration( 12 | IAbpPushDeviceStoreConfiguration deviceStoreConfiguration, 13 | IAbpPushRequestStoreConfiguration requestStoreConfiguration 14 | ) 15 | { 16 | DeviceStore = deviceStoreConfiguration; 17 | RequestStore = requestStoreConfiguration; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Configuration/IAbpPushConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Abp.Collections; 3 | using Abp.Configuration.Startup; 4 | using Abp.Push.Devices; 5 | using Abp.Push.Providers; 6 | 7 | namespace Abp.Push.Configuration 8 | { 9 | /// 10 | /// Used to configure push system. 11 | /// 12 | public interface IAbpPushConfiguration 13 | { 14 | /// 15 | /// Gets the ABP configuration object. 16 | /// 17 | IAbpStartupConfiguration AbpConfiguration { get; } 18 | 19 | /// 20 | /// Gets the push store configuration. 21 | /// 22 | IAbpPushStoreConfiguration StoreConfiguration { get; } 23 | 24 | /// 25 | /// Gets the list of push definition providers. 26 | /// 27 | ITypeList Providers { get; } 28 | 29 | /// 30 | /// Gets the list of push service providers. 31 | /// 32 | List ServiceProviders { get; } 33 | 34 | /// 35 | /// Gets the list of device platforms. 36 | /// 37 | List DevicePlatforms { get; } 38 | 39 | int MaxUserCountForForegroundDistribution { get; } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Configuration/IAbpPushDeviceStoreConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Abp.Configuration.Startup; 2 | 3 | namespace Abp.Push.Configuration 4 | { 5 | /// 6 | /// Used to configure push device store. 7 | /// 8 | public interface IAbpPushDeviceStoreConfiguration 9 | { 10 | /// 11 | /// Gets the ABP configuration object. 12 | /// 13 | IAbpStartupConfiguration AbpConfiguration { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Configuration/IAbpPushRequestStoreConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Abp.Configuration.Startup; 2 | 3 | namespace Abp.Push.Configuration 4 | { 5 | /// 6 | /// Used to configure push reequest store. 7 | /// 8 | public interface IAbpPushRequestStoreConfiguration 9 | { 10 | /// 11 | /// Gets the ABP configuration object. 12 | /// 13 | IAbpStartupConfiguration AbpConfiguration { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Configuration/IAbpPushStoreConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Abp.Push.Configuration 2 | { 3 | /// 4 | /// Used to configure push system. 5 | /// 6 | public interface IAbpPushStoreConfiguration 7 | { 8 | IAbpPushDeviceStoreConfiguration DeviceStore { get; } 9 | 10 | IAbpPushRequestStoreConfiguration RequestStore { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Devices/AbpPushDevice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | using Abp.Domain.Entities; 5 | using Abp.Domain.Entities.Auditing; 6 | 7 | namespace Abp.Push.Devices 8 | { 9 | [Table("AbpPushDevices")] 10 | public abstract class AbpPushDevice : CreationAuditedEntity, IHasDeviceInfo, IMayHaveTenant 11 | { 12 | /// 13 | /// Maximum length of property. 14 | /// Value: 512. 15 | /// 16 | public const int MaxDeviceNameLength = 512; 17 | 18 | /// 19 | /// Maximum length of property. 20 | /// Value: 512. 21 | /// 22 | public const int MaxDevicePlatformLength = 256; 23 | 24 | /// 25 | /// Maximum length of property. 26 | /// Value: 1024 (1 kB). 27 | /// 28 | public const int MaxDeviceIdentifierLength = 1024; 29 | 30 | /// 31 | /// Maximum length of property. 32 | /// Value: 1024 (1 kB). 33 | /// 34 | public const int MaxProviderLength = 512; 35 | 36 | /// 37 | /// Maximum length of property. 38 | /// Value: 1024 (1 kB). 39 | /// 40 | public const int MaxProviderKeyLength = 1024; 41 | 42 | /// 43 | /// Maximum length of property. 44 | /// Value: 1048576 (1 MB). 45 | /// 46 | public const int MaxDataLength = 1024 * 1024; 47 | 48 | /// 49 | /// Maximum length of property. 50 | /// Value: 512. 51 | /// 52 | public const int MaxDataTypeNameLength = 512; 53 | 54 | /// 55 | /// Tenant Id. 56 | /// 57 | public virtual int? TenantId { get; set; } 58 | 59 | /// 60 | /// User Id. 61 | /// 62 | public virtual long? UserId { get; set; } 63 | 64 | /// 65 | /// Device Platform. 66 | /// 67 | [MaxLength(MaxDevicePlatformLength)] 68 | public virtual string DevicePlatform { get; set; } 69 | 70 | /// 71 | /// Device name. 72 | /// 73 | [MaxLength(MaxDeviceNameLength)] 74 | public virtual string DeviceName { get; set; } 75 | 76 | /// 77 | /// Normalized device name. 78 | /// 79 | [MaxLength(MaxDeviceNameLength)] 80 | public virtual string NormalizedDeviceName { get; private set; } 81 | 82 | /// 83 | /// Device identifier. 84 | /// 85 | public virtual Guid DeviceIdentifier { get; set; } 86 | 87 | /// 88 | /// Service Provider type. 89 | /// 90 | [MaxLength(MaxProviderLength)] 91 | public virtual string ServiceProvider { get; set; } 92 | 93 | /// 94 | /// Service Provider key. 95 | /// Follows convention in 96 | /// 97 | [MaxLength(MaxProviderKeyLength)] 98 | public virtual string ServiceProviderKey { get; set; } 99 | 100 | /// 101 | /// data payload as JSON string. (optional) 102 | /// 103 | [MaxLength(MaxDataLength)] 104 | public virtual PushDeviceData Data { get; set; } 105 | 106 | /// 107 | /// Expiration time. 108 | /// 109 | public virtual DateTime? ExpirationTime { get; set; } 110 | 111 | /// 112 | /// Type of the JSON serialized . 113 | /// It's AssemblyQualifiedName of the type. 114 | /// 115 | public virtual string DataTypeName { get; set; } 116 | 117 | public virtual void SetNormalizedNames() 118 | { 119 | NormalizedDeviceName = DeviceName?.ToUpperInvariant(); 120 | } 121 | 122 | public override string ToString() 123 | { 124 | return $"tenantId: { TenantId } , userId: { UserId }, platform: { DevicePlatform } , identifier: { DeviceIdentifier } , serviceProvider: { ServiceProvider } , serviceProviderKey: { ServiceProviderKey }"; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Devices/AbpPushDeviceManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Immutable; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using System.Transactions; 7 | using Abp.Domain.Services; 8 | using Abp.Push.Configuration; 9 | using Abp.Runtime.Session; 10 | using Abp.UI; 11 | 12 | namespace Abp.Push.Devices 13 | { 14 | public abstract class AbpPushDeviceManager : DomainService where TDevice : AbpPushDevice, new() 15 | { 16 | public IAbpSession AbpSession { get; set; } 17 | protected readonly IPushDeviceStore DeviceStore; 18 | protected readonly IAbpPushConfiguration Configuration; 19 | 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | protected AbpPushDeviceManager( 24 | IPushDeviceStore deviceStore, 25 | IAbpPushConfiguration pushConfiguration 26 | ) 27 | { 28 | DeviceStore = deviceStore; 29 | Configuration = pushConfiguration; 30 | 31 | AbpSession = NullAbpSession.Instance; 32 | 33 | LocalizationSourceName = AbpPushConsts.LocalizationSourceName; 34 | } 35 | 36 | /// 37 | /// Adds a push device. 38 | /// 39 | /// The device. 40 | public virtual async Task AddAsync(TDevice device) 41 | { 42 | try 43 | { 44 | Check.NotNull(device, nameof(device)); 45 | 46 | ValidateServiceProvider(device); 47 | ValidateDevicePlatform(device); 48 | 49 | using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.RequiresNew)) 50 | { 51 | await CreateOrUpdateAsync(device); 52 | await uow.CompleteAsync(); 53 | } 54 | return true; 55 | } 56 | catch (Exception ex) 57 | { 58 | Logger.Error("AddAsync failed, device: " + device?.ToString(), ex); 59 | } 60 | return false; 61 | } 62 | 63 | protected virtual async Task CreateOrUpdateAsync(TDevice entity) 64 | { 65 | var existingDevice = await FindAsync(entity.ServiceProvider, entity.ServiceProviderKey); 66 | if (existingDevice == null) 67 | { 68 | Logger.Debug("Device push existed " + entity.ToString()); 69 | existingDevice = MapToPushDevice(existingDevice, entity); 70 | await DeviceStore.UpdateDeviceAsync(existingDevice); 71 | } 72 | else 73 | { 74 | var newDevice = MapToPushDevice(entity); 75 | await DeviceStore.InsertDeviceAsync(newDevice); 76 | } 77 | } 78 | 79 | /// 80 | /// Find a push device by provider and provider key. 81 | /// 82 | /// The service provider. 83 | /// The service provider key. 84 | /// The push device, if it is found 85 | public virtual async Task FindAsync(string serviceProvider, string serviceProviderKey) 86 | { 87 | return await DeviceStore.GetDeviceOrNullAsync(AbpSession.TenantId, serviceProvider, serviceProviderKey); 88 | } 89 | 90 | protected virtual TDevice MapToPushDevice(TDevice entity) 91 | { 92 | entity.SetNormalizedNames(); 93 | return entity; 94 | } 95 | 96 | protected virtual TDevice MapToPushDevice(TDevice existingEntity, TDevice entity) 97 | { 98 | existingEntity.ServiceProviderKey = entity.ServiceProviderKey; 99 | existingEntity.DevicePlatform = entity.DevicePlatform; 100 | existingEntity.DeviceIdentifier = entity.DeviceIdentifier; 101 | existingEntity.DeviceName = entity.DeviceName; 102 | existingEntity.SetNormalizedNames(); 103 | return existingEntity; 104 | } 105 | 106 | protected virtual void ValidateServiceProvider(TDevice entity) 107 | { 108 | var serviceProvider = Configuration.ServiceProviders 109 | .FirstOrDefault(p => p.Name == entity.ServiceProvider); 110 | if (serviceProvider == null) 111 | { 112 | throw new UserFriendlyException(L("Push.ServiceProvider.Invalid")); 113 | } 114 | } 115 | 116 | protected virtual void ValidateDevicePlatform(TDevice entity) 117 | { 118 | var devicePlatform = Configuration.DevicePlatforms 119 | .FirstOrDefault(p => p.Name == entity.DevicePlatform); 120 | if (devicePlatform == null) 121 | { 122 | throw new UserFriendlyException(L("Push.DevicePlatform.Invalid")); 123 | } 124 | } 125 | 126 | /// 127 | /// Removes a push device 128 | /// 129 | /// The device. 130 | public virtual async Task RemoveAsync(TDevice device) 131 | { 132 | try 133 | { 134 | Check.NotNull(device, nameof(device)); 135 | 136 | using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.RequiresNew)) 137 | { 138 | await DeviceStore.DeleteDeviceAsync(device); 139 | await uow.CompleteAsync(); 140 | } 141 | } 142 | catch (Exception ex) 143 | { 144 | Logger.Error("RemoveAsync failed, device: " + device?.ToString(), ex); 145 | } 146 | } 147 | 148 | /// 149 | /// Removes a push device by device identifier. 150 | /// 151 | /// The device identifier. 152 | public virtual async Task RemoveAsync(IDeviceIdentifier deviceIdentifier) 153 | { 154 | try 155 | { 156 | using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.RequiresNew)) 157 | { 158 | await DeviceStore.DeleteDeviceAsync(deviceIdentifier); 159 | await uow.CompleteAsync(); 160 | } 161 | } 162 | catch (Exception ex) 163 | { 164 | Logger.Error("RemoveAsync failed, device identifier: " + deviceIdentifier?.ToString(), ex); 165 | } 166 | } 167 | 168 | /// 169 | /// Removes a push device by provider and provider key. 170 | /// 171 | /// The service provider. 172 | /// The service provider key. 173 | public virtual async Task RemoveAsync(string serviceProvider, string serviceProviderKey) 174 | { 175 | try 176 | { 177 | using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.RequiresNew)) 178 | { 179 | await DeviceStore.DeleteDevicesByProviderAsync(AbpSession.TenantId, serviceProvider, serviceProviderKey); 180 | await uow.CompleteAsync(); 181 | } 182 | } 183 | catch (Exception ex) 184 | { 185 | Logger.Error("RemoveAsync failed, service provider: " + serviceProvider + ", service provider key: " + serviceProviderKey, ex); 186 | } 187 | } 188 | 189 | /// 190 | /// Removes all push devices by user identifier. 191 | /// 192 | /// The user identifier. 193 | public virtual async Task RemoveAllByUserIdAsync(IUserIdentifier userIdentifier) 194 | { 195 | await DeviceStore.DeleteDevicesByUserAsync(userIdentifier); 196 | } 197 | 198 | /// 199 | /// Gets all push devices. 200 | /// 201 | public virtual async Task> GetAllAsync(int? skipCount = null, int? maxResultCount = null) 202 | { 203 | var devices = await DeviceStore.GetDevicesAsync(skipCount, maxResultCount); 204 | return devices.ToImmutableList(); 205 | } 206 | 207 | /// 208 | /// Gets all push device by user identifier. 209 | /// 210 | public virtual async Task> GetAllByUserAsync(IUserIdentifier userIdentifier, int? skipCount = null, int? maxResultCount = null) 211 | { 212 | var devices = await DeviceStore.GetDevicesByUserAsync(userIdentifier, skipCount, maxResultCount); 213 | return devices.ToImmutableList(); 214 | } 215 | 216 | /// 217 | /// Gets all push devices by user identifier and provider. 218 | /// 219 | public virtual async Task> GetAllByUserProviderAsync(IUserIdentifier userIdentifier, string serviceProvider, int? skipCount = null, int? maxResultCount = null) 220 | { 221 | var devices = await DeviceStore.GetDevicesByUserPlatformAsync(userIdentifier, serviceProvider, skipCount, maxResultCount); 222 | return devices.ToImmutableList(); 223 | } 224 | 225 | /// 226 | /// Gets total count of push devices by user identifier. 227 | /// 228 | public virtual Task GetCountByUserAsync(IUserIdentifier userIdentifier) 229 | { 230 | return DeviceStore.GetDeviceCountByUserAsync(userIdentifier); 231 | } 232 | 233 | /// 234 | /// Gets total count of push devices by user identifier and provider. 235 | /// 236 | public virtual Task GetCountByUserIdProviderAsync(IUserIdentifier userIdentifier, string serviceProvider) 237 | { 238 | return DeviceStore.GetDeviceCountByUserProviderAsync(userIdentifier, serviceProvider); 239 | } 240 | 241 | /// 242 | /// Gets total count of push devices by user identifier and platform. 243 | /// 244 | public virtual Task GetCountByUserIdPlatformAsync(IUserIdentifier userIdentifier, string devicePlatform) 245 | { 246 | return DeviceStore.GetDeviceCountByUserPlatformAsync(userIdentifier, devicePlatform); 247 | } 248 | } 249 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Devices/AbpPushDeviceManagerExtensions.cs: -------------------------------------------------------------------------------- 1 | using Abp.Threading; 2 | 3 | namespace Abp.Push.Devices 4 | { 5 | /// 6 | /// Extension methods for . 7 | /// 8 | public static class AbpPushDeviceManagerExtensions 9 | { 10 | /// 11 | /// Determines whether the specified user has any devices can be pushed to. 12 | /// 13 | /// The push device manager. 14 | /// The user. 15 | public static bool HasAny(this AbpPushDeviceManager manager, IUserIdentifier user) 16 | where TDevice : AbpPushDevice, new() 17 | { 18 | return AsyncHelper.RunSync(() => manager.GetCountByUserAsync(user)) > 0; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Devices/DeviceIdentifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Abp.Extensions; 4 | 5 | namespace Abp.Push.Devices 6 | { 7 | /// 8 | /// Used to identify a device. 9 | /// 10 | [Serializable] 11 | public class DeviceIdentifier : IDeviceIdentifier 12 | { 13 | /// 14 | /// Tenant Id of the device. 15 | /// Can be null for host devices in a multi tenant application. 16 | /// 17 | public int? TenantId { get; protected set; } 18 | 19 | /// 20 | /// Id of the device. 21 | /// 22 | public long DeviceId { get; protected set; } 23 | 24 | /// 25 | /// Initializes a new instance of the class. 26 | /// 27 | protected DeviceIdentifier() 28 | { 29 | 30 | } 31 | 32 | /// 33 | /// Initializes a new instance of the class. 34 | /// 35 | /// Tenant Id of the device. 36 | /// Id of the device. 37 | public DeviceIdentifier(int? tenantId, long deviceId) 38 | { 39 | TenantId = tenantId; 40 | DeviceId = deviceId; 41 | } 42 | 43 | /// 44 | /// Parses given string and creates a new object. 45 | /// 46 | /// 47 | /// Should be formatted one of the followings: 48 | /// 49 | /// - "deviceId@tenantId". Ex: "42@3" (for tenant devices). 50 | /// - "deviceId". Ex: 1 (for host devices) 51 | /// 52 | public static DeviceIdentifier Parse(string deviceIdentifierString) 53 | { 54 | if (deviceIdentifierString.IsNullOrEmpty()) 55 | { 56 | throw new ArgumentNullException(nameof(deviceIdentifierString), "deviceAtTenant can not be null or empty!"); 57 | } 58 | 59 | var splitted = deviceIdentifierString.Split('@'); 60 | if (splitted.Length == 1) 61 | { 62 | return new DeviceIdentifier(null, splitted[0].To()); 63 | 64 | } 65 | 66 | if (splitted.Length == 2) 67 | { 68 | return new DeviceIdentifier(splitted[1].To(), splitted[0].To()); 69 | } 70 | 71 | throw new ArgumentException("deviceAtTenant is not properly formatted", nameof(deviceIdentifierString)); 72 | } 73 | 74 | /// 75 | /// Creates a string represents this instance. 76 | /// Formatted one of the followings: 77 | /// 78 | /// - "deviceId@tenantId". Ex: "42@3" (for tenant devices). 79 | /// - "deviceId". Ex: 1 (for host devices) 80 | /// 81 | /// Returning string can be used in method to re-create identical object. 82 | /// 83 | public string ToDeviceIdentifierString() 84 | { 85 | if (TenantId == null) 86 | { 87 | return DeviceId.ToString(); 88 | } 89 | 90 | return DeviceId + "@" + TenantId; 91 | } 92 | 93 | public override bool Equals(object obj) 94 | { 95 | if (obj == null || !(obj is DeviceIdentifier)) 96 | { 97 | return false; 98 | } 99 | 100 | //Same instances must be considered as equal 101 | if (ReferenceEquals(this, obj)) 102 | { 103 | return true; 104 | } 105 | 106 | //Transient objects are not considered as equal 107 | var other = (DeviceIdentifier)obj; 108 | 109 | //Must have a IS-A relation of types or must be same type 110 | var typeOfThis = GetType(); 111 | var typeOfOther = other.GetType(); 112 | if (!typeOfThis.GetTypeInfo().IsAssignableFrom(typeOfOther) && !typeOfOther.GetTypeInfo().IsAssignableFrom(typeOfThis)) 113 | { 114 | return false; 115 | } 116 | 117 | return TenantId == other.TenantId && DeviceId == other.DeviceId; 118 | } 119 | 120 | /// 121 | public override int GetHashCode() 122 | { 123 | return TenantId == null ? (int)DeviceId : (int)(TenantId.Value ^ DeviceId); 124 | } 125 | 126 | /// 127 | public static bool operator ==(DeviceIdentifier left, DeviceIdentifier right) 128 | { 129 | if (Equals(left, null)) 130 | { 131 | return Equals(right, null); 132 | } 133 | 134 | return left.Equals(right); 135 | } 136 | 137 | /// 138 | public static bool operator !=(DeviceIdentifier left, DeviceIdentifier right) 139 | { 140 | return !(left == right); 141 | } 142 | 143 | public override string ToString() 144 | { 145 | return ToDeviceIdentifierString(); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Devices/DeviceIdentifierExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Abp.Push.Devices 2 | { 3 | /// 4 | /// Extension methods for and . 5 | /// 6 | public static class DeviceIdentifierExtensions 7 | { 8 | /// 9 | /// Creates a new object from any object implements . 10 | /// 11 | /// Device identifier. 12 | public static DeviceIdentifier ToDeviceIdentifier(this IDeviceIdentifier deviceIdentifier) 13 | { 14 | return new DeviceIdentifier(deviceIdentifier.TenantId, deviceIdentifier.DeviceId); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Devices/DevicePlatformInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Abp.Push.Devices 4 | { 5 | public class DevicePlatformInfo 6 | { 7 | public string Name { get; set; } 8 | 9 | public Type PlatformResolverType { get; set; } 10 | 11 | public DevicePlatformInfo(string name, Type platformResolverType) 12 | { 13 | Name = name; 14 | PlatformResolverType = platformResolverType; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Devices/IDeviceIdentifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Abp.Push.Devices 4 | { 5 | /// 6 | /// Interface to get a device identifier. 7 | /// 8 | public interface IDeviceIdentifier 9 | { 10 | /// 11 | /// Tenant Id. Can be null for host device. 12 | /// 13 | int? TenantId { get; } 14 | 15 | /// 16 | /// Id of the device. 17 | /// 18 | long DeviceId { get; } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Devices/IHasDeviceInfo.cs: -------------------------------------------------------------------------------- 1 | namespace Abp.Push.Devices 2 | { 3 | /// 4 | /// Represents information of a device 5 | /// 6 | public interface IHasDeviceInfo 7 | { 8 | string DevicePlatform { get; } 9 | 10 | /// 11 | /// Device name 12 | /// 13 | string DeviceName { get; } 14 | 15 | /// 16 | /// Normalized device name 17 | /// 18 | string NormalizedDeviceName { get; } 19 | 20 | /// 21 | /// Device identifier 22 | /// 23 | TIdentifier DeviceIdentifier { get; } 24 | 25 | 26 | } 27 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Devices/IPushDeviceStore.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace Abp.Push.Devices 5 | { 6 | /// 7 | /// Used to store (persist) push devices. 8 | /// 9 | public interface IPushDeviceStore where TDevice : AbpPushDevice 10 | { 11 | /// 12 | /// Inserts a push device. 13 | /// 14 | Task InsertDeviceAsync(TDevice device); 15 | 16 | /// 17 | /// Updates a push device. 18 | /// 19 | Task UpdateDeviceAsync(TDevice device); 20 | 21 | /// 22 | /// Inserts or updates a push device. 23 | /// 24 | Task InsertOrUpdateDeviceAsync(TDevice device); 25 | 26 | /// 27 | /// Delete a push device. 28 | /// 29 | Task DeleteDeviceAsync(TDevice device); 30 | 31 | /// 32 | /// Delete a push device by device identifier. 33 | /// 34 | Task DeleteDeviceAsync(IDeviceIdentifier deviceIdentifier); 35 | 36 | /// 37 | /// Delete all push devices by service provider. 38 | /// The tenant id. 39 | /// The service provider. 40 | /// 41 | Task DeleteDevicesByProviderAsync(int? tenantId, string serviceProvider); 42 | 43 | /// 44 | /// Delete all push devices by service provider and service provider key. 45 | /// The tenant id. 46 | /// The service provider. 47 | /// The service provider key. 48 | /// 49 | Task DeleteDevicesByProviderAsync(int? tenantId, string serviceProvider, string serviceProviderKey); 50 | 51 | /// 52 | /// Delete all push devices by device platform. 53 | /// The tenant id. 54 | /// The device platform. 55 | /// 56 | Task DeleteDevicesByPlatformAsync(int? tenantId, string devicePlatform); 57 | 58 | /// 59 | /// Delete a push device by user identifier. 60 | /// 61 | /// The user identifier. 62 | Task DeleteDevicesByUserAsync(IUserIdentifier userIdentifier); 63 | 64 | /// 65 | /// Delete all push devices by user identifier and service provider. 66 | /// The user identifier. 67 | /// The service provider. 68 | /// 69 | Task DeleteDevicesByUserProviderAsync(IUserIdentifier userIdentifier, string serviceProvider); 70 | 71 | /// 72 | /// Delete all push devices by user identifier and device platform. 73 | /// The user identifier. 74 | /// The device platform. 75 | /// 76 | Task DeleteDevicesByUserPlatformAsync(IUserIdentifier userIdentifier, string devicePlatform); 77 | 78 | /// 79 | /// Get a push device by service provider and service provider key. 80 | /// 81 | /// The tenant id. 82 | /// The service provider. 83 | /// The service provider key. 84 | /// The push device, if it is found 85 | Task GetDeviceOrNullAsync(int? tenantId, string serviceProvider, string serviceProviderKey); 86 | 87 | /// 88 | /// Gets all push devices. 89 | /// 90 | /// The tenant id. 91 | Task> GetDevicesAsync(int? tenantId, int? skipCount = null, int? maxResultCount = null); 92 | 93 | /// 94 | /// Gets all push devices by service provider. 95 | /// The tenant id. 96 | /// The service provider. 97 | /// 98 | Task> GetDevicesByProviderAsync(int? tenantId, string serviceProvider, int? skipCount = null, int? maxResultCount = null); 99 | 100 | /// 101 | /// Gets all push devices by device platform. 102 | /// The tenant id. 103 | /// The device platform. 104 | /// 105 | Task> GetDevicesByPlatformAsync(int? tenantId, string devicePlatform, int? skipCount = null, int? maxResultCount = null); 106 | 107 | /// 108 | /// Get a push device by user identifier and service provider and service provider key. 109 | /// 110 | /// The user identifier. 111 | /// The service provider. 112 | /// The service provider key. 113 | /// The push device, if it is found 114 | Task GetUserDeviceOrNullAsync(IUserIdentifier userIdentifier, string serviceProvider, string serviceProviderKey); 115 | 116 | /// 117 | /// Gets all push devices by user identifier. 118 | /// The user identifier. 119 | /// 120 | Task> GetDevicesByUserAsync(IUserIdentifier userIdentifier, int? skipCount = null, int? maxResultCount = null); 121 | 122 | /// 123 | /// Gets all push devices by user identifier and service provider. 124 | /// The user identifier. 125 | /// The service provider. 126 | /// 127 | Task> GetDevicesByUserProviderAsync(IUserIdentifier userIdentifier, string serviceProvider, int? skipCount = null, int? maxResultCount = null); 128 | 129 | /// 130 | /// Gets all push devices by user identifier and device platform. 131 | /// The user identifier. 132 | /// The device platform. 133 | /// 134 | Task> GetDevicesByUserPlatformAsync(IUserIdentifier userIdentifier, string devicePlatform, int? skipCount = null, int? maxResultCount = null); 135 | 136 | /// 137 | /// Gets push devices count by user identifier. 138 | /// The user identifier. 139 | /// 140 | Task GetDeviceCountByUserAsync(IUserIdentifier userIdentifier); 141 | 142 | /// 143 | /// Gets push device count by user identifier and service provider. 144 | /// The user identifier. 145 | /// The service provider. 146 | /// 147 | Task GetDeviceCountByUserProviderAsync(IUserIdentifier userIdentifier, string serviceProvider); 148 | 149 | /// 150 | /// Gets push device count by user identifier and device platform. 151 | /// The user identifier. 152 | /// The device platform. 153 | /// 154 | Task GetDeviceCountByUserPlatformAsync(IUserIdentifier userIdentifier, string devicePlatform); 155 | } 156 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Devices/PushDeviceData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Abp.Collections.Extensions; 4 | using Abp.Json; 5 | 6 | namespace Abp.Push.Devices 7 | { 8 | /// 9 | /// Used to store info for a push device. 10 | /// It can be directly used or can be derived. 11 | /// 12 | [Serializable] 13 | public class PushDeviceData 14 | { 15 | /// 16 | /// Gets push device info data type name. 17 | /// It returns the full class name by default. 18 | /// 19 | public virtual string Type => GetType().FullName; 20 | 21 | /// 22 | /// Shortcut to set/get . 23 | /// 24 | public object this[string key] 25 | { 26 | get { return Properties.GetOrDefault(key); } 27 | set { Properties[key] = value; } 28 | } 29 | 30 | /// 31 | /// Can be used to add custom properties to this push device. 32 | /// 33 | public Dictionary Properties 34 | { 35 | get { return _properties; } 36 | set 37 | { 38 | if (value == null) 39 | { 40 | throw new ArgumentNullException(nameof(value)); 41 | } 42 | 43 | /* Not assign value, but add dictionary items. This is required for backward compability. */ 44 | foreach (var keyValue in value) 45 | { 46 | if (!_properties.ContainsKey(keyValue.Key)) 47 | { 48 | _properties[keyValue.Key] = keyValue.Value; 49 | } 50 | } 51 | } 52 | } 53 | private readonly Dictionary _properties; 54 | 55 | /// 56 | /// Createa a new PushDeviceData object. 57 | /// 58 | public PushDeviceData() 59 | { 60 | _properties = new Dictionary(); 61 | } 62 | 63 | public override string ToString() 64 | { 65 | return this.ToJsonString(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Devices/PushDeviceExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Abp.Push.Devices 2 | { 3 | public static class PushDeviceExtensions 4 | { 5 | public static UserIdentifier ToUserIdentifierOrNull(this AbpPushDevice device) 6 | { 7 | return device.UserId.HasValue ? new UserIdentifier(device.TenantId, device.UserId.Value) : null; 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Devices/PushDeviceInfo.cs: -------------------------------------------------------------------------------- 1 | namespace Abp.Push.Devices 2 | { 3 | public class PushDeviceInfo 4 | { 5 | public static PushDeviceInfo Empty = new PushDeviceInfo(); 6 | 7 | public string ProviderKey { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/IPushDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Abp.Application.Features; 4 | using Abp.Authorization; 5 | using Abp.Localization; 6 | 7 | namespace Abp.Push 8 | { 9 | /// 10 | /// Interface of a push definition 11 | /// 12 | public interface IPushDefinition 13 | { 14 | /// 15 | /// Unique name of the push. 16 | /// 17 | string Name { get; } 18 | 19 | /// 20 | /// Related entity type with this push (optional). 21 | /// 22 | Type EntityType { get; } 23 | 24 | /// 25 | /// Display name of the push. 26 | /// Optional. 27 | /// 28 | ILocalizableString DisplayName { get; } 29 | 30 | /// 31 | /// Description for the push. 32 | /// Optional. 33 | /// 34 | ILocalizableString Description { get; } 35 | 36 | /// 37 | /// A permission dependency. This push will be available to a user if this dependency is satisfied. 38 | /// Optional. 39 | /// 40 | IPermissionDependency PermissionDependency { get; } 41 | 42 | /// 43 | /// A feature dependency. This push will be available to a tenant if this feature is enabled. 44 | /// Optional. 45 | /// 46 | IFeatureDependency FeatureDependency { get; } 47 | 48 | /// 49 | /// Gets/sets arbitrary objects related to this object. 50 | /// Gets null if given key does not exists. 51 | /// This is a shortcut for dictionary. 52 | /// 53 | /// Key 54 | object this[string key] { get; set; } 55 | 56 | /// 57 | /// Arbitrary objects related to this object. 58 | /// These objects must be serializable. 59 | /// 60 | IDictionary Attributes { get; } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/IPushDefinitionContext.cs: -------------------------------------------------------------------------------- 1 | namespace Abp.Push 2 | { 3 | /// 4 | /// Used as a context while defining pushes. 5 | /// 6 | public interface IPushDefinitionContext 7 | { 8 | /// 9 | /// Gets the push definition manager. 10 | /// 11 | IPushDefinitionManager Manager { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/IPushDefinitionManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace Abp.Push 5 | { 6 | public interface IPushDefinitionManager : IPushDefinitionManager 7 | { 8 | } 9 | 10 | /// 11 | /// Used to manage push definitions. 12 | /// 13 | public interface IPushDefinitionManager 14 | where TDefinition : IPushDefinition 15 | { 16 | /// 17 | /// Adds the specified push definition. 18 | /// 19 | void Add(TDefinition pushDefinition); 20 | 21 | /// 22 | /// Gets a push definition by name. 23 | /// Throws exception if there is no push definition with given name. 24 | /// 25 | TDefinition Get(string name); 26 | 27 | /// 28 | /// Gets a push definition by name. 29 | /// Returns null if there is no push definition with given name. 30 | /// 31 | TDefinition GetOrNull(string name); 32 | 33 | /// 34 | /// Gets all push definitions. 35 | /// 36 | IReadOnlyList GetAll(); 37 | 38 | /// 39 | /// Checks if given push definition by name is available for given user. 40 | /// 41 | /// Definition Name. 42 | /// User. 43 | Task IsAvailableAsync(string name, IUserIdentifier user); 44 | 45 | /// 46 | /// Gets all available push definitions for given user. 47 | /// 48 | /// User. 49 | Task> GetAllAvailableAsync(IUserIdentifier user); 50 | } 51 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Localization/AbpPush.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Localization/AbpPushLocalizationConfigurer.cs: -------------------------------------------------------------------------------- 1 | using Abp.Configuration.Startup; 2 | using Abp.Localization.Dictionaries; 3 | using Abp.Localization.Dictionaries.Xml; 4 | using Abp.Reflection.Extensions; 5 | 6 | namespace Abp.Push.Localization 7 | { 8 | internal static class AbpPushLocalizationConfigurer 9 | { 10 | public static void Configure(ILocalizationConfiguration localizationConfiguration) 11 | { 12 | localizationConfiguration.Sources.Add( 13 | new DictionaryBasedLocalizationSource( 14 | AbpPushConsts.LocalizationSourceName, 15 | new XmlEmbeddedFileLocalizationDictionaryProvider( 16 | typeof(AbpPushCommonModule).GetAssembly(), "Abp.Push.Localization" 17 | ) 18 | ) 19 | ); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Providers/AbpPushProviderManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Abp.Dependency; 5 | using Abp.Domain.Services; 6 | using Abp.Push.Configuration; 7 | using Abp.Push.Devices; 8 | 9 | namespace Abp.Push.Providers 10 | { 11 | public class AbpPushProviderManager : DomainService, IPushProviderManager, ITransientDependency 12 | { 13 | protected readonly IIocResolver IocResolver; 14 | protected readonly IAbpPushConfiguration Configuration; 15 | 16 | public AbpPushProviderManager( 17 | IIocResolver iocResolver, 18 | IAbpPushConfiguration pushConfiguration 19 | ) 20 | { 21 | IocResolver = iocResolver; 22 | Configuration = pushConfiguration; 23 | } 24 | 25 | public virtual Task IsValidDeviceAsync(string provider, string providerKey) 26 | { 27 | using (var apiClient = CreateApiClient(provider)) 28 | { 29 | return apiClient.Object.IsValidDeviceAsync(providerKey); 30 | } 31 | } 32 | 33 | public virtual Task GetDeviceInfoAsync(string provider, string providerKey) 34 | where TDeviceInfo : PushDeviceInfo 35 | { 36 | using (var apiClient = CreateApiClient(provider)) 37 | { 38 | return apiClient.Object.GetDeviceInfoAsync(providerKey); 39 | } 40 | } 41 | 42 | public virtual Task PushAsync(string provider, TPayload payload) 43 | where TPayload : PushPayload 44 | { 45 | using (var providerApi = CreateApiClient(provider)) 46 | { 47 | return providerApi.Object.PushAsync(payload); 48 | } 49 | } 50 | 51 | protected virtual IDisposableDependencyObjectWrapper CreateApiClient(string provider) 52 | { 53 | var providerInfo = Configuration.ServiceProviders.FirstOrDefault(p => p.Name == provider); 54 | if (providerInfo == null) 55 | { 56 | throw new ArgumentException("Unknown push service provider: " + provider); 57 | } 58 | 59 | var providerApi = IocResolver.ResolveAsDisposable(providerInfo.ApiClientType); 60 | providerApi.Object.Initialize(providerInfo); 61 | return providerApi; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Providers/IPushApiClient.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Abp.Push.Devices; 3 | 4 | namespace Abp.Push.Providers 5 | { 6 | public interface IPushApiClient 7 | { 8 | ServiceProviderInfo ProviderInfo { get; } 9 | 10 | void Initialize(ServiceProviderInfo providerInfo); 11 | 12 | Task IsValidDeviceAsync(string providerKey); 13 | 14 | Task GetDeviceInfoAsync(string providerKey) where TDeviceInfo : PushDeviceInfo; 15 | 16 | Task PushAsync(TPayload payload) where TPayload : PushPayload; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Providers/IPushProviderManager.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Abp.Push.Devices; 3 | 4 | namespace Abp.Push.Providers 5 | { 6 | public interface IPushProviderManager 7 | { 8 | Task IsValidDeviceAsync(string provider, string providerKey); 9 | 10 | Task GetDeviceInfoAsync(string provider, string providerKey) where TDeviceInfo : PushDeviceInfo; 11 | 12 | Task PushAsync(string provider, TPayload payload) where TPayload : PushPayload; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Providers/IPushServiceProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Abp.Push.Devices; 3 | using Abp.Push.Requests; 4 | 5 | namespace Abp.Push.Providers 6 | { 7 | /// 8 | /// Interface of push service provider 9 | /// 10 | public interface IPushServiceProvider 11 | { 12 | ServiceProviderInfo ProviderInfo { get; } 13 | 14 | void Initialize(ServiceProviderInfo providerInfo); 15 | 16 | /// 17 | /// This method tries to deliver a single push to specified users. 18 | /// If a user does not have any registered device, it should ignore him. 19 | /// 20 | Task PushAsync(IUserIdentifier[] userIdentifiers, PushRequest pushRequest); 21 | 22 | /// 23 | /// This method tries to deliver a single push to specified devices. 24 | /// 25 | Task PushAsync(IDeviceIdentifier[] deviceIdentifiers, PushRequest pushRequest); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Providers/PushApiClientBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Threading.Tasks; 4 | using Abp.Dependency; 5 | using Abp.Push.Devices; 6 | 7 | namespace Abp.Push.Providers 8 | { 9 | public abstract class PushApiClientBase : AbpServiceBase, IPushApiClient, ITransientDependency 10 | { 11 | public ServiceProviderInfo ProviderInfo { get; set; } 12 | 13 | public void Initialize(ServiceProviderInfo providerInfo) 14 | { 15 | ProviderInfo = providerInfo; 16 | } 17 | 18 | public virtual async Task IsValidDeviceAsync(string providerKey) 19 | { 20 | if (string.IsNullOrWhiteSpace(providerKey)) 21 | { 22 | return false; 23 | } 24 | var deviceInfo = await GetDeviceInfoAsync(providerKey); 25 | return deviceInfo.ProviderKey == providerKey; 26 | } 27 | 28 | public abstract Task GetDeviceInfoAsync(string providerKey) where TDeviceInfo : PushDeviceInfo; 29 | 30 | public abstract Task PushAsync(TPayload payload) where TPayload : PushPayload; 31 | 32 | protected virtual string ToBase64Utf8(string source) 33 | { 34 | return ToBase64(source, Encoding.UTF8); 35 | } 36 | 37 | protected virtual string ToBase64(string source, Encoding encoding) 38 | { 39 | return Convert.ToBase64String(encoding.GetBytes(source)); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Providers/PushPayload.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Abp.Push.Providers 4 | { 5 | [Serializable] 6 | public class PushPayload 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Providers/PushServiceProviderBase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Abp.Dependency; 3 | using Abp.Push.Devices; 4 | using Abp.Push.Requests; 5 | 6 | namespace Abp.Push.Providers 7 | { 8 | public abstract class PushServiceProviderBase : AbpServiceBase, IPushServiceProvider, ITransientDependency 9 | { 10 | public ServiceProviderInfo ProviderInfo { get; set; } 11 | 12 | public void Initialize(ServiceProviderInfo providerInfo) 13 | { 14 | ProviderInfo = providerInfo; 15 | } 16 | 17 | public abstract Task PushAsync(IUserIdentifier[] userIdentifiers, PushRequest pushRequest); 18 | 19 | public abstract Task PushAsync(IDeviceIdentifier[] deviceIdentifiers, PushRequest pushRequest); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Providers/ServiceProviderInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Abp.Push.Providers 4 | { 5 | public class ServiceProviderInfo 6 | { 7 | public string Name { get; set; } 8 | 9 | public string ClientId { get; set; } 10 | 11 | public string ClientSecret { get; set; } 12 | 13 | public Type ProviderType { get; set; } 14 | 15 | public Type ApiClientType { get; set; } 16 | 17 | public ServiceProviderInfo(string name, Type providerType, Type apiClientType, string clientId, string clientSecret) 18 | { 19 | Name = name; 20 | ProviderType = providerType; 21 | ApiClientType = apiClientType; 22 | ClientId = clientId; 23 | ClientSecret = clientSecret; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/PushDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Abp.Application.Features; 4 | using Abp.Authorization; 5 | using Abp.Collections.Extensions; 6 | using Abp.Localization; 7 | 8 | namespace Abp.Push 9 | { 10 | /// 11 | /// Definition for a push. 12 | /// 13 | public class PushDefinition : IPushDefinition 14 | { 15 | /// 16 | /// Unique name of the push. 17 | /// 18 | public string Name { get; private set; } 19 | 20 | /// 21 | /// Related entity type with this push (optional). 22 | /// 23 | public Type EntityType { get; private set; } 24 | 25 | /// 26 | /// Display name of the push. 27 | /// Optional. 28 | /// 29 | public ILocalizableString DisplayName { get; set; } 30 | 31 | /// 32 | /// Description for the push. 33 | /// Optional. 34 | /// 35 | public ILocalizableString Description { get; set; } 36 | 37 | /// 38 | /// A permission dependency. This push will be available to a user if this dependency is satisfied. 39 | /// Optional. 40 | /// 41 | public IPermissionDependency PermissionDependency { get; set; } 42 | 43 | /// 44 | /// A feature dependency. This push will be available to a tenant if this feature is enabled. 45 | /// Optional. 46 | /// 47 | public IFeatureDependency FeatureDependency { get; set; } 48 | 49 | /// 50 | /// Gets/sets arbitrary objects related to this object. 51 | /// Gets null if given key does not exists. 52 | /// This is a shortcut for dictionary. 53 | /// 54 | /// Key 55 | public object this[string key] 56 | { 57 | get { return Attributes.GetOrDefault(key); } 58 | set { Attributes[key] = value; } 59 | } 60 | 61 | /// 62 | /// Arbitrary objects related to this object. 63 | /// These objects must be serializable. 64 | /// 65 | public IDictionary Attributes { get; private set; } 66 | 67 | /// 68 | /// Initializes a new instance of the class. 69 | /// 70 | /// Unique name of the push. 71 | /// Related entity type with this push (optional). 72 | /// Display name of the push. 73 | /// Description for the push 74 | /// A permission dependency. This push will be available to a user if this dependency is satisfied. 75 | /// A feature dependency. This push will be available to a tenant if this feature is enabled. 76 | public PushDefinition(string name, Type entityType = null, ILocalizableString displayName = null, ILocalizableString description = null, IPermissionDependency permissionDependency = null, IFeatureDependency featureDependency = null) 77 | { 78 | if (string.IsNullOrWhiteSpace(name)) 79 | { 80 | throw new ArgumentNullException(nameof(name), "name can not be null, empty or whitespace!"); 81 | } 82 | 83 | Name = name; 84 | EntityType = entityType; 85 | DisplayName = displayName; 86 | Description = description; 87 | PermissionDependency = permissionDependency; 88 | FeatureDependency = featureDependency; 89 | 90 | Attributes = new Dictionary(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/PushDefinitionContext.cs: -------------------------------------------------------------------------------- 1 | namespace Abp.Push 2 | { 3 | internal class PushDefinitionContext : IPushDefinitionContext 4 | { 5 | public IPushDefinitionManager Manager { get; private set; } 6 | 7 | public PushDefinitionContext(IPushDefinitionManager manager) 8 | { 9 | Manager = manager; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/PushDefinitionManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Immutable; 3 | using System.Threading.Tasks; 4 | using Abp.Application.Features; 5 | using Abp.Authorization; 6 | using Abp.Collections.Extensions; 7 | using Abp.Dependency; 8 | using Abp.Push.Configuration; 9 | 10 | namespace Abp.Push 11 | { 12 | /// 13 | /// Implements . 14 | /// 15 | public class PushDefinitionManager : AbpServiceBase, IPushDefinitionManager, ISingletonDependency 16 | { 17 | protected readonly IAbpPushConfiguration Configuration; 18 | protected readonly IIocResolver IocResolver; 19 | 20 | private readonly IDictionary _pushDefinitions; 21 | 22 | public PushDefinitionManager( 23 | IIocResolver iocResolver, 24 | IAbpPushConfiguration configuration) 25 | { 26 | Configuration = configuration; 27 | IocResolver = iocResolver; 28 | 29 | _pushDefinitions = new Dictionary(); 30 | } 31 | 32 | public virtual void Initialize() 33 | { 34 | var context = new PushDefinitionContext(this); 35 | 36 | foreach (var providerType in Configuration.Providers) 37 | { 38 | using (var provider = IocResolver.ResolveAsDisposable(providerType)) 39 | { 40 | provider.Object.SetDefinitions(context); 41 | } 42 | } 43 | } 44 | 45 | public virtual void Add(PushDefinition pushDefinition) 46 | { 47 | if (_pushDefinitions.ContainsKey(pushDefinition.Name)) 48 | { 49 | throw new AbpInitializationException("There is already a push definition with given name: " + pushDefinition.Name + ". Push names must be unique!"); 50 | } 51 | 52 | _pushDefinitions[pushDefinition.Name] = pushDefinition; 53 | } 54 | 55 | public virtual PushDefinition Get(string name) 56 | { 57 | var definition = GetOrNull(name); 58 | if (definition == null) 59 | { 60 | throw new AbpException("There is no push definition with given name: " + name); 61 | } 62 | 63 | return definition; 64 | } 65 | 66 | public virtual PushDefinition GetOrNull(string name) 67 | { 68 | return _pushDefinitions.GetOrDefault(name); 69 | } 70 | 71 | public virtual IReadOnlyList GetAll() 72 | { 73 | return _pushDefinitions.Values.ToImmutableList(); 74 | } 75 | 76 | public virtual async Task IsAvailableAsync(string name, IUserIdentifier user) 77 | { 78 | var pushDefinition = GetOrNull(name); 79 | if (pushDefinition == null) 80 | { 81 | return true; 82 | } 83 | 84 | if (pushDefinition.FeatureDependency != null) 85 | { 86 | using (var featureDependencyContext = IocResolver.ResolveAsDisposable()) 87 | { 88 | featureDependencyContext.Object.TenantId = user.TenantId; 89 | 90 | if (!await pushDefinition.FeatureDependency.IsSatisfiedAsync(featureDependencyContext.Object)) 91 | { 92 | return false; 93 | } 94 | } 95 | } 96 | 97 | if (pushDefinition.PermissionDependency != null) 98 | { 99 | using (var permissionDependencyContext = IocResolver.ResolveAsDisposable()) 100 | { 101 | permissionDependencyContext.Object.User = user.ToUserIdentifier(); 102 | 103 | if (!await pushDefinition.PermissionDependency.IsSatisfiedAsync(permissionDependencyContext.Object)) 104 | { 105 | return false; 106 | } 107 | } 108 | } 109 | 110 | return true; 111 | } 112 | 113 | public virtual async Task> GetAllAvailableAsync(IUserIdentifier user) 114 | { 115 | var availableDefinitions = new List(); 116 | 117 | using (var permissionDependencyContext = IocResolver.ResolveAsDisposable()) 118 | { 119 | permissionDependencyContext.Object.User = user.ToUserIdentifier(); 120 | 121 | using (var featureDependencyContext = IocResolver.ResolveAsDisposable()) 122 | { 123 | featureDependencyContext.Object.TenantId = user.TenantId; 124 | 125 | foreach (var pushDefinition in GetAll()) 126 | { 127 | if (pushDefinition.PermissionDependency != null && 128 | !await pushDefinition.PermissionDependency.IsSatisfiedAsync(permissionDependencyContext.Object)) 129 | { 130 | continue; 131 | } 132 | 133 | if (user.TenantId.HasValue && 134 | pushDefinition.FeatureDependency != null && 135 | !await pushDefinition.FeatureDependency.IsSatisfiedAsync(featureDependencyContext.Object)) 136 | { 137 | continue; 138 | } 139 | 140 | availableDefinitions.Add(pushDefinition); 141 | } 142 | } 143 | } 144 | 145 | return availableDefinitions.ToImmutableList(); 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/PushDefinitionManagerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Abp.Threading; 3 | 4 | namespace Abp.Push 5 | { 6 | /// 7 | /// Extension methods for . 8 | /// 9 | public static class PushDefinitionManagerExtensions 10 | { 11 | /// 12 | /// Gets all available push definitions for given user. 13 | /// 14 | /// Push definition manager 15 | /// User 16 | public static IReadOnlyList GetAllAvailable(this IPushDefinitionManager pushDefinitionManager, UserIdentifier user) 17 | { 18 | return AsyncHelper.RunSync(() => pushDefinitionManager.GetAllAvailableAsync(user)); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/PushDefinitionProvider.cs: -------------------------------------------------------------------------------- 1 | using Abp.Dependency; 2 | 3 | namespace Abp.Push 4 | { 5 | /// 6 | /// This class should be implemented in order to define pushes. 7 | /// 8 | public abstract class PushDefinitionProvider : ITransientDependency 9 | { 10 | /// 11 | /// Used to add/manipulate push definitions. 12 | /// 13 | /// Context 14 | public abstract void SetDefinitions(IPushDefinitionContext context); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/AbpPushRequestDistributor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Abp.Collections.Extensions; 6 | using Abp.Configuration; 7 | using Abp.Dependency; 8 | using Abp.Domain.Uow; 9 | using Abp.Extensions; 10 | using Abp.Push.Configuration; 11 | using Abp.Push.Providers; 12 | 13 | namespace Abp.Push.Requests 14 | { 15 | /// 16 | /// Used to distribute push requests to users. 17 | /// 18 | public class AbpPushRequestDistributor : AbpServiceBase, IPushRequestDistributor, ITransientDependency 19 | { 20 | protected readonly IPushRequestStore RequestStore; 21 | protected readonly IPushDefinitionManager DefinitionManager; 22 | protected readonly IAbpPushConfiguration Configuration; 23 | protected readonly IIocResolver IocResolver; 24 | protected readonly IGuidGenerator GuidGenerator; 25 | 26 | /// 27 | /// Initializes a new instance of the class. 28 | /// 29 | public AbpPushRequestDistributor( 30 | IPushRequestStore pushRequestStore, 31 | IPushDefinitionManager pushDefinitionManager, 32 | IAbpPushConfiguration pushConfiguration, 33 | IIocResolver iocResolver, 34 | IGuidGenerator guidGenerator) 35 | { 36 | RequestStore = pushRequestStore; 37 | DefinitionManager = pushDefinitionManager; 38 | Configuration = pushConfiguration; 39 | IocResolver = iocResolver; 40 | GuidGenerator = guidGenerator; 41 | } 42 | 43 | [UnitOfWork] 44 | public virtual async Task DistributeAsync(Guid pushRequestId) 45 | { 46 | PushRequest pushRequest = null; 47 | // set tenantId = null, because push request only store in host side 48 | using (UnitOfWorkManager.Current.SetTenantId(null)) 49 | { 50 | pushRequest = await RequestStore.GetRequestOrNullAsync(pushRequestId); 51 | if (pushRequest == null) 52 | { 53 | Logger.WarnFormat("PushRequestDistributionJob can not continue since could not found push request by id: {0} ", pushRequestId); 54 | return; 55 | } 56 | } 57 | 58 | // TODO: change GetUsers() to GetDevices() 59 | var users = await GetUsers(pushRequest); 60 | if (users.IsNullOrEmpty()) 61 | { 62 | Logger.WarnFormat("Push Request with id: {0} does not have any target user", pushRequest.Id); 63 | } 64 | 65 | try 66 | { 67 | foreach (var providerInfo in Configuration.ServiceProviders) 68 | { 69 | // TODO: allow PushRequest to store target providers 70 | using (var provider = CreateProvider(providerInfo.Name)) 71 | { 72 | await provider.Object.PushAsync(users, pushRequest); 73 | } 74 | } 75 | 76 | await RequestStore.DeleteRequestAsync(pushRequest.Id); 77 | } 78 | catch (Exception ex) 79 | { 80 | Logger.Warn(ex.ToString(), ex); 81 | } 82 | } 83 | 84 | [UnitOfWork] 85 | protected virtual async Task GetUsers(PushRequest request) 86 | { 87 | var userIds = new List(); 88 | 89 | if (request.UserIds.IsNullOrEmpty()) 90 | { 91 | //Get subscribed users 92 | 93 | List subscriptions; 94 | 95 | if (request.TenantIds.IsNullOrWhiteSpace() || 96 | request.TenantIds.Trim() == PushRequest.AllTenantIdsString) 97 | { 98 | //Get all subscribed users of all tenants 99 | subscriptions = await RequestStore.GetSubscriptionsAsync( 100 | request.Name, 101 | request.EntityTypeName, 102 | request.EntityId 103 | ); 104 | } 105 | else 106 | { 107 | var tenantIds = PushRequest.GetTenantIds(request.TenantIds); 108 | 109 | //Get all subscribed users of specified tenant(s) 110 | subscriptions = await RequestStore.GetSubscriptionsAsync( 111 | tenantIds, 112 | request.Name, 113 | request.EntityTypeName, 114 | request.EntityId 115 | ); 116 | } 117 | 118 | //Remove invalid subscriptions 119 | var invalidSubscriptions = new Dictionary(); 120 | 121 | var subscriptionGroups = subscriptions.GroupBy(s => s.TenantId); 122 | foreach (var subscriptionGroup in subscriptionGroups) 123 | { 124 | using (CurrentUnitOfWork.SetTenantId(subscriptionGroup.Key)) 125 | { 126 | foreach (var subscription in subscriptionGroup) 127 | { 128 | if (!await DefinitionManager.IsAvailableAsync(request.Name, new UserIdentifier(subscription.TenantId, subscription.UserId)) || 129 | // TODO: exclude system push request from checking user setting 130 | !SettingManager.GetSettingValueForUser(AbpPushSettingNames.Receive, subscription.TenantId, subscription.UserId)) 131 | { 132 | invalidSubscriptions[subscription.Id] = subscription; 133 | } 134 | } 135 | } 136 | } 137 | 138 | subscriptions.RemoveAll(s => invalidSubscriptions.ContainsKey(s.Id)); 139 | 140 | //Get user ids 141 | userIds = subscriptions 142 | .Select(s => new UserIdentifier(s.TenantId, s.UserId)) 143 | .ToList(); 144 | } 145 | else 146 | { 147 | //Directly get from UserIds 148 | userIds = request 149 | .UserIds 150 | .Split(",") 151 | .Select(uidAsStr => UserIdentifier.Parse(uidAsStr)) 152 | // TODO: exclude system push request from checking user setting 153 | .Where(uid => SettingManager.GetSettingValueForUser(AbpPushSettingNames.Receive, uid.TenantId, uid.UserId)) 154 | .ToList(); 155 | } 156 | 157 | if (!request.ExcludedUserIds.IsNullOrEmpty()) 158 | { 159 | //Exclude specified users. 160 | var excludedUserIds = request 161 | .ExcludedUserIds 162 | .Split(",") 163 | .Select(uidAsStr => UserIdentifier.Parse(uidAsStr)) 164 | .ToList(); 165 | 166 | userIds.RemoveAll(uid => excludedUserIds.Any(euid => euid.Equals(uid))); 167 | } 168 | 169 | return userIds.ToArray(); 170 | } 171 | 172 | public virtual IDisposableDependencyObjectWrapper CreateProvider(string provider) 173 | { 174 | var providerInfo = Configuration.ServiceProviders.FirstOrDefault(s => s.Name == provider); 175 | if (providerInfo == null) 176 | { 177 | throw new Exception("Unknown push provider: " + provider); 178 | } 179 | 180 | var pushProvider = IocResolver.ResolveAsDisposable(providerInfo.ProviderType); 181 | pushProvider.Object.Initialize(providerInfo); 182 | return pushProvider; 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/AbpPushRequestPublisher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Abp.BackgroundJobs; 5 | using Abp.Collections.Extensions; 6 | using Abp.Dependency; 7 | using Abp.Domain.Entities; 8 | using Abp.Domain.Uow; 9 | using Abp.Extensions; 10 | using Abp.Json; 11 | using Abp.Push.Configuration; 12 | using Abp.Runtime.Session; 13 | 14 | namespace Abp.Push.Requests 15 | { 16 | /// 17 | /// Implements . 18 | /// 19 | public class AbpPushRequestPublisher : AbpServiceBase, IPushRequestPublisher, ITransientDependency 20 | { 21 | public int MaxUserCountToDirectlyDistributeARequest { get; protected set; } = 5; 22 | 23 | /// 24 | /// Reference to ABP session. 25 | /// 26 | public IAbpSession AbpSession { get; set; } 27 | 28 | protected readonly IPushRequestStore RequestStore; 29 | protected readonly IBackgroundJobManager BackgroundJobManager; 30 | protected readonly IPushRequestDistributor RequestDistributor; 31 | protected readonly IAbpPushConfiguration Configuration; 32 | protected readonly IGuidGenerator GuidGenerator; 33 | 34 | /// 35 | /// Initializes a new instance of the class. 36 | /// 37 | public AbpPushRequestPublisher( 38 | IPushRequestStore pushRequestStore, 39 | IBackgroundJobManager backgroundJobManager, 40 | IPushRequestDistributor pushRequestDistributor, 41 | IAbpPushConfiguration pushConfiguration, 42 | IGuidGenerator guidGenerator 43 | ) 44 | { 45 | RequestStore = pushRequestStore; 46 | BackgroundJobManager = backgroundJobManager; 47 | RequestDistributor = pushRequestDistributor; 48 | Configuration = pushConfiguration; 49 | GuidGenerator = guidGenerator; 50 | 51 | AbpSession = NullAbpSession.Instance; 52 | } 53 | 54 | [UnitOfWork] 55 | public virtual async Task PublishAsync( 56 | string pushRequestName, 57 | PushRequestData data = null, 58 | EntityIdentifier entityIdentifier = null, 59 | PushRequestPriority priority = PushRequestPriority.Normal, 60 | IUserIdentifier[] userIds = null, 61 | IUserIdentifier[] excludedUserIds = null, 62 | int?[] tenantIds = null) 63 | { 64 | if (pushRequestName.IsNullOrEmpty()) 65 | { 66 | throw new ArgumentException("PushRequestName can not be null or whitespace!", nameof(pushRequestName)); 67 | } 68 | 69 | if (!tenantIds.IsNullOrEmpty() && !userIds.IsNullOrEmpty()) 70 | { 71 | throw new ArgumentException("tenantIds can be set only if userIds is not set!", nameof(tenantIds)); 72 | } 73 | 74 | if (tenantIds.IsNullOrEmpty() && userIds.IsNullOrEmpty()) 75 | { 76 | tenantIds = new[] { AbpSession.TenantId }; 77 | } 78 | 79 | var pushRequest = new PushRequest(GuidGenerator.Create()) 80 | { 81 | Name = pushRequestName, 82 | EntityTypeName = entityIdentifier?.Type.FullName, 83 | EntityTypeAssemblyQualifiedName = entityIdentifier?.Type.AssemblyQualifiedName, 84 | EntityId = entityIdentifier?.Id.ToJsonString(), 85 | Priority = priority, 86 | UserIds = userIds.IsNullOrEmpty() ? null : userIds.Select(uid => uid.ToUserIdentifier().ToUserIdentifierString()).JoinAsString(","), 87 | ExcludedUserIds = excludedUserIds.IsNullOrEmpty() ? null : excludedUserIds.Select(uid => uid.ToUserIdentifier().ToUserIdentifierString()).JoinAsString(","), 88 | TenantIds = PushRequest.ToTenantIds(tenantIds), 89 | Data = data?.ToJsonString(), 90 | DataTypeName = data?.GetType().AssemblyQualifiedName 91 | }; 92 | 93 | await RequestStore.InsertRequestAsync(pushRequest); 94 | 95 | await CurrentUnitOfWork.SaveChangesAsync(); //To get Id of the push request 96 | 97 | if (userIds != null && userIds.Length <= Configuration.MaxUserCountForForegroundDistribution) 98 | { 99 | //We can directly distribute the push request since there are not much receivers 100 | await RequestDistributor.DistributeAsync(pushRequest.Id); 101 | } 102 | else 103 | { 104 | //We enqueue a background job since distributing may get a long time 105 | await BackgroundJobManager.EnqueueAsync( 106 | new PushRequestDistributionJobArgs( 107 | pushRequest.Id 108 | ) 109 | ); 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/AbpPushRequestSubscriptionManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Abp.Dependency; 5 | using Abp.Domain.Entities; 6 | using Abp.Json; 7 | 8 | namespace Abp.Push.Requests 9 | { 10 | /// 11 | /// Implements . 12 | /// 13 | public class AbpPushRequestSubscriptionManager : IPushRequestSubscriptionManager, ITransientDependency 14 | { 15 | protected readonly IPushRequestStore RequestStore; 16 | protected readonly IPushDefinitionManager DefinitionManager; 17 | protected readonly IGuidGenerator GuidGenerator; 18 | 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | public AbpPushRequestSubscriptionManager( 23 | IPushRequestStore pushRequestStore, 24 | IPushDefinitionManager pushDefinitionManager, 25 | IGuidGenerator guidGenerator) 26 | { 27 | RequestStore = pushRequestStore; 28 | DefinitionManager = pushDefinitionManager; 29 | GuidGenerator = guidGenerator; 30 | } 31 | 32 | public virtual async Task SubscribeAsync(IUserIdentifier user, string pushRequestName, EntityIdentifier entityIdentifier = null) 33 | { 34 | if (await IsSubscribedAsync(user, pushRequestName, entityIdentifier)) 35 | { 36 | return; 37 | } 38 | 39 | await RequestStore.InsertSubscriptionAsync( 40 | new PushRequestSubscription( 41 | GuidGenerator.Create(), 42 | user.TenantId, 43 | user.UserId, 44 | pushRequestName, 45 | entityIdentifier 46 | ) 47 | ); 48 | } 49 | 50 | public virtual async Task SubscribeToAllAvailablePushRequestsAsync(IUserIdentifier user) 51 | { 52 | var pushRequestDefinitions = (await DefinitionManager 53 | .GetAllAvailableAsync(user)) 54 | .Where(nd => nd.EntityType == null) 55 | .ToList(); 56 | 57 | foreach (var notificationDefinition in pushRequestDefinitions) 58 | { 59 | await SubscribeAsync(user, notificationDefinition.Name); 60 | } 61 | } 62 | 63 | public virtual async Task UnsubscribeAsync(IUserIdentifier user, string pushRequestName, EntityIdentifier entityIdentifier = null) 64 | { 65 | await RequestStore.DeleteSubscriptionAsync( 66 | user, 67 | pushRequestName, 68 | entityIdentifier?.Type.FullName, 69 | entityIdentifier?.Id.ToJsonString() 70 | ); 71 | } 72 | 73 | // Can work only for single database approach 74 | public virtual async Task> GetSubscriptionsAsync(string pushRequestName, EntityIdentifier entityIdentifier = null, int skipCount = 0, int maxResultCount = int.MaxValue) 75 | { 76 | return await RequestStore.GetSubscriptionsAsync(pushRequestName, 77 | entityIdentifier?.Type.FullName, 78 | entityIdentifier?.Id.ToJsonString(), 79 | skipCount: skipCount, 80 | maxResultCount: maxResultCount 81 | ); 82 | } 83 | 84 | public virtual async Task> GetSubscriptionsAsync(int? tenantId, string pushRequestName, EntityIdentifier entityIdentifier = null, int skipCount = 0, int maxResultCount = int.MaxValue) 85 | { 86 | return await RequestStore.GetSubscriptionsAsync(new[] { tenantId }, 87 | pushRequestName, 88 | entityIdentifier?.Type.FullName, 89 | entityIdentifier?.Id.ToJsonString(), 90 | skipCount: skipCount, 91 | maxResultCount: maxResultCount 92 | ); 93 | } 94 | 95 | public virtual async Task> GetSubscribedPushRequestsAsync(IUserIdentifier user, int skipCount = 0, int maxResultCount = int.MaxValue) 96 | { 97 | return await RequestStore.GetSubscriptionsAsync(user, 98 | skipCount: skipCount, 99 | maxResultCount: maxResultCount); 100 | } 101 | 102 | public virtual Task IsSubscribedAsync(IUserIdentifier user, string pushRequestName, EntityIdentifier entityIdentifier = null) 103 | { 104 | return RequestStore.IsSubscribedAsync( 105 | user, 106 | pushRequestName, 107 | entityIdentifier?.Type.FullName, 108 | entityIdentifier?.Id.ToJsonString() 109 | ); 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/IPushRequestDistributor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Abp.Push.Requests 5 | { 6 | /// 7 | /// Used to distribute push requests. 8 | /// 9 | public interface IPushRequestDistributor 10 | { 11 | /// 12 | /// Distributes given push request. 13 | /// 14 | /// The push request id. 15 | Task DistributeAsync(Guid pushRequestId); 16 | } 17 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/IPushRequestPublisher.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Abp.Domain.Entities; 3 | 4 | namespace Abp.Push.Requests 5 | { 6 | /// 7 | /// Used to publish push request. 8 | /// 9 | public interface IPushRequestPublisher 10 | { 11 | /// 12 | /// Publishes a new push request. 13 | /// 14 | /// Unique push request name 15 | /// Push Request data (optional) 16 | /// The entity identifier if this request is related to an entity 17 | /// The push request priority 18 | /// 19 | /// Target user id(s). 20 | /// Used to send push request to specific user(s) (without checking the subscription). 21 | /// If this is null/empty, the request is sent to subscribed users. 22 | /// 23 | /// 24 | /// Excluded user id(s). 25 | /// This can be set to exclude some users while publishing requests to subscribed users. 26 | /// It's normally not set if is set. 27 | /// 28 | /// 29 | /// Target tenant id(s). 30 | /// Used to send push request to subscribed users of specific tenant(s). 31 | /// This should not be set if is set. 32 | /// can be passed to indicate all tenants. 33 | /// But this can only work in a single database approach (all tenants are stored in host database). 34 | /// If this is null, then it's automatically set to the current tenant on . 35 | /// 36 | Task PublishAsync( 37 | string pushRequestName, 38 | PushRequestData data = null, 39 | EntityIdentifier entityIdentifier = null, 40 | PushRequestPriority priority = PushRequestPriority.Normal, 41 | IUserIdentifier[] userIds = null, 42 | IUserIdentifier[] excludedUserIds = null, 43 | int?[] tenantIds = null); 44 | } 45 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/IPushRequestStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Abp.Push.Requests 6 | { 7 | public interface IPushRequestStore 8 | { 9 | #region Subscriptions 10 | 11 | /// 12 | /// Inserts a push request subscription. 13 | /// 14 | Task InsertSubscriptionAsync(PushRequestSubscription subscription); 15 | 16 | /// 17 | /// Deletes a push request subscription. 18 | /// 19 | Task DeleteSubscriptionAsync(IUserIdentifier user, string pushRequestName, string entityTypeName, string entityId); 20 | 21 | /// 22 | /// Gets subscriptions for a push request. 23 | /// 24 | Task> GetSubscriptionsAsync(string pushRequestName, string entityTypeName, string entityId, int skipCount = 0, int maxResultCount = int.MaxValue); 25 | 26 | /// 27 | /// Gets subscriptions for a push request for specified tenant(s). 28 | /// 29 | Task> GetSubscriptionsAsync(int?[] tenantIds, string pushRequestName, string entityTypeName, string entityId, int skipCount = 0, int maxResultCount = int.MaxValue); 30 | 31 | /// 32 | /// Gets subscriptions for a user. 33 | /// 34 | Task> GetSubscriptionsAsync(IUserIdentifier user, int skipCount = 0, int maxResultCount = int.MaxValue); 35 | 36 | /// 37 | /// Checks if a user subscribed for a push request 38 | /// 39 | Task IsSubscribedAsync(IUserIdentifier user, string pushRequestName, string entityTypeName, string entityId); 40 | 41 | #endregion 42 | 43 | /// 44 | /// Inserts a push request. 45 | /// 46 | /// The push request. 47 | Task InsertRequestAsync(PushRequest request); 48 | 49 | /// 50 | /// Gets a push request. 51 | /// 52 | /// The push request id. 53 | Task GetRequestOrNullAsync(Guid requestId); 54 | 55 | /// 56 | /// Gets push requests. 57 | /// 58 | /// Push request priority 59 | Task> GetRequestsAsync(PushRequestPriority? priority = null, int skipCount = 0, int maxResultCount = int.MaxValue); 60 | 61 | /// 62 | /// Gets push request count. 63 | /// 64 | /// Push request priority 65 | Task GethRequestCountAsync(PushRequestPriority? priority = null); 66 | 67 | /// 68 | /// Deletes a push request. 69 | /// 70 | /// The push request id. 71 | Task DeleteRequestAsync(Guid requestId); 72 | 73 | /// 74 | /// Updates a push request priority. 75 | /// 76 | Task UpdateRequestPriorityAsync(Guid requestId, PushRequestPriority priority); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/IPushRequestSubscriptionManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Abp.Domain.Entities; 4 | 5 | namespace Abp.Push.Requests 6 | { 7 | /// 8 | /// Used to manage subscriptions for push requests. 9 | /// 10 | public interface IPushRequestSubscriptionManager 11 | { 12 | /// 13 | /// Subscribes to a push request for given user and push request informations. 14 | /// 15 | /// User 16 | /// Name of the push request. 17 | /// entity identifier 18 | Task SubscribeAsync(IUserIdentifier user, string pushRequestName, EntityIdentifier entityIdentifier = null); 19 | 20 | /// 21 | /// Subscribes to all available push request for given user. 22 | /// It does not subscribe entity related push request. 23 | /// 24 | /// User. 25 | Task SubscribeToAllAvailablePushRequestsAsync(IUserIdentifier user); 26 | 27 | /// 28 | /// Unsubscribes from a push request. 29 | /// 30 | /// User. 31 | /// Name of the push request. 32 | /// entity identifier 33 | Task UnsubscribeAsync(IUserIdentifier user, string pushRequestName, EntityIdentifier entityIdentifier = null); 34 | 35 | /// 36 | /// Gets all subscribtions for given push request (including all tenants). 37 | /// This only works for single database approach in a multitenant application! 38 | /// 39 | /// Name of the push request. 40 | /// entity identifier 41 | Task> GetSubscriptionsAsync(string pushRequestName, EntityIdentifier entityIdentifier = null, int skipCount = 0, int maxResultCount = int.MaxValue); 42 | 43 | /// 44 | /// Gets all subscribtions for given push request. 45 | /// 46 | /// Tenant id. Null for the host. 47 | /// Name of the push request. 48 | /// entity identifier 49 | Task> GetSubscriptionsAsync(int? tenantId, string pushRequestName, EntityIdentifier entityIdentifier = null, int skipCount = 0, int maxResultCount = int.MaxValue); 50 | 51 | /// 52 | /// Gets subscribed push requests for a user. 53 | /// 54 | /// User. 55 | Task> GetSubscribedPushRequestsAsync(IUserIdentifier user, int skipCount = 0, int maxResultCount = int.MaxValue); 56 | 57 | /// 58 | /// Checks if a user subscribed for a push request. 59 | /// 60 | /// User. 61 | /// Name of the push request. 62 | /// entity identifier 63 | Task IsSubscribedAsync(IUserIdentifier user, string pushRequestName, EntityIdentifier entityIdentifier = null); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/NullPushRequestStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Abp.Push.Requests 6 | { 7 | /// 8 | /// Null pattern implementation of . 9 | /// 10 | public class NullPushRequestStore : IPushRequestStore 11 | { 12 | public Task InsertSubscriptionAsync(PushRequestSubscription subscription) 13 | { 14 | return Task.FromResult(0); 15 | } 16 | 17 | public Task DeleteSubscriptionAsync(IUserIdentifier user, string pushRequestName, string entityTypeName, string entityId) 18 | { 19 | return Task.FromResult(0); 20 | } 21 | 22 | public Task> GetSubscriptionsAsync(string pushRequestName, string entityTypeName, string entityId, int skipCount = 0, int maxResultCount = int.MaxValue) 23 | { 24 | return Task.FromResult(new List()); 25 | } 26 | 27 | public Task> GetSubscriptionsAsync(int?[] tenantIds, string pushRequestName, string entityTypeName, string entityId, int skipCount = 0, int maxResultCount = int.MaxValue) 28 | { 29 | return Task.FromResult(new List()); 30 | } 31 | 32 | public Task> GetSubscriptionsAsync(IUserIdentifier user, int skipCount = 0, int maxResultCount = int.MaxValue) 33 | { 34 | return Task.FromResult(new List()); 35 | } 36 | 37 | public Task IsSubscribedAsync(IUserIdentifier user, string pushRequestName, string entityTypeName, string entityId) 38 | { 39 | return Task.FromResult(false); 40 | } 41 | 42 | public Task DeleteRequestAsync(Guid requestId) 43 | { 44 | return Task.FromResult(0); 45 | } 46 | 47 | public Task InsertRequestAsync(PushRequest request) 48 | { 49 | return Task.FromResult(0); 50 | } 51 | 52 | public Task GetRequestOrNullAsync(Guid requestId) 53 | { 54 | return Task.FromResult(null as PushRequest); 55 | } 56 | 57 | public Task> GetRequestsAsync(PushRequestPriority? priority = null, int skipCount = 0, int maxResultCount = int.MaxValue) 58 | { 59 | return Task.FromResult(new List()); 60 | } 61 | 62 | public Task GethRequestCountAsync(PushRequestPriority? priority = null) 63 | { 64 | return Task.FromResult(0); 65 | } 66 | 67 | public Task UpdateRequestPriorityAsync(Guid pushRequestId, PushRequestPriority priority) 68 | { 69 | return Task.FromResult(0); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/PushRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | using System.Linq; 5 | using Abp.Collections.Extensions; 6 | using Abp.Domain.Entities.Auditing; 7 | using Abp.Extensions; 8 | using Abp.MultiTenancy; 9 | 10 | namespace Abp.Push.Requests 11 | { 12 | /// 13 | /// Used to store a push request. 14 | /// This push request is distributed to tenants and users by . 15 | /// 16 | [Serializable] 17 | [Table("AbpPushRequests")] 18 | [MultiTenancySide(MultiTenancySides.Host)] 19 | public class PushRequest : CreationAuditedEntity 20 | { 21 | /// 22 | /// Maximum length of property. 23 | /// Value: 512. 24 | /// 25 | public const int MaxNameLength = 512; 26 | 27 | /// 28 | /// Maximum length of property. 29 | /// Value: 4,194,304 (4 MB). 30 | /// 31 | public const int MaxDataLength = 4 * 1024 * 1024; 32 | 33 | /// 34 | /// Maximum length of property. 35 | /// Value: 512. 36 | /// 37 | public const int MaxDataTypeNameLength = 512; 38 | 39 | /// 40 | /// Maximum length of property. 41 | /// Value: 512. 42 | /// 43 | public const int MaxEntityTypeNameLength = 512; 44 | 45 | /// 46 | /// Maximum length of property. 47 | /// Value: 512. 48 | /// 49 | public const int MaxEntityTypeAssemblyQualifiedNameLength = 512; 50 | 51 | /// 52 | /// Maximum length of property. 53 | /// Value: 256. 54 | /// 55 | public const int MaxEntityIdLength = 256; 56 | 57 | /// 58 | /// Maximum length of property. 59 | /// Value: 2,097,152 (2 MB). 60 | /// 61 | public const int MaxUserIdsLength = 2 * 1024 * 1024; 62 | 63 | /// 64 | /// Maximum length of property. 65 | /// Value: 1,048,576 (1 MB). 66 | /// 67 | public const int MaxTenantIdsLength = 1024 * 1024; 68 | 69 | /// 70 | /// Maximum length of property. 71 | /// Value: 4,194,304 (4 MB). 72 | /// 73 | public const int MaxLastExecutionResultLength = 4 * 1024 * 1024; 74 | 75 | /// 76 | /// Unique push request name. 77 | /// 78 | [Required] 79 | [MaxLength(MaxNameLength)] 80 | public virtual string Name { get; set; } 81 | 82 | /// 83 | /// Request data as JSON string. 84 | /// 85 | [MaxLength(MaxDataLength)] 86 | public virtual string Data { get; set; } 87 | 88 | /// 89 | /// Type of the JSON serialized . 90 | /// It's AssemblyQualifiedName of the type. 91 | /// 92 | [MaxLength(MaxDataTypeNameLength)] 93 | public virtual string DataTypeName { get; set; } 94 | 95 | /// 96 | /// Gets/sets entity type name, if this is an entity level push request. 97 | /// It's FullName of the entity type. 98 | /// 99 | [MaxLength(MaxEntityTypeNameLength)] 100 | public virtual string EntityTypeName { get; set; } 101 | 102 | /// 103 | /// AssemblyQualifiedName of the entity type. 104 | /// 105 | [MaxLength(MaxEntityTypeAssemblyQualifiedNameLength)] 106 | public virtual string EntityTypeAssemblyQualifiedName { get; set; } 107 | 108 | /// 109 | /// Gets/sets primary key of the entity, if this is an entity level push request. 110 | /// 111 | [MaxLength(MaxEntityIdLength)] 112 | public virtual string EntityId { get; set; } 113 | 114 | /// 115 | /// Target users of the request. 116 | /// If this is set, it overrides subscribed users. 117 | /// If this is null/empty, then push request is sent to all subscribed users. 118 | /// 119 | [MaxLength(MaxUserIdsLength)] 120 | public virtual string UserIds { get; set; } 121 | 122 | /// 123 | /// Excluded users. 124 | /// This can be set to exclude some users while publishing requests to subscribed users. 125 | /// It's not normally used if is not null. 126 | /// 127 | [MaxLength(MaxUserIdsLength)] 128 | public virtual string ExcludedUserIds { get; set; } 129 | 130 | /// 131 | /// Target tenants of the request. 132 | /// Used to send request to subscribed users of specific tenant(s). 133 | /// This is valid only if UserIds is null. 134 | /// If it's "0", then indicates to all tenants. 135 | /// 136 | [MaxLength(MaxTenantIdsLength)] 137 | public virtual string TenantIds { get; set; } 138 | 139 | /// 140 | /// Gets or sets the priority. 141 | /// 142 | /// The priority. 143 | public virtual PushRequestPriority Priority { get; set; } 144 | 145 | /// 146 | /// Gets or sets the expiration time. 147 | /// 148 | /// The expiration time. 149 | public virtual DateTime? ExpirationTime { get; set; } 150 | 151 | /// 152 | /// Gets or sets the max failed count. 153 | /// 154 | /// The max failed count. 155 | public virtual int? MaxFailedCount { get; set; } 156 | 157 | /// 158 | /// Gets or sets the failed count. 159 | /// 160 | /// The failed count. 161 | public virtual int FailedCount { get; set; } 162 | 163 | /// 164 | /// Gets or sets the last execution time. 165 | /// 166 | /// The last execution time. 167 | public virtual DateTime? LastExecutionTime { get; set; } 168 | 169 | /// 170 | /// Gets or sets the last execution result. 171 | /// 172 | /// The last execution result. 173 | [MaxLength(MaxLastExecutionResultLength)] 174 | public virtual string LastExecutionResult { get; set; } 175 | 176 | public PushRequest() 177 | { 178 | } 179 | 180 | /// 181 | /// Initializes a new instance of the class. 182 | /// 183 | public PushRequest(Guid id) 184 | { 185 | Id = id; 186 | Priority = PushRequestPriority.Normal; 187 | } 188 | 189 | /// 190 | /// Indicates all tenant ids for property. 191 | /// Value: "0". 192 | /// 193 | public const string AllTenantIdsString = "0"; 194 | 195 | /// 196 | /// Indicates all tenants. 197 | /// 198 | public static int AllTenantIds = 0; 199 | 200 | public static string ToTenantIds(int?[] tenantIds) 201 | { 202 | if (tenantIds.IsNullOrEmpty()) 203 | { 204 | return null; 205 | } 206 | return tenantIds.JoinAsString(","); 207 | } 208 | 209 | public static int?[] GetTenantIds(string tenantIdsString) 210 | { 211 | if (tenantIdsString.IsNullOrWhiteSpace()) 212 | { 213 | return new int?[] { }; 214 | } 215 | 216 | return tenantIdsString 217 | .Split(",") 218 | .Select(tenantIdAsStr => tenantIdAsStr == "null" ? (int?)null : (int?)tenantIdAsStr.To()) 219 | .ToArray(); 220 | } 221 | } 222 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/PushRequestData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Abp.Collections.Extensions; 4 | using Abp.Json; 5 | 6 | namespace Abp.Push.Requests 7 | { 8 | /// 9 | /// Used to store data for a push request. 10 | /// It can be directly used or can be derived. 11 | /// 12 | [Serializable] 13 | public class PushRequestData 14 | { 15 | /// 16 | /// Gets push request data type name. 17 | /// It returns the full class name by default. 18 | /// 19 | public virtual string Type => GetType().FullName; 20 | 21 | /// 22 | /// Shortcut to set/get . 23 | /// 24 | public object this[string key] 25 | { 26 | get { return Properties.GetOrDefault(key); } 27 | set { Properties[key] = value; } 28 | } 29 | 30 | /// 31 | /// Can be used to add custom properties to this request. 32 | /// 33 | public Dictionary Properties 34 | { 35 | get { return _properties; } 36 | set 37 | { 38 | if (value == null) 39 | { 40 | throw new ArgumentNullException(nameof(value)); 41 | } 42 | 43 | /* Not assign value, but add dictionary items. This is required for backward compability. */ 44 | foreach (var keyValue in value) 45 | { 46 | if (!_properties.ContainsKey(keyValue.Key)) 47 | { 48 | _properties[keyValue.Key] = keyValue.Value; 49 | } 50 | } 51 | } 52 | } 53 | private readonly Dictionary _properties; 54 | 55 | /// 56 | /// Createa a new RequestData object. 57 | /// 58 | public PushRequestData() 59 | { 60 | _properties = new Dictionary(); 61 | } 62 | 63 | public override string ToString() 64 | { 65 | // TODO: loop reference during serialization 66 | return this.ToJsonString(); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/PushRequestDistributionJob.cs: -------------------------------------------------------------------------------- 1 | using Abp.BackgroundJobs; 2 | using Abp.Dependency; 3 | using Abp.Threading; 4 | 5 | namespace Abp.Push.Requests 6 | { 7 | /// 8 | /// This background job distributes push requests to users. 9 | /// 10 | public class PushRequestDistributionJob : BackgroundJob, ITransientDependency 11 | { 12 | private readonly IPushRequestDistributor _pushRequestDistributor; 13 | 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | public PushRequestDistributionJob(IPushRequestDistributor pushRequestDistributer) 18 | { 19 | _pushRequestDistributor = pushRequestDistributer; 20 | } 21 | 22 | public override void Execute(PushRequestDistributionJobArgs args) 23 | { 24 | AsyncHelper.RunSync(() => _pushRequestDistributor.DistributeAsync(args.PushRequestId)); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/PushRequestDistributionJobArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Abp.Push.Requests 4 | { 5 | /// 6 | /// Arguments for . 7 | /// 8 | [Serializable] 9 | public class PushRequestDistributionJobArgs 10 | { 11 | /// 12 | /// Push Request Id. 13 | /// 14 | public Guid PushRequestId { get; set; } 15 | 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | public PushRequestDistributionJobArgs(Guid pushRequestId) 20 | { 21 | PushRequestId = pushRequestId; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/PushRequestEntityData.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace Abp.Push.Requests 5 | { 6 | [Serializable] 7 | public class PushRequestEntityData : PushRequestData 8 | { 9 | /// 10 | /// Converted from EntityTypeName 11 | /// 12 | [JsonProperty(PropertyName = "name", NullValueHandling = NullValueHandling.Ignore)] 13 | public string EntityName { get; set; } 14 | 15 | [JsonProperty(PropertyName = "id", NullValueHandling = NullValueHandling.Ignore)] 16 | public string EntityId { get; set; } 17 | 18 | [JsonProperty(PropertyName = "event", NullValueHandling = NullValueHandling.Ignore)] 19 | public string EntityEvent { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/PushRequestPriority.cs: -------------------------------------------------------------------------------- 1 | namespace Abp.Push.Requests 2 | { 3 | public enum PushRequestPriority 4 | { 5 | RealTime, 6 | High, 7 | AboveNormal, 8 | Normal, 9 | BelowNormal, 10 | Low, 11 | Idle 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/PushRequestPublisherExtensions.cs: -------------------------------------------------------------------------------- 1 | using Abp.Domain.Entities; 2 | using Abp.Threading; 3 | 4 | namespace Abp.Push.Requests 5 | { 6 | public static class PushRequestPublisherExtensions 7 | { 8 | /// 9 | /// Publishes a new push request. 10 | /// 11 | /// Push Request publisher 12 | /// Unique push request name 13 | /// Push request data (optional) 14 | /// The entity identifier if this push request is related to an entity 15 | /// Push request priority 16 | /// Target user id(s). Used to send push request to specific user(s). If this is null/empty, the notification is sent to all subscribed users 17 | public static void Publish(this IPushRequestPublisher pushRequestPublisher, string pushRequestName, PushRequestData data = null, EntityIdentifier entityIdentifier = null, PushRequestPriority priority = PushRequestPriority.Normal, IUserIdentifier[] userIds = null) 18 | { 19 | AsyncHelper.RunSync(() => pushRequestPublisher.PublishAsync(pushRequestName, data, entityIdentifier, priority, userIds)); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/PushRequestSubscription.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | using Abp.Domain.Entities; 5 | using Abp.Domain.Entities.Auditing; 6 | using Abp.Json; 7 | 8 | namespace Abp.Push.Requests 9 | { 10 | /// 11 | /// Used to store a push request subscription. 12 | /// 13 | [Table("AbpPushRequestSubscriptions")] 14 | public class PushRequestSubscription : CreationAuditedEntity, IMayHaveTenant 15 | { 16 | /// 17 | /// Tenant id of the subscribed user. 18 | /// 19 | public virtual int? TenantId { get; set; } 20 | 21 | /// 22 | /// User Id. 23 | /// 24 | public virtual long UserId { get; set; } 25 | 26 | /// 27 | /// Push request unique name. 28 | /// 29 | [MaxLength(PushRequest.MaxNameLength)] 30 | public virtual string PushRequestName { get; set; } 31 | 32 | /// 33 | /// Gets/sets entity type name, if this is an entity level push request. 34 | /// It's FullName of the entity type. 35 | /// 36 | [MaxLength(PushRequest.MaxEntityTypeNameLength)] 37 | public virtual string EntityTypeName { get; set; } 38 | 39 | /// 40 | /// AssemblyQualifiedName of the entity type. 41 | /// 42 | [MaxLength(PushRequest.MaxEntityTypeAssemblyQualifiedNameLength)] 43 | public virtual string EntityTypeAssemblyQualifiedName { get; set; } 44 | 45 | /// 46 | /// Gets/sets primary key of the entity, if this is an entity level push request. 47 | /// 48 | [MaxLength(PushRequest.MaxEntityIdLength)] 49 | public virtual string EntityId { get; set; } 50 | 51 | /// 52 | /// Initializes a new instance of the class. 53 | /// 54 | public PushRequestSubscription() 55 | { 56 | } 57 | 58 | /// 59 | /// Initializes a new instance of the class. 60 | /// 61 | public PushRequestSubscription(Guid id, int? tenantId, long userId, string pushRequestName, EntityIdentifier entityIdentifier = null) 62 | { 63 | Id = id; 64 | TenantId = tenantId; 65 | PushRequestName = pushRequestName; 66 | UserId = userId; 67 | EntityTypeName = entityIdentifier == null ? null : entityIdentifier.Type.FullName; 68 | EntityTypeAssemblyQualifiedName = entityIdentifier == null ? null : entityIdentifier.Type.AssemblyQualifiedName; 69 | EntityId = entityIdentifier == null ? null : entityIdentifier.Id.ToJsonString(); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /src/Abp.Push.Common/Push/Requests/PushRequestSubscriptionManagerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Abp.Domain.Entities; 3 | using Abp.Threading; 4 | 5 | namespace Abp.Push.Requests 6 | { 7 | public static class PushRequestSubscriptionManagerExtensions 8 | { 9 | /// 10 | /// Subscribes to a push request. 11 | /// 12 | /// Push request subscription manager 13 | /// User. 14 | /// Name of the push request. 15 | /// entity identifier 16 | public static void Subscribe(this IPushRequestSubscriptionManager pushRequestSubscriptionManager, IUserIdentifier user, string pushRequestName, EntityIdentifier entityIdentifier = null) 17 | { 18 | AsyncHelper.RunSync(() => pushRequestSubscriptionManager.SubscribeAsync(user, pushRequestName, entityIdentifier)); 19 | } 20 | 21 | /// 22 | /// Subscribes to all available push requests for given user. 23 | /// It does not subscribe entity related push requests. 24 | /// 25 | /// Push request subscription manager 26 | /// User. 27 | public static void SubscribeToAllAvailablePushRequests(this IPushRequestSubscriptionManager pushRequestSubscriptionManager, IUserIdentifier user) 28 | { 29 | AsyncHelper.RunSync(() => pushRequestSubscriptionManager.SubscribeToAllAvailablePushRequestsAsync(user)); 30 | } 31 | 32 | /// 33 | /// Unsubscribes from a push request. 34 | /// 35 | /// Push request subscription manager 36 | /// User. 37 | /// Name of the push request. 38 | /// entity identifier 39 | public static void Unsubscribe(this IPushRequestSubscriptionManager pushRequestSubscriptionManager, UserIdentifier user, string pushRequestName, EntityIdentifier entityIdentifier = null) 40 | { 41 | AsyncHelper.RunSync(() => pushRequestSubscriptionManager.UnsubscribeAsync(user, pushRequestName, entityIdentifier)); 42 | } 43 | 44 | /// 45 | /// Gets all subscribtions for given push request. 46 | /// Can work only for single database approach 47 | /// 48 | /// Push request subscription manager 49 | /// Name of the push request. 50 | /// entity identifier 51 | public static List GetSubscriptions(this IPushRequestSubscriptionManager pushRequestSubscriptionManager, string pushRequestName, EntityIdentifier entityIdentifier = null, int skipCount = 0, int maxResultCount = int.MaxValue) 52 | { 53 | return AsyncHelper.RunSync(() => pushRequestSubscriptionManager.GetSubscriptionsAsync(pushRequestName, entityIdentifier, skipCount, maxResultCount)); 54 | } 55 | 56 | /// 57 | /// Gets all subscribtions for given push request. 58 | /// 59 | /// Push request subscription manager 60 | /// Tenant id. Null for the host. 61 | /// Name of the push request. 62 | /// entity identifier 63 | public static List GetSubscriptions(this IPushRequestSubscriptionManager pushRequestSubscriptionManager, int? tenantId, string pushRequestName, EntityIdentifier entityIdentifier = null, int skipCount = 0, int maxResultCount = int.MaxValue) 64 | { 65 | return AsyncHelper.RunSync(() => pushRequestSubscriptionManager.GetSubscriptionsAsync(tenantId, pushRequestName, entityIdentifier, skipCount, maxResultCount)); 66 | } 67 | 68 | /// 69 | /// Gets subscribed push requests for a user. 70 | /// 71 | /// Push request subscription manager 72 | /// User. 73 | public static List GetSubscribedPushRequests(this IPushRequestSubscriptionManager pushRequestSubscriptionManager, IUserIdentifier user, int skipCount = 0, int maxResultCount = int.MaxValue) 74 | { 75 | return AsyncHelper.RunSync(() => pushRequestSubscriptionManager.GetSubscribedPushRequestsAsync(user, skipCount, maxResultCount)); 76 | } 77 | 78 | /// 79 | /// Checks if a user subscribed for a push request. 80 | /// 81 | /// Push request subscription manager 82 | /// User. 83 | /// Name of the push request. 84 | /// entity identifier 85 | public static bool IsSubscribed(this IPushRequestSubscriptionManager pushRequestSubscriptionManager, IUserIdentifier user, string pushRequestName, EntityIdentifier entityIdentifier = null) 86 | { 87 | return AsyncHelper.RunSync(() => pushRequestSubscriptionManager.IsSubscribedAsync(user, pushRequestName, entityIdentifier)); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/Abp.Push.EntityFrameworkCore/Abp.Push.EntityFrameworkCore.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | netstandard2.0 7 | Abp.Push.EntityFrameworkCore 8 | Abp.Push.EntityFrameworkCore 9 | Abp.Push.EntityFrameworkCore 10 | Abp 11 | false 12 | false 13 | false 14 | false 15 | false 16 | false 17 | true 18 | False 19 | Library 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/Abp.Push.EntityFrameworkCore/Abp.Push.EntityFrameworkCore.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Abp.Push.EntityFrameworkCore 5 | $version$ 6 | $title$ 7 | Ryancyq 8 | Ryancyq 9 | true 10 | Abp Push EntityFrameworkCore Module 11 | Summary of changes made in this release of the package. 12 | Copyright 2019 13 | netstandard2.0 asp.net mvc abp aspnetboilerplate 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Abp.Push.EntityFrameworkCore/Push/EntityFrameworkCore/AbpPushEntityFrameworkCoreConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Abp.Push.Devices; 3 | using Abp.Push.Requests; 4 | 5 | namespace Abp.Push.EntityFrameworkCore 6 | { 7 | public static class AbpPushEntityFrameworkCoreConfigurationExtensions 8 | { 9 | public static void ConfigurePushEntities(this ModelBuilder modelBuilder, string prefix = null, string schemaName = null) 10 | where TDevice : AbpPushDevice, new() 11 | { 12 | prefix = prefix ?? "Abp"; 13 | modelBuilder.Entity(device => 14 | { 15 | var tableName = prefix + "PushDevices"; 16 | if (schemaName == null) 17 | { 18 | device.ToTable(tableName); 19 | } 20 | else 21 | { 22 | device.ToTable(tableName, schemaName); 23 | } 24 | 25 | device.HasIndex(e => new { e.TenantId, e.UserId }); 26 | device.HasIndex(e => new { e.TenantId, e.DeviceIdentifier }); 27 | device.HasIndex(e => new { e.TenantId, e.NormalizedDeviceName }); 28 | device.HasIndex(e => new { e.TenantId, e.DevicePlatform }); 29 | device.HasIndex(e => new { e.TenantId, e.ServiceProvider }); 30 | }); 31 | 32 | modelBuilder.Entity(request => 33 | { 34 | var tableName = prefix + "PushRequests"; 35 | if (schemaName == null) 36 | { 37 | request.ToTable(tableName); 38 | } 39 | else 40 | { 41 | request.ToTable(tableName, schemaName); 42 | } 43 | 44 | request.HasIndex(e => new { e.Name }); 45 | request.HasIndex(e => new { e.CreationTime }); 46 | request.HasIndex(e => new { e.ExpirationTime }); 47 | }); 48 | 49 | modelBuilder.Entity(subscription => 50 | { 51 | var tableName = prefix + "PushRequestSubscriptions"; 52 | if (schemaName == null) 53 | { 54 | subscription.ToTable(tableName); 55 | } 56 | else 57 | { 58 | subscription.ToTable(tableName, schemaName); 59 | } 60 | 61 | subscription.HasIndex(e => new { e.PushRequestName, e.EntityTypeName, e.EntityId, e.UserId }); 62 | subscription.HasIndex(e => new { e.TenantId, e.PushRequestName, e.EntityTypeName, e.EntityId, e.UserId }); 63 | }); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Abp.Push.EntityFrameworkCore/Push/EntityFrameworkCore/AbpPushEntityFrameworkCoreModule.cs: -------------------------------------------------------------------------------- 1 | using Abp.Modules; 2 | using Abp.Reflection.Extensions; 3 | 4 | namespace Abp.Push 5 | { 6 | [DependsOn(typeof(AbpPushModule))] 7 | public class AbpPushEntityFrameworkCoreModule : AbpModule 8 | { 9 | /// 10 | public override void PreInitialize() 11 | { 12 | } 13 | 14 | /// 15 | public override void Initialize() 16 | { 17 | IocManager.RegisterAssemblyByConvention(typeof(AbpPushEntityFrameworkCoreModule).GetAssembly()); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Abp.Push.EntityFrameworkCore/Push/EntityFrameworkCore/IAbpPushDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Abp.Push.Devices; 3 | using Abp.Push.Requests; 4 | 5 | namespace Abp.Push.EntityFrameworkCore 6 | { 7 | public interface IAbpPushDbContext where TDevice : AbpPushDevice, new() 8 | { 9 | DbSet PushDevices { get; set; } 10 | 11 | DbSet PushRequests { get; set; } 12 | 13 | DbSet PushRequestSubscriptions { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Abp.Push/Abp.Push.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | netstandard2.0 7 | Abp Push 8 | Abp.Push 9 | Abp.Push 10 | Abp 11 | false 12 | false 13 | false 14 | false 15 | false 16 | false 17 | true 18 | False 19 | Library 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Abp.Push/Abp.Push.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Abp.Push 5 | $version$ 6 | $title$ 7 | Ryancyq 8 | Ryancyq 9 | true 10 | Abp Push Module 11 | Summary of changes made in this release of the package. 12 | Copyright 2019 13 | netstandard2.0 asp.net mvc abp aspnetboilerplate 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Abp.Push/Push/AbpPushModule.cs: -------------------------------------------------------------------------------- 1 | using Abp.Configuration.Startup; 2 | using Abp.Dependency; 3 | using Abp.Modules; 4 | using Abp.Push.Requests; 5 | using Abp.Reflection.Extensions; 6 | 7 | namespace Abp.Push 8 | { 9 | [DependsOn(typeof(AbpPushCommonModule))] 10 | public class AbpPushModule : AbpModule 11 | { 12 | /// 13 | public override void PreInitialize() 14 | { 15 | Configuration.ReplaceService(DependencyLifeStyle.Transient); 16 | } 17 | 18 | /// 19 | public override void Initialize() 20 | { 21 | IocManager.RegisterAssemblyByConvention(typeof(AbpPushModule).GetAssembly()); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Abp.Push/Push/Configuration/AbpPushModuleConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Abp.Configuration.Startup; 3 | using Abp.Push.Requests; 4 | using Abp.Push.Devices; 5 | 6 | namespace Abp.Push.Configuration 7 | { 8 | public static class AbpPushModuleConfigurationExtensions 9 | { 10 | /// 11 | /// Configures persistent push request store. 12 | /// 13 | public static void ConfigStore(this IAbpPushConfiguration configuration, Action configureAction) 14 | { 15 | configureAction(configuration.AbpConfiguration.Modules.AbpPush().StoreConfiguration); 16 | } 17 | 18 | /// 19 | /// Configures persistent push store. 20 | /// 21 | public static void UsePersistentStore(this IAbpPushStoreConfiguration configuration) 22 | where TDevice : AbpPushDevice, new() 23 | { 24 | configuration.DeviceStore.UsePersistentStore(); 25 | configuration.RequestStore.UsePersistentStore(); 26 | } 27 | 28 | /// 29 | /// Configures persistent push device store. 30 | /// 31 | public static void UsePersistentStore(this IAbpPushRequestStoreConfiguration configuration) 32 | { 33 | configuration.AbpConfiguration.ReplaceService(); 34 | } 35 | 36 | /// 37 | /// Configures persistent push device store. 38 | /// 39 | public static void UsePersistentStore(this IAbpPushDeviceStoreConfiguration configuration) 40 | where TDevice : AbpPushDevice, new() 41 | { 42 | configuration.AbpConfiguration.ReplaceService, AbpPersistentPushDeviceStore>(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Abp.Push/Push/Devices/AbpPersistentPushDeviceStore.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Abp.Domain.Repositories; 5 | using Abp.Domain.Uow; 6 | using Abp.Linq; 7 | 8 | namespace Abp.Push.Devices 9 | { 10 | /// 11 | /// Implements using repositories. 12 | /// 13 | public abstract class AbpPersistentPushDeviceStore : AbpServiceBase, IPushDeviceStore 14 | where TDevice : AbpPushDevice, new() 15 | { 16 | public IAsyncQueryableExecuter AsyncQueryableExecuter { get; set; } 17 | 18 | protected readonly IRepository DeviceRepository; 19 | 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | protected AbpPersistentPushDeviceStore( 24 | IRepository deviceRepository) 25 | { 26 | DeviceRepository = deviceRepository; 27 | } 28 | 29 | [UnitOfWork] 30 | public virtual async Task DeleteDeviceAsync(TDevice device) 31 | { 32 | using (UnitOfWorkManager.Current.SetTenantId(device.TenantId)) 33 | { 34 | await DeviceRepository.DeleteAsync(device); 35 | await UnitOfWorkManager.Current.SaveChangesAsync(); 36 | } 37 | } 38 | 39 | [UnitOfWork] 40 | public virtual async Task DeleteDeviceAsync(IDeviceIdentifier deviceIdentifier) 41 | { 42 | using (UnitOfWorkManager.Current.SetTenantId(deviceIdentifier.TenantId)) 43 | { 44 | await DeviceRepository.DeleteAsync(d => d.TenantId == deviceIdentifier.TenantId && 45 | d.Id == deviceIdentifier.DeviceId); 46 | await UnitOfWorkManager.Current.SaveChangesAsync(); 47 | } 48 | } 49 | 50 | [UnitOfWork] 51 | public virtual async Task DeleteDevicesByPlatformAsync(int? tenantId, string devicePlatform) 52 | { 53 | using (UnitOfWorkManager.Current.SetTenantId(tenantId)) 54 | { 55 | await DeviceRepository.DeleteAsync(d => d.DevicePlatform == devicePlatform); 56 | await UnitOfWorkManager.Current.SaveChangesAsync(); 57 | } 58 | } 59 | 60 | [UnitOfWork] 61 | public virtual async Task DeleteDevicesByProviderAsync(int? tenantId, string serviceProvider) 62 | { 63 | using (UnitOfWorkManager.Current.SetTenantId(tenantId)) 64 | { 65 | await DeviceRepository.DeleteAsync(d => d.ServiceProvider == serviceProvider); 66 | await UnitOfWorkManager.Current.SaveChangesAsync(); 67 | } 68 | } 69 | 70 | [UnitOfWork] 71 | public virtual async Task DeleteDevicesByProviderAsync(int? tenantId, string serviceProvider, string serviceProviderKey) 72 | { 73 | using (UnitOfWorkManager.Current.SetTenantId(tenantId)) 74 | { 75 | await DeviceRepository.DeleteAsync(d => d.ServiceProvider == serviceProvider && 76 | d.ServiceProviderKey == serviceProviderKey); 77 | await UnitOfWorkManager.Current.SaveChangesAsync(); 78 | } 79 | } 80 | 81 | [UnitOfWork] 82 | public virtual async Task DeleteDevicesByUserAsync(IUserIdentifier userIdentifier) 83 | { 84 | using (UnitOfWorkManager.Current.SetTenantId(userIdentifier.TenantId)) 85 | { 86 | await DeviceRepository.DeleteAsync(d => d.UserId == userIdentifier.UserId); 87 | await UnitOfWorkManager.Current.SaveChangesAsync(); 88 | } 89 | } 90 | 91 | [UnitOfWork] 92 | public virtual async Task DeleteDevicesByUserPlatformAsync(IUserIdentifier userIdentifier, string devicePlatform) 93 | { 94 | using (UnitOfWorkManager.Current.SetTenantId(userIdentifier.TenantId)) 95 | { 96 | await DeviceRepository.DeleteAsync(d => d.UserId == userIdentifier.UserId && 97 | d.DevicePlatform == devicePlatform); 98 | await UnitOfWorkManager.Current.SaveChangesAsync(); 99 | } 100 | } 101 | 102 | [UnitOfWork] 103 | public virtual async Task DeleteDevicesByUserProviderAsync(IUserIdentifier userIdentifier, string serviceProvider) 104 | { 105 | using (UnitOfWorkManager.Current.SetTenantId(userIdentifier.TenantId)) 106 | { 107 | await DeviceRepository.DeleteAsync(d => d.UserId == userIdentifier.UserId && 108 | d.ServiceProvider == serviceProvider); 109 | await UnitOfWorkManager.Current.SaveChangesAsync(); 110 | } 111 | } 112 | 113 | [UnitOfWork] 114 | public virtual async Task GetDeviceOrNullAsync(int? tenantId, string serviceProvider, string serviceProviderKey) 115 | { 116 | using (UnitOfWorkManager.Current.SetTenantId(tenantId)) 117 | { 118 | return await DeviceRepository.FirstOrDefaultAsync(d => d.ServiceProvider == serviceProvider && 119 | d.ServiceProviderKey == serviceProviderKey); 120 | } 121 | } 122 | 123 | [UnitOfWork] 124 | public virtual async Task GetUserDeviceOrNullAsync(IUserIdentifier userIdentifier, string serviceProvider, string serviceProviderKey) 125 | { 126 | using (UnitOfWorkManager.Current.SetTenantId(userIdentifier.TenantId)) 127 | { 128 | return await DeviceRepository.FirstOrDefaultAsync(d => d.UserId == userIdentifier.UserId && 129 | d.ServiceProvider == serviceProvider && 130 | d.ServiceProviderKey == serviceProviderKey); 131 | } 132 | 133 | } 134 | 135 | [UnitOfWork] 136 | public virtual async Task> GetDevicesAsync(int? tenantId, int? skipCount = null, int? maxResultCount = null) 137 | { 138 | using (UnitOfWorkManager.Current.SetTenantId(tenantId)) 139 | { 140 | var query = DeviceRepository.GetAll(); 141 | query = ApplySkipTake(query, skipCount, maxResultCount); 142 | 143 | return await AsyncQueryableExecuter.ToListAsync(query); 144 | } 145 | } 146 | 147 | [UnitOfWork] 148 | public virtual async Task> GetDevicesByPlatformAsync(int? tenantId, string devicePlatform, int? skipCount = null, int? maxResultCount = null) 149 | { 150 | using (UnitOfWorkManager.Current.SetTenantId(tenantId)) 151 | { 152 | var query = DeviceRepository.GetAll(); 153 | query = ApplyPlatform(query, devicePlatform); 154 | query = ApplySkipTake(query, skipCount, maxResultCount); 155 | 156 | return await AsyncQueryableExecuter.ToListAsync(query); 157 | } 158 | } 159 | 160 | [UnitOfWork] 161 | public virtual async Task> GetDevicesByProviderAsync(int? tenantId, string serviceProvider, int? skipCount = null, int? maxResultCount = null) 162 | { 163 | using (UnitOfWorkManager.Current.SetTenantId(tenantId)) 164 | { 165 | var query = DeviceRepository.GetAll(); 166 | query = ApplyProvider(query, serviceProvider); 167 | query = ApplySkipTake(query, skipCount, maxResultCount); 168 | 169 | return await AsyncQueryableExecuter.ToListAsync(query); 170 | } 171 | } 172 | 173 | [UnitOfWork] 174 | public virtual async Task> GetDevicesByUserAsync(IUserIdentifier userIdentifier, int? skipCount = null, int? maxResultCount = null) 175 | { 176 | using (UnitOfWorkManager.Current.SetTenantId(userIdentifier.TenantId)) 177 | { 178 | var query = DeviceRepository.GetAll(); 179 | query = ApplyUser(query, userIdentifier); 180 | query = ApplySkipTake(query, skipCount, maxResultCount); 181 | 182 | return await AsyncQueryableExecuter.ToListAsync(query); 183 | } 184 | } 185 | 186 | [UnitOfWork] 187 | public virtual async Task> GetDevicesByUserPlatformAsync(IUserIdentifier userIdentifier, string devicePlatform, int? skipCount = null, int? maxResultCount = null) 188 | { 189 | using (UnitOfWorkManager.Current.SetTenantId(userIdentifier.TenantId)) 190 | { 191 | var query = DeviceRepository.GetAll(); 192 | query = ApplyPlatform(query, devicePlatform); 193 | query = ApplyUser(query, userIdentifier); 194 | query = ApplySkipTake(query, skipCount, maxResultCount); 195 | 196 | return await AsyncQueryableExecuter.ToListAsync(query); 197 | } 198 | } 199 | 200 | [UnitOfWork] 201 | public virtual async Task> GetDevicesByUserProviderAsync(IUserIdentifier userIdentifier, string serviceProvider, int? skipCount = null, int? maxResultCount = null) 202 | { 203 | using (UnitOfWorkManager.Current.SetTenantId(userIdentifier.TenantId)) 204 | { 205 | var query = DeviceRepository.GetAll(); 206 | query = ApplyProvider(query, serviceProvider); 207 | query = ApplyUser(query, userIdentifier); 208 | query = ApplySkipTake(query, skipCount, maxResultCount); 209 | 210 | return await AsyncQueryableExecuter.ToListAsync(query); 211 | } 212 | } 213 | 214 | [UnitOfWork] 215 | public virtual async Task GetDeviceCountByUserAsync(IUserIdentifier userIdentifier) 216 | { 217 | using (UnitOfWorkManager.Current.SetTenantId(userIdentifier.TenantId)) 218 | { 219 | var query = DeviceRepository.GetAll(); 220 | query = ApplyUser(query, userIdentifier); 221 | 222 | return await AsyncQueryableExecuter.CountAsync(query); 223 | } 224 | } 225 | 226 | [UnitOfWork] 227 | public virtual async Task GetDeviceCountByUserPlatformAsync(IUserIdentifier userIdentifier, string devicePlatform) 228 | { 229 | using (UnitOfWorkManager.Current.SetTenantId(userIdentifier.TenantId)) 230 | { 231 | var query = DeviceRepository.GetAll(); 232 | query = ApplyPlatform(query, devicePlatform); 233 | query = ApplyUser(query, userIdentifier); 234 | 235 | return await AsyncQueryableExecuter.CountAsync(query); 236 | } 237 | } 238 | 239 | [UnitOfWork] 240 | public virtual async Task GetDeviceCountByUserProviderAsync(IUserIdentifier userIdentifier, string serviceProvider) 241 | { 242 | using (UnitOfWorkManager.Current.SetTenantId(userIdentifier.TenantId)) 243 | { 244 | var query = DeviceRepository.GetAll(); 245 | query = ApplyProvider(query, serviceProvider); 246 | query = ApplyUser(query, userIdentifier); 247 | 248 | return await AsyncQueryableExecuter.CountAsync(query); 249 | } 250 | } 251 | 252 | [UnitOfWork] 253 | public virtual async Task InsertDeviceAsync(TDevice device) 254 | { 255 | using (UnitOfWorkManager.Current.SetTenantId(device.TenantId)) 256 | { 257 | await DeviceRepository.InsertAsync(device); 258 | await UnitOfWorkManager.Current.SaveChangesAsync(); 259 | } 260 | } 261 | 262 | [UnitOfWork] 263 | public virtual async Task InsertOrUpdateDeviceAsync(TDevice device) 264 | { 265 | using (UnitOfWorkManager.Current.SetTenantId(device.TenantId)) 266 | { 267 | await DeviceRepository.InsertOrUpdateAsync(device); 268 | await UnitOfWorkManager.Current.SaveChangesAsync(); 269 | } 270 | } 271 | 272 | [UnitOfWork] 273 | public virtual async Task UpdateDeviceAsync(TDevice device) 274 | { 275 | using (UnitOfWorkManager.Current.SetTenantId(device.TenantId)) 276 | { 277 | await DeviceRepository.UpdateAsync(device); 278 | await UnitOfWorkManager.Current.SaveChangesAsync(); 279 | } 280 | } 281 | 282 | protected virtual IQueryable ApplyUser(IQueryable query, IUserIdentifier userIdentifier) 283 | { 284 | return query.Where(d => d.UserId == userIdentifier.UserId); 285 | } 286 | 287 | protected virtual IQueryable ApplyPlatform(IQueryable query, string devicePlatform) 288 | { 289 | return query.Where(d => d.DevicePlatform == devicePlatform); 290 | } 291 | 292 | protected virtual IQueryable ApplyProvider(IQueryable query, string serviceProvider) 293 | { 294 | return query.Where(d => d.ServiceProvider == serviceProvider); 295 | } 296 | 297 | protected virtual IQueryable ApplyProviderIdentity(IQueryable query, string serviceProvider, string serviceProviderKey) 298 | { 299 | return ApplyProvider(query, serviceProvider).Where(pl => pl.ServiceProviderKey == serviceProviderKey); 300 | } 301 | 302 | protected virtual IQueryable ApplySkipTake(IQueryable query, int? skipCount, int? maxResultCount) 303 | { 304 | if (skipCount.HasValue) 305 | { 306 | query = query.Skip(skipCount.Value); 307 | } 308 | 309 | if (maxResultCount.HasValue) 310 | { 311 | query = query.Take(maxResultCount.Value); 312 | } 313 | 314 | return query; 315 | } 316 | } 317 | } -------------------------------------------------------------------------------- /src/Abp.Push/Push/Requests/AbpInMemoryPushRequestStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Abp.Collections.Extensions; 7 | using Abp.Linq.Extensions; 8 | 9 | namespace Abp.Push.Requests 10 | { 11 | /// 12 | /// Implements using in memory storage (thread-safe). 13 | /// 14 | public class AbpInMemoryPushRequestStore : AbpServiceBase, IPushRequestStore 15 | { 16 | protected readonly ConcurrentDictionary> MultiTenantSubscriptions; 17 | // push request only defined on Host side 18 | protected readonly ConcurrentDictionary Requests; 19 | protected readonly IGuidGenerator GuidGenerator; 20 | 21 | // Set the initial capacity to some prime number above that, to ensure that 22 | // the ConcurrentDictionary does not need to be resized while initializing it. 23 | protected readonly int MultiTenancyInitialCapacity = 101; 24 | protected readonly int RequestInitialCapacity = 257; 25 | protected readonly int SubscriptionInitialCapacity = 1009; 26 | 27 | protected int TimeoutMiliseconds = 5000; 28 | protected readonly int ConcurrencyLevel; 29 | 30 | private readonly object _syncObj = new object(); 31 | 32 | public AbpInMemoryPushRequestStore(IGuidGenerator guidGenerator) 33 | { 34 | GuidGenerator = guidGenerator; 35 | 36 | // The higher the concurrencyLevel, the higher the theoretical number of operations 37 | // that could be performed concurrently on the ConcurrentDictionary. However, global 38 | // operations like resizing the dictionary take longer as the concurrencyLevel rises. 39 | // For the purposes of this example, we'll compromise at numCores * 2. 40 | int numProcs = Environment.ProcessorCount; 41 | ConcurrencyLevel = numProcs * 2; 42 | 43 | // above is copied from https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=netcore-2.0 44 | 45 | Requests = new ConcurrentDictionary(ConcurrencyLevel, RequestInitialCapacity); 46 | MultiTenantSubscriptions = new ConcurrentDictionary>(ConcurrencyLevel, MultiTenancyInitialCapacity); 47 | } 48 | 49 | private ConcurrentDictionary CreateSubscriptionCollection() 50 | { 51 | return new ConcurrentDictionary(ConcurrencyLevel, SubscriptionInitialCapacity); 52 | } 53 | 54 | #region Subscriptions 55 | 56 | public virtual Task InsertSubscriptionAsync(PushRequestSubscription subscription) 57 | { 58 | var pushSubscriptions = MultiTenantSubscriptions.GetOrAdd(subscription.TenantId, CreateSubscriptionCollection()); 59 | if (pushSubscriptions.ContainsKey(subscription.Id)) 60 | { 61 | throw new AbpException(string.Format("Subscription {0} already exists", subscription.Id)); 62 | } 63 | 64 | subscription.Id = GuidGenerator.Create(); 65 | if (!pushSubscriptions.TryAdd(subscription.Id, subscription)){ 66 | throw new AbpException(string.Format("Failed to insert subscription {0}:{1}", subscription.PushRequestName, subscription.Id)); 67 | } 68 | return Task.CompletedTask; 69 | } 70 | 71 | public virtual Task DeleteSubscriptionAsync(IUserIdentifier user, string pushRequestName, string entityTypeName, string entityId) 72 | { 73 | var pushSubscriptions = MultiTenantSubscriptions.GetOrAdd(user.TenantId, CreateSubscriptionCollection()); 74 | var deleteSubscriptions = pushSubscriptions.Where(ps => 75 | { 76 | var subscription = ps.Value; 77 | return ps.Value.TenantId == user.TenantId && 78 | ps.Value.UserId == user.UserId && 79 | ps.Value.PushRequestName == pushRequestName && 80 | ps.Value.EntityTypeName == entityTypeName && 81 | ps.Value.EntityId == entityId; 82 | }).Select(ps => ps.Key); 83 | 84 | foreach(var key in deleteSubscriptions){ 85 | pushSubscriptions.TryRemove(key, out PushRequestSubscription subscription); 86 | } 87 | return Task.CompletedTask; 88 | } 89 | 90 | public virtual Task> GetSubscriptionsAsync(string pushRequestName, string entityTypeName, string entityId, int skipCount = 0, int maxResultCount = int.MaxValue) 91 | { 92 | var allPushSubscriptions = MultiTenantSubscriptions.Values; 93 | var subscriptions = allPushSubscriptions.SelectMany(dict => 94 | { 95 | return dict.Values.Where(ps => ps.PushRequestName == pushRequestName && 96 | ps.EntityTypeName == entityTypeName && 97 | ps.EntityId == entityId); 98 | }) 99 | .Skip(skipCount) 100 | .Take(maxResultCount) 101 | .ToList(); 102 | return Task.FromResult(subscriptions); 103 | } 104 | 105 | public virtual Task> GetSubscriptionsAsync(int?[] tenantIds, string pushRequestName, string entityTypeName, string entityId, int skipCount = 0, int maxResultCount = int.MaxValue) 106 | { 107 | var tenantIdsList = new List(); 108 | if (tenantIds != null && tenantIds.Length > 0) 109 | { 110 | tenantIdsList.AddRange(tenantIds); 111 | } 112 | 113 | var allPushSubscriptions = MultiTenantSubscriptions 114 | .Where(ms => tenantIdsList.Contains(ms.Key)) 115 | .Select(ms => ms.Value); 116 | var subscriptions = allPushSubscriptions.SelectMany(dict => 117 | { 118 | return dict.Values.Where(ps => ps.PushRequestName == pushRequestName && 119 | ps.EntityTypeName == entityTypeName && 120 | ps.EntityId == entityId); 121 | }) 122 | .Skip(skipCount) 123 | .Take(maxResultCount) 124 | .ToList(); 125 | return Task.FromResult(subscriptions); 126 | } 127 | 128 | public virtual Task> GetSubscriptionsAsync(IUserIdentifier user, int skipCount = 0, int maxResultCount = int.MaxValue) 129 | { 130 | var pushSubscriptions = MultiTenantSubscriptions.GetOrAdd(user.TenantId, CreateSubscriptionCollection()); 131 | var subscriptions = pushSubscriptions.Values 132 | .Skip(skipCount) 133 | .Take(maxResultCount) 134 | .ToList(); 135 | return Task.FromResult(subscriptions); 136 | } 137 | 138 | public virtual Task IsSubscribedAsync(IUserIdentifier user, string pushRequestName, string entityTypeName, string entityId) 139 | { 140 | var pushSubscriptions = MultiTenantSubscriptions.GetOrAdd(user.TenantId, CreateSubscriptionCollection()); 141 | var hasSubscription = pushSubscriptions.Values 142 | .Any(ps => ps.UserId == user.UserId && 143 | ps.PushRequestName == pushRequestName && 144 | ps.EntityTypeName == entityTypeName && 145 | ps.EntityId == entityId 146 | ); 147 | return Task.FromResult(hasSubscription); 148 | } 149 | 150 | #endregion 151 | 152 | public virtual Task DeleteRequestAsync(Guid requestId) 153 | { 154 | Requests.TryRemove(requestId, out PushRequest request); 155 | return Task.CompletedTask; 156 | } 157 | 158 | public virtual Task InsertRequestAsync(PushRequest request) 159 | { 160 | if (Requests.ContainsKey(request.Id)) 161 | { 162 | throw new AbpException(string.Format("Request {0} already exists", request.Id)); 163 | } 164 | 165 | request.Id = GuidGenerator.Create(); 166 | if (!Requests.TryAdd(request.Id, request)) 167 | { 168 | throw new AbpException(string.Format("Failed to insert request {0}:{1}", request.Name, request.Id)); 169 | } 170 | return Task.CompletedTask; 171 | } 172 | 173 | public virtual Task GetRequestOrNullAsync(Guid requestId) 174 | { 175 | if(Requests.TryGetValue(requestId, out PushRequest request)){ 176 | return Task.FromResult(request); 177 | } 178 | return Task.FromResult(null as PushRequest); 179 | } 180 | 181 | public virtual Task> GetRequestsAsync(PushRequestPriority? priority = null, int skipCount = 0, int maxResultCount = int.MaxValue) 182 | { 183 | var requests = Requests.Values 184 | .WhereIf(priority.HasValue, pr => pr.Priority == priority.Value) 185 | .Skip(skipCount) 186 | .Take(maxResultCount) 187 | .ToList(); 188 | return Task.FromResult(requests); 189 | } 190 | 191 | public virtual Task GethRequestCountAsync(PushRequestPriority? priority = null) 192 | { 193 | var requestCount = Requests.Values 194 | .WhereIf(priority.HasValue, pr => pr.Priority == priority.Value) 195 | .Count(); 196 | return Task.FromResult(requestCount); 197 | } 198 | 199 | public virtual Task UpdateRequestPriorityAsync(Guid requestId, PushRequestPriority priority) 200 | { 201 | if (Requests.TryGetValue(requestId, out PushRequest request)) 202 | { 203 | request.Priority = priority; 204 | Requests.AddOrUpdate(requestId, request, (key, oldValue) => 205 | { 206 | oldValue.Priority = request.Priority; 207 | return oldValue; 208 | }); 209 | } 210 | return Task.CompletedTask; 211 | } 212 | } 213 | } -------------------------------------------------------------------------------- /src/Abp.Push/Push/Requests/AbpPersistentPushRequestStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Abp.Domain.Repositories; 6 | using Abp.Domain.Uow; 7 | using Abp.Linq; 8 | using Abp.Linq.Extensions; 9 | 10 | namespace Abp.Push.Requests 11 | { 12 | /// 13 | /// Implements using repositories. 14 | /// 15 | public class AbpPersistentPushRequestStore : AbpServiceBase, IPushRequestStore 16 | { 17 | public IAsyncQueryableExecuter AsyncQueryableExecuter { get; set; } 18 | 19 | protected readonly IRepository RequestRepository; 20 | protected readonly IRepository SubscriptionRepository; 21 | 22 | public AbpPersistentPushRequestStore( 23 | IRepository pushRequestRepository, 24 | IRepository pushSubscriptionRepository 25 | ) 26 | { 27 | RequestRepository = pushRequestRepository; 28 | SubscriptionRepository = pushSubscriptionRepository; 29 | 30 | AsyncQueryableExecuter = NullAsyncQueryableExecuter.Instance; 31 | } 32 | 33 | #region Subscriptions 34 | 35 | [UnitOfWork] 36 | public virtual async Task InsertSubscriptionAsync(PushRequestSubscription subscription) 37 | { 38 | using (UnitOfWorkManager.Current.SetTenantId(subscription.TenantId)) 39 | { 40 | await SubscriptionRepository.InsertAsync(subscription); 41 | await UnitOfWorkManager.Current.SaveChangesAsync(); 42 | } 43 | } 44 | 45 | [UnitOfWork] 46 | public virtual async Task DeleteSubscriptionAsync(IUserIdentifier user, string pushRequestName, string entityTypeName, string entityId) 47 | { 48 | using (UnitOfWorkManager.Current.SetTenantId(user.TenantId)) 49 | { 50 | await SubscriptionRepository.DeleteAsync(s => 51 | s.UserId == user.UserId && 52 | s.PushRequestName == pushRequestName && 53 | s.EntityTypeName == entityTypeName && 54 | s.EntityId == entityId 55 | ); 56 | await UnitOfWorkManager.Current.SaveChangesAsync(); 57 | } 58 | } 59 | 60 | [UnitOfWork] 61 | public virtual async Task> GetSubscriptionsAsync(string pushRequestName, string entityTypeName, string entityId, int skipCount = 0, int maxResultCount = int.MaxValue) 62 | { 63 | using (UnitOfWorkManager.Current.DisableFilter(AbpDataFilters.MayHaveTenant)) 64 | { 65 | var query = SubscriptionRepository.GetAll() 66 | .Where(s => 67 | s.PushRequestName == pushRequestName && 68 | s.EntityTypeName == entityTypeName && 69 | s.EntityId == entityId 70 | ); 71 | query = query.PageBy(skipCount, maxResultCount); 72 | 73 | return await AsyncQueryableExecuter.ToListAsync(query); 74 | } 75 | } 76 | 77 | [UnitOfWork] 78 | public virtual async Task> GetSubscriptionsAsync(int?[] tenantIds, string pushRequestName, string entityTypeName, string entityId, int skipCount = 0, int maxResultCount = int.MaxValue) 79 | { 80 | var tenantIdsList = new List(); 81 | if (tenantIds != null && tenantIds.Length > 0) 82 | { 83 | tenantIdsList.AddRange(tenantIds); 84 | } 85 | 86 | using (UnitOfWorkManager.Current.DisableFilter(AbpDataFilters.MayHaveTenant)) 87 | { 88 | var query = SubscriptionRepository.GetAll() 89 | .Where(s => 90 | tenantIdsList.Contains(s.TenantId) && 91 | s.PushRequestName == pushRequestName && 92 | s.EntityTypeName == entityTypeName && 93 | s.EntityId == entityId 94 | ); 95 | query = query.PageBy(skipCount, maxResultCount); 96 | 97 | return await AsyncQueryableExecuter.ToListAsync(query); 98 | } 99 | } 100 | 101 | [UnitOfWork] 102 | public virtual async Task> GetSubscriptionsAsync(IUserIdentifier user, int skipCount = 0, int maxResultCount = int.MaxValue) 103 | { 104 | using (UnitOfWorkManager.Current.SetTenantId(user.TenantId)) 105 | { 106 | var query = SubscriptionRepository.GetAll() 107 | .Where(s => s.UserId == user.UserId); 108 | query = query.PageBy(skipCount, maxResultCount); 109 | 110 | return await AsyncQueryableExecuter.ToListAsync(query); 111 | } 112 | } 113 | 114 | [UnitOfWork] 115 | public virtual async Task IsSubscribedAsync(IUserIdentifier user, string pushRequestName, string entityTypeName, string entityId) 116 | { 117 | using (UnitOfWorkManager.Current.SetTenantId(user.TenantId)) 118 | { 119 | return await SubscriptionRepository.CountAsync(s => 120 | s.UserId == user.UserId && 121 | s.PushRequestName == pushRequestName && 122 | s.EntityTypeName == entityTypeName && 123 | s.EntityId == entityId 124 | ) > 0; 125 | } 126 | } 127 | 128 | #endregion 129 | 130 | [UnitOfWork] 131 | public virtual async Task DeleteRequestAsync(Guid requestId) 132 | { 133 | // push request only defined on Host side 134 | using (UnitOfWorkManager.Current.SetTenantId(null)) 135 | { 136 | await RequestRepository.DeleteAsync(requestId); 137 | await UnitOfWorkManager.Current.SaveChangesAsync(); 138 | } 139 | } 140 | 141 | [UnitOfWork] 142 | public virtual async Task InsertRequestAsync(PushRequest request) 143 | { 144 | // push request only defined on Host side 145 | using (UnitOfWorkManager.Current.SetTenantId(null)) 146 | { 147 | await RequestRepository.DeleteAsync(request); 148 | await UnitOfWorkManager.Current.SaveChangesAsync(); 149 | } 150 | } 151 | 152 | [UnitOfWork] 153 | public virtual async Task GetRequestOrNullAsync(Guid requestId) 154 | { 155 | // push request only defined on Host side 156 | using (UnitOfWorkManager.Current.SetTenantId(null)) 157 | { 158 | return await RequestRepository.FirstOrDefaultAsync(requestId); 159 | } 160 | } 161 | 162 | public virtual async Task> GetRequestsAsync(PushRequestPriority? priority = null, int skipCount = 0, int maxResultCount = int.MaxValue) 163 | { 164 | // push request only defined on Host side 165 | using (UnitOfWorkManager.Current.SetTenantId(null)) 166 | { 167 | var query = RequestRepository.GetAll() 168 | .WhereIf(priority.HasValue, pr => pr.Priority == priority.Value); 169 | query = query.PageBy(skipCount, maxResultCount); 170 | 171 | return await AsyncQueryableExecuter.ToListAsync(query); 172 | } 173 | } 174 | 175 | public virtual async Task GethRequestCountAsync(PushRequestPriority? priority = null) 176 | { 177 | // push request only defined on Host side 178 | using (UnitOfWorkManager.Current.SetTenantId(null)) 179 | { 180 | var query = RequestRepository.GetAll() 181 | .WhereIf(priority.HasValue, pr => pr.Priority == priority.Value); 182 | 183 | return await AsyncQueryableExecuter.CountAsync(query); 184 | } 185 | } 186 | 187 | [UnitOfWork] 188 | public virtual async Task UpdateRequestPriorityAsync(Guid requestId, PushRequestPriority priority) 189 | { 190 | // push request only defined on Host side 191 | using (UnitOfWorkManager.Current.SetTenantId(null)) 192 | { 193 | var pushRequest = await RequestRepository.FirstOrDefaultAsync(requestId); 194 | if (pushRequest == null) 195 | { 196 | return; 197 | } 198 | 199 | pushRequest.Priority = priority; 200 | await UnitOfWorkManager.Current.SaveChangesAsync(); 201 | } 202 | } 203 | } 204 | } -------------------------------------------------------------------------------- /test/Abp.Push.Tests/Abp.Push.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net461;net5.0 4 | net5.0 5 | Abp.Push.Tests 6 | Abp.Push.Tests 7 | true 8 | false 9 | false 10 | false 11 | false 12 | false 13 | false 14 | true 15 | true 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | all 27 | runtime; build; native; contentfiles; analyzers 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /test/Abp.Push.Tests/Push/PushRequestDistributor_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Abp.Configuration; 5 | using Abp.Dependency; 6 | using Abp.Domain.Uow; 7 | using Abp.Push; 8 | using Abp.Push.Configuration; 9 | using Abp.Push.Providers; 10 | using Abp.Push.Requests; 11 | using NSubstitute; 12 | using Xunit; 13 | 14 | namespace Abp.Tests.Push 15 | { 16 | public class PushRequestDistributor_Tests : TestBaseWithLocalIocManager 17 | { 18 | private readonly IPushRequestStore _store; 19 | private readonly IPushDefinitionManager _definitionManager; 20 | private readonly IAbpPushConfiguration _configuration; 21 | private readonly IIocResolver _iocResolver; 22 | private readonly IGuidGenerator _generator; 23 | private readonly AbpPushRequestDistributor _distributor; 24 | 25 | public PushRequestDistributor_Tests() 26 | { 27 | _store = Substitute.For(); 28 | _definitionManager = Substitute.For(); 29 | _configuration = Substitute.For(); 30 | _configuration.ServiceProviders.Returns(new List()); 31 | _iocResolver = Substitute.For(); 32 | _generator = Substitute.For(); 33 | _distributor = new AbpPushRequestDistributor( 34 | _store, 35 | _definitionManager, 36 | _configuration, 37 | _iocResolver, 38 | _generator 39 | ); 40 | _distributor.UnitOfWorkManager = Substitute.For(); 41 | _distributor.UnitOfWorkManager.Current.Returns(Substitute.For()); 42 | 43 | var _settingManager = Substitute.For(); 44 | _settingManager.GetSettingValueForUser(Arg.Any(), Arg.Any(), Arg.Any()) 45 | .Returns("true"); 46 | _distributor.SettingManager = _settingManager; 47 | } 48 | 49 | [Fact] 50 | public async Task Should_Distribute_Push_Request() 51 | { 52 | //Arrange 53 | var guid = _generator.Create(); 54 | _store.GetRequestOrNullAsync(Arg.Any()).ReturnsForAnyArgs(CreatePushRequest()); 55 | _store.GetSubscriptionsAsync(Arg.Any(), Arg.Any(), Arg.Any()) 56 | .ReturnsForAnyArgs(new List { CreatePushRequestSubscription() }); 57 | 58 | //Act 59 | await _distributor.DistributeAsync(guid); 60 | 61 | //Assert 62 | await _store.Received().GetRequestOrNullAsync(Arg.Any()); 63 | await _store.Received().DeleteRequestAsync(Arg.Is(n => n.Equals(guid))); 64 | } 65 | 66 | private static PushRequest CreatePushRequest() 67 | { 68 | return new PushRequest 69 | { 70 | UserIds = "1,2,3" 71 | }; 72 | } 73 | 74 | private static PushRequestSubscription CreatePushRequestSubscription() 75 | { 76 | return new PushRequestSubscription(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/Abp.Push.Tests/Push/PushRequestPublisher_Tests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Abp.BackgroundJobs; 3 | using Abp.Domain.Uow; 4 | using Abp.Push.Configuration; 5 | using Abp.Push.Requests; 6 | using NSubstitute; 7 | using Xunit; 8 | 9 | namespace Abp.Tests.Push 10 | { 11 | public class PushRequestPublisher_Tests : TestBaseWithLocalIocManager 12 | { 13 | private readonly IPushRequestStore _store; 14 | private readonly IBackgroundJobManager _backgroundJobManager; 15 | private readonly IPushRequestDistributor _distributor; 16 | private readonly IAbpPushConfiguration _configuration; 17 | private readonly IGuidGenerator _generator; 18 | private readonly AbpPushRequestPublisher _publisher; 19 | 20 | public PushRequestPublisher_Tests() 21 | { 22 | _store = Substitute.For(); 23 | _backgroundJobManager = Substitute.For(); 24 | _distributor = Substitute.For(); 25 | _configuration = Substitute.For(); 26 | _generator = Substitute.For(); 27 | _publisher = new AbpPushRequestPublisher( 28 | _store, 29 | _backgroundJobManager, 30 | _distributor, 31 | _configuration, 32 | _generator 33 | ); 34 | _publisher.UnitOfWorkManager = Substitute.For(); 35 | _publisher.UnitOfWorkManager.Current.Returns(Substitute.For()); 36 | } 37 | 38 | [Fact] 39 | public async Task Should_Publish_Push_Request() 40 | { 41 | //Arrange 42 | var pushRequestData = CreatePushRequestData(); 43 | 44 | //Act 45 | await _publisher.PublishAsync("TestPushRequest", pushRequestData, priority: PushRequestPriority.AboveNormal); 46 | 47 | //Assert 48 | await _store.Received() 49 | .InsertRequestAsync( 50 | Arg.Is(n => 51 | n.Name == "TestPushRequest" && 52 | n.Priority == PushRequestPriority.AboveNormal && 53 | n.DataTypeName == pushRequestData.GetType().AssemblyQualifiedName && 54 | n.Data.Contains("42") 55 | ) 56 | ); 57 | 58 | await _backgroundJobManager.Received() 59 | .EnqueueAsync( 60 | Arg.Any() 61 | ); 62 | } 63 | 64 | private static PushRequestData CreatePushRequestData() 65 | { 66 | var pushRequestData = new PushRequestData(); 67 | pushRequestData["TestValue"] = 42; 68 | return pushRequestData; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /test/Abp.Push.Tests/TestBaseWithLocalIocManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Abp.Dependency; 3 | 4 | namespace Abp.Tests 5 | { 6 | public abstract class TestBaseWithLocalIocManager : IDisposable 7 | { 8 | protected IIocManager LocalIocManager; 9 | 10 | protected TestBaseWithLocalIocManager() 11 | { 12 | LocalIocManager = new IocManager(); 13 | } 14 | 15 | public virtual void Dispose() 16 | { 17 | LocalIocManager.Dispose(); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /tools/gitlink/GitLink.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryancyq/abp-push/50edc82781ae750a9ebdcfd3b033114edada58c4/tools/gitlink/GitLink.exe -------------------------------------------------------------------------------- /tools/nuget/nuget.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryancyq/abp-push/50edc82781ae750a9ebdcfd3b033114edada58c4/tools/nuget/nuget.exe --------------------------------------------------------------------------------