├── .editorconfig
├── .gitattributes
├── .gitignore
├── Bookings.Domain
├── Bookings.Domain.csproj
├── Bookings
│ ├── Booking.cs
│ ├── BookingEvents.cs
│ ├── BookingId.cs
│ └── BookingState.cs
├── DomainModule.cs
├── Money.cs
├── RoomId.cs
├── Services.cs
└── StayPeriod.cs
├── Bookings.Payments
├── Application
│ ├── CommandApi.cs
│ └── CommandService.cs
├── Bookings.Payments.csproj
├── Domain
│ ├── Money.cs
│ ├── Payment.cs
│ └── PaymentEvents.cs
├── Infrastructure
│ ├── Logging.cs
│ └── Mongo.cs
├── Integration
│ └── Payments.cs
├── Program.cs
├── Registrations.cs
└── appsettings.json
├── Bookings.sln
├── Bookings.sln.DotSettings
├── Bookings
├── .dockerignore
├── Application
│ ├── BookingsCommandService.cs
│ ├── Commands.cs
│ └── Queries
│ │ ├── BookingDocument.cs
│ │ ├── BookingStateProjection.cs
│ │ ├── MyBookings.cs
│ │ └── MyBookingsProjection.cs
├── Bookings.csproj
├── Dockerfile
├── HttpApi
│ └── Bookings
│ │ ├── CommandApi.cs
│ │ └── QueryApi.cs
├── Infrastructure
│ └── Mongo.cs
├── Integration
│ └── Payments.cs
├── Program.cs
├── Registrations.cs
└── appsettings.json
├── Directory.Build.props
├── NuGet.config
├── README.md
├── deploy
└── cloudrun
│ ├── .gitignore
│ ├── Pulumi.yaml
│ ├── index.ts
│ ├── package.json
│ ├── tsconfig.json
│ └── yarn.lock
├── docker-compose.yml
├── grafana
├── __inputs.json
└── datasources.yml
└── prometheus
└── prometheus.yml
/.editorconfig:
--------------------------------------------------------------------------------
1 |
2 | [*]
3 |
4 | # ReSharper properties
5 | resharper_instance_members_qualify_declared_in = this_class
6 | resharper_int_align_assignments = false
7 | resharper_use_heuristics_for_body_style = true
8 | resharper_wrap_after_invocation_lpar = false
9 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Explicitly declare text files we want to always be normalized and converted
2 | # to native line endings on checkout.
3 |
4 | *.pdf binary
5 | *.sch binary
6 | *.isch binary
7 | *.ist binary
8 |
9 | # Explicitly declare text files we want to always be normalized and converted
10 | # to native line endings on checkout.
11 | *.c text
12 | *.h text
13 | *.cs text
14 | *.config text
15 | *.xml text
16 | *.manifest text
17 | *.bat text
18 | *.cmd text
19 | *.sh text
20 | *.txt text
21 | *.dat text
22 | *.rc text
23 | *.ps1 text
24 | *.psm1 text
25 | *.js text
26 | *.css text
27 | *.html text
28 | *.sln text
29 | *.DotSettings text
30 | *.csproj text
31 | *.ncrunchproject text
32 | *.fs text
33 | *.fsproj text
34 | *.liquid text
35 | *.boo text
36 | *.pp text
37 | *.targets text
38 | *.markdown text
39 | *.md text
40 | *.bat text
41 | *.xslt text
42 |
43 | # Declare files that will always have CRLF line endings on checkout.
44 |
45 | # Denote all files that are truly binary and should not be modified.
46 | *.ico binary
47 | *.gif binary
48 | *.png binary
49 | *.jpg binary
50 | *.dll binary
51 | *.exe binary
52 | *.pdb binary
53 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/macos,csharp
3 |
4 | ### Csharp ###
5 | ## Ignore Visual Studio temporary files, build results, and
6 | ## files generated by popular Visual Studio add-ons.
7 | ##
8 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # User-specific files (MonoDevelop/Xamarin Studio)
17 | *.userprefs
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | bld/
27 | [Bb]in/
28 | [Oo]bj/
29 | [Ll]og/
30 |
31 | # Visual Studio 2015 cache/options directory
32 | .vs/
33 | # Uncomment if you have tasks that create the project's static files in wwwroot
34 | #wwwroot/
35 |
36 | # MSTest test Results
37 | [Tt]est[Rr]esult*/
38 | [Bb]uild[Ll]og.*
39 |
40 | # NUNIT
41 | *.VisualState.xml
42 | TestResult.xml
43 |
44 | # Build Results of an ATL Project
45 | [Dd]ebugPS/
46 | [Rr]eleasePS/
47 | dlldata.c
48 |
49 | # .NET Core
50 | project.lock.json
51 | project.fragment.lock.json
52 | artifacts/
53 | **/Properties/launchSettings.json
54 |
55 | *_i.c
56 | *_p.c
57 | *_i.h
58 | *.ilk
59 | *.meta
60 | *.obj
61 | *.pch
62 | *.pdb
63 | *.pgc
64 | *.pgd
65 | *.rsp
66 | *.sbr
67 | *.tlb
68 | *.tli
69 | *.tlh
70 | *.tmp
71 | *.tmp_proj
72 | *.log
73 | *.vspscc
74 | *.vssscc
75 | .builds
76 | *.pidb
77 | *.svclog
78 | *.scc
79 |
80 | # Chutzpah Test files
81 | _Chutzpah*
82 |
83 | # Visual C++ cache files
84 | ipch/
85 | *.aps
86 | *.ncb
87 | *.opendb
88 | *.opensdf
89 | *.sdf
90 | *.cachefile
91 | *.VC.db
92 | *.VC.VC.opendb
93 |
94 | # Visual Studio profiler
95 | *.psess
96 | *.vsp
97 | *.vspx
98 | *.sap
99 |
100 | # TFS 2012 Local Workspace
101 | $tf/
102 |
103 | # Guidance Automation Toolkit
104 | *.gpState
105 |
106 | # ReSharper is a .NET coding add-in
107 | _ReSharper*/
108 | *.[Rr]e[Ss]harper
109 | *.DotSettings.user
110 |
111 | # JustCode is a .NET coding add-in
112 | .JustCode
113 |
114 | # TeamCity is a build add-in
115 | _TeamCity*
116 |
117 | # DotCover is a Code Coverage Tool
118 | *.dotCover
119 |
120 | # Visual Studio code coverage results
121 | *.coverage
122 | *.coveragexml
123 |
124 | # NCrunch
125 | _NCrunch_*
126 | .*crunch*.local.xml
127 | nCrunchTemp_*
128 |
129 | # MightyMoose
130 | *.mm.*
131 | AutoTest.Net/
132 |
133 | # Web workbench (sass)
134 | .sass-cache/
135 |
136 | # Installshield output folder
137 | [Ee]xpress/
138 |
139 | # DocProject is a documentation generator add-in
140 | DocProject/buildhelp/
141 | DocProject/Help/*.HxT
142 | DocProject/Help/*.HxC
143 | DocProject/Help/*.hhc
144 | DocProject/Help/*.hhk
145 | DocProject/Help/*.hhp
146 | DocProject/Help/Html2
147 | DocProject/Help/html
148 |
149 | # Click-Once directory
150 | publish/
151 |
152 | # Publish Web Output
153 | *.[Pp]ublish.xml
154 | *.azurePubxml
155 | # TODO: Uncomment the next line to ignore your web deploy settings.
156 | # By default, sensitive information, such as encrypted password
157 | # should be stored in the .pubxml.user file.
158 | #*.pubxml
159 | *.pubxml.user
160 | *.publishproj
161 |
162 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
163 | # checkin your Azure Web App publish settings, but sensitive information contained
164 | # in these scripts will be unencrypted
165 | PublishScripts/
166 |
167 | # NuGet Packages
168 | *.nupkg
169 | # The packages folder can be ignored because of Package Restore
170 | **/packages/*
171 | # except build/, which is used as an MSBuild target.
172 | !**/packages/build/
173 | # Uncomment if necessary however generally it will be regenerated when needed
174 | #!**/packages/repositories.config
175 | # NuGet v3's project.json files produces more ignorable files
176 | *.nuget.props
177 | *.nuget.targets
178 |
179 | # Microsoft Azure Build Output
180 | csx/
181 | *.build.csdef
182 |
183 | # Microsoft Azure Emulator
184 | ecf/
185 | rcf/
186 |
187 | # Windows Store app package directories and files
188 | AppPackages/
189 | BundleArtifacts/
190 | Package.StoreAssociation.xml
191 | _pkginfo.txt
192 |
193 | # Visual Studio cache files
194 | # files ending in .cache can be ignored
195 | *.[Cc]ache
196 | # but keep track of directories ending in .cache
197 | !*.[Cc]ache/
198 |
199 | # Others
200 | ClientBin/
201 | ~$*
202 | *~
203 | *.dbmdl
204 | *.dbproj.schemaview
205 | *.jfm
206 | *.pfx
207 | *.publishsettings
208 | orleans.codegen.cs
209 |
210 | # Since there are multiple workflows, uncomment next line to ignore bower_components
211 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
212 | #bower_components/
213 |
214 | # RIA/Silverlight projects
215 | Generated_Code/
216 |
217 | # Backup & report files from converting an old project file
218 | # to a newer Visual Studio version. Backup files are not needed,
219 | # because we have git ;-)
220 | _UpgradeReport_Files/
221 | Backup*/
222 | UpgradeLog*.XML
223 | UpgradeLog*.htm
224 |
225 | # SQL Server files
226 | *.mdf
227 | *.ldf
228 | *.ndf
229 |
230 | # Business Intelligence projects
231 | *.rdl.data
232 | *.bim.layout
233 | *.bim_*.settings
234 |
235 | # Microsoft Fakes
236 | FakesAssemblies/
237 |
238 | # GhostDoc plugin setting file
239 | *.GhostDoc.xml
240 |
241 | # Node.js Tools for Visual Studio
242 | .ntvs_analysis.dat
243 | node_modules/
244 |
245 | # Typescript v1 declaration files
246 | typings/
247 |
248 | # Visual Studio 6 build log
249 | *.plg
250 |
251 | # Visual Studio 6 workspace options file
252 | *.opt
253 |
254 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
255 | *.vbw
256 |
257 | # Visual Studio LightSwitch build output
258 | **/*.HTMLClient/GeneratedArtifacts
259 | **/*.DesktopClient/GeneratedArtifacts
260 | **/*.DesktopClient/ModelManifest.xml
261 | **/*.Server/GeneratedArtifacts
262 | **/*.Server/ModelManifest.xml
263 | _Pvt_Extensions
264 |
265 | # JetBrains Rider
266 | .idea/
267 | *.sln.iml
268 |
269 | # CodeRush
270 | .cr/
271 |
272 | ### macOS ###
273 | *.DS_Store
274 | .AppleDouble
275 | .LSOverride
276 |
277 | # Icon must end with two \r
278 | Icon
279 |
280 | # Thumbnails
281 | ._*
282 |
283 | # Files that might appear in the root of a volume
284 | .DocumentRevisions-V100
285 | .fseventsd
286 | .Spotlight-V100
287 | .TemporaryItems
288 | .Trashes
289 | .VolumeIcon.icns
290 | .com.apple.timemachine.donotpresent
291 |
292 | # Directories potentially created on remote AFP share
293 | .AppleDB
294 | .AppleDesktop
295 | Network Trash Folder
296 | Temporary Items
297 | .apdisk
298 |
299 | # End of https://www.gitignore.io/api/macos,csharpifest.xml
300 | _Pvt_Extensions
301 |
302 | ## Custom ##
303 | exclusive.lck
304 | *.dll
305 | *.exe
306 | .vscode
307 | version.txt
308 | /public/
309 |
--------------------------------------------------------------------------------
/Bookings.Domain/Bookings.Domain.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Bookings.Domain/Bookings/Booking.cs:
--------------------------------------------------------------------------------
1 | using Eventuous;
2 | using static Bookings.Domain.Bookings.BookingEvents;
3 | using static Bookings.Domain.Services;
4 |
5 | namespace Bookings.Domain.Bookings;
6 |
7 | public class Booking : Aggregate {
8 | public async Task BookRoom(
9 | string guestId,
10 | RoomId roomId,
11 | StayPeriod period,
12 | Money price,
13 | Money prepaid,
14 | DateTimeOffset bookedAt,
15 | IsRoomAvailable isRoomAvailable
16 | ) {
17 | EnsureDoesntExist();
18 | await EnsureRoomAvailable(roomId, period, isRoomAvailable);
19 |
20 | var outstanding = price - prepaid;
21 |
22 | Apply(
23 | new V1.RoomBooked(
24 | guestId,
25 | roomId,
26 | period.CheckIn,
27 | period.CheckOut,
28 | price.Amount,
29 | prepaid.Amount,
30 | outstanding.Amount,
31 | price.Currency,
32 | bookedAt
33 | )
34 | );
35 |
36 | MarkFullyPaidIfNecessary(bookedAt);
37 | }
38 |
39 | public void RecordPayment(
40 | Money paid,
41 | string paymentId,
42 | string paidBy,
43 | DateTimeOffset paidAt
44 | ) {
45 | EnsureExists();
46 |
47 | if (State.HasPaymentBeenRegistered(paymentId)) return;
48 |
49 | var outstanding = State.Outstanding - paid;
50 |
51 | Apply(
52 | new V1.PaymentRecorded(
53 | paid.Amount,
54 | outstanding.Amount,
55 | paid.Currency,
56 | paymentId,
57 | paidBy,
58 | paidAt
59 | )
60 | );
61 |
62 | MarkFullyPaidIfNecessary(paidAt);
63 | MarkOverpaid(paidAt);
64 | }
65 |
66 | void MarkFullyPaidIfNecessary(DateTimeOffset when) {
67 | if (State.Outstanding.Amount <= 0)
68 | Apply(new V1.BookingFullyPaid(when));
69 | }
70 |
71 | void MarkOverpaid(DateTimeOffset when) {
72 | if (State.Outstanding.Amount < 0)
73 | Apply(new V1.BookingOverpaid(when));
74 | }
75 |
76 | static async Task EnsureRoomAvailable(RoomId roomId, StayPeriod period, IsRoomAvailable isRoomAvailable) {
77 | var roomAvailable = await isRoomAvailable(roomId, period);
78 | if (!roomAvailable) throw new DomainException("Room not available");
79 | }
80 | }
--------------------------------------------------------------------------------
/Bookings.Domain/Bookings/BookingEvents.cs:
--------------------------------------------------------------------------------
1 | using Eventuous;
2 | using NodaTime;
3 |
4 | namespace Bookings.Domain.Bookings;
5 |
6 | public static class BookingEvents {
7 | public static class V1 {
8 | [EventType("V1.RoomBooked")]
9 | public record RoomBooked(
10 | string GuestId,
11 | string RoomId,
12 | LocalDate CheckInDate,
13 | LocalDate CheckOutDate,
14 | float BookingPrice,
15 | float PrepaidAmount,
16 | float OutstandingAmount,
17 | string Currency,
18 | DateTimeOffset BookingDate
19 | );
20 |
21 | [EventType("V1.PaymentRecorded")]
22 | public record PaymentRecorded(
23 | float PaidAmount,
24 | float Outstanding,
25 | string Currency,
26 | string PaymentId,
27 | string PaidBy,
28 | DateTimeOffset PaidAt
29 | );
30 |
31 | [EventType("V1.FullyPaid")]
32 | public record BookingFullyPaid(DateTimeOffset FullyPaidAt);
33 |
34 | [EventType("V1.Overpaid")]
35 | public record BookingOverpaid(DateTimeOffset OverpaidAt);
36 |
37 | [EventType("V1.BookingCancelled")]
38 | public record BookingCancelled(string CancelledBy, DateTimeOffset CancelledAt);
39 | }
40 | }
--------------------------------------------------------------------------------
/Bookings.Domain/Bookings/BookingId.cs:
--------------------------------------------------------------------------------
1 | using Eventuous;
2 |
3 | namespace Bookings.Domain.Bookings;
4 |
5 | public record BookingId(string Value) : AggregateId(Value);
--------------------------------------------------------------------------------
/Bookings.Domain/Bookings/BookingState.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 | using Eventuous;
3 | using static Bookings.Domain.Bookings.BookingEvents;
4 | // ReSharper disable MemberCanBePrivate.Global
5 | // ReSharper disable UnusedAutoPropertyAccessor.Global
6 | // ReSharper disable NotAccessedPositionalProperty.Global
7 |
8 | namespace Bookings.Domain.Bookings;
9 |
10 | public record BookingState : State {
11 | public string GuestId { get; init; } = null!;
12 | public RoomId RoomId { get; init; } = null!;
13 | public StayPeriod Period { get; init; } = null!;
14 | public Money Price { get; init; } = null!;
15 | public Money Outstanding { get; init; } = null!;
16 | public bool Paid { get; init; }
17 |
18 | public ImmutableArray Payments { get; init; } = ImmutableArray.Empty;
19 |
20 | internal bool HasPaymentBeenRegistered(string paymentId) => Payments.Any(x => x.PaymentId == paymentId);
21 |
22 | public BookingState() {
23 | On(HandleBooked);
24 | On(HandlePayment);
25 | On((state, paid) => state with { Paid = true });
26 | }
27 |
28 | static BookingState HandlePayment(BookingState state, V1.PaymentRecorded e)
29 | => state with {
30 | Outstanding = new Money { Amount = e.Outstanding, Currency = e.Currency },
31 | Payments = state.Payments.Add(new PaymentRecord(e.PaymentId, new Money(e.PaidAmount, e.Currency)))
32 | };
33 |
34 | static BookingState HandleBooked(BookingState state, V1.RoomBooked booked)
35 | => state with {
36 | RoomId = new RoomId(booked.RoomId),
37 | Period = new StayPeriod(booked.CheckInDate, booked.CheckOutDate),
38 | GuestId = booked.GuestId,
39 | Price = new Money { Amount = booked.BookingPrice, Currency = booked.Currency },
40 | Outstanding = new Money { Amount = booked.OutstandingAmount, Currency = booked.Currency }
41 | };
42 | }
43 |
44 | public record PaymentRecord(string PaymentId, Money PaidAmount);
45 |
46 | public record DiscountRecord(Money Discount, string Reason);
--------------------------------------------------------------------------------
/Bookings.Domain/DomainModule.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using System.Runtime.CompilerServices;
3 | using Eventuous;
4 |
5 | namespace Bookings.Domain;
6 |
7 | static class DomainModule {
8 | [ModuleInitializer]
9 | [SuppressMessage("Usage", "CA2255", MessageId = "The \'ModuleInitializer\' attribute should not be used in libraries")]
10 | internal static void InitializeDomainModule() => TypeMap.RegisterKnownEventTypes();
11 | }
--------------------------------------------------------------------------------
/Bookings.Domain/Money.cs:
--------------------------------------------------------------------------------
1 | using Eventuous;
2 |
3 | namespace Bookings.Domain;
4 |
5 | public record Money {
6 | public float Amount { get; internal init; }
7 | public string Currency { get; internal init; } = null!;
8 |
9 | static readonly string[] SupportedCurrencies = {"USD", "GPB", "EUR"};
10 |
11 | internal Money() { }
12 |
13 | public Money(float amount, string currency) {
14 | if (!SupportedCurrencies.Contains(currency)) throw new DomainException($"Unsupported currency: {currency}");
15 |
16 | Amount = amount;
17 | Currency = currency;
18 | }
19 |
20 | public bool IsSameCurrency(Money another) => Currency == another.Currency;
21 |
22 | public static Money operator -(Money one, Money another) {
23 | if (!one.IsSameCurrency(another)) throw new DomainException("Cannot operate on different currencies");
24 |
25 | return new Money(one.Amount - another.Amount, one.Currency);
26 | }
27 |
28 | public static implicit operator double(Money money) => money.Amount;
29 | }
--------------------------------------------------------------------------------
/Bookings.Domain/RoomId.cs:
--------------------------------------------------------------------------------
1 | using Eventuous;
2 |
3 | namespace Bookings.Domain;
4 |
5 | public record RoomId(string Value) : AggregateId(Value);
--------------------------------------------------------------------------------
/Bookings.Domain/Services.cs:
--------------------------------------------------------------------------------
1 | namespace Bookings.Domain;
2 |
3 | public static class Services {
4 | public delegate ValueTask IsRoomAvailable(RoomId roomId, StayPeriod period);
5 |
6 | public delegate Money ConvertCurrency(Money from, string targetCurrency);
7 | }
--------------------------------------------------------------------------------
/Bookings.Domain/StayPeriod.cs:
--------------------------------------------------------------------------------
1 | using Eventuous;
2 | using NodaTime;
3 |
4 | namespace Bookings.Domain;
5 |
6 | public record StayPeriod {
7 | public LocalDate CheckIn { get; }
8 | public LocalDate CheckOut { get; }
9 |
10 | internal StayPeriod() { }
11 |
12 | public StayPeriod(LocalDate checkIn, LocalDate checkOut) {
13 | if (checkIn > checkOut) throw new DomainException("Check in date must be before check out date");
14 |
15 | (CheckIn, CheckOut) = (checkIn, checkOut);
16 | }
17 | }
--------------------------------------------------------------------------------
/Bookings.Payments/Application/CommandApi.cs:
--------------------------------------------------------------------------------
1 | using Bookings.Payments.Domain;
2 | using Eventuous;
3 | using Eventuous.AspNetCore.Web;
4 | using Microsoft.AspNetCore.Mvc;
5 | using static Bookings.Payments.Application.PaymentCommands;
6 |
7 | namespace Bookings.Payments.Application;
8 |
9 | [Route("payment")]
10 | public class CommandApi : CommandHttpApiBase {
11 | public CommandApi(IApplicationService service) : base(service) { }
12 |
13 | [HttpPost]
14 | public Task> RegisterPayment([FromBody] RecordPayment cmd, CancellationToken cancellationToken)
15 | => Handle(cmd, cancellationToken);
16 | }
--------------------------------------------------------------------------------
/Bookings.Payments/Application/CommandService.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 | using Bookings.Payments.Domain;
3 | using Eventuous;
4 | using Eventuous.AspNetCore.Web;
5 |
6 | namespace Bookings.Payments.Application;
7 |
8 | public class CommandService : CommandService {
9 | public CommandService(IAggregateStore store) : base(store) {
10 | OnNew(
11 | cmd => new PaymentId(cmd.PaymentId),
12 | (payment, cmd) => payment.ProcessPayment(
13 | new PaymentId(cmd.PaymentId),
14 | cmd.BookingId,
15 | new Money(cmd.Amount, cmd.Currency),
16 | cmd.Method,
17 | cmd.Provider
18 | )
19 | );
20 | }
21 | }
22 |
23 | // [AggregateCommands(typeof(Payment))]
24 | public static class PaymentCommands {
25 | [HttpCommand]
26 | public record RecordPayment(
27 | string PaymentId,
28 | string BookingId,
29 | float Amount,
30 | string Currency,
31 | string Method,
32 | string Provider,
33 | [property: JsonIgnore] string PaidBy
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/Bookings.Payments/Bookings.Payments.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | true
25 | PreserveNewest
26 | PreserveNewest
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Bookings.Payments/Domain/Money.cs:
--------------------------------------------------------------------------------
1 | using Eventuous;
2 |
3 | namespace Bookings.Payments.Domain;
4 |
5 | public record Money {
6 | public float Amount { get; internal init; }
7 | public string Currency { get; internal init; } = null!;
8 |
9 | static readonly string[] SupportedCurrencies = { "USD", "GPB", "EUR" };
10 |
11 | // ReSharper disable once UnusedMember.Global
12 | internal Money() { }
13 |
14 | public Money(float amount, string currency) {
15 | if (!SupportedCurrencies.Contains(currency)) throw new DomainException($"Unsupported currency: {currency}");
16 |
17 | Amount = amount;
18 | Currency = currency;
19 | }
20 |
21 | public bool IsSameCurrency(Money another) => Currency == another.Currency;
22 |
23 | public static Money operator -(Money one, Money another) {
24 | if (!one.IsSameCurrency(another)) throw new DomainException("Cannot operate on different currencies");
25 |
26 | return new Money(one.Amount - another.Amount, one.Currency);
27 | }
28 |
29 | public static implicit operator double(Money money) => money.Amount;
30 | }
--------------------------------------------------------------------------------
/Bookings.Payments/Domain/Payment.cs:
--------------------------------------------------------------------------------
1 | using Eventuous;
2 | using static Bookings.Payments.Domain.PaymentEvents;
3 |
4 | namespace Bookings.Payments.Domain;
5 |
6 | public class Payment : Aggregate {
7 | public void ProcessPayment(
8 | PaymentId paymentId, string bookingId, Money amount, string method, string provider
9 | )
10 | => Apply(new PaymentRecorded(paymentId, bookingId, amount.Amount, amount.Currency, method, provider));
11 | }
12 |
13 | public record PaymentState : State {
14 | public string BookingId { get; init; } = null!;
15 | public float Amount { get; init; }
16 |
17 | public PaymentState() {
18 | On(
19 | (state, recorded) => state with {
20 | BookingId = recorded.BookingId,
21 | Amount = recorded.Amount
22 | }
23 | );
24 | }
25 | }
26 |
27 | public record PaymentId(string Value) : AggregateId(Value);
--------------------------------------------------------------------------------
/Bookings.Payments/Domain/PaymentEvents.cs:
--------------------------------------------------------------------------------
1 | using Eventuous;
2 |
3 | namespace Bookings.Payments.Domain;
4 |
5 | public static class PaymentEvents {
6 | [EventType("PaymentRecorded")]
7 | public record PaymentRecorded(
8 | string PaymentId, string BookingId, float Amount, string Currency, string Method, string Provider
9 | );
10 | }
--------------------------------------------------------------------------------
/Bookings.Payments/Infrastructure/Logging.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 | using Serilog.Events;
3 |
4 | namespace Bookings.Payments.Infrastructure;
5 |
6 | public static class Logging {
7 | public static void ConfigureLog()
8 | => Log.Logger = new LoggerConfiguration()
9 | .MinimumLevel.Debug()
10 | .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
11 | .MinimumLevel.Override("Microsoft.AspNetCore.Hosting.Diagnostics", LogEventLevel.Warning)
12 | .MinimumLevel.Override("Microsoft.AspNetCore.Routing", LogEventLevel.Warning)
13 | .MinimumLevel.Override("Grpc", LogEventLevel.Information)
14 | .MinimumLevel.Override("EventStore", LogEventLevel.Information)
15 | .Enrich.FromLogContext()
16 | .WriteTo.Console(
17 | outputTemplate:
18 | "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {NewLine}{Exception}"
19 | )
20 | .CreateLogger();
21 | }
22 |
--------------------------------------------------------------------------------
/Bookings.Payments/Infrastructure/Mongo.cs:
--------------------------------------------------------------------------------
1 | using MongoDB.Driver;
2 | using MongoDB.Driver.Core.Extensions.DiagnosticSources;
3 |
4 | namespace Bookings.Payments.Infrastructure;
5 |
6 | public static class Mongo {
7 | public static IMongoDatabase ConfigureMongo(IConfiguration configuration) {
8 | var settings = MongoClientSettings.FromConnectionString(configuration["Mongo:ConnectionString"]);
9 | settings.ClusterConfigurator = cb => cb.Subscribe(new DiagnosticsActivityEventSubscriber());
10 | return new MongoClient(settings).GetDatabase(configuration["Mongo:Database"]);
11 | }
12 | }
--------------------------------------------------------------------------------
/Bookings.Payments/Integration/Payments.cs:
--------------------------------------------------------------------------------
1 | using Bookings.Payments.Domain;
2 | using Eventuous;
3 | using Eventuous.EventStore.Producers;
4 | using Eventuous.Gateway;
5 | using Eventuous.Subscriptions.Context;
6 | using static Bookings.Payments.Integration.IntegrationEvents;
7 |
8 | namespace Bookings.Payments.Integration;
9 |
10 | public static class PaymentsGateway {
11 | static readonly StreamName Stream = new("PaymentsIntegration");
12 |
13 | public static ValueTask[]> Transform(IMessageConsumeContext original) {
14 | var result = original.Message is PaymentEvents.PaymentRecorded evt
15 | ? new GatewayMessage(
16 | Stream,
17 | new BookingPaymentRecorded(evt.PaymentId, evt.BookingId, evt.Amount, evt.Currency),
18 | new Metadata(),
19 | new EventStoreProduceOptions()
20 | )
21 | : null;
22 | return ValueTask.FromResult(result != null ? new []{result} : Array.Empty>());
23 | }
24 | }
25 |
26 | public static class IntegrationEvents {
27 | [EventType("BookingPaymentRecorded")]
28 | public record BookingPaymentRecorded(string PaymentId, string BookingId, float Amount, string Currency);
29 | }
--------------------------------------------------------------------------------
/Bookings.Payments/Program.cs:
--------------------------------------------------------------------------------
1 | using Bookings.Payments;
2 | using Bookings.Payments.Domain;
3 | using Bookings.Payments.Infrastructure;
4 | using Eventuous;
5 | using Eventuous.AspNetCore;
6 | using Serilog;
7 |
8 | TypeMap.RegisterKnownEventTypes();
9 | Logging.ConfigureLog();
10 |
11 | var builder = WebApplication.CreateBuilder(args);
12 |
13 | builder.Services.AddEndpointsApiExplorer();
14 | builder.Services.AddSwaggerGen();
15 |
16 | // OpenTelemetry instrumentation must be added before adding Eventuous services
17 | builder.Services.AddTelemetry();
18 |
19 | builder.Services.AddServices(builder.Configuration);
20 | builder.Host.UseSerilog();
21 |
22 | var app = builder.Build();
23 | app.AddEventuousLogs();
24 |
25 | app.UseSwagger();
26 | app.UseOpenTelemetryPrometheusScrapingEndpoint();
27 |
28 | // Here we discover commands by their annotations
29 | // app.MapDiscoveredCommands();
30 | app.MapDiscoveredCommands();
31 |
32 | app.UseSwaggerUI();
33 |
34 | app.Run();
--------------------------------------------------------------------------------
/Bookings.Payments/Registrations.cs:
--------------------------------------------------------------------------------
1 | using Bookings.Payments.Application;
2 | using Bookings.Payments.Domain;
3 | using Bookings.Payments.Infrastructure;
4 | using Bookings.Payments.Integration;
5 | using Eventuous.Diagnostics.OpenTelemetry;
6 | using Eventuous.EventStore;
7 | using Eventuous.EventStore.Producers;
8 | using Eventuous.EventStore.Subscriptions;
9 | using Eventuous.Producers;
10 | using Eventuous.Projections.MongoDB;
11 | using OpenTelemetry.Metrics;
12 | using OpenTelemetry.Resources;
13 | using OpenTelemetry.Trace;
14 |
15 | namespace Bookings.Payments;
16 |
17 | public static class Registrations {
18 | public static void AddServices(this IServiceCollection services, IConfiguration configuration) {
19 | services.AddEventStoreClient(configuration["EventStore:ConnectionString"]!);
20 | services.AddAggregateStore();
21 | services.AddCommandService();
22 | services.AddSingleton(Mongo.ConfigureMongo(configuration));
23 | services.AddCheckpointStore();
24 | services.AddEventProducer();
25 |
26 | services
27 | .AddGateway(
28 | subscriptionId: "IntegrationSubscription",
29 | routeAndTransform: PaymentsGateway.Transform
30 | );
31 | }
32 |
33 | public static void AddTelemetry(this IServiceCollection services) {
34 | services.AddOpenTelemetry()
35 | .WithMetrics(
36 | builder => builder
37 | .AddAspNetCoreInstrumentation()
38 | .AddEventuous()
39 | .AddEventuousSubscriptions()
40 | .AddPrometheusExporter()
41 | );
42 |
43 | services.AddOpenTelemetry()
44 | .WithTracing(
45 | builder => builder
46 | .AddAspNetCoreInstrumentation()
47 | .AddGrpcClientInstrumentation()
48 | .AddEventuousTracing()
49 | .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("payments"))
50 | .SetSampler(new AlwaysOnSampler())
51 | .AddZipkinExporter()
52 | );
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Bookings.Payments/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Mongo": {
3 | "ConnectionString": "mongodb://mongoadmin:secret@localhost:27017",
4 | "Database": "Payments"
5 | },
6 | "EventStore": {
7 | "ConnectionString": "esdb://localhost:2113?tls=false"
8 | },
9 | "Logging": {
10 | "LogLevel": {
11 | "Default": "Debug",
12 | "Microsoft.AspNetCore": "Warning"
13 | }
14 | },
15 | "AllowedHosts": "*"
16 | }
17 |
--------------------------------------------------------------------------------
/Bookings.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bookings", "Bookings\Bookings.csproj", "{7B4C2F9A-452E-4C08-86D5-F761C67089A6}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bookings.Domain", "Bookings.Domain\Bookings.Domain.csproj", "{280363FF-F078-44B7-9A32-20FA7D6046E5}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bookings.Payments", "Bookings.Payments\Bookings.Payments.csproj", "{EBD8B561-B354-4FDB-BF88-0BF0681F5530}"
8 | EndProject
9 | Global
10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
11 | Debug|Any CPU = Debug|Any CPU
12 | Release|Any CPU = Release|Any CPU
13 | EndGlobalSection
14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
15 | {7B4C2F9A-452E-4C08-86D5-F761C67089A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
16 | {7B4C2F9A-452E-4C08-86D5-F761C67089A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
17 | {7B4C2F9A-452E-4C08-86D5-F761C67089A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
18 | {7B4C2F9A-452E-4C08-86D5-F761C67089A6}.Release|Any CPU.Build.0 = Release|Any CPU
19 | {280363FF-F078-44B7-9A32-20FA7D6046E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {280363FF-F078-44B7-9A32-20FA7D6046E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {280363FF-F078-44B7-9A32-20FA7D6046E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {280363FF-F078-44B7-9A32-20FA7D6046E5}.Release|Any CPU.Build.0 = Release|Any CPU
23 | {EBD8B561-B354-4FDB-BF88-0BF0681F5530}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {EBD8B561-B354-4FDB-BF88-0BF0681F5530}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {EBD8B561-B354-4FDB-BF88-0BF0681F5530}.Release|Any CPU.ActiveCfg = Release|Any CPU
26 | {EBD8B561-B354-4FDB-BF88-0BF0681F5530}.Release|Any CPU.Build.0 = Release|Any CPU
27 | EndGlobalSection
28 | GlobalSection(NestedProjects) = preSolution
29 | EndGlobalSection
30 | EndGlobal
31 |
--------------------------------------------------------------------------------
/Bookings.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
--------------------------------------------------------------------------------
/Bookings/.dockerignore:
--------------------------------------------------------------------------------
1 | **/.dockerignore
2 | **/.env
3 | **/.git
4 | **/.gitignore
5 | **/.project
6 | **/.settings
7 | **/.toolstarget
8 | **/.vs
9 | **/.vscode
10 | **/.idea
11 | **/*.*proj.user
12 | **/*.dbmdl
13 | **/*.jfm
14 | **/azds.yaml
15 | **/bin
16 | **/charts
17 | **/docker-compose*
18 | **/Dockerfile*
19 | **/node_modules
20 | **/npm-debug.log
21 | **/obj
22 | **/secrets.dev.yaml
23 | **/values.dev.yaml
24 | LICENSE
25 | README.md
--------------------------------------------------------------------------------
/Bookings/Application/BookingsCommandService.cs:
--------------------------------------------------------------------------------
1 | using Bookings.Domain;
2 | using Bookings.Domain.Bookings;
3 | using Eventuous;
4 | using NodaTime;
5 | using static Bookings.Application.BookingCommands;
6 |
7 | namespace Bookings.Application;
8 |
9 | public class BookingsCommandService : CommandService {
10 | public BookingsCommandService(IAggregateStore store, Services.IsRoomAvailable isRoomAvailable) : base(store) {
11 | OnNewAsync(
12 | cmd => new BookingId(cmd.BookingId),
13 | (booking, cmd, _) => booking.BookRoom(
14 | cmd.GuestId,
15 | new RoomId(cmd.RoomId),
16 | new StayPeriod(LocalDate.FromDateTime(cmd.CheckInDate), LocalDate.FromDateTime(cmd.CheckOutDate)),
17 | new Money(cmd.BookingPrice, cmd.Currency),
18 | new Money(cmd.PrepaidAmount, cmd.Currency),
19 | DateTimeOffset.Now,
20 | isRoomAvailable
21 | )
22 | );
23 |
24 | OnExisting(
25 | cmd => new BookingId(cmd.BookingId),
26 | (booking, cmd) => booking.RecordPayment(
27 | new Money(cmd.PaidAmount, cmd.Currency),
28 | cmd.PaymentId,
29 | cmd.PaidBy,
30 | DateTimeOffset.Now
31 | )
32 | );
33 | }
34 | }
--------------------------------------------------------------------------------
/Bookings/Application/Commands.cs:
--------------------------------------------------------------------------------
1 | namespace Bookings.Application;
2 |
3 | public static class BookingCommands {
4 | public record BookRoom(
5 | string BookingId,
6 | string GuestId,
7 | string RoomId,
8 | DateTime CheckInDate,
9 | DateTime CheckOutDate,
10 | float BookingPrice,
11 | float PrepaidAmount,
12 | string Currency,
13 | DateTimeOffset BookingDate
14 | );
15 |
16 | public record RecordPayment(string BookingId, float PaidAmount, string Currency, string PaymentId, string PaidBy);
17 | }
--------------------------------------------------------------------------------
/Bookings/Application/Queries/BookingDocument.cs:
--------------------------------------------------------------------------------
1 | using Eventuous.Projections.MongoDB.Tools;
2 | using NodaTime;
3 | // ReSharper disable UnusedAutoPropertyAccessor.Global
4 |
5 | namespace Bookings.Application.Queries;
6 |
7 | public record BookingDocument : ProjectedDocument {
8 | public BookingDocument(string id) : base(id) { }
9 |
10 | public string GuestId { get; init; } = null!;
11 | public string RoomId { get; init; } = null!;
12 | public LocalDate CheckInDate { get; init; }
13 | public LocalDate CheckOutDate { get; init; }
14 | public float BookingPrice { get; init; }
15 | public float PaidAmount { get; init; }
16 | public float Outstanding { get; init; }
17 | public bool Paid { get; init; }
18 | }
--------------------------------------------------------------------------------
/Bookings/Application/Queries/BookingStateProjection.cs:
--------------------------------------------------------------------------------
1 | using Eventuous.Projections.MongoDB;
2 | using Eventuous.Subscriptions.Context;
3 | using MongoDB.Driver;
4 | using static Bookings.Domain.Bookings.BookingEvents;
5 |
6 | // ReSharper disable UnusedAutoPropertyAccessor.Global
7 |
8 | namespace Bookings.Application.Queries;
9 |
10 | public class BookingStateProjection : MongoProjection {
11 | public BookingStateProjection(IMongoDatabase database) : base(database) {
12 | On(stream => stream.GetId(), HandleRoomBooked);
13 |
14 | On(
15 | b => b
16 | .UpdateOne
17 | .DefaultId()
18 | .Update((evt, update) =>
19 | update.Set(x => x.Outstanding, evt.Outstanding)
20 | )
21 | );
22 |
23 | On(b => b
24 | .UpdateOne
25 | .DefaultId()
26 | .Update((_, update) => update.Set(x => x.Paid, true))
27 | );
28 | }
29 |
30 | static UpdateDefinition HandleRoomBooked(
31 | IMessageConsumeContext ctx, UpdateDefinitionBuilder update
32 | ) {
33 | var evt = ctx.Message;
34 |
35 | return update.SetOnInsert(x => x.Id, ctx.Stream.GetId())
36 | .Set(x => x.GuestId, evt.GuestId)
37 | .Set(x => x.RoomId, evt.RoomId)
38 | .Set(x => x.CheckInDate, evt.CheckInDate)
39 | .Set(x => x.CheckOutDate, evt.CheckOutDate)
40 | .Set(x => x.BookingPrice, evt.BookingPrice)
41 | .Set(x => x.Outstanding, evt.OutstandingAmount);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Bookings/Application/Queries/MyBookings.cs:
--------------------------------------------------------------------------------
1 | using Eventuous.Projections.MongoDB.Tools;
2 | using NodaTime;
3 |
4 | namespace Bookings.Application.Queries;
5 |
6 | public record MyBookings : ProjectedDocument {
7 | public MyBookings(string id) : base(id) { }
8 |
9 | public List Bookings { get; init; } = new();
10 |
11 | public record Booking(string BookingId, LocalDate CheckInDate, LocalDate CheckOutDate, float Price);
12 | }
--------------------------------------------------------------------------------
/Bookings/Application/Queries/MyBookingsProjection.cs:
--------------------------------------------------------------------------------
1 | using Eventuous.Projections.MongoDB;
2 | using MongoDB.Driver;
3 | using static Bookings.Domain.Bookings.BookingEvents;
4 |
5 | namespace Bookings.Application.Queries;
6 |
7 | public class MyBookingsProjection : MongoProjection {
8 | public MyBookingsProjection(IMongoDatabase database) : base(database) {
9 | On(b => b
10 | .UpdateOne
11 | .Id(ctx => ctx.Message.GuestId)
12 | .UpdateFromContext((ctx, update) =>
13 | update.AddToSet(
14 | x => x.Bookings,
15 | new MyBookings.Booking(ctx.Stream.GetId(),
16 | ctx.Message.CheckInDate,
17 | ctx.Message.CheckOutDate,
18 | ctx.Message.BookingPrice
19 | )
20 | )
21 | )
22 | );
23 |
24 | On(
25 | b => b.UpdateOne
26 | .Filter((ctx, doc) =>
27 | doc.Bookings.Select(booking => booking.BookingId).Contains(ctx.Stream.GetId())
28 | )
29 | .UpdateFromContext((ctx, update) =>
30 | update.PullFilter(
31 | x => x.Bookings,
32 | x => x.BookingId == ctx.Stream.GetId()
33 | )
34 | )
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Bookings/Bookings.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Linux
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | true
31 | PreserveNewest
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Bookings/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
2 | WORKDIR /app
3 |
4 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
5 | WORKDIR /src
6 | COPY . .
7 | RUN dotnet restore "Bookings/Bookings.csproj"
8 | WORKDIR "/src/Bookings"
9 | RUN dotnet build "Bookings.csproj" -c Release -o /app/build
10 |
11 | FROM build AS publish
12 | RUN dotnet publish "Bookings.csproj" -c Release -o /app/publish
13 |
14 | FROM base AS final
15 | WORKDIR /app
16 | COPY --from=publish /app/publish .
17 | ENTRYPOINT ["dotnet", "Bookings.dll"]
18 |
--------------------------------------------------------------------------------
/Bookings/HttpApi/Bookings/CommandApi.cs:
--------------------------------------------------------------------------------
1 | using Bookings.Domain.Bookings;
2 | using Eventuous;
3 | using Eventuous.AspNetCore.Web;
4 | using Microsoft.AspNetCore.Authorization;
5 | using Microsoft.AspNetCore.Mvc;
6 | using static Bookings.Application.BookingCommands;
7 |
8 | namespace Bookings.HttpApi.Bookings;
9 |
10 | [Route("/booking")]
11 | public class CommandApi : CommandHttpApiBase {
12 | public CommandApi(ICommandService service) : base(service) { }
13 |
14 | [HttpPost]
15 | [Route("book")]
16 | public Task> BookRoom([FromBody] BookRoom cmd, CancellationToken cancellationToken)
17 | => Handle(cmd, cancellationToken);
18 |
19 | ///
20 | /// This endpoint is for demo purposes only. The normal flow to register booking payments is to submit
21 | /// a command via the Booking.Payments HTTP API. It then gets propagated to the Booking aggregate
22 | /// via the integration messaging flow.
23 | ///
24 | /// Command to register the payment
25 | /// Cancellation token
26 | ///
27 | [HttpPost]
28 | [Route("recordPayment")]
29 | public Task> RecordPayment(
30 | [FromBody] RecordPayment cmd, CancellationToken cancellationToken
31 | )
32 | => Handle(cmd, cancellationToken);
33 | }
34 |
--------------------------------------------------------------------------------
/Bookings/HttpApi/Bookings/QueryApi.cs:
--------------------------------------------------------------------------------
1 | using Bookings.Domain.Bookings;
2 | using Eventuous;
3 | using Microsoft.AspNetCore.Mvc;
4 |
5 | namespace Bookings.HttpApi.Bookings;
6 |
7 | [Route("/bookings")]
8 | public class QueryApi : ControllerBase {
9 | readonly IAggregateStore _store;
10 |
11 | public QueryApi(IAggregateStore store) => _store = store;
12 |
13 | [HttpGet]
14 | [Route("{id}")]
15 | public async Task GetBooking(string id, CancellationToken cancellationToken) {
16 | var booking = await _store.Load(StreamName.For(id), cancellationToken);
17 | return booking.State;
18 | }
19 | }
--------------------------------------------------------------------------------
/Bookings/Infrastructure/Mongo.cs:
--------------------------------------------------------------------------------
1 | using MongoDb.Bson.NodaTime;
2 | using MongoDB.Driver;
3 | using MongoDB.Driver.Core.Extensions.DiagnosticSources;
4 |
5 | namespace Bookings.Infrastructure;
6 |
7 | public static class Mongo {
8 | public static IMongoDatabase ConfigureMongo(IConfiguration configuration) {
9 | NodaTimeSerializers.Register();
10 | var config = configuration.GetSection("Mongo").Get();
11 |
12 | var settings = MongoClientSettings.FromConnectionString(config!.ConnectionString);
13 |
14 | if (config.User != null && config.Password != null) {
15 | settings.Credential = new MongoCredential(
16 | null,
17 | new MongoInternalIdentity("admin", config.User),
18 | new PasswordEvidence(config.Password)
19 | );
20 | }
21 |
22 | settings.ClusterConfigurator = cb => cb.Subscribe(new DiagnosticsActivityEventSubscriber());
23 | return new MongoClient(settings).GetDatabase(config.Database);
24 | }
25 |
26 | public record MongoSettings {
27 | public string ConnectionString { get; init; } = null!;
28 | public string Database { get; init; } = null!;
29 | public string? User { get; init; }
30 | public string? Password { get; init; }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Bookings/Integration/Payments.cs:
--------------------------------------------------------------------------------
1 | using Bookings.Domain.Bookings;
2 | using Eventuous;
3 | using static Bookings.Application.BookingCommands;
4 | using static Bookings.Integration.IntegrationEvents;
5 | using EventHandler = Eventuous.Subscriptions.EventHandler;
6 |
7 | namespace Bookings.Integration;
8 |
9 | public class PaymentsIntegrationHandler : EventHandler {
10 | public static readonly StreamName Stream = new("PaymentsIntegration");
11 |
12 | readonly ICommandService _applicationService;
13 |
14 | public PaymentsIntegrationHandler(ICommandService applicationService) {
15 | _applicationService = applicationService;
16 | On(async ctx => await HandlePayment(ctx.Message, ctx.CancellationToken));
17 | }
18 |
19 | Task HandlePayment(BookingPaymentRecorded evt, CancellationToken cancellationToken)
20 | => _applicationService.Handle(
21 | new RecordPayment(
22 | evt.BookingId,
23 | evt.Amount,
24 | evt.Currency,
25 | evt.PaymentId,
26 | ""
27 | ),
28 | cancellationToken
29 | );
30 | }
31 |
32 | static class IntegrationEvents {
33 | [EventType("BookingPaymentRecorded")]
34 | public record BookingPaymentRecorded(string PaymentId, string BookingId, float Amount, string Currency);
35 | }
--------------------------------------------------------------------------------
/Bookings/Program.cs:
--------------------------------------------------------------------------------
1 | using Bookings;
2 | using Bookings.Domain.Bookings;
3 | using Eventuous;
4 | using Eventuous.AspNetCore;
5 | using Eventuous.Diagnostics.Logging;
6 | using Eventuous.Spyglass;
7 | using Microsoft.AspNetCore.Http.Json;
8 | using NodaTime;
9 | using NodaTime.Serialization.SystemTextJson;
10 | using Serilog;
11 | using Serilog.Events;
12 |
13 | TypeMap.RegisterKnownEventTypes(typeof(BookingEvents.V1.RoomBooked).Assembly);
14 |
15 | Log.Logger = new LoggerConfiguration()
16 | .MinimumLevel.Verbose()
17 | .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
18 | .MinimumLevel.Override("Grpc", LogEventLevel.Information)
19 | .MinimumLevel.Override("Grpc.Net.Client.Internal.GrpcCall", LogEventLevel.Error)
20 | .MinimumLevel.Override("Microsoft.AspNetCore.Mvc.Infrastructure", LogEventLevel.Warning)
21 | .Enrich.FromLogContext()
22 | .WriteTo.Console()
23 | .WriteTo.Seq("http://localhost:5341")
24 | .CreateLogger();
25 |
26 | var builder = WebApplication.CreateBuilder(args);
27 | builder.Host.UseSerilog();
28 |
29 | builder.Services
30 | .AddControllers()
31 | .AddJsonOptions(cfg => cfg.JsonSerializerOptions.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb));
32 | builder.Services.AddEndpointsApiExplorer();
33 | builder.Services.AddSwaggerGen();
34 | builder.Services.AddTelemetry();
35 | builder.Services.AddEventuous(builder.Configuration);
36 | builder.Services.AddEventuousSpyglass();
37 |
38 | builder.Services.Configure(options
39 | => options.SerializerOptions.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb)
40 | );
41 |
42 | var app = builder.Build();
43 |
44 | app.UseSerilogRequestLogging();
45 | app.UseSwagger().UseSwaggerUI();
46 | app.MapControllers();
47 | app.UseOpenTelemetryPrometheusScrapingEndpoint();
48 | app.MapEventuousSpyglass(null);
49 |
50 | var factory = app.Services.GetRequiredService();
51 | var listener = new LoggingEventListener(factory, "OpenTelemetry");
52 |
53 | try {
54 | app.Run("http://*:5051");
55 | return 0;
56 | }
57 | catch (Exception e) {
58 | Log.Fatal(e, "Host terminated unexpectedly");
59 | return 1;
60 | }
61 | finally {
62 | Log.CloseAndFlush();
63 | listener.Dispose();
64 | }
65 |
--------------------------------------------------------------------------------
/Bookings/Registrations.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using Bookings.Application;
3 | using Bookings.Application.Queries;
4 | using Bookings.Domain;
5 | using Bookings.Domain.Bookings;
6 | using Bookings.Infrastructure;
7 | using Bookings.Integration;
8 | using Eventuous;
9 | using Eventuous.Diagnostics.OpenTelemetry;
10 | using Eventuous.EventStore;
11 | using Eventuous.EventStore.Subscriptions;
12 | using Eventuous.Projections.MongoDB;
13 | using Eventuous.Subscriptions.Registrations;
14 | using NodaTime;
15 | using NodaTime.Serialization.SystemTextJson;
16 | using OpenTelemetry.Metrics;
17 | using OpenTelemetry.Resources;
18 | using OpenTelemetry.Trace;
19 |
20 | namespace Bookings;
21 |
22 | public static class Registrations {
23 | public static void AddEventuous(this IServiceCollection services, IConfiguration configuration) {
24 | DefaultEventSerializer.SetDefaultSerializer(
25 | new DefaultEventSerializer(
26 | new JsonSerializerOptions(JsonSerializerDefaults.Web).ConfigureForNodaTime(
27 | DateTimeZoneProviders.Tzdb
28 | )
29 | )
30 | );
31 |
32 | services.AddEventStoreClient(configuration["EventStore:ConnectionString"]!);
33 | services.AddAggregateStore();
34 | services.AddCommandService();
35 |
36 | services.AddSingleton((id, period) => new ValueTask(true));
37 |
38 | services.AddSingleton((from, currency)
39 | => new Money(from.Amount * 2, currency)
40 | );
41 |
42 | services.AddSingleton(Mongo.ConfigureMongo(configuration));
43 | services.AddCheckpointStore();
44 |
45 | services.AddSubscription(
46 | "BookingsProjections",
47 | builder => builder
48 | .UseCheckpointStore()
49 | .AddEventHandler()
50 | .AddEventHandler()
51 | .WithPartitioningByStream(2)
52 | );
53 |
54 | services.AddSubscription(
55 | "PaymentIntegration",
56 | builder => builder
57 | .Configure(x => x.StreamName = PaymentsIntegrationHandler.Stream)
58 | .AddEventHandler()
59 | );
60 | }
61 |
62 | public static void AddTelemetry(this IServiceCollection services) {
63 | var otelEnabled = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT") != null;
64 |
65 | services.AddOpenTelemetry()
66 | .WithMetrics(
67 | builder => {
68 | builder
69 | .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("bookings"))
70 | .AddAspNetCoreInstrumentation()
71 | .AddEventuous()
72 | .AddEventuousSubscriptions()
73 | .AddPrometheusExporter();
74 | if (otelEnabled) builder.AddOtlpExporter();
75 | }
76 | );
77 |
78 | services.AddOpenTelemetry()
79 | .WithTracing(
80 | builder => {
81 | builder
82 | .AddAspNetCoreInstrumentation()
83 | .AddGrpcClientInstrumentation()
84 | .AddEventuousTracing()
85 | .AddMongoDBInstrumentation()
86 | .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("bookings"))
87 | .SetSampler(new AlwaysOnSampler());
88 |
89 | if (otelEnabled)
90 | builder.AddOtlpExporter();
91 | else
92 | builder.AddZipkinExporter();
93 | }
94 | );
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Bookings/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Mongo": {
3 | "ConnectionString": "mongodb://localhost:27017",
4 | "User": "mongoadmin",
5 | "Password": "secret",
6 | "Database": "Bookings"
7 | },
8 | "EventStore": {
9 | "ConnectionString": "esdb://localhost:2113?tls=false"
10 | },
11 | "AllowedHosts": "*"
12 | }
13 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | net7.0
4 | enable
5 | enable
6 | 0.14.1-alpha.0.7
7 |
8 |
--------------------------------------------------------------------------------
/NuGet.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Deprecation notice
2 |
3 | Samples are moved to the [core repository](https://github.com/Eventuous/eventuous/tree/dev/samples).
4 |
5 | # Eventuous sample with EventStoreDB
6 |
7 | Simple example of using Eventuous with [EventStoreDB](https://eventstore.com).
8 |
9 |
--------------------------------------------------------------------------------
/deploy/cloudrun/.gitignore:
--------------------------------------------------------------------------------
1 | /bin/
2 | /node_modules/
3 |
--------------------------------------------------------------------------------
/deploy/cloudrun/Pulumi.yaml:
--------------------------------------------------------------------------------
1 | name: cloudrun
2 | runtime: nodejs
3 | description: Eventuous sample application deployment to Google Cloud Run
4 |
--------------------------------------------------------------------------------
/deploy/cloudrun/index.ts:
--------------------------------------------------------------------------------
1 | import * as pulumi from "@pulumi/pulumi";
2 | import * as gcp from "@pulumi/gcp";
3 | import * as docker from "@pulumi/docker";
4 | import {input as inputs} from "@pulumi/gcp/types";
5 |
6 | const config = new pulumi.Config();
7 | const connectorName = config.require("vpcConnectorName");
8 |
9 | const imageName = "bookings";
10 | const location = gcp.config.region || "europe-west2";
11 |
12 | const myImage = new docker.Image(imageName, {
13 | imageName: pulumi.interpolate`gcr.io/${gcp.config.project}/${imageName}:latest`,
14 | build: {
15 | context: "../..",
16 | extraOptions: ["--platform=linux/amd64"],
17 | dockerfile: "../../Bookings/Dockerfile",
18 | },
19 | });
20 |
21 | let env: pulumi.Input[] = [
22 | {name: "Mongo__ConnectionString", value: config.require("mongoConnectionString"),},
23 | {name: "Mongo__User", value: config.require("mongoUser"),},
24 | {name: "Mongo__Password", value: config.require("mongoPassword"),},
25 | {name: "EventStore__ConnectionString", value: config.require("esdbConnectionString"),},
26 | {name: "ASPNETCORE_ENVIRONMENT", value: "Development",},
27 | {name: "ASPNETCORE_URLS", value: "http://*:5003",},
28 | ];
29 |
30 | const otelEndpoint = config.get("otelEndpoint");
31 | if (otelEndpoint) {
32 | env.push({name: "OTEL_EXPORTER_OTLP_ENDPOINT", value: otelEndpoint,});
33 | }
34 | const otelHeaders = config.get("otelHeaders");
35 | if (otelHeaders) {
36 | env.push({name: "OTEL_EXPORTER_OTLP_HEADERS", value: otelHeaders,});
37 | }
38 |
39 | const bookingsService = new gcp.cloudrun.Service("bookings", {
40 | location,
41 | template: {
42 | spec: {
43 | containers: [{
44 | image: myImage.imageName,
45 | resources: {
46 | limits: {
47 | memory: "512M",
48 | cpu: "1000m"
49 | },
50 | },
51 | ports: [{
52 | name: "http1",
53 | containerPort: 5003,
54 | }],
55 | envs: env,
56 | }],
57 | containerConcurrency: 50,
58 | },
59 | metadata: {
60 | annotations: {
61 | "autoscaling.knative.dev/maxScale": "1",
62 | "run.googleapis.com/vpc-access-connector": connectorName,
63 | "run.googleapis.com/vpc-access-egress": "private-ranges-only"
64 | }
65 | }
66 | },
67 | autogenerateRevisionName: true,
68 | });
69 |
70 | const iam = new gcp.cloudrun.IamMember("iam-everyone", {
71 | service: bookingsService.name,
72 | location,
73 | role: "roles/run.invoker",
74 | member: "allUsers",
75 | });
76 |
77 | // Export the URL
78 | export const url = bookingsService.statuses[0].url;
79 |
--------------------------------------------------------------------------------
/deploy/cloudrun/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cloudrun",
3 | "devDependencies": {
4 | "@types/node": "^14"
5 | },
6 | "dependencies": {
7 | "@pulumi/docker": "^3.2.0",
8 | "@pulumi/gcp": "^6.0.0",
9 | "@pulumi/pulumi": "^3.0.0"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/deploy/cloudrun/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "outDir": "bin",
5 | "target": "es2016",
6 | "module": "commonjs",
7 | "moduleResolution": "node",
8 | "sourceMap": true,
9 | "experimentalDecorators": true,
10 | "pretty": true,
11 | "noFallthroughCasesInSwitch": true,
12 | "noImplicitReturns": true,
13 | "forceConsistentCasingInFileNames": true
14 | },
15 | "files": [
16 | "index.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/deploy/cloudrun/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@grpc/grpc-js@~1.3.8":
6 | version "1.3.8"
7 | resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.3.8.tgz#0d7dce9de7aeb20702a28f0704e61b0bf34061c1"
8 | integrity sha512-4qJqqn+CU/nBydz9ePJP+oa8dz0U42Ut/GejlbyaQ1xTkynCc+ndNHHnISlNeHawDsv4MOAyP3mV/EnDNUw2zA==
9 | dependencies:
10 | "@types/node" ">=12.12.47"
11 |
12 | "@logdna/tail-file@^2.0.6":
13 | version "2.2.0"
14 | resolved "https://registry.yarnpkg.com/@logdna/tail-file/-/tail-file-2.2.0.tgz#158a362d293f940dacfd07c835bf3ae2f9e0455a"
15 | integrity sha512-XGSsWDweP80Fks16lwkAUIr54ICyBs6PsI4mpfTLQaWgEJRtY9xEV+PeyDpJ+sJEGZxqINlpmAwe/6tS1pP8Ng==
16 |
17 | "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
18 | version "1.1.2"
19 | resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
20 | integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==
21 |
22 | "@protobufjs/base64@^1.1.2":
23 | version "1.1.2"
24 | resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
25 | integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==
26 |
27 | "@protobufjs/codegen@^2.0.4":
28 | version "2.0.4"
29 | resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb"
30 | integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==
31 |
32 | "@protobufjs/eventemitter@^1.1.0":
33 | version "1.1.0"
34 | resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70"
35 | integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==
36 |
37 | "@protobufjs/fetch@^1.1.0":
38 | version "1.1.0"
39 | resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45"
40 | integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==
41 | dependencies:
42 | "@protobufjs/aspromise" "^1.1.1"
43 | "@protobufjs/inquire" "^1.1.0"
44 |
45 | "@protobufjs/float@^1.0.2":
46 | version "1.0.2"
47 | resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1"
48 | integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==
49 |
50 | "@protobufjs/inquire@^1.1.0":
51 | version "1.1.0"
52 | resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089"
53 | integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==
54 |
55 | "@protobufjs/path@^1.1.2":
56 | version "1.1.2"
57 | resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d"
58 | integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==
59 |
60 | "@protobufjs/pool@^1.1.0":
61 | version "1.1.0"
62 | resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54"
63 | integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==
64 |
65 | "@protobufjs/utf8@^1.1.0":
66 | version "1.1.0"
67 | resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
68 | integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
69 |
70 | "@pulumi/docker@^3.2.0":
71 | version "3.2.0"
72 | resolved "https://registry.yarnpkg.com/@pulumi/docker/-/docker-3.2.0.tgz#bbfeccb6dbf4a522e844f4de889c822cf0743ee1"
73 | integrity sha512-Mm8xz1vMhuxOOtyCU/V2Py7QW+M6zMxPBBtTjKzvWBYxLe8cygh12+EjCDFK6E9X+x3mV+ZrEkon+JY93kQWhw==
74 | dependencies:
75 | "@pulumi/pulumi" "^3.0.0"
76 | semver "^5.4.0"
77 |
78 | "@pulumi/gcp@^6.0.0":
79 | version "6.24.0"
80 | resolved "https://registry.yarnpkg.com/@pulumi/gcp/-/gcp-6.24.0.tgz#312634a69e410083cf23e2166792ba934ba18eab"
81 | integrity sha512-ZIAIe+O6n/a9EOlvlL5VdbxTMJ5JN7haTKOmYesnvoiB84N0a+iqij+DXPH3Vm1oL9NGOlFpuZk0mtPHtvVJfQ==
82 | dependencies:
83 | "@pulumi/pulumi" "^3.0.0"
84 | "@types/express" "^4.16.0"
85 | read-package-json "^2.0.13"
86 |
87 | "@pulumi/pulumi@^3.0.0":
88 | version "3.32.1"
89 | resolved "https://registry.yarnpkg.com/@pulumi/pulumi/-/pulumi-3.32.1.tgz#d91a9fc14524ce749a8195b67aab8e26658f7027"
90 | integrity sha512-R3pOp8BWEfQjZUdZXp4FRYjM4lCvDDwe4toe1uyqhtCoGSdtomHz0s6bHZ7SlLtjCgtWJklbi0p5V6WyiJ8IPg==
91 | dependencies:
92 | "@grpc/grpc-js" "~1.3.8"
93 | "@logdna/tail-file" "^2.0.6"
94 | "@pulumi/query" "^0.3.0"
95 | google-protobuf "^3.5.0"
96 | ini "^2.0.0"
97 | js-yaml "^3.14.0"
98 | minimist "^1.2.6"
99 | normalize-package-data "^2.4.0"
100 | protobufjs "^6.8.6"
101 | read-package-tree "^5.3.1"
102 | require-from-string "^2.0.1"
103 | semver "^6.1.0"
104 | source-map-support "^0.4.16"
105 | ts-node "^7.0.1"
106 | typescript "~3.7.3"
107 | upath "^1.1.0"
108 |
109 | "@pulumi/query@^0.3.0":
110 | version "0.3.0"
111 | resolved "https://registry.yarnpkg.com/@pulumi/query/-/query-0.3.0.tgz#f496608e86a18c3dd31b6c533408e2441c29071d"
112 | integrity sha512-xfo+yLRM2zVjVEA4p23IjQWzyWl1ZhWOGobsBqRpIarzLvwNH/RAGaoehdxlhx4X92302DrpdIFgTICMN4P38w==
113 |
114 | "@types/body-parser@*":
115 | version "1.19.2"
116 | resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0"
117 | integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==
118 | dependencies:
119 | "@types/connect" "*"
120 | "@types/node" "*"
121 |
122 | "@types/connect@*":
123 | version "3.4.35"
124 | resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
125 | integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==
126 | dependencies:
127 | "@types/node" "*"
128 |
129 | "@types/express-serve-static-core@^4.17.18":
130 | version "4.17.28"
131 | resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8"
132 | integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==
133 | dependencies:
134 | "@types/node" "*"
135 | "@types/qs" "*"
136 | "@types/range-parser" "*"
137 |
138 | "@types/express@^4.16.0":
139 | version "4.17.13"
140 | resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034"
141 | integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==
142 | dependencies:
143 | "@types/body-parser" "*"
144 | "@types/express-serve-static-core" "^4.17.18"
145 | "@types/qs" "*"
146 | "@types/serve-static" "*"
147 |
148 | "@types/long@^4.0.1":
149 | version "4.0.2"
150 | resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a"
151 | integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==
152 |
153 | "@types/mime@^1":
154 | version "1.3.2"
155 | resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
156 | integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
157 |
158 | "@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0":
159 | version "17.0.34"
160 | resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.34.tgz#3b0b6a50ff797280b8d000c6281d229f9c538cef"
161 | integrity sha512-XImEz7XwTvDBtzlTnm8YvMqGW/ErMWBsKZ+hMTvnDIjGCKxwK5Xpc+c/oQjOauwq8M4OS11hEkpjX8rrI/eEgA==
162 |
163 | "@types/node@^14":
164 | version "14.18.18"
165 | resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.18.tgz#5c9503030df484ccffcbb935ea9a9e1d6fad1a20"
166 | integrity sha512-B9EoJFjhqcQ9OmQrNorItO+OwEOORNn3S31WuiHvZY/dm9ajkB7AKD/8toessEtHHNL+58jofbq7hMMY9v4yig==
167 |
168 | "@types/qs@*":
169 | version "6.9.7"
170 | resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
171 | integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
172 |
173 | "@types/range-parser@*":
174 | version "1.2.4"
175 | resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
176 | integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
177 |
178 | "@types/serve-static@*":
179 | version "1.13.10"
180 | resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9"
181 | integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==
182 | dependencies:
183 | "@types/mime" "^1"
184 | "@types/node" "*"
185 |
186 | argparse@^1.0.7:
187 | version "1.0.10"
188 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
189 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
190 | dependencies:
191 | sprintf-js "~1.0.2"
192 |
193 | arrify@^1.0.0:
194 | version "1.0.1"
195 | resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
196 | integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==
197 |
198 | asap@^2.0.0:
199 | version "2.0.6"
200 | resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
201 | integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
202 |
203 | balanced-match@^1.0.0:
204 | version "1.0.2"
205 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
206 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
207 |
208 | brace-expansion@^1.1.7:
209 | version "1.1.11"
210 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
211 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
212 | dependencies:
213 | balanced-match "^1.0.0"
214 | concat-map "0.0.1"
215 |
216 | buffer-from@^1.0.0, buffer-from@^1.1.0:
217 | version "1.1.2"
218 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
219 | integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
220 |
221 | call-bind@^1.0.0, call-bind@^1.0.2:
222 | version "1.0.2"
223 | resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
224 | integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
225 | dependencies:
226 | function-bind "^1.1.1"
227 | get-intrinsic "^1.0.2"
228 |
229 | concat-map@0.0.1:
230 | version "0.0.1"
231 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
232 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
233 |
234 | debuglog@^1.0.1:
235 | version "1.0.1"
236 | resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
237 | integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
238 |
239 | define-properties@^1.1.3, define-properties@^1.1.4:
240 | version "1.1.4"
241 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1"
242 | integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==
243 | dependencies:
244 | has-property-descriptors "^1.0.0"
245 | object-keys "^1.1.1"
246 |
247 | dezalgo@^1.0.0:
248 | version "1.0.4"
249 | resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81"
250 | integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==
251 | dependencies:
252 | asap "^2.0.0"
253 | wrappy "1"
254 |
255 | diff@^3.1.0:
256 | version "3.5.0"
257 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
258 | integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
259 |
260 | es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.5:
261 | version "1.20.1"
262 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814"
263 | integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==
264 | dependencies:
265 | call-bind "^1.0.2"
266 | es-to-primitive "^1.2.1"
267 | function-bind "^1.1.1"
268 | function.prototype.name "^1.1.5"
269 | get-intrinsic "^1.1.1"
270 | get-symbol-description "^1.0.0"
271 | has "^1.0.3"
272 | has-property-descriptors "^1.0.0"
273 | has-symbols "^1.0.3"
274 | internal-slot "^1.0.3"
275 | is-callable "^1.2.4"
276 | is-negative-zero "^2.0.2"
277 | is-regex "^1.1.4"
278 | is-shared-array-buffer "^1.0.2"
279 | is-string "^1.0.7"
280 | is-weakref "^1.0.2"
281 | object-inspect "^1.12.0"
282 | object-keys "^1.1.1"
283 | object.assign "^4.1.2"
284 | regexp.prototype.flags "^1.4.3"
285 | string.prototype.trimend "^1.0.5"
286 | string.prototype.trimstart "^1.0.5"
287 | unbox-primitive "^1.0.2"
288 |
289 | es-to-primitive@^1.2.1:
290 | version "1.2.1"
291 | resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
292 | integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
293 | dependencies:
294 | is-callable "^1.1.4"
295 | is-date-object "^1.0.1"
296 | is-symbol "^1.0.2"
297 |
298 | esprima@^4.0.0:
299 | version "4.0.1"
300 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
301 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
302 |
303 | fs.realpath@^1.0.0:
304 | version "1.0.0"
305 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
306 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
307 |
308 | function-bind@^1.1.1:
309 | version "1.1.1"
310 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
311 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
312 |
313 | function.prototype.name@^1.1.5:
314 | version "1.1.5"
315 | resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621"
316 | integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==
317 | dependencies:
318 | call-bind "^1.0.2"
319 | define-properties "^1.1.3"
320 | es-abstract "^1.19.0"
321 | functions-have-names "^1.2.2"
322 |
323 | functions-have-names@^1.2.2:
324 | version "1.2.3"
325 | resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
326 | integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
327 |
328 | get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
329 | version "1.1.1"
330 | resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
331 | integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
332 | dependencies:
333 | function-bind "^1.1.1"
334 | has "^1.0.3"
335 | has-symbols "^1.0.1"
336 |
337 | get-symbol-description@^1.0.0:
338 | version "1.0.0"
339 | resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6"
340 | integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==
341 | dependencies:
342 | call-bind "^1.0.2"
343 | get-intrinsic "^1.1.1"
344 |
345 | glob@^7.1.1:
346 | version "7.2.3"
347 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
348 | integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
349 | dependencies:
350 | fs.realpath "^1.0.0"
351 | inflight "^1.0.4"
352 | inherits "2"
353 | minimatch "^3.1.1"
354 | once "^1.3.0"
355 | path-is-absolute "^1.0.0"
356 |
357 | google-protobuf@^3.5.0:
358 | version "3.20.1"
359 | resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.20.1.tgz#1b255c2b59bcda7c399df46c65206aa3c7a0ce8b"
360 | integrity sha512-XMf1+O32FjYIV3CYu6Tuh5PNbfNEU5Xu22X+Xkdb/DUexFlCzhvv7d5Iirm4AOwn8lv4al1YvIhzGrg2j9Zfzw==
361 |
362 | graceful-fs@^4.1.2:
363 | version "4.2.10"
364 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
365 | integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
366 |
367 | has-bigints@^1.0.1, has-bigints@^1.0.2:
368 | version "1.0.2"
369 | resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
370 | integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
371 |
372 | has-property-descriptors@^1.0.0:
373 | version "1.0.0"
374 | resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861"
375 | integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==
376 | dependencies:
377 | get-intrinsic "^1.1.1"
378 |
379 | has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3:
380 | version "1.0.3"
381 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
382 | integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
383 |
384 | has-tostringtag@^1.0.0:
385 | version "1.0.0"
386 | resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25"
387 | integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==
388 | dependencies:
389 | has-symbols "^1.0.2"
390 |
391 | has@^1.0.3:
392 | version "1.0.3"
393 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
394 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
395 | dependencies:
396 | function-bind "^1.1.1"
397 |
398 | hosted-git-info@^2.1.4:
399 | version "2.8.9"
400 | resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
401 | integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
402 |
403 | inflight@^1.0.4:
404 | version "1.0.6"
405 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
406 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
407 | dependencies:
408 | once "^1.3.0"
409 | wrappy "1"
410 |
411 | inherits@2:
412 | version "2.0.4"
413 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
414 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
415 |
416 | ini@^2.0.0:
417 | version "2.0.0"
418 | resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
419 | integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
420 |
421 | internal-slot@^1.0.3:
422 | version "1.0.3"
423 | resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"
424 | integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==
425 | dependencies:
426 | get-intrinsic "^1.1.0"
427 | has "^1.0.3"
428 | side-channel "^1.0.4"
429 |
430 | is-bigint@^1.0.1:
431 | version "1.0.4"
432 | resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
433 | integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==
434 | dependencies:
435 | has-bigints "^1.0.1"
436 |
437 | is-boolean-object@^1.1.0:
438 | version "1.1.2"
439 | resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
440 | integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==
441 | dependencies:
442 | call-bind "^1.0.2"
443 | has-tostringtag "^1.0.0"
444 |
445 | is-callable@^1.1.4, is-callable@^1.2.4:
446 | version "1.2.4"
447 | resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945"
448 | integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==
449 |
450 | is-core-module@^2.8.1:
451 | version "2.9.0"
452 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"
453 | integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==
454 | dependencies:
455 | has "^1.0.3"
456 |
457 | is-date-object@^1.0.1:
458 | version "1.0.5"
459 | resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f"
460 | integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==
461 | dependencies:
462 | has-tostringtag "^1.0.0"
463 |
464 | is-negative-zero@^2.0.2:
465 | version "2.0.2"
466 | resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
467 | integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==
468 |
469 | is-number-object@^1.0.4:
470 | version "1.0.7"
471 | resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc"
472 | integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==
473 | dependencies:
474 | has-tostringtag "^1.0.0"
475 |
476 | is-regex@^1.1.4:
477 | version "1.1.4"
478 | resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
479 | integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
480 | dependencies:
481 | call-bind "^1.0.2"
482 | has-tostringtag "^1.0.0"
483 |
484 | is-shared-array-buffer@^1.0.2:
485 | version "1.0.2"
486 | resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79"
487 | integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==
488 | dependencies:
489 | call-bind "^1.0.2"
490 |
491 | is-string@^1.0.5, is-string@^1.0.7:
492 | version "1.0.7"
493 | resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
494 | integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==
495 | dependencies:
496 | has-tostringtag "^1.0.0"
497 |
498 | is-symbol@^1.0.2, is-symbol@^1.0.3:
499 | version "1.0.4"
500 | resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
501 | integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==
502 | dependencies:
503 | has-symbols "^1.0.2"
504 |
505 | is-weakref@^1.0.2:
506 | version "1.0.2"
507 | resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
508 | integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==
509 | dependencies:
510 | call-bind "^1.0.2"
511 |
512 | js-yaml@^3.14.0:
513 | version "3.14.1"
514 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
515 | integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
516 | dependencies:
517 | argparse "^1.0.7"
518 | esprima "^4.0.0"
519 |
520 | json-parse-even-better-errors@^2.3.0:
521 | version "2.3.1"
522 | resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
523 | integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
524 |
525 | long@^4.0.0:
526 | version "4.0.0"
527 | resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
528 | integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==
529 |
530 | make-error@^1.1.1:
531 | version "1.3.6"
532 | resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
533 | integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
534 |
535 | minimatch@^3.1.1:
536 | version "3.1.2"
537 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
538 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
539 | dependencies:
540 | brace-expansion "^1.1.7"
541 |
542 | minimist@^1.2.0, minimist@^1.2.6:
543 | version "1.2.6"
544 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
545 | integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
546 |
547 | mkdirp@^0.5.1:
548 | version "0.5.6"
549 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
550 | integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
551 | dependencies:
552 | minimist "^1.2.6"
553 |
554 | normalize-package-data@^2.0.0, normalize-package-data@^2.4.0:
555 | version "2.5.0"
556 | resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
557 | integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
558 | dependencies:
559 | hosted-git-info "^2.1.4"
560 | resolve "^1.10.0"
561 | semver "2 || 3 || 4 || 5"
562 | validate-npm-package-license "^3.0.1"
563 |
564 | npm-normalize-package-bin@^1.0.0:
565 | version "1.0.1"
566 | resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2"
567 | integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==
568 |
569 | object-inspect@^1.12.0, object-inspect@^1.9.0:
570 | version "1.12.0"
571 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0"
572 | integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==
573 |
574 | object-keys@^1.1.1:
575 | version "1.1.1"
576 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
577 | integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
578 |
579 | object.assign@^4.1.2:
580 | version "4.1.2"
581 | resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
582 | integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
583 | dependencies:
584 | call-bind "^1.0.0"
585 | define-properties "^1.1.3"
586 | has-symbols "^1.0.1"
587 | object-keys "^1.1.1"
588 |
589 | object.getownpropertydescriptors@^2.0.3:
590 | version "2.1.3"
591 | resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e"
592 | integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw==
593 | dependencies:
594 | call-bind "^1.0.2"
595 | define-properties "^1.1.3"
596 | es-abstract "^1.19.1"
597 |
598 | once@^1.3.0:
599 | version "1.4.0"
600 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
601 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
602 | dependencies:
603 | wrappy "1"
604 |
605 | path-is-absolute@^1.0.0:
606 | version "1.0.1"
607 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
608 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
609 |
610 | path-parse@^1.0.7:
611 | version "1.0.7"
612 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
613 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
614 |
615 | protobufjs@^6.8.6:
616 | version "6.11.2"
617 | resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b"
618 | integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==
619 | dependencies:
620 | "@protobufjs/aspromise" "^1.1.2"
621 | "@protobufjs/base64" "^1.1.2"
622 | "@protobufjs/codegen" "^2.0.4"
623 | "@protobufjs/eventemitter" "^1.1.0"
624 | "@protobufjs/fetch" "^1.1.0"
625 | "@protobufjs/float" "^1.0.2"
626 | "@protobufjs/inquire" "^1.1.0"
627 | "@protobufjs/path" "^1.1.2"
628 | "@protobufjs/pool" "^1.1.0"
629 | "@protobufjs/utf8" "^1.1.0"
630 | "@types/long" "^4.0.1"
631 | "@types/node" ">=13.7.0"
632 | long "^4.0.0"
633 |
634 | read-package-json@^2.0.0, read-package-json@^2.0.13:
635 | version "2.1.2"
636 | resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a"
637 | integrity sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==
638 | dependencies:
639 | glob "^7.1.1"
640 | json-parse-even-better-errors "^2.3.0"
641 | normalize-package-data "^2.0.0"
642 | npm-normalize-package-bin "^1.0.0"
643 |
644 | read-package-tree@^5.3.1:
645 | version "5.3.1"
646 | resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636"
647 | integrity sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==
648 | dependencies:
649 | read-package-json "^2.0.0"
650 | readdir-scoped-modules "^1.0.0"
651 | util-promisify "^2.1.0"
652 |
653 | readdir-scoped-modules@^1.0.0:
654 | version "1.1.0"
655 | resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309"
656 | integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==
657 | dependencies:
658 | debuglog "^1.0.1"
659 | dezalgo "^1.0.0"
660 | graceful-fs "^4.1.2"
661 | once "^1.3.0"
662 |
663 | regexp.prototype.flags@^1.4.3:
664 | version "1.4.3"
665 | resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac"
666 | integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==
667 | dependencies:
668 | call-bind "^1.0.2"
669 | define-properties "^1.1.3"
670 | functions-have-names "^1.2.2"
671 |
672 | require-from-string@^2.0.1:
673 | version "2.0.2"
674 | resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
675 | integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
676 |
677 | resolve@^1.10.0:
678 | version "1.22.0"
679 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
680 | integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
681 | dependencies:
682 | is-core-module "^2.8.1"
683 | path-parse "^1.0.7"
684 | supports-preserve-symlinks-flag "^1.0.0"
685 |
686 | "semver@2 || 3 || 4 || 5", semver@^5.4.0:
687 | version "5.7.1"
688 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
689 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
690 |
691 | semver@^6.1.0:
692 | version "6.3.0"
693 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
694 | integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
695 |
696 | side-channel@^1.0.4:
697 | version "1.0.4"
698 | resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
699 | integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
700 | dependencies:
701 | call-bind "^1.0.0"
702 | get-intrinsic "^1.0.2"
703 | object-inspect "^1.9.0"
704 |
705 | source-map-support@^0.4.16:
706 | version "0.4.18"
707 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
708 | integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==
709 | dependencies:
710 | source-map "^0.5.6"
711 |
712 | source-map-support@^0.5.6:
713 | version "0.5.21"
714 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
715 | integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
716 | dependencies:
717 | buffer-from "^1.0.0"
718 | source-map "^0.6.0"
719 |
720 | source-map@^0.5.6:
721 | version "0.5.7"
722 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
723 | integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
724 |
725 | source-map@^0.6.0:
726 | version "0.6.1"
727 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
728 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
729 |
730 | spdx-correct@^3.0.0:
731 | version "3.1.1"
732 | resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
733 | integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==
734 | dependencies:
735 | spdx-expression-parse "^3.0.0"
736 | spdx-license-ids "^3.0.0"
737 |
738 | spdx-exceptions@^2.1.0:
739 | version "2.3.0"
740 | resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d"
741 | integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==
742 |
743 | spdx-expression-parse@^3.0.0:
744 | version "3.0.1"
745 | resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
746 | integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==
747 | dependencies:
748 | spdx-exceptions "^2.1.0"
749 | spdx-license-ids "^3.0.0"
750 |
751 | spdx-license-ids@^3.0.0:
752 | version "3.0.11"
753 | resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95"
754 | integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==
755 |
756 | sprintf-js@~1.0.2:
757 | version "1.0.3"
758 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
759 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
760 |
761 | string.prototype.trimend@^1.0.5:
762 | version "1.0.5"
763 | resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0"
764 | integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==
765 | dependencies:
766 | call-bind "^1.0.2"
767 | define-properties "^1.1.4"
768 | es-abstract "^1.19.5"
769 |
770 | string.prototype.trimstart@^1.0.5:
771 | version "1.0.5"
772 | resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef"
773 | integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==
774 | dependencies:
775 | call-bind "^1.0.2"
776 | define-properties "^1.1.4"
777 | es-abstract "^1.19.5"
778 |
779 | supports-preserve-symlinks-flag@^1.0.0:
780 | version "1.0.0"
781 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
782 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
783 |
784 | ts-node@^7.0.1:
785 | version "7.0.1"
786 | resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf"
787 | integrity sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==
788 | dependencies:
789 | arrify "^1.0.0"
790 | buffer-from "^1.1.0"
791 | diff "^3.1.0"
792 | make-error "^1.1.1"
793 | minimist "^1.2.0"
794 | mkdirp "^0.5.1"
795 | source-map-support "^0.5.6"
796 | yn "^2.0.0"
797 |
798 | typescript@~3.7.3:
799 | version "3.7.7"
800 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.7.tgz#c931733e2ec10dda56b855b379cc488a72a81199"
801 | integrity sha512-MmQdgo/XenfZPvVLtKZOq9jQQvzaUAUpcKW8Z43x9B2fOm4S5g//tPtMweZUIP+SoBqrVPEIm+dJeQ9dfO0QdA==
802 |
803 | unbox-primitive@^1.0.2:
804 | version "1.0.2"
805 | resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
806 | integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==
807 | dependencies:
808 | call-bind "^1.0.2"
809 | has-bigints "^1.0.2"
810 | has-symbols "^1.0.3"
811 | which-boxed-primitive "^1.0.2"
812 |
813 | upath@^1.1.0:
814 | version "1.2.0"
815 | resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
816 | integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
817 |
818 | util-promisify@^2.1.0:
819 | version "2.1.0"
820 | resolved "https://registry.yarnpkg.com/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53"
821 | integrity sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=
822 | dependencies:
823 | object.getownpropertydescriptors "^2.0.3"
824 |
825 | validate-npm-package-license@^3.0.1:
826 | version "3.0.4"
827 | resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
828 | integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
829 | dependencies:
830 | spdx-correct "^3.0.0"
831 | spdx-expression-parse "^3.0.0"
832 |
833 | which-boxed-primitive@^1.0.2:
834 | version "1.0.2"
835 | resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
836 | integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==
837 | dependencies:
838 | is-bigint "^1.0.1"
839 | is-boolean-object "^1.1.0"
840 | is-number-object "^1.0.4"
841 | is-string "^1.0.5"
842 | is-symbol "^1.0.3"
843 |
844 | wrappy@1:
845 | version "1.0.2"
846 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
847 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
848 |
849 | yn@^2.0.0:
850 | version "2.0.0"
851 | resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a"
852 | integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=
853 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.7'
2 |
3 | services:
4 |
5 | esdb:
6 | container_name: esdemo-esdb
7 | image: ghcr.io/eventstore/eventstore:21.10.0-alpha-arm64v8
8 | # image: eventstore/eventstore:latest #20.10.2-buster-slim
9 | ports:
10 | - '2113:2113'
11 | - '1113:1113'
12 | environment:
13 | EVENTSTORE_INSECURE: 'true'
14 | EVENTSTORE_CLUSTER_SIZE: 1
15 | EVENTSTORE_EXT_TCP_PORT: 1113
16 | EVENTSTORE_HTTP_PORT: 2113
17 | EVENTSTORE_ENABLE_EXTERNAL_TCP: 'true'
18 | EVENTSTORE_RUN_PROJECTIONS: all
19 | EVENTSTORE_START_STANDARD_PROJECTIONS: "true"
20 | EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP: "true"
21 |
22 | mongo:
23 | container_name: esdemo-mongo
24 | image: mongo
25 | ports:
26 | - '27017:27017'
27 | environment:
28 | MONGO_INITDB_ROOT_USERNAME: mongoadmin
29 | MONGO_INITDB_ROOT_PASSWORD: secret
30 |
31 | zipkin:
32 | image: openzipkin/zipkin
33 | container_name: esdemo-zipkin
34 | ports:
35 | - "9411:9411"
36 |
37 | prometheus:
38 | container_name: esdemo-prometheus
39 | image: prom/prometheus:v2.17.1
40 | ports:
41 | - "9090:9090"
42 | volumes:
43 | - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
44 |
45 | grafana:
46 | container_name: esdemo-grafana
47 | image: grafana/grafana:6.7.2
48 | ports:
49 | - "3000:3000"
50 | volumes:
51 | - ./grafana/datasources.yml:/etc/grafana/provisioning/datasources/prometheus.yaml
52 | - ./grafana/dashboards:/dashboards
53 |
54 | seq:
55 | image: datalust/seq:latest
56 | container_name: esdemo-seq
57 | environment:
58 | - ACCEPT_EULA=Y
59 | ports:
60 | - "5341:80"
61 |
62 | networks:
63 | default:
64 | name: eventuous-demo
65 |
--------------------------------------------------------------------------------
/grafana/__inputs.json:
--------------------------------------------------------------------------------
1 | {
2 | "__inputs": [
3 | {
4 | "name": "DS_PROMETHEUS",
5 | "label": "prometheus",
6 | "description": "Default data source",
7 | "type": "datasource",
8 | "pluginId": "prometheus",
9 | "pluginName": "Prometheus"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/grafana/datasources.yml:
--------------------------------------------------------------------------------
1 | apiVersion: 1
2 |
3 | datasources:
4 | - name: prometheus
5 | type: prometheus
6 | access: proxy
7 | orgId: 1
8 | url: http://prometheus:9090
9 | isDefault: true
10 | version: 1
11 | editable: false
--------------------------------------------------------------------------------
/prometheus/prometheus.yml:
--------------------------------------------------------------------------------
1 | global:
2 | scrape_interval: 5s
3 |
4 | scrape_configs:
5 | - job_name: 'prometheus'
6 | static_configs:
7 | - targets: ['localhost:9090']
8 | - job_name: 'bookings'
9 | static_configs:
10 | - targets:
11 | - 'host.docker.internal:5000'
--------------------------------------------------------------------------------