microsoft / microsoft/typespec
Support for context and flexible decorator arguments
- 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
Assessment
This issue has not been assessed yet.