microsoft / microsoft/typespec

Support for context and flexible decorator arguments

Open
#4,763 0 comments 1 reaction 0 assignees View on GitHub
compiler:core design:needed triaged:core
Dominant language
Java
Stars
5.9k
Forks
394
Avg merge
1d 23h
Merged PRs (30d)
104

Description

### Clear and concise description of the problem

We would like support for expressing context and more flexible directive arguments.

# Background

[Exograph](https://exograph.dev) is a developer tool that automatically creates backends based on model descriptions. It uses its own "Exograph(Spec)" language (see [FAQ](https://exograph.dev/docs/faq#the-exograph-language) for the reasoning). However, with the availability of TypeSpec, we sense an opportunity to use a more standard language. Exograph's needs align well with what TypeSpec offers, except for a few constructs.

# Simple example

Here is a simple program to express a todo list ([playground](https://exograph.dev/playground)).

```exo
@postgres
module TodoService {
@access(true)
type Todo {
@pk id: Int = autoIncrement()
title: String
completed: Boolean
}
}
```

This maps well to the current TypeSpec version:

```tsp
import "../exograph";
using Exograph;

@postgres
namespace TodoService {
@access(true)
model Todo {
@pk id: serial = serial.autoIncrement();
title: string;
completed: boolean;
}
}
```

Where Exograph's decorators are defined thus:

```tsp
extern dec postgres(target: Namespace);
extern dec access(target: Model | ModelProperty, value: valueof boolean);
extern dec pk(target: ModelProperty);

scalar serial extends int32 {
init autoIncrement();
}
```

# More complex example

Things get more interesting when we introduce access control. Here is the todo model with access control ([playground](https://exograph.dev/playground?id=multi-user-todo)).

```exo
context AuthContext {
@jwt("sub") id: String
}

@postgres
module TodoDatabase {
@access(self.userId == AuthContext.id)
type Todo {
@pk id: Int = autoIncrement()
title: String
completed: Boolean
@readonly userId: String = AuthContext.id
}
}
```

Here is a possible TypeSpec mapping assuming a few new constructs:

```tsp
import "../exograph";
using Exograph;

context AuthContext {
@jwt("sub") id: string
}

@postgres
namespace TodoDatabase {
@access(self.userId == AuthContext.id)
model Todo {
@pk id: serial = serial.autoIncrement();
title: string;
completed: boolean;
@readonly userId: string = AuthContext.id
}
}
```

This needs a few new constructs:

- _context_: Expresses any "ambient" values. In Exograph, it is used to capture request-specific (jwt, header, cookie, current time, etc.) and process-specific (env, OS, etc.) values, as well as [computed values](https://exograph.dev/docs/core-concept/context#processed-value) based on those. It most naturally maps to a "static" construct (so its properties can be referred to with just the type and not an instance).
- _context-based initialization_: Provides the default value for a model property to a value from context. In this particular example, Exograph automatically assigns any newly created todo's `userId` to the current user (and due to `@readonly` won't let it be changed).
- _flexible decorator argument_: Allows decorator arguments to be simple (non-turing complete) expressions. In Exograph, we would use it in `@access` control expressions, but later also for [validation rules](https://github.com/exograph/exograph/issues/81) (`@validation(self.graduationDate > self.birthday)`), etc.

More on the last item. Some alternatives could be explored here:

- _Tagged template_: This would map [the equivalent Javascript concept](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates). For example it could be a [CEL](https://github.com/google/cel-spec/blob/master/doc/langdef.md) expression: ``cel`${self.userId} == ${AuthContext.id}` `` or even plain `` cel`self.userId == AuthContext.id` ``. However, this may make tooling complex to implement. For example, it would be nicer to syntax highlight, show errors, jump to definition in each IDE. (Aside: Exograph currently doesn't use CEL. Instead, it leans towards [TypeScript-inspired syntax to express higher-order functions](https://exograph.dev/docs/postgres/access-control#using-higher-order-functions), but CEL may be a choice as would any similar language if TypeSpec includes one).
- _Plain strings_: The same as above but without the tagging. It can be implemented today, but the tooling issues and syntax highlighting will be more ad hoc (no tag to guide).

### Checklist

- [x] Follow our [Code of Conduct](https://github.com/microsoft/typespec/blob/main/CODE_OF_CONDUCT.md)
- [x] Read the [docs](https://typespec.io/docs/).
- [x] Check that there isn't already an issue that request the same feature to avoid creating a duplicate.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.