SlideShare ist ein Scribd-Unternehmen logo
1 von 47
Building a friendly .NET SDK to
connect to Space
Maarten Balliauw
@maartenballiauw
Agenda
JetBrains Space
JetBrains Space HTTP API
Building a .NET SDK for the Space HTTP API
Code generation
Developer experience
JetBrains Space
JetBrains Space
Integrated Team Environment
https://jetbrains.space
People
Teams
Projects
-
Workspaces
for teams
Collaboration
JetBrains Space
demo
The Space HTTP API
Integrated Team Environment
More information in Space == more value from Space
Internal integrations out of the box
Vacation shown in profile
Blog post targeted to team/location
Calendar integration in blog post
Everything can be a ToDo item
...
What with external integrations? Extensibility?
Everything as an HTTP API!
What do you want to build? Which integration do you need?
Import data
Post to chat
Manage users, teams, and locations programatically
Build dashboards
No good answer, so opening up everything
Large HTTP API surface...
HTTP API Playground to explore!
HTTP API Playground
demo
$fields
Top-level fields returned by default
Unless $fields query parameter used
Use $fields to be specific, or get nested fields
$fields=id,icon,name
$fields=id,name,boards(id,description)
$fields=id,name,boards(id,info(columns))
$fields=*,boards(id,description)
Building a .NET SDK
for the Space HTTP API
Hello, .NET! 👋
Goal: make our users succesful at using Space in their team/organization
Provide a ready-to-use SDK
Minimal configuration needed
Discoverability
TL;DR: Lead integration developers on the shortest path to success
How? Handcrafted
No.
Over 150 API endpoints
Over 700 object types
Space EAP / Space Beta means churn!
How? OpenAPI?
Use NSwag, generate based on OpenAPI / Swagger document
Some incompatible object types needed custom code generation
How? OpenAPI?
Not very developer friendly...
public class Body
{
public string Member { get; set; }
public string Reason { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public string Since { get; set; }
public string Till { get; set; }
public bool Available { get; set; } = false;
public string Icon { get; set; }
}
public async Task<AbsenceRecord> GetAbsencesAsync(
string fields, Body body, CancellationToken cancellationToken)
{
}
How? Custom code generation
Space exposes HTTP API Model
Enumerations
DTOs (objects passed around)
Resources
+ data types, nullability,
default values, ...
https://your.jetbrains.space/api/http/http-api-model?$fields=dto,enums,urlParams,resources(*,nestedResources!)
Enumeration model
public class ApiEnum
{
public string Id { get; set; }
public ApiDeprecation? Deprecation { get; set; }
public string Name { get; set; }
public List<string> Values { get; set; }
}
public class ApiDeprecation
{
public string? Message { get; set; }
public string? Since { get; set; }
public bool ForRemoval { get; set; }
}
Enumeration code generator
public class EnumerationVisitor
{
public string Visit(ApiEnum apiEnum)
{
var builder = new StringBuilder();
if (apiEnum.Deprecation != null)
builder.AppendLine(Visit(apiEnum.Deprecation));
builder.AppendLine("public enum " + apiEnum.Name);
builder.AppendLine("{");
foreach (var apiEnumValue in apiEnum.Values)
{
builder.AppendLine($" {apiEnumValue},");
}
builder.AppendLine("}");
return builder.ToString();
}
public string Visit(ApiDeprecation apiDeprecation)
=> $"[Obsolete("{apiDeprecation.Message}")]";
}
public enum AbsenceListMode
{
All,
WithAccessibleReasonUnapproved,
WithAccessibleReasonAll,
}
DTO model
public class ApiDto
{
public string Id { get; set; }
public ApiDeprecation? Deprecation { get; set; }
public string Name { get; set; } = default!;
public List<ApiDtoField> Fields { get; set; }
public Reference<ApiDto>? Extends { get; set; }
public List<Reference<ApiDto>> Implements { get; set; }
public List<Reference<ApiDto>> Inheritors { get; set; }
public HierarchyRole HierarchyRole { get; set; }
public bool Record { get; set; }
}
public class ApiField
{
public string Name { get; set; }
public ApiFieldType Type { get; set; }
public bool Optional { get; set; }
public ApiDefaultValue? DefaultValue { get; set; }
}
Endpoints model
Endpoint id, name, base path
Resource id, name, path, parameters
Resource id, name, path, parameters
Resource id, name, path, parameters
...
+ parameter types
+ return types
+ default values (const, variable)
Custom code generation
demo
Roslyn/StringBuilder/...?
Generally, a few ways to generate code:
IL-based
IL Generation (Reflection.Emit)
Model-based
CodeDOM
Expression trees
Roslyn syntax generation
Template-based (T4, Razor, ...)
Strings all the way
https://amzn.to/2EWsA44
Strings all the way!
See string, recognize the output easily
IL-based – Too complex
Model-based – Less readable (very verbose)
Template-based – Less readable (many small files to include)
builder.AppendLine(
$"public {subject.Type.ToCSharpType()} {subject.ToCSharpPropertyName()} {{ get; set; }}");
var propertyDeclaration = SyntaxFactory.PropertyDeclaration(
SyntaxFactory.ParseTypeName(subject.Type.ToCSharpType()), subject.ToCSharpPropertyName())
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
.AddAccessorListAccessors(
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)),
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)));
Extension methods everywhere
Many places where we need:
C# class name
C# variable name
C# type from Space model type
...
public static class ApiDocumentationExtensions
{
public static string ToCSharpDocumentationComment(
this string subject)
{
var builder = new StringBuilder();
builder.AppendLine("/// <summary>");
builder.AppendLine("/// " + subject);
builder.AppendLine("/// </summary>");
return builder.ToString();
}
}
public static class ApiDtoExtensions
{
public static string ToCSharpClassName(this ApiDto subject)
=> CSharpIdentifier.ForClassOrNamespace(subject.Name);
}
System.Text.Json
“System.Text.Json is a built-in (de)serializer for .NET Core 3 and beyond.”
Developer experience!
No extra dependencies needed
No dependency on a Newtonsoft.Json that you are not using
System.Text.Json
Not a 100% replacement for Newtonsoft.Json...
https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-
from-newtonsoft-how-to
System.Text.Json is extensible enough
Custom JsonConverter<T>
JsonConverter<T>
demo
https://blog.maartenballiauw.be/post/2020/01/29/deserializing-
json-into-polymorphic-classes-with-systemtextjson.html
Code generation & developer
experience
The ideal “Getting started”
1. Register application in Space
2. Install SpaceDotNet.Client package
dotnet add package SpaceDotNet.Client
3. Create connection instance
var connection = new ClientCredentialsConnection(
"https://your.jetbrains.space/",
"client-id",
"client-secret",
new HttpClient());
4. Profit!
Large API surface
var teamDirectoryClient = new TeamDirectoryClient(connection);
var birthdays = await teamDirectoryClient
.CalendarEvents
.BirthdayEvents
.GetAllBirthdayEventsAsync(...);
Remember $fields ?
Top-level fields returned by default
Unless $fields query parameter used
Use $fields to be specific, or get nested fields
$fields
Top-level fields returned by default
Use partial to be specific, or get nested fields
var memberProfile = await teamDirectoryClient.Profiles
.GetProfileAsync(ProfileIdentifier.Username("Heather.Stewart"));
var memberProfile = await teamDirectoryClient.Profiles
.GetProfileAsync(ProfileIdentifier.Username("Heather.Stewart"), _ => _
.WithAllFieldsWildcard() // with all top level fields
.WithManagers(managers => managers // include managers
.WithId() // with their Id
.WithUsername() // and their Username
.WithName(name => name // and their Name
.WithFirstName() // with FirstName
.WithLastName()))); // and LastName
Developer experience
demo
Batches (e.g. Get all ToDo items)
public class Batch<T>
{
List<T>? Data { get; }
string? Next { get; }
int? TotalCount { get; }
bool HasNext();
}
var batch = await _todoClient.GetAllToDoItemsAsync(
from: weekStart.AsSpaceDate(),
partial: _ => _
.WithData(data => data
// ...
.WithTotalCount()
.WithNext());
do
{
foreach (var todo in batch.Data)
{
// ...
}
batch = await _todoClient.GetAllToDoItemsAsync(
from: weekStart.AsSpaceDate(),
skip: batch.Next,
partial: _ => _ /* ... */);
}
while (batch.HasNext());
await foreach (var todo in _todoClient
.GetAllToDoItemsAsyncEnumerable(from: weekStart.AsSpaceDate(), partial: _ => _
.WithId()
.WithContent(content => content
.ForInherited<TodoItemContentMdText>(md => md
.WithAllFieldsWildcard()))
.WithStatus()))
{
// ...
}
IAsyncEnumerable to the rescue!
⚠ Don’t use it for .Count()
var batch = await _todoClient.GetAllToDoItemsAsync(
from: weekStart.AsSpaceDate(), partial: _ => _.WithTotalCount());
var numberOfResults = batch.TotalCount;
Batches and
IAsyncEnumerable
demo
Request bodies are flattened
GetAbsences(AbsencesRequest request)
GetAbsences(string member, string reason, ...)
Factory methods for inheritors
Which option would you prefer?
await chatClient.Messages.SendMessageAsync(
recipient: new MessageRecipientChannel()
{
Channel = new ChatChannelFromName()
{
Name = chatChannelName
}
},
await chatClient.Messages.SendMessageAsync(
recipient: MessageRecipient.Channel(ChatChannel.FromName(chatChannelName)),
Default values
Unfortunatey not possible in C#...
Workaround:
Unfortunately not obvious what the default is when not specified.
Should we add XMLdoc instead?
public async IAsyncEnumerable<AbsenceRecord> GetAllAbsencesAsyncEnumerable(
AbsenceListMode viewMode = AbsenceListMode.All, string? member = null)
public IAsyncEnumerable<AbsenceRecord> GetAllAbsencesAsyncEnumerable(
AbsenceListMode? viewMode = null, string? member = null)
=> BatchEnumerator.AllItems(
() => GetAllAbsencesAsync(viewMode: viewMode ?? AbsenceListMode.All, member: member);
Code generation
demo
Developer experience (extras)
Use SpaceDotNet in web apps
public void ConfigureServices(IServiceCollection services)
{
// ...
// Space authentication
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = SpaceDefaults.AuthenticationScheme;
})
.AddCookie()
.AddSpace(options => Configuration.Bind("Space", options))
.AddSpaceTokenManagement(); // auto-refreshes tokens in the background and provides a Space connection
// Space client API
services.AddSpaceClientApi();
// Space webhook receiver
services.AddSpaceWebHookHandler<CateringWebHookHandler>(options => Configuration.Bind("Space", options));
Use SpaceDotNet in web apps
.AddSpace()
Use Space for authN
.AddSpaceClientApi()
Register ...Client with service collection
.AddSpaceTokenManagement() (experimental)
Auto-refreshes tokens in the background
Space client always auth’ed as currently logged in user
.AddSpaceWebHookHandler<T>() (experimental)
Webhook support
Use SpaceDotNet
in web apps
demo
Conclusion
Conclusion
JetBrains Space is an Integrated Team Environment
JetBrains Space HTTP API exposes everything
Building a .NET SDK for the Space HTTP API
Goal: make our users succesful at using Space
Provide a ready-to-use SDK
Minimal configuration needed
Discoverability
Code generation
Focus on developer experience
Thank you!
https://blog.maartenballiauw.be
@maartenballiauw

Weitere ähnliche Inhalte

Was ist angesagt?

Building a Data Pipeline from Scratch - Joe Crobak
Building a Data Pipeline from Scratch - Joe CrobakBuilding a Data Pipeline from Scratch - Joe Crobak
Building a Data Pipeline from Scratch - Joe CrobakHakka Labs
 
Apache Zeppelin Meetup Christian Tzolov 1/21/16
Apache Zeppelin Meetup Christian Tzolov 1/21/16 Apache Zeppelin Meetup Christian Tzolov 1/21/16
Apache Zeppelin Meetup Christian Tzolov 1/21/16 PivotalOpenSourceHub
 
Building Data Pipelines in Python
Building Data Pipelines in PythonBuilding Data Pipelines in Python
Building Data Pipelines in PythonC4Media
 
Building cloud-enabled genomics workflows with Luigi and Docker
Building cloud-enabled genomics workflows with Luigi and DockerBuilding cloud-enabled genomics workflows with Luigi and Docker
Building cloud-enabled genomics workflows with Luigi and DockerJacob Feala
 
Building a Large Scale SEO/SEM Application with Apache Solr
Building a Large Scale SEO/SEM Application with Apache SolrBuilding a Large Scale SEO/SEM Application with Apache Solr
Building a Large Scale SEO/SEM Application with Apache SolrRahul Jain
 
04 integrate entityframework
04 integrate entityframework04 integrate entityframework
04 integrate entityframeworkErhwen Kuo
 
Speed up Interactive Analytic Queries over Existing Big Data on Hadoop with P...
Speed up Interactive Analytic Queries over Existing Big Data on Hadoop with P...Speed up Interactive Analytic Queries over Existing Big Data on Hadoop with P...
Speed up Interactive Analytic Queries over Existing Big Data on Hadoop with P...viirya
 
Building a unified data pipeline in Apache Spark
Building a unified data pipeline in Apache SparkBuilding a unified data pipeline in Apache Spark
Building a unified data pipeline in Apache SparkDataWorks Summit
 
03 integrate webapisignalr
03 integrate webapisignalr03 integrate webapisignalr
03 integrate webapisignalrErhwen Kuo
 
Getting started with Apollo Client and GraphQL
Getting started with Apollo Client and GraphQLGetting started with Apollo Client and GraphQL
Getting started with Apollo Client and GraphQLMorgan Dedmon
 
Presto: Distributed SQL on Anything - Strata Hadoop 2017 San Jose, CA
Presto: Distributed SQL on Anything -  Strata Hadoop 2017 San Jose, CAPresto: Distributed SQL on Anything -  Strata Hadoop 2017 San Jose, CA
Presto: Distributed SQL on Anything - Strata Hadoop 2017 San Jose, CAkbajda
 
Opaque: A Data Analytics Platform with Strong Security: Spark Summit East tal...
Opaque: A Data Analytics Platform with Strong Security: Spark Summit East tal...Opaque: A Data Analytics Platform with Strong Security: Spark Summit East tal...
Opaque: A Data Analytics Platform with Strong Security: Spark Summit East tal...Spark Summit
 
Presto meetup 2015-03-19 @Facebook
Presto meetup 2015-03-19 @FacebookPresto meetup 2015-03-19 @Facebook
Presto meetup 2015-03-19 @FacebookTreasure Data, Inc.
 
Metadata based statistics for DSpace
Metadata based statistics for DSpaceMetadata based statistics for DSpace
Metadata based statistics for DSpaceBram Luyten
 
Case study of Rujhaan.com (A social news app )
Case study of Rujhaan.com (A social news app )Case study of Rujhaan.com (A social news app )
Case study of Rujhaan.com (A social news app )Rahul Jain
 
NigthClazz Spark - Machine Learning / Introduction à Spark et Zeppelin
NigthClazz Spark - Machine Learning / Introduction à Spark et ZeppelinNigthClazz Spark - Machine Learning / Introduction à Spark et Zeppelin
NigthClazz Spark - Machine Learning / Introduction à Spark et ZeppelinZenika
 

Was ist angesagt? (20)

Building a Data Pipeline from Scratch - Joe Crobak
Building a Data Pipeline from Scratch - Joe CrobakBuilding a Data Pipeline from Scratch - Joe Crobak
Building a Data Pipeline from Scratch - Joe Crobak
 
Apache Zeppelin Meetup Christian Tzolov 1/21/16
Apache Zeppelin Meetup Christian Tzolov 1/21/16 Apache Zeppelin Meetup Christian Tzolov 1/21/16
Apache Zeppelin Meetup Christian Tzolov 1/21/16
 
Building Data Pipelines in Python
Building Data Pipelines in PythonBuilding Data Pipelines in Python
Building Data Pipelines in Python
 
Overview of the Hive Stinger Initiative
Overview of the Hive Stinger InitiativeOverview of the Hive Stinger Initiative
Overview of the Hive Stinger Initiative
 
Hazelcast
HazelcastHazelcast
Hazelcast
 
Building cloud-enabled genomics workflows with Luigi and Docker
Building cloud-enabled genomics workflows with Luigi and DockerBuilding cloud-enabled genomics workflows with Luigi and Docker
Building cloud-enabled genomics workflows with Luigi and Docker
 
Building a Large Scale SEO/SEM Application with Apache Solr
Building a Large Scale SEO/SEM Application with Apache SolrBuilding a Large Scale SEO/SEM Application with Apache Solr
Building a Large Scale SEO/SEM Application with Apache Solr
 
Internals of Presto Service
Internals of Presto ServiceInternals of Presto Service
Internals of Presto Service
 
04 integrate entityframework
04 integrate entityframework04 integrate entityframework
04 integrate entityframework
 
Speed up Interactive Analytic Queries over Existing Big Data on Hadoop with P...
Speed up Interactive Analytic Queries over Existing Big Data on Hadoop with P...Speed up Interactive Analytic Queries over Existing Big Data on Hadoop with P...
Speed up Interactive Analytic Queries over Existing Big Data on Hadoop with P...
 
Building a unified data pipeline in Apache Spark
Building a unified data pipeline in Apache SparkBuilding a unified data pipeline in Apache Spark
Building a unified data pipeline in Apache Spark
 
03 integrate webapisignalr
03 integrate webapisignalr03 integrate webapisignalr
03 integrate webapisignalr
 
Getting started with Apollo Client and GraphQL
Getting started with Apollo Client and GraphQLGetting started with Apollo Client and GraphQL
Getting started with Apollo Client and GraphQL
 
Presto: Distributed SQL on Anything - Strata Hadoop 2017 San Jose, CA
Presto: Distributed SQL on Anything -  Strata Hadoop 2017 San Jose, CAPresto: Distributed SQL on Anything -  Strata Hadoop 2017 San Jose, CA
Presto: Distributed SQL on Anything - Strata Hadoop 2017 San Jose, CA
 
Opaque: A Data Analytics Platform with Strong Security: Spark Summit East tal...
Opaque: A Data Analytics Platform with Strong Security: Spark Summit East tal...Opaque: A Data Analytics Platform with Strong Security: Spark Summit East tal...
Opaque: A Data Analytics Platform with Strong Security: Spark Summit East tal...
 
Presto meetup 2015-03-19 @Facebook
Presto meetup 2015-03-19 @FacebookPresto meetup 2015-03-19 @Facebook
Presto meetup 2015-03-19 @Facebook
 
Open source data ingestion
Open source data ingestionOpen source data ingestion
Open source data ingestion
 
Metadata based statistics for DSpace
Metadata based statistics for DSpaceMetadata based statistics for DSpace
Metadata based statistics for DSpace
 
Case study of Rujhaan.com (A social news app )
Case study of Rujhaan.com (A social news app )Case study of Rujhaan.com (A social news app )
Case study of Rujhaan.com (A social news app )
 
NigthClazz Spark - Machine Learning / Introduction à Spark et Zeppelin
NigthClazz Spark - Machine Learning / Introduction à Spark et ZeppelinNigthClazz Spark - Machine Learning / Introduction à Spark et Zeppelin
NigthClazz Spark - Machine Learning / Introduction à Spark et Zeppelin
 

Ähnlich wie Building a friendly .NET SDK to connect to Space

Architecture | Busy Java Developers Guide to NoSQL | Ted Neward
Architecture | Busy Java Developers Guide to NoSQL | Ted NewardArchitecture | Busy Java Developers Guide to NoSQL | Ted Neward
Architecture | Busy Java Developers Guide to NoSQL | Ted NewardJAX London
 
03 form-data
03 form-data03 form-data
03 form-datasnopteck
 
Introduction To Groovy 2005
Introduction To Groovy 2005Introduction To Groovy 2005
Introduction To Groovy 2005Tugdual Grall
 
Groovy Introduction - JAX Germany - 2008
Groovy Introduction - JAX Germany - 2008Groovy Introduction - JAX Germany - 2008
Groovy Introduction - JAX Germany - 2008Guillaume Laforge
 
Prompt engineering for iOS developers (How LLMs and GenAI work)
Prompt engineering for iOS developers (How LLMs and GenAI work)Prompt engineering for iOS developers (How LLMs and GenAI work)
Prompt engineering for iOS developers (How LLMs and GenAI work)Andrey Volobuev
 
Visual Studio 2010 and .NET 4.0 Overview
Visual Studio 2010 and .NET 4.0 OverviewVisual Studio 2010 and .NET 4.0 Overview
Visual Studio 2010 and .NET 4.0 Overviewbwullems
 
Micro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateMicro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateKiev ALT.NET
 
Data access 2.0? Please welcome: Spring Data!
Data access 2.0? Please welcome: Spring Data!Data access 2.0? Please welcome: Spring Data!
Data access 2.0? Please welcome: Spring Data!Oliver Gierke
 
Twitter Author Prediction from Tweets using Bayesian Network
Twitter Author Prediction from Tweets using Bayesian NetworkTwitter Author Prediction from Tweets using Bayesian Network
Twitter Author Prediction from Tweets using Bayesian NetworkHendy Irawan
 
Dart Power Tools
Dart Power ToolsDart Power Tools
Dart Power ToolsMatt Norris
 
Embedded Typesafe Domain Specific Languages for Java
Embedded Typesafe Domain Specific Languages for JavaEmbedded Typesafe Domain Specific Languages for Java
Embedded Typesafe Domain Specific Languages for JavaJevgeni Kabanov
 
All you need to know about JavaScript Functions
All you need to know about JavaScript FunctionsAll you need to know about JavaScript Functions
All you need to know about JavaScript FunctionsOluwaleke Fakorede
 
CouchDB on Android
CouchDB on AndroidCouchDB on Android
CouchDB on AndroidSven Haiges
 
Object Graph Mapping with Spring Data Neo4j 3 - Nicki Watt & Michael Hunger @...
Object Graph Mapping with Spring Data Neo4j 3 - Nicki Watt & Michael Hunger @...Object Graph Mapping with Spring Data Neo4j 3 - Nicki Watt & Michael Hunger @...
Object Graph Mapping with Spring Data Neo4j 3 - Nicki Watt & Michael Hunger @...Neo4j
 

Ähnlich wie Building a friendly .NET SDK to connect to Space (20)

Architecture | Busy Java Developers Guide to NoSQL | Ted Neward
Architecture | Busy Java Developers Guide to NoSQL | Ted NewardArchitecture | Busy Java Developers Guide to NoSQL | Ted Neward
Architecture | Busy Java Developers Guide to NoSQL | Ted Neward
 
03 form-data
03 form-data03 form-data
03 form-data
 
Introduction To Groovy 2005
Introduction To Groovy 2005Introduction To Groovy 2005
Introduction To Groovy 2005
 
Groovy Introduction - JAX Germany - 2008
Groovy Introduction - JAX Germany - 2008Groovy Introduction - JAX Germany - 2008
Groovy Introduction - JAX Germany - 2008
 
Clean Code
Clean CodeClean Code
Clean Code
 
68837.ppt
68837.ppt68837.ppt
68837.ppt
 
Prompt engineering for iOS developers (How LLMs and GenAI work)
Prompt engineering for iOS developers (How LLMs and GenAI work)Prompt engineering for iOS developers (How LLMs and GenAI work)
Prompt engineering for iOS developers (How LLMs and GenAI work)
 
Visual Studio 2010 and .NET 4.0 Overview
Visual Studio 2010 and .NET 4.0 OverviewVisual Studio 2010 and .NET 4.0 Overview
Visual Studio 2010 and .NET 4.0 Overview
 
Micro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateMicro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicate
 
Data access 2.0? Please welcome: Spring Data!
Data access 2.0? Please welcome: Spring Data!Data access 2.0? Please welcome: Spring Data!
Data access 2.0? Please welcome: Spring Data!
 
Twitter Author Prediction from Tweets using Bayesian Network
Twitter Author Prediction from Tweets using Bayesian NetworkTwitter Author Prediction from Tweets using Bayesian Network
Twitter Author Prediction from Tweets using Bayesian Network
 
Tornadoweb
TornadowebTornadoweb
Tornadoweb
 
Dart Power Tools
Dart Power ToolsDart Power Tools
Dart Power Tools
 
Linq intro
Linq introLinq intro
Linq intro
 
Embedded Typesafe Domain Specific Languages for Java
Embedded Typesafe Domain Specific Languages for JavaEmbedded Typesafe Domain Specific Languages for Java
Embedded Typesafe Domain Specific Languages for Java
 
All you need to know about JavaScript Functions
All you need to know about JavaScript FunctionsAll you need to know about JavaScript Functions
All you need to know about JavaScript Functions
 
Angular Schematics
Angular SchematicsAngular Schematics
Angular Schematics
 
Coding Ajax
Coding AjaxCoding Ajax
Coding Ajax
 
CouchDB on Android
CouchDB on AndroidCouchDB on Android
CouchDB on Android
 
Object Graph Mapping with Spring Data Neo4j 3 - Nicki Watt & Michael Hunger @...
Object Graph Mapping with Spring Data Neo4j 3 - Nicki Watt & Michael Hunger @...Object Graph Mapping with Spring Data Neo4j 3 - Nicki Watt & Michael Hunger @...
Object Graph Mapping with Spring Data Neo4j 3 - Nicki Watt & Michael Hunger @...
 

Mehr von Maarten Balliauw

Bringing nullability into existing code - dammit is not the answer.pptx
Bringing nullability into existing code - dammit is not the answer.pptxBringing nullability into existing code - dammit is not the answer.pptx
Bringing nullability into existing code - dammit is not the answer.pptxMaarten Balliauw
 
Nerd sniping myself into a rabbit hole... Streaming online audio to a Sonos s...
Nerd sniping myself into a rabbit hole... Streaming online audio to a Sonos s...Nerd sniping myself into a rabbit hole... Streaming online audio to a Sonos s...
Nerd sniping myself into a rabbit hole... Streaming online audio to a Sonos s...Maarten Balliauw
 
Microservices for building an IDE - The innards of JetBrains Rider - NDC Oslo...
Microservices for building an IDE - The innards of JetBrains Rider - NDC Oslo...Microservices for building an IDE - The innards of JetBrains Rider - NDC Oslo...
Microservices for building an IDE - The innards of JetBrains Rider - NDC Oslo...Maarten Balliauw
 
Indexing and searching NuGet.org with Azure Functions and Search - .NET fwday...
Indexing and searching NuGet.org with Azure Functions and Search - .NET fwday...Indexing and searching NuGet.org with Azure Functions and Search - .NET fwday...
Indexing and searching NuGet.org with Azure Functions and Search - .NET fwday...Maarten Balliauw
 
NDC Sydney 2019 - Microservices for building an IDE – The innards of JetBrain...
NDC Sydney 2019 - Microservices for building an IDE – The innards of JetBrain...NDC Sydney 2019 - Microservices for building an IDE – The innards of JetBrain...
NDC Sydney 2019 - Microservices for building an IDE – The innards of JetBrain...Maarten Balliauw
 
JetBrains Australia 2019 - Exploring .NET’s memory management – a trip down m...
JetBrains Australia 2019 - Exploring .NET’s memory management – a trip down m...JetBrains Australia 2019 - Exploring .NET’s memory management – a trip down m...
JetBrains Australia 2019 - Exploring .NET’s memory management – a trip down m...Maarten Balliauw
 
.NET Conf 2019 - Indexing and searching NuGet.org with Azure Functions and Se...
.NET Conf 2019 - Indexing and searching NuGet.org with Azure Functions and Se....NET Conf 2019 - Indexing and searching NuGet.org with Azure Functions and Se...
.NET Conf 2019 - Indexing and searching NuGet.org with Azure Functions and Se...Maarten Balliauw
 
CloudBurst 2019 - Indexing and searching NuGet.org with Azure Functions and S...
CloudBurst 2019 - Indexing and searching NuGet.org with Azure Functions and S...CloudBurst 2019 - Indexing and searching NuGet.org with Azure Functions and S...
CloudBurst 2019 - Indexing and searching NuGet.org with Azure Functions and S...Maarten Balliauw
 
NDC Oslo 2019 - Indexing and searching NuGet.org with Azure Functions and Search
NDC Oslo 2019 - Indexing and searching NuGet.org with Azure Functions and SearchNDC Oslo 2019 - Indexing and searching NuGet.org with Azure Functions and Search
NDC Oslo 2019 - Indexing and searching NuGet.org with Azure Functions and SearchMaarten Balliauw
 
Approaches for application request throttling - Cloud Developer Days Poland
Approaches for application request throttling - Cloud Developer Days PolandApproaches for application request throttling - Cloud Developer Days Poland
Approaches for application request throttling - Cloud Developer Days PolandMaarten Balliauw
 
Indexing and searching NuGet.org with Azure Functions and Search - Cloud Deve...
Indexing and searching NuGet.org with Azure Functions and Search - Cloud Deve...Indexing and searching NuGet.org with Azure Functions and Search - Cloud Deve...
Indexing and searching NuGet.org with Azure Functions and Search - Cloud Deve...Maarten Balliauw
 
Approaches for application request throttling - dotNetCologne
Approaches for application request throttling - dotNetCologneApproaches for application request throttling - dotNetCologne
Approaches for application request throttling - dotNetCologneMaarten Balliauw
 
CodeStock - Exploring .NET memory management - a trip down memory lane
CodeStock - Exploring .NET memory management - a trip down memory laneCodeStock - Exploring .NET memory management - a trip down memory lane
CodeStock - Exploring .NET memory management - a trip down memory laneMaarten Balliauw
 
ConFoo Montreal - Microservices for building an IDE - The innards of JetBrain...
ConFoo Montreal - Microservices for building an IDE - The innards of JetBrain...ConFoo Montreal - Microservices for building an IDE - The innards of JetBrain...
ConFoo Montreal - Microservices for building an IDE - The innards of JetBrain...Maarten Balliauw
 
ConFoo Montreal - Approaches for application request throttling
ConFoo Montreal - Approaches for application request throttlingConFoo Montreal - Approaches for application request throttling
ConFoo Montreal - Approaches for application request throttlingMaarten Balliauw
 
Microservices for building an IDE – The innards of JetBrains Rider - TechDays...
Microservices for building an IDE – The innards of JetBrains Rider - TechDays...Microservices for building an IDE – The innards of JetBrains Rider - TechDays...
Microservices for building an IDE – The innards of JetBrains Rider - TechDays...Maarten Balliauw
 
JetBrains Day Seoul - Exploring .NET’s memory management – a trip down memory...
JetBrains Day Seoul - Exploring .NET’s memory management – a trip down memory...JetBrains Day Seoul - Exploring .NET’s memory management – a trip down memory...
JetBrains Day Seoul - Exploring .NET’s memory management – a trip down memory...Maarten Balliauw
 
DotNetFest - Let’s refresh our memory! Memory management in .NET
DotNetFest - Let’s refresh our memory! Memory management in .NETDotNetFest - Let’s refresh our memory! Memory management in .NET
DotNetFest - Let’s refresh our memory! Memory management in .NETMaarten Balliauw
 
VISUG - Approaches for application request throttling
VISUG - Approaches for application request throttlingVISUG - Approaches for application request throttling
VISUG - Approaches for application request throttlingMaarten Balliauw
 
What is going on - Application diagnostics on Azure - TechDays Finland
What is going on - Application diagnostics on Azure - TechDays FinlandWhat is going on - Application diagnostics on Azure - TechDays Finland
What is going on - Application diagnostics on Azure - TechDays FinlandMaarten Balliauw
 

Mehr von Maarten Balliauw (20)

Bringing nullability into existing code - dammit is not the answer.pptx
Bringing nullability into existing code - dammit is not the answer.pptxBringing nullability into existing code - dammit is not the answer.pptx
Bringing nullability into existing code - dammit is not the answer.pptx
 
Nerd sniping myself into a rabbit hole... Streaming online audio to a Sonos s...
Nerd sniping myself into a rabbit hole... Streaming online audio to a Sonos s...Nerd sniping myself into a rabbit hole... Streaming online audio to a Sonos s...
Nerd sniping myself into a rabbit hole... Streaming online audio to a Sonos s...
 
Microservices for building an IDE - The innards of JetBrains Rider - NDC Oslo...
Microservices for building an IDE - The innards of JetBrains Rider - NDC Oslo...Microservices for building an IDE - The innards of JetBrains Rider - NDC Oslo...
Microservices for building an IDE - The innards of JetBrains Rider - NDC Oslo...
 
Indexing and searching NuGet.org with Azure Functions and Search - .NET fwday...
Indexing and searching NuGet.org with Azure Functions and Search - .NET fwday...Indexing and searching NuGet.org with Azure Functions and Search - .NET fwday...
Indexing and searching NuGet.org with Azure Functions and Search - .NET fwday...
 
NDC Sydney 2019 - Microservices for building an IDE – The innards of JetBrain...
NDC Sydney 2019 - Microservices for building an IDE – The innards of JetBrain...NDC Sydney 2019 - Microservices for building an IDE – The innards of JetBrain...
NDC Sydney 2019 - Microservices for building an IDE – The innards of JetBrain...
 
JetBrains Australia 2019 - Exploring .NET’s memory management – a trip down m...
JetBrains Australia 2019 - Exploring .NET’s memory management – a trip down m...JetBrains Australia 2019 - Exploring .NET’s memory management – a trip down m...
JetBrains Australia 2019 - Exploring .NET’s memory management – a trip down m...
 
.NET Conf 2019 - Indexing and searching NuGet.org with Azure Functions and Se...
.NET Conf 2019 - Indexing and searching NuGet.org with Azure Functions and Se....NET Conf 2019 - Indexing and searching NuGet.org with Azure Functions and Se...
.NET Conf 2019 - Indexing and searching NuGet.org with Azure Functions and Se...
 
CloudBurst 2019 - Indexing and searching NuGet.org with Azure Functions and S...
CloudBurst 2019 - Indexing and searching NuGet.org with Azure Functions and S...CloudBurst 2019 - Indexing and searching NuGet.org with Azure Functions and S...
CloudBurst 2019 - Indexing and searching NuGet.org with Azure Functions and S...
 
NDC Oslo 2019 - Indexing and searching NuGet.org with Azure Functions and Search
NDC Oslo 2019 - Indexing and searching NuGet.org with Azure Functions and SearchNDC Oslo 2019 - Indexing and searching NuGet.org with Azure Functions and Search
NDC Oslo 2019 - Indexing and searching NuGet.org with Azure Functions and Search
 
Approaches for application request throttling - Cloud Developer Days Poland
Approaches for application request throttling - Cloud Developer Days PolandApproaches for application request throttling - Cloud Developer Days Poland
Approaches for application request throttling - Cloud Developer Days Poland
 
Indexing and searching NuGet.org with Azure Functions and Search - Cloud Deve...
Indexing and searching NuGet.org with Azure Functions and Search - Cloud Deve...Indexing and searching NuGet.org with Azure Functions and Search - Cloud Deve...
Indexing and searching NuGet.org with Azure Functions and Search - Cloud Deve...
 
Approaches for application request throttling - dotNetCologne
Approaches for application request throttling - dotNetCologneApproaches for application request throttling - dotNetCologne
Approaches for application request throttling - dotNetCologne
 
CodeStock - Exploring .NET memory management - a trip down memory lane
CodeStock - Exploring .NET memory management - a trip down memory laneCodeStock - Exploring .NET memory management - a trip down memory lane
CodeStock - Exploring .NET memory management - a trip down memory lane
 
ConFoo Montreal - Microservices for building an IDE - The innards of JetBrain...
ConFoo Montreal - Microservices for building an IDE - The innards of JetBrain...ConFoo Montreal - Microservices for building an IDE - The innards of JetBrain...
ConFoo Montreal - Microservices for building an IDE - The innards of JetBrain...
 
ConFoo Montreal - Approaches for application request throttling
ConFoo Montreal - Approaches for application request throttlingConFoo Montreal - Approaches for application request throttling
ConFoo Montreal - Approaches for application request throttling
 
Microservices for building an IDE – The innards of JetBrains Rider - TechDays...
Microservices for building an IDE – The innards of JetBrains Rider - TechDays...Microservices for building an IDE – The innards of JetBrains Rider - TechDays...
Microservices for building an IDE – The innards of JetBrains Rider - TechDays...
 
JetBrains Day Seoul - Exploring .NET’s memory management – a trip down memory...
JetBrains Day Seoul - Exploring .NET’s memory management – a trip down memory...JetBrains Day Seoul - Exploring .NET’s memory management – a trip down memory...
JetBrains Day Seoul - Exploring .NET’s memory management – a trip down memory...
 
DotNetFest - Let’s refresh our memory! Memory management in .NET
DotNetFest - Let’s refresh our memory! Memory management in .NETDotNetFest - Let’s refresh our memory! Memory management in .NET
DotNetFest - Let’s refresh our memory! Memory management in .NET
 
VISUG - Approaches for application request throttling
VISUG - Approaches for application request throttlingVISUG - Approaches for application request throttling
VISUG - Approaches for application request throttling
 
What is going on - Application diagnostics on Azure - TechDays Finland
What is going on - Application diagnostics on Azure - TechDays FinlandWhat is going on - Application diagnostics on Azure - TechDays Finland
What is going on - Application diagnostics on Azure - TechDays Finland
 

Kürzlich hochgeladen

Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontologyjohnbeverley2021
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...Zilliz
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Jeffrey Haguewood
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FMESafe Software
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobeapidays
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdfSandro Moreira
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Bhuvaneswari Subramani
 
WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDropbox
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWERMadyBayot
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusZilliz
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyKhushali Kathiriya
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Orbitshub
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...apidays
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamUiPathCommunity
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...apidays
 

Kürzlich hochgeladen (20)

Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
 
WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering Developers
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 

Building a friendly .NET SDK to connect to Space

  • 1. Building a friendly .NET SDK to connect to Space Maarten Balliauw @maartenballiauw
  • 2. Agenda JetBrains Space JetBrains Space HTTP API Building a .NET SDK for the Space HTTP API Code generation Developer experience
  • 4. JetBrains Space Integrated Team Environment https://jetbrains.space People Teams Projects - Workspaces for teams Collaboration
  • 7. Integrated Team Environment More information in Space == more value from Space Internal integrations out of the box Vacation shown in profile Blog post targeted to team/location Calendar integration in blog post Everything can be a ToDo item ... What with external integrations? Extensibility?
  • 8. Everything as an HTTP API! What do you want to build? Which integration do you need? Import data Post to chat Manage users, teams, and locations programatically Build dashboards No good answer, so opening up everything Large HTTP API surface... HTTP API Playground to explore!
  • 10. $fields Top-level fields returned by default Unless $fields query parameter used Use $fields to be specific, or get nested fields $fields=id,icon,name $fields=id,name,boards(id,description) $fields=id,name,boards(id,info(columns)) $fields=*,boards(id,description)
  • 11. Building a .NET SDK for the Space HTTP API
  • 12. Hello, .NET! 👋 Goal: make our users succesful at using Space in their team/organization Provide a ready-to-use SDK Minimal configuration needed Discoverability TL;DR: Lead integration developers on the shortest path to success
  • 13. How? Handcrafted No. Over 150 API endpoints Over 700 object types Space EAP / Space Beta means churn!
  • 14. How? OpenAPI? Use NSwag, generate based on OpenAPI / Swagger document Some incompatible object types needed custom code generation
  • 15. How? OpenAPI? Not very developer friendly... public class Body { public string Member { get; set; } public string Reason { get; set; } public string Description { get; set; } public string Location { get; set; } public string Since { get; set; } public string Till { get; set; } public bool Available { get; set; } = false; public string Icon { get; set; } } public async Task<AbsenceRecord> GetAbsencesAsync( string fields, Body body, CancellationToken cancellationToken) { }
  • 16. How? Custom code generation Space exposes HTTP API Model Enumerations DTOs (objects passed around) Resources + data types, nullability, default values, ... https://your.jetbrains.space/api/http/http-api-model?$fields=dto,enums,urlParams,resources(*,nestedResources!)
  • 17. Enumeration model public class ApiEnum { public string Id { get; set; } public ApiDeprecation? Deprecation { get; set; } public string Name { get; set; } public List<string> Values { get; set; } } public class ApiDeprecation { public string? Message { get; set; } public string? Since { get; set; } public bool ForRemoval { get; set; } }
  • 18. Enumeration code generator public class EnumerationVisitor { public string Visit(ApiEnum apiEnum) { var builder = new StringBuilder(); if (apiEnum.Deprecation != null) builder.AppendLine(Visit(apiEnum.Deprecation)); builder.AppendLine("public enum " + apiEnum.Name); builder.AppendLine("{"); foreach (var apiEnumValue in apiEnum.Values) { builder.AppendLine($" {apiEnumValue},"); } builder.AppendLine("}"); return builder.ToString(); } public string Visit(ApiDeprecation apiDeprecation) => $"[Obsolete("{apiDeprecation.Message}")]"; } public enum AbsenceListMode { All, WithAccessibleReasonUnapproved, WithAccessibleReasonAll, }
  • 19. DTO model public class ApiDto { public string Id { get; set; } public ApiDeprecation? Deprecation { get; set; } public string Name { get; set; } = default!; public List<ApiDtoField> Fields { get; set; } public Reference<ApiDto>? Extends { get; set; } public List<Reference<ApiDto>> Implements { get; set; } public List<Reference<ApiDto>> Inheritors { get; set; } public HierarchyRole HierarchyRole { get; set; } public bool Record { get; set; } } public class ApiField { public string Name { get; set; } public ApiFieldType Type { get; set; } public bool Optional { get; set; } public ApiDefaultValue? DefaultValue { get; set; } }
  • 20. Endpoints model Endpoint id, name, base path Resource id, name, path, parameters Resource id, name, path, parameters Resource id, name, path, parameters ... + parameter types + return types + default values (const, variable)
  • 22. Roslyn/StringBuilder/...? Generally, a few ways to generate code: IL-based IL Generation (Reflection.Emit) Model-based CodeDOM Expression trees Roslyn syntax generation Template-based (T4, Razor, ...) Strings all the way https://amzn.to/2EWsA44
  • 23. Strings all the way! See string, recognize the output easily IL-based – Too complex Model-based – Less readable (very verbose) Template-based – Less readable (many small files to include) builder.AppendLine( $"public {subject.Type.ToCSharpType()} {subject.ToCSharpPropertyName()} {{ get; set; }}"); var propertyDeclaration = SyntaxFactory.PropertyDeclaration( SyntaxFactory.ParseTypeName(subject.Type.ToCSharpType()), subject.ToCSharpPropertyName()) .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) .AddAccessorListAccessors( SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)));
  • 24. Extension methods everywhere Many places where we need: C# class name C# variable name C# type from Space model type ... public static class ApiDocumentationExtensions { public static string ToCSharpDocumentationComment( this string subject) { var builder = new StringBuilder(); builder.AppendLine("/// <summary>"); builder.AppendLine("/// " + subject); builder.AppendLine("/// </summary>"); return builder.ToString(); } } public static class ApiDtoExtensions { public static string ToCSharpClassName(this ApiDto subject) => CSharpIdentifier.ForClassOrNamespace(subject.Name); }
  • 25. System.Text.Json “System.Text.Json is a built-in (de)serializer for .NET Core 3 and beyond.” Developer experience! No extra dependencies needed No dependency on a Newtonsoft.Json that you are not using
  • 26. System.Text.Json Not a 100% replacement for Newtonsoft.Json... https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate- from-newtonsoft-how-to System.Text.Json is extensible enough Custom JsonConverter<T>
  • 28. Code generation & developer experience
  • 29. The ideal “Getting started” 1. Register application in Space 2. Install SpaceDotNet.Client package dotnet add package SpaceDotNet.Client 3. Create connection instance var connection = new ClientCredentialsConnection( "https://your.jetbrains.space/", "client-id", "client-secret", new HttpClient()); 4. Profit!
  • 30. Large API surface var teamDirectoryClient = new TeamDirectoryClient(connection); var birthdays = await teamDirectoryClient .CalendarEvents .BirthdayEvents .GetAllBirthdayEventsAsync(...);
  • 31. Remember $fields ? Top-level fields returned by default Unless $fields query parameter used Use $fields to be specific, or get nested fields
  • 32. $fields Top-level fields returned by default Use partial to be specific, or get nested fields var memberProfile = await teamDirectoryClient.Profiles .GetProfileAsync(ProfileIdentifier.Username("Heather.Stewart")); var memberProfile = await teamDirectoryClient.Profiles .GetProfileAsync(ProfileIdentifier.Username("Heather.Stewart"), _ => _ .WithAllFieldsWildcard() // with all top level fields .WithManagers(managers => managers // include managers .WithId() // with their Id .WithUsername() // and their Username .WithName(name => name // and their Name .WithFirstName() // with FirstName .WithLastName()))); // and LastName
  • 34. Batches (e.g. Get all ToDo items) public class Batch<T> { List<T>? Data { get; } string? Next { get; } int? TotalCount { get; } bool HasNext(); } var batch = await _todoClient.GetAllToDoItemsAsync( from: weekStart.AsSpaceDate(), partial: _ => _ .WithData(data => data // ... .WithTotalCount() .WithNext()); do { foreach (var todo in batch.Data) { // ... } batch = await _todoClient.GetAllToDoItemsAsync( from: weekStart.AsSpaceDate(), skip: batch.Next, partial: _ => _ /* ... */); } while (batch.HasNext());
  • 35. await foreach (var todo in _todoClient .GetAllToDoItemsAsyncEnumerable(from: weekStart.AsSpaceDate(), partial: _ => _ .WithId() .WithContent(content => content .ForInherited<TodoItemContentMdText>(md => md .WithAllFieldsWildcard())) .WithStatus())) { // ... } IAsyncEnumerable to the rescue! ⚠ Don’t use it for .Count() var batch = await _todoClient.GetAllToDoItemsAsync( from: weekStart.AsSpaceDate(), partial: _ => _.WithTotalCount()); var numberOfResults = batch.TotalCount;
  • 37. Request bodies are flattened GetAbsences(AbsencesRequest request) GetAbsences(string member, string reason, ...)
  • 38. Factory methods for inheritors Which option would you prefer? await chatClient.Messages.SendMessageAsync( recipient: new MessageRecipientChannel() { Channel = new ChatChannelFromName() { Name = chatChannelName } }, await chatClient.Messages.SendMessageAsync( recipient: MessageRecipient.Channel(ChatChannel.FromName(chatChannelName)),
  • 39. Default values Unfortunatey not possible in C#... Workaround: Unfortunately not obvious what the default is when not specified. Should we add XMLdoc instead? public async IAsyncEnumerable<AbsenceRecord> GetAllAbsencesAsyncEnumerable( AbsenceListMode viewMode = AbsenceListMode.All, string? member = null) public IAsyncEnumerable<AbsenceRecord> GetAllAbsencesAsyncEnumerable( AbsenceListMode? viewMode = null, string? member = null) => BatchEnumerator.AllItems( () => GetAllAbsencesAsync(viewMode: viewMode ?? AbsenceListMode.All, member: member);
  • 42. Use SpaceDotNet in web apps public void ConfigureServices(IServiceCollection services) { // ... // Space authentication services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = SpaceDefaults.AuthenticationScheme; }) .AddCookie() .AddSpace(options => Configuration.Bind("Space", options)) .AddSpaceTokenManagement(); // auto-refreshes tokens in the background and provides a Space connection // Space client API services.AddSpaceClientApi(); // Space webhook receiver services.AddSpaceWebHookHandler<CateringWebHookHandler>(options => Configuration.Bind("Space", options));
  • 43. Use SpaceDotNet in web apps .AddSpace() Use Space for authN .AddSpaceClientApi() Register ...Client with service collection .AddSpaceTokenManagement() (experimental) Auto-refreshes tokens in the background Space client always auth’ed as currently logged in user .AddSpaceWebHookHandler<T>() (experimental) Webhook support
  • 46. Conclusion JetBrains Space is an Integrated Team Environment JetBrains Space HTTP API exposes everything Building a .NET SDK for the Space HTTP API Goal: make our users succesful at using Space Provide a ready-to-use SDK Minimal configuration needed Discoverability Code generation Focus on developer experience

Hinweis der Redaktion

  1. https://pixabay.com
  2. Clear cookies and auth in demo.jetbrains.space! Setup everything to wok with demo.jetbrains.space NGrok running SpaceDotNet opened in Rider
  3. Show home page and mention what is shown (relevat info for me) Show project, mention repo’s, issues, checklists, ... Always possible to annotate and ask questions inline in code. Other folks will see this pop up in chat, so they do not necesarily have to switch context if it’s a minor question. Mention issues, and again, chat integration. Everything can be a to-do as well, use the lightning bolt icon. Automation/CI integrated. Absences integrated is where things get interesting. Who should I contact for certain responsibility? Is that other perso naround to look at my code review? Blogs. Organization-wide, for specific teams or locations. E.g. To share post-mortems, or team announcements, without overloading others. Again, relevant infomation to you. Many other things such as a knowledge base, artifact repo’s for NuGet, NPM, Docker, ...
  4. Show HTTP API playground, send a chat message or something like that. Or get profile info. Mention different endpoints, API’s grouped by functionality Mention request builder, all that, easy to reproduce Show getting a profile, selecting properties, and highlight $fields
  5. Request body as object $fields... Good luck!
  6. Show SpaceDotNet.Generator API model classes, show: ApiEnum, ApiDto, HierarchyRole enumeration Show Program.cs to see how it is bootstrapped Show generated code Show CSharpApiModelEnumGenerator, the simplest of all Mention things are string-based Show the extension methods created (ToCSharpClassName, ToCSharpDeprecation, ...) Show generation adds a custom EnumerationConverter
  7. KotlinPoet in Kotlin SDK
  8. Walk through several of them EnumerationConverter CanConvert -> should be Enumeration Read -> use string, otherwise null Write -> write string value ClassNameDtoTypeConverter CanConvert -> IClassNameConvertible Read We need className from object Then rewind so we can read full class. How to do so? var readerAtStart = reader;  struct gets copied, so we can work with copy to read className Using type map to keep track of types we’re using Write -> Just write the full object
  9. Show SpaceDotNet.Sample.CommandLine Show connection seup Show using TeamDirectoryClient Get pofiles with their name – explain $fields here. No partial -> get top level Partial -> extension methods++ Add .WithFirstName to name, tab, get completion to add it Change code, add: Console.WriteLine($"{profile.Managers[0].Name.FirstName}"); Run, see exception Code generator – show how it’s done CSharpPartialExtensionsGenerator writes the partial extensions Show the effect of FeatureFlags.GenerateBackingFieldsForDtoProperties in generated code Explain C#9 init properties would be nice to use, or even record types, but can we then provide such meaningful error message?
  10. Show CSharpApiModelResourceGenerator GenerateMethodForBatchApiEndpoint, not much special to see Let’s look at generated method instead, e.g. GetAllProfilesAsyncEnumerable Calls into BatchEnumerator.AllItems Show BatchEnumerator.AllItems logic (get page, yield items, get next page, yield results, ...) Side note about [EnumeratorCancellation] Passed in at runtime, used to ensure you can e.g. Enumerate multiple times with different cancellation token ....WithCancellation() at call site
  11. Play with FeatureFlags DoNotExposeRequestObjects – request bodies flattened GenerateInheritorFactoryMethods – factory methods for inheritors GenerateOptionalParameterDefaultReferenceTypes – default values, but will not compile Alternative generates default value checks
  12. Show SpaceDotNet.Samples.Web Show authentication token flow to log in After logging in, show dashboard Index.cshtml is the dashboard Note constructor injection fo e.g. TeamDirectoryClient, and sicne we added Space token management it’s the currently logged in user. Show webhook in action as well In Space itself, converse with webhook In code look at CateringWebHookHandler, mention only few methods need implementing all else is wired up for you Uses MVC under the covers