Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: 0.38 WIP #3625

Closed
wants to merge 56 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
7b42f2d
docs: 0.38 WIP
mindspank Dec 5, 2023
0f9c7d1
doc: add available time range todo
mindspank Dec 6, 2023
b4589fe
doc: more time range
mindspank Dec 6, 2023
c53a4c8
Updated release notes with relevant bug fixes and minor improvements …
AndrewRTsao Dec 9, 2023
bd6ed77
Fix devtool debug server port clash (#3626)
begelundmuller Dec 5, 2023
6cef4e3
Updating UI label "Last Period" => "Previous Period" (#3621)
AdityaHegde Dec 5, 2023
4f9f711
Fixing model table not updating after fixing the model sql (#3623)
AdityaHegde Dec 6, 2023
91c71d2
Runtime: Reduce chance and impact of deadlocked DuckDB calls (#3627)
begelundmuller Dec 6, 2023
efd4570
Remove Babel (#3634)
ericpgreen2 Dec 6, 2023
899d5aa
User-friendly error instead of "Out of buffer" on ingesting TIME colu…
esevastyanov Dec 6, 2023
ef55c68
Configure Orval's generated error type (#3614)
ericpgreen2 Dec 6, 2023
7fd93cc
UI support for updating the theme dynamically (#3542)
AdityaHegde Dec 7, 2023
095c945
Embedded dashboards (#3590)
pjain1 Dec 7, 2023
b5e0bb7
CLI: E2E test fixes (#3644)
rakeshsharma14317 Dec 7, 2023
125d4e5
Only apply `overscroll-behavior: none` to Rill Developer (#3639)
ericpgreen2 Dec 7, 2023
6a5ffa0
Add last refresh time to dashboard (#3638)
ericpgreen2 Dec 7, 2023
78bd195
Limit ignore in web-local to only dev route! (#3649)
bcolloran Dec 7, 2023
4586924
More `svelte-check` ignore cleanups (#3647)
bcolloran Dec 7, 2023
9002710
Docs: Adding page for source refresh (#3657)
AndrewRTsao Dec 8, 2023
29a284f
Add documentation for themes in dashboards (#3641)
AdityaHegde Dec 8, 2023
a324ed3
Fix invisible spinner (#3661)
AdityaHegde Dec 8, 2023
7d943ea
Runtime: Emit gRPC code in server activity events (#3645)
begelundmuller Dec 8, 2023
3f8e28f
deadlock duckDB patch (#3665)
k-anshul Dec 8, 2023
c02d4b7
Make `formatMsInterval` tighter and add tests (#3669)
bcolloran Dec 8, 2023
64aec56
Wrap project names with underscores (#3667)
ericpgreen2 Dec 8, 2023
e6ade87
Fix text wrapping in the Imported Source dialog (#3668)
ericpgreen2 Dec 9, 2023
17503b1
nit changes (#3674)
rakeshsharma14317 Dec 11, 2023
e0062e1
Runtime: expand view for external table storage (#3662)
k-anshul Dec 11, 2023
6deb436
Runtime: Prevent DuckDB from mutating the catalog during informations…
begelundmuller Dec 11, 2023
df87909
Adding backwards compatibility for older time comparison (#3676)
AdityaHegde Dec 11, 2023
951dc31
Docs: Adding clarification to using cron syntax for source refreshes …
AndrewRTsao Dec 12, 2023
f6bbf3c
Upgrade to Svelte 4 (#3543)
ericpgreen2 Dec 12, 2023
90ce19a
Runtime: cast to enum writes to new db (#3675)
k-anshul Dec 12, 2023
767db0d
Adding support for daylight savings time in timeseries queries (#3494)
AdityaHegde Dec 12, 2023
6a7d784
Cleaning up our Snowflake documentation and adding more details / pre…
AndrewRTsao Dec 12, 2023
2eb4318
update to leaderboard test to account for inconsistent Playwright beh…
briangregoryholmes Dec 12, 2023
9010111
Cloud UI: Add "data [last] refreshed" timestamp to project status pag…
ericpgreen2 Dec 13, 2023
383f322
Runtime: otel fix for db close leaks (#3690)
k-anshul Dec 13, 2023
619b7f3
Feat: Add dimension label and percent of total to charts (#3672)
djbarnwal Dec 14, 2023
9e2c7b3
Update rill-iso-extensions.md (#3697)
medriscoll Dec 14, 2023
d96e4c5
update tailwind config to remove outdated color warning (#3693)
briangregoryholmes Dec 14, 2023
ee300e3
Fix theme colors and remove warning (#3698)
AdityaHegde Dec 14, 2023
6c4b7fa
horizontal splitter overflow (#3686)
briangregoryholmes Dec 14, 2023
151ff0a
removed WithTween wrapper around clipPath to prevent fickering when c…
briangregoryholmes Dec 14, 2023
e8bf76a
doc: hide content class (#3699)
mindspank Dec 14, 2023
cafcc99
Update themes.md (#3703)
jkhwu Dec 14, 2023
96376bf
Remove artificial time series chart delay (#3696)
briangregoryholmes Dec 14, 2023
6b59e7a
svletecheck --ignore cleanup: fix most of "time-controls" folder (#3651)
bcolloran Dec 14, 2023
e8aed0b
suggest models in the code editor (#3705)
briangregoryholmes Dec 14, 2023
b1582fe
properly extend tailwind colors with custom theme overwrite (#3704)
briangregoryholmes Dec 15, 2023
77e5bc1
Runtime: Refactor conn cache to contain and detect hanging opens/clos…
begelundmuller Dec 15, 2023
d483a4f
Runtime: change reconcile and parse errors to warns (#3691)
begelundmuller Dec 15, 2023
e188481
Fix negative SVG values in Graphic Context (#3673)
djbarnwal Dec 15, 2023
fef0f7d
Runtime: unified `duckdb` connector for `motherduck` and external `db…
k-anshul Dec 15, 2023
ff09c7a
support for Add Filter button (#3671)
briangregoryholmes Dec 18, 2023
f7a4d39
remove order by true from toplist api (#3711)
pjain1 Dec 18, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
web-admin/build
web-local/build
22 changes: 15 additions & 7 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
'plugin:svelte/recommended',
"prettier",
],
plugins: ["svelte3", "@typescript-eslint"],
ignorePatterns: ["*.cjs"],
overrides: [{ files: ["*.svelte"], processor: "svelte3/svelte3" }],
settings: {
"svelte3/typescript": () => require("typescript"),
},
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
parserOptions: {
sourceType: "module",
ecmaVersion: 2019,
extraFileExtensions: [".svelte"],
},
env: {
browser: true,
es2017: true,
node: true,
},
overrides: [
{
files: ['*.svelte'],
parser: 'svelte-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser'
}
}
],
ignorePatterns: ["*.cjs"],
rules: {
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
"@typescript-eslint/ban-ts-comment": "warn",
"svelte/no-at-html-tags": "warn",
},
};
4 changes: 2 additions & 2 deletions .github/workflows/web-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ jobs:
run: |-
npx prettier --check "web-common/**/*"
npx eslint web-common --quiet
npx svelte-check --threshold error --workspace web-common --no-tsconfig --ignore "src/components/(data-graphic|column-profile),src/features/dashboards/(time-series|time-controls),src/features/sources/workspace/SourceWorkspaceHeader.svelte,src/features/models/workspace/ModelWorkspaceHeader.svelte,src/features/dashboards/workspace/Dashboard.svelte"
npx svelte-check --threshold error --workspace web-common --no-tsconfig --ignore "src/components/data-graphic,src/features/dashboards/time-series,src/features/dashboards/time-controls/TimeRangeSelector.svelte,src/features/dashboards/time-controls/TimeControls.svelte"

- name: Prettier checks and lint for web local
if: steps.filter.outputs.local == 'true'
run: |-
npx prettier --check "web-local/**/*"
npx eslint web-local --quiet
npx svelte-check --workspace web-local --no-tsconfig --ignore "src/routes/dev,src/routes/\(application\)/(dashboard|model|source)/[name]"
npx svelte-check --workspace web-local --no-tsconfig --ignore "src/routes/dev"

- name: Prettier checks and lint for web admin
if: steps.filter.outputs.admin == 'true'
Expand Down
3 changes: 1 addition & 2 deletions admin/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ func New(adminHost, bearerToken, userAgent string) (*Client, error) {

opts := []grpc.DialOption{
grpc.WithUserAgent(userAgent),
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()),
grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor()),
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
}

if uri.Scheme == "http" {
Expand Down
2 changes: 1 addition & 1 deletion admin/database/postgres/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/jackc/pgconn"
"github.com/jmoiron/sqlx"
"github.com/rilldata/rill/admin/database"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"

// Load postgres driver
_ "github.com/jackc/pgx/v4/stdlib"
Expand Down
2 changes: 1 addition & 1 deletion admin/server/auth/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
"github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
Expand Down
225 changes: 204 additions & 21 deletions admin/server/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package server

import (
"context"
"errors"
"net/http"
"strconv"
"strings"
"time"

"github.com/rilldata/rill/admin/database"
"github.com/rilldata/rill/admin/pkg/urlutil"
"github.com/rilldata/rill/admin/server/auth"
adminv1 "github.com/rilldata/rill/proto/gen/rill/admin/v1"
"github.com/rilldata/rill/runtime/pkg/observability"
Expand Down Expand Up @@ -159,6 +163,7 @@ func (s *Server) GetDeploymentCredentials(ctx context.Context, req *adminv1.GetD
attribute.String("args.organization", req.Organization),
attribute.String("args.project", req.Project),
attribute.String("args.branch", req.Branch),
attribute.String("args.ttl_seconds", strconv.FormatUint(uint64(req.TtlSeconds), 10)),
)

proj, err := s.admin.DB.FindProjectByName(ctx, req.Organization, req.Project)
Expand Down Expand Up @@ -188,33 +193,56 @@ func (s *Server) GetDeploymentCredentials(ctx context.Context, req *adminv1.GetD
}

var attr map[string]any
switch forVal := req.For.(type) {
case *adminv1.GetDeploymentCredentialsRequest_UserId:
forOrgPerms, err := s.admin.OrganizationPermissionsForUser(ctx, proj.OrganizationID, forVal.UserId)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
if req.For != nil {
switch forVal := req.For.(type) {
case *adminv1.GetDeploymentCredentialsRequest_UserId:
attr, err = s.getAttributesFor(ctx, forVal.UserId, proj.OrganizationID, proj.ID)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
case *adminv1.GetDeploymentCredentialsRequest_UserEmail:
user, err := s.admin.DB.FindUserByEmail(ctx, forVal.UserEmail)
// if email is not found in the database, we assume it is a non-admin user
if errors.Is(err, database.ErrNotFound) {
attr = map[string]any{
"email": forVal.UserEmail,
"domain": forVal.UserEmail[strings.LastIndex(forVal.UserEmail, "@")+1:],
"admin": false,
}
break
}
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
attr, err = s.getAttributesFor(ctx, user.ID, proj.OrganizationID, proj.ID)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
case *adminv1.GetDeploymentCredentialsRequest_Attributes:
attr = forVal.Attributes.AsMap()
default:
return nil, status.Error(codes.InvalidArgument, "invalid 'for' type")
}

forProjPerms, err := s.admin.ProjectPermissionsForUser(ctx, proj.ID, forVal.UserId, forOrgPerms)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
// if no attributes found, we add standard non-admin user attrs to ensure security policies are applied correctly
if len(attr) == 0 {
attr = map[string]any{
"email": "",
"domain": "",
"admin": false,
}
}

attr, err = s.jwtAttributesForUser(ctx, forVal.UserId, proj.OrganizationID, forProjPerms)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
case *adminv1.GetDeploymentCredentialsRequest_Attrs:
attr = forVal.Attrs.AsMap()
default:
return nil, status.Error(codes.InvalidArgument, "invalid 'for' type")
ttlDuration := time.Hour
if req.TtlSeconds > 0 {
ttlDuration = time.Duration(req.TtlSeconds) * time.Second
}

// Generate JWT
jwt, err := s.issuer.NewToken(runtimeauth.TokenOptions{
AudienceURL: prodDepl.RuntimeAudience,
Subject: claims.OwnerID(),
TTL: time.Hour,
TTL: ttlDuration,
InstancePermissions: map[string][]runtimeauth.Permission{
prodDepl.RuntimeInstanceID: {
// TODO: Remove ReadProfiling and ReadRepo (may require frontend changes)
Expand All @@ -233,8 +261,163 @@ func (s *Server) GetDeploymentCredentials(ctx context.Context, req *adminv1.GetD
s.admin.Used.Deployment(prodDepl.ID)

return &adminv1.GetDeploymentCredentialsResponse{
RuntimeHost: prodDepl.RuntimeHost,
RuntimeInstanceId: prodDepl.RuntimeInstanceID,
Jwt: jwt,
RuntimeHost: prodDepl.RuntimeHost,
InstanceId: prodDepl.RuntimeInstanceID,
AccessToken: jwt,
TtlSeconds: uint32(ttlDuration.Seconds()),
}, nil
}

func (s *Server) GetIFrame(ctx context.Context, req *adminv1.GetIFrameRequest) (*adminv1.GetIFrameResponse, error) {
observability.AddRequestAttributes(ctx,
attribute.String("args.organization", req.Organization),
attribute.String("args.project", req.Project),
attribute.String("args.branch", req.Branch),
attribute.String("args.kind", req.Kind),
attribute.String("args.resource", req.Resource),
attribute.String("args.ttl_seconds", strconv.FormatUint(uint64(req.TtlSeconds), 10)),
attribute.String("args.state", req.State),
)

if req.Resource == "" {
return nil, status.Error(codes.InvalidArgument, "resource must be specified")
}

proj, err := s.admin.DB.FindProjectByName(ctx, req.Organization, req.Project)
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}

if proj.ProdDeploymentID == nil {
return nil, status.Error(codes.InvalidArgument, "project does not have a deployment")
}

prodDepl, err := s.admin.DB.FindDeployment(ctx, *proj.ProdDeploymentID)
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}

if req.Branch != "" && req.Branch != prodDepl.Branch {
return nil, status.Error(codes.InvalidArgument, "project does not have a deployment for given branch")
}

claims := auth.GetClaims(ctx)
permissions := claims.ProjectPermissions(ctx, proj.OrganizationID, proj.ID)

// If the user is not a superuser, they must have ManageProd permissions
if !permissions.ManageProd && !claims.Superuser(ctx) {
return nil, status.Error(codes.PermissionDenied, "does not have permission to manage deployment")
}

var attr map[string]any
if req.For != nil {
switch forVal := req.For.(type) {
case *adminv1.GetIFrameRequest_UserId:
attr, err = s.getAttributesFor(ctx, forVal.UserId, proj.OrganizationID, proj.ID)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
case *adminv1.GetIFrameRequest_UserEmail:
user, err := s.admin.DB.FindUserByEmail(ctx, forVal.UserEmail)
// if email is not found in the database, we assume it is a non-admin user
if errors.Is(err, database.ErrNotFound) {
attr = map[string]any{
"email": forVal.UserEmail,
"domain": forVal.UserEmail[strings.LastIndex(forVal.UserEmail, "@")+1:],
"admin": false,
}
break
}
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
attr, err = s.getAttributesFor(ctx, user.ID, proj.OrganizationID, proj.ID)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
case *adminv1.GetIFrameRequest_Attributes:
attr = forVal.Attributes.AsMap()
default:
return nil, status.Error(codes.InvalidArgument, "invalid 'for' type")
}
}
// if no attributes found, we add standard non-admin user attrs to ensure security policies are applied correctly
if len(attr) == 0 {
attr = map[string]any{
"email": "",
"domain": "",
"admin": false,
}
}

// default here is higher than GetDeploymentCredentials as most embedders probably won't implement refresh and state management
ttlDuration := 24 * time.Hour
if req.TtlSeconds > 0 {
ttlDuration = time.Duration(req.TtlSeconds) * time.Second
}

// Generate JWT
jwt, err := s.issuer.NewToken(runtimeauth.TokenOptions{
AudienceURL: prodDepl.RuntimeAudience,
Subject: claims.OwnerID(),
TTL: ttlDuration,
InstancePermissions: map[string][]runtimeauth.Permission{
prodDepl.RuntimeInstanceID: {
// TODO: Remove ReadProfiling and ReadRepo (may require frontend changes)
runtimeauth.ReadObjects,
runtimeauth.ReadMetrics,
runtimeauth.ReadProfiling,
runtimeauth.ReadRepo,
},
},
Attributes: attr,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "could not issue jwt: %s", err.Error())
}

s.admin.Used.Deployment(prodDepl.ID)

if req.Kind == "" {
req.Kind = "MetricsView"
}

iFrameURL, err := urlutil.WithQuery(urlutil.MustJoinURL(s.opts.FrontendURL, "/-/embed"), map[string]string{
"runtime_host": prodDepl.RuntimeHost,
"instance_id": prodDepl.RuntimeInstanceID,
"access_token": jwt,
"kind": req.Kind,
"resource": req.Resource,
"state": "",
"theme": req.Query["theme"],
})
if err != nil {
return nil, status.Errorf(codes.Internal, "could not construct iframe url: %s", err.Error())
}

return &adminv1.GetIFrameResponse{
IframeSrc: iFrameURL,
RuntimeHost: prodDepl.RuntimeHost,
InstanceId: prodDepl.RuntimeInstanceID,
AccessToken: jwt,
TtlSeconds: uint32(ttlDuration.Seconds()),
}, nil
}

func (s *Server) getAttributesFor(ctx context.Context, userID, orgID, projID string) (map[string]any, error) {
forOrgPerms, err := s.admin.OrganizationPermissionsForUser(ctx, orgID, userID)
if err != nil {
return nil, err
}

forProjPerms, err := s.admin.ProjectPermissionsForUser(ctx, projID, userID, forOrgPerms)
if err != nil {
return nil, err
}

attr, err := s.jwtAttributesForUser(ctx, userID, orgID, forProjPerms)
if err != nil {
return nil, err
}
return attr, nil
}
4 changes: 2 additions & 2 deletions admin/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/rilldata/rill/runtime/pkg/ratelimit"
runtimeauth "github.com/rilldata/rill/runtime/server/auth"
"github.com/rs/cors"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.uber.org/zap"
"google.golang.org/grpc"
Expand Down Expand Up @@ -121,7 +122,6 @@ func (s *Server) ServeGRPC(ctx context.Context) error {
server := grpc.NewServer(
grpc.ChainStreamInterceptor(
middleware.TimeoutStreamServerInterceptor(timeoutSelector),
observability.TracingStreamServerInterceptor(),
observability.LoggingStreamServerInterceptor(s.logger),
errorMappingStreamServerInterceptor(),
grpc_auth.StreamServerInterceptor(checkUserAgent),
Expand All @@ -131,14 +131,14 @@ func (s *Server) ServeGRPC(ctx context.Context) error {
),
grpc.ChainUnaryInterceptor(
middleware.TimeoutUnaryServerInterceptor(timeoutSelector),
observability.TracingUnaryServerInterceptor(),
observability.LoggingUnaryServerInterceptor(s.logger),
errorMappingUnaryServerInterceptor(),
grpc_auth.UnaryServerInterceptor(checkUserAgent),
grpc_validator.UnaryServerInterceptor(),
s.authenticator.UnaryServerInterceptor(),
grpc_auth.UnaryServerInterceptor(s.checkRateLimit),
),
grpc.StatsHandler(otelgrpc.NewServerHandler()),
)

adminv1.RegisterAdminServiceServer(server, s)
Expand Down
Loading
Loading