SlideShare a Scribd company logo
1 of 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

More Related Content

What's hot

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
 

What's hot (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
 

Similar to 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
 
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
 
Introduction to-csharp
Introduction to-csharpIntroduction to-csharp
Introduction to-csharpSDFG5
 

Similar to 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
 
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 @...
 
Introduction to-csharp
Introduction to-csharpIntroduction to-csharp
Introduction to-csharp
 

More from 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
 

More from 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
 

Recently uploaded

Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsMiki Katsuragi
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesZilliz
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 

Recently uploaded (20)

Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering Tips
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector Databases
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 

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

Editor's Notes

  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