Git Product home page Git Product logo

vaadin / hilla Goto Github PK

View Code? Open in Web Editor NEW
874.0 25.0 57.0 35.16 MB

Build better business applications, faster. No more juggling REST endpoints or deciphering GraphQL queries. Hilla seamlessly connects Spring Boot and React to accelerate application development.

Home Page: https://hilla.dev

License: Apache License 2.0

TypeScript 38.48% JavaScript 1.13% Java 58.33% HTML 0.28% CSS 0.34% Kotlin 1.43% Shell 0.01%
typescript java reactive lit lit-element stateless client-side frontend vaadin react

hilla's Introduction

Hilla

The modern web framework for Java

Latest Stable Version Releases

hilla.dev · Docs · Forum


Hilla integrates a Spring Boot Java backend with a reactive TypeScript front end. It helps you build apps faster with type-safe server communication, included UI components, and integrated tooling.

Simple type-safe server communication

Hilla helps you access the backend easily with type-safe endpoints.

index.ts

// Type info is automatically generated based on Java
import Person from 'Frontend/generated/com/vaadin/hilla/demo/entity/Person';
import { PersonEndpoint } from 'Frontend/generated/endpoints';

async function getPeopleWithPhoneNumber() {
  const people: Person[] = await PersonEndpoint.findAll();

  // Compile error: The property is 'phone', not 'phoneNumber'
  return people.filter((person) => !!person.phoneNumber);
}

console.log('People with phone numbers: ', getPeopleWithPhoneNumber());

PersonEndpoint.java

@Endpoint
@AnonymousAllowed
public class PersonEndpoint {

    private PersonRepository repository;

    public PersonEndpoint(PersonRepository repository) {
        this.repository = repository;
    }

    public @Nonnull List<@Nonnull Person> findAll() {
        return repository.findAll();
    }
}

Person.java

@Entity
public class Person {

    @Id
    @GeneratedValue
    private Integer id;

    @Nonnull private String firstName;
    @Nonnull private String lastName;
    @Email @Nonnull private String email;
    @Nonnull private String phone;

    // getters, setters
}

Learn more at hilla.dev

Get started

Follow the tutorials at https://hilla.dev/docs

Contributing

The best way to contribute is to try out Hilla and provide feedback to the development team in our Forum or with GitHub issues.

Development

If you want to develop Hilla, you can clone the repo and run tests using the following commands:

git clone https://github.com/vaadin/hilla.git
npm install
npm run build
npm test

You need the following versions of Node.js and npm:

  • Node.js: >= 18 LTS (native support for ES Modules and NodeJS execution of the newest hooks),
  • npm: ^9 (package-lock.json is of version 3)

Frontend CI Java CI codecov

hilla's People

Contributors

alvarezguille avatar artur- avatar aspan avatar caalador avatar cromoteca avatar dependabot[bot] avatar fluorumlabs avatar gilberto-torrezan avatar haijian-vaadin avatar haprog avatar jojule avatar jouni avatar krissvaa avatar lodin avatar manolo avatar mcollovati avatar mshabarov avatar mvysny avatar platosha avatar rbrki07 avatar sissbruecker avatar snyk-bot avatar taefi avatar tarekoraby avatar tdq avatar tltv avatar vaadin-bot avatar vursen avatar web-padawan avatar zhesun88 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hilla's Issues

embedding Flow components into TypeScript views does not work

If I follow the Creating an Embedded Vaadin Application Tutorial in order to embed a server-side Vaadin component into a TypeScript Vaadin view, my application does not work as expected:

  • the tutorial tells me that I need to include the webcomponents-loader script and gives code snippets how to do that. These code snippets do not work. Moreover, the entire step of loading WebComponents polyfils is not needed because all browsers supported by Vaadin 15+ do support Web Components natively.
  • If I skip the WC polyfils (or if I manage to find my way to load them), embedding still fails with a Uncaught TypeError: window.Vaadin.Flow.initApplication is not a function error:
    image

ServerCounter.java:

public class ServerCounter extends Div {

    public ServerCounter() {
        add(new Label("Server-side counter"));
    }

    public static class Exporter extends WebComponentExporter<ServerCounter> {
        public Exporter() {
            super("server-counter");
        }

        @Override
        protected void configureInstance(
                WebComponent<ServerCounter> webComponent,
                ServerCounter counter) {
        }
    }
}

index.html:

<script type="module" src="web-component/server-counter.js"></script>

main-layout.ts:

render() {
  return html`
      <server-counter></server-counter>
      <slot></slot>
  `;
}

@JsonProperty does not work as documented

The current documentation for the @JsonProperty annotation is misleading: in reality it seem to have no effect on the TypeScript type generator (as of Vaadin 15.0.4).

  1. After reading the docs I would expect that @JsonProperty("bookId") private String id; in Java would lead to bookId: string; in TypeScript. This is not the case; the generated TS type has id: string.
  2. After reading the docs I would expect that
     public class Student {
       // NOTE: no field, only getter and setter
    
       @JsonProperty("name")
       public void setFirstName(String name) {
          this.name = name;
       }
    
       @JsonProperty("name")
       public String getFirstName() {
          return name;
       }
     }
    would lead to name: string; in TypeScript. This is not the case; the generated TS type has neither name nor firstName property at all.

The @JsonProperty annotation

It is used to define a method as a "setter" or "getter" for a logical property, or to define a field for being serialized and deserialized as a specific logical property.

The annotation value indicates the name of the property in the JSON object, by default it takes the java name of the method or field.

public class Student {
  @JsonProperty("bookId")
  private String id;
  private String name;

  @JsonProperty("name")
  public void setFirstName(String name) {
     this.name = name;
  }

  @JsonProperty("name")
  public String getFirstName() {
     return name;
  }

  @JsonProperty
  public int getRating() {
     return StudentRating.getRatingFor(name);
  }
}

Consider allowing overriding of the default EndpointAccessChecker bean

@campbellbartlett commented on Mon Dec 30 2019

Would it be possible to consider allowing custom AccessChecker beans to be defined so that a developer could implement their own?

This would be beneficial when using other authentication methods that aren't 0Auth2 or AnonymousAuth (in a project I'm working on, I'm using SocialLogin via auth0.com, which means there is no concept of username / password in the token I'm sending to the backend and the token is authenticated using other mechanisms before reaching the VaadinConnectAccessChecker).

I think it would be as easy as adding:
@ConditionalOnMissingBean to the Bean definition for the EndpointAccessChecker in the EndpointControllerConfiguration.java class.

This way, if a developer wishes to override the default access check behavior they can do so by defining their own Bean that extends the EndpointAccessChecker.


@vlukashov commented on Mon Dec 30 2019

Hi @campbellbartlett! Thanks for the suggestion! I think it makes total sense.

Please note that Vaadin Connect features are being integrated into the core of Vaadin framework (with Vaadin 15 as the target release version). As we make these features a part of our core product, there is no plan to keep maintaining this repo separately.

Would it work for you if I move this issue to the https://github.com/vaadin/flow repo so that it gets implemented in Vaadin 15 instead? There is a team actively working on it now, and this feature can be added rather soon to one of the next Vaadin 15 alphas.

If this is news to you, you could check the quick start page for TypeScript views in Vaadin 15 to get some more info on this, and also to try it out.


@campbellbartlett commented on Mon Dec 30 2019

Thanks! I had no idea Connect was being integrated into Vaadin 15, I’ll check it out. Go ahead and move this issue to wherever it needs to be.

EDIT: updated VaadinConnect... class names to Endpoint..., as the classes were renamed

JS Typed Arrays (like Uint8Array) should be supported

When transferring primitive array-like data using Connect endpoints, I want primitive Java arrays like byte[] to be mapped to typed JS arrays like Int8Array, so that (a) the transfer is more efficient in terms of how many bytes are sent over the wire and (b) I do not have to write the boilerplate code to convert from a typed array into an regular JS array of numbers.

Example:

@VaadinService
public class DataService {
    public void foo(byte[] data) {
    }
}
export function foo(
  data: Int8Array
): Promise<void>

source

CCDM: UI.getCurrent().getElement().appendChild does not work in V15

JavaScriptBootstrapUI puts the view in a wrapper appended as a virtualChild.

We might override the getElement() to return the wrapper, it apparently works, but breaks $server methods in client side.
After a further investigation, I saw that the problem is that gwt ServerEventObject class puts the $server method in body (put attention in the initPromiseHandler) whereas calls to this method from server side are bound to the wrapper element (see PublishedServerEventHandlerRpcHandler.invokeMethod).

Files are not served from the configured `publicPath` in webpack

Description of the bug

In webpack.generated.js the public path is configured as

  output: {
    filename: `${build}/vaadin-[name]-[contenthash].cache.js`,
    path: mavenOutputFolderForFlowBundledFiles,
    publicPath: 'VAADIN/',
  },

Many plugins such as css-loader and file-loader uses the publicPath to decide where to put files that can be loaded by the browser. However, Vaadin does not serve these files so the loaders just fail to work.

Minimal reproducible example

https://github.com/Artur-/css-test

Expected behavior

A CSS file that includes an image as background: url(./smiley.jpg) should work

Actual behavior

The url is correctly rewritten to http://localhost:8080/VAADIN/7d75bb859852dba3f174ee59db92b2c2.jpg or similar and the file is correctly created in ./target/classes/META-INF/VAADIN/7d75bb859852dba3f174ee59db92b2c2.jpg (when in production mode) but the file is not served.

Versions:

- Vaadin / Flow version: 14

Relocating a view file requires a server restart

While the app is running, if I move a view file from frontend/views/another-view/another-view.ts to frontend/views/another-view.ts and update the index.ts file respectively, the Webpack compilation doesn't run properly until I restart the app.

support `@VaadinService` classes from .jars, without Java sources

When creating UI in TypeScript in a Vaadin project, I want to use auto-generated TS wrappers for @VaadinService classes that I do not have Java sources for, so that I can implement @VaadinService classes in other JVM languages (Kotlin, Scala) or in separate .jar artifacts.

This discussion has started as the issue vaadin/flow#327 in the Vaadin Connect repo (a Vaadin Labs project).

flow-client should not cause TS errors depending tsconfig.json of user project

Description of the bug

Frontend compilation fails because it's trying to build node_modules/@vaadin/form/*.ts as part of the generated project which has a tsconfig.json configuration that's not compatible with those sources.

In the context of user projects it should not matter what TS configuration we choose to use in flow-client. Even if the flow internal tsconfig.json file is updated later, it should not break existing projects which might have conflicting TS configuration for the projects purposes.

Minimal reproducible example

  1. Download app from https://start.vaadin.com/?preset=try-ts
  2. Edit pom.xml to use V17 and Flow 3.2-SNAPSHOT:
    <properties>
        ...
        <vaadin.version>17.0.0.alpha5</vaadin.version>
        <flow.version>3.2-SNAPSHOT</flow.version>
    </properties>
    ...
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.vaadin</groupId>
                <artifactId>flow-bom</artifactId>
                <version>${flow.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            ...
        </dependencies>
    </dependencyManagement>
    
  3. Try to run the project with mvn

Expected behavior

Frontend compilation should not fail

Actual behavior

You see:

ERROR in [at-loader] ./node_modules/@vaadin/form/BinderNode.ts:115:9 
    TS2339: Property 'flat' does not exist on type '(readonly ValueError<any>[])[]'.
...
------------------ Frontend compilation failed. ------------------

Versions:

  • Vaadin / Flow version: Vaadin 17 alpha, Flow 3.2-SNAPSHOT

Workaround

Can be workarounded in the generated project by editing tsconfig.json to be compatible with the tsconfig used in flow-client. In this case it is enough to add "lib": ["DOM", "ES2019.Array"], to the "compilerOptions" section.

add a @TypeScriptType annotation to configure the generated TypeScript types

When defining Java types that are used from TypeScript, I want have a @TypeScriptType annotation that I can put on a Java property to define its type in the generated TypeScript entity, so that I can avoid downcasting in cases when I know the how this property is represented in JSON / JavaScript at the run time.

The new annotation should work with all ways of defining a TypeScript property: fields, getters and setters, getters and setters annotated with @JsonProperty.

For example, as of Spring Boot 2.2 / Jackson 2.10 the UUID Java type has a default serializer that converts UUIDs to strings. In this case it would be more convenient to map Java UUID to TS string rather than to any, which is the default for non-primitive types.

Expected:

public class Request {
    @TypeScriptType("string")
    private UUID uuid1;
    private UUID uuid2;
}
export default interface Request {
  uuid1: string;
  uuid2: any;
}

Tight coupling between VaadinConnectController and VaadinConnectAccessChecker

VaadinConnectAccessChecker instance is created in VaadinConnectControllerConfiguration, BUT its non-final field xsrfProtectionEnabled is set by the VaadinConnectController in its constructor. That makes VaadinConnectAccessChecker a stateful singleton:

  1. Before VaadinConnectController was initialized it has one state
  2. After VaadinConnectController was initialized it may have different state

In addition to that, the state can be mutated any time via

    /**
     * Enable or disable XSRF token checking in endpoints.
     *
     * @param xsrfProtectionEnabled enable or disable protection.
     */
    public void enableCsrf(boolean xsrfProtectionEnabled) {
        this.xsrfProtectionEnabled = xsrfProtectionEnabled;
    }

VaadinSession should only be created if needed

Right now, VaadinService forces a VaadinSession to always exist: https://github.com/vaadin/flow/blob/master/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java#L695

When you are only creating TS views, you do not need a VaadinSession. In some cases you do not even need a HttpSession. While the memory overhead is probably small, and the time spent locking an unnecessary session is probably minimal, it is not really needed for anything so the time spent is just waste.

Creating the VaadinSession eagerly is not only a bit confusing: "Do I need this for a stateless app?" but also raises questions if the app is broken as you constantly see rows like this in the log:

com.vaadin.flow.server.VaadinSession     : A VaadinSession instance not associated to any service is getting unbound. Session destroy events will not be fired and UIs in the session will not get detached. This might happen if a session is deserialized but never used before it expires.

Documentation should make it clear that VaadinSession is only needed for stateful UIs and you should store other information such as login tokens outside the VaadinSession (inside the HttpSession if you so choose). The code should then follow the documentation

The handling of "namesake" Endpoints in Flow 4

In Vaadin 17 preview, with flow version 4.0.0.alpha3

If you have class Endpoint1 (in package A) as...

@Endpoint
@AnonymousAllowed
@Component("notEndpoint")
public class Endpoint1 {

    public String meriKreikan2() {
        return "Kreikan Meri 2!";;
    }
}

And then another Endpoint1 class in package B as...

@Endpoint
@AnonymousAllowed
public class Endpoint1 {
    public String meriKreikan1() {
        return "Kreikan Meri 1!";
    }
}

Outcome is that generated typescrypt Endpoint1 class will look something like this...

import client from './connect-client.default';

function _meriKreikan1(): Promise<string> {
  return client.call('Endpoint1', 'meriKreikan1');
}
export {_meriKreikan1 as meriKreikan1};

function _meriKreikan2(): Promise<string> {
  return client.call('Endpoint1', 'meriKreikan2');
}
export {_meriKreikan2 as meriKreikan2};

I assume there should be some duplicate endpoint checks somewhere? I didn't notice even warning when these generated classes were made. Also it was even happy to that two endpoints shared identical public methods. Spring is only thing that noticed duplicates, that's why spring name had to be renamed in Component annotation.

Assuming you try to call both meriKreikan1 and meriKreikan2 from the client side, one of those will work, and another will give 404. No good errors shown anywhere to give a hint that it was caused by duplicate endpoints.

(Vaadin 15) Currently no option to redirect Edge/IE users to custom page

Original Forum Post: https://vaadin.com/forum/thread/18201160/vaadin-15-how-to-redirect-edge-old-browser-users

With Vaadin 15+ the old Edge Browser is no longer supported. When entering, a standard website is shown, which says that the browser is not supported. On this website the user can decide to visit anyways. When the user clicks this an endless loading indicator + white page is shown.

In Vaadin 14 I simply redirected to a static webpage. Sadly this is not working anymore. Here is the old code working in Vaadin 14:

    @Override
    public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
        ui = beforeEnterEvent.getUI();

        if (ui.getSession().getBrowser().isIE() || ui.getSession().getBrowser().isEdge()) {
            LOG.debug("edge / ie browser not supported redirect");
            Redirect.redirectTo("/browser-notsupported.html");
        }
    }

Is there a way i can do something similar in Vaadin 15? Is there maybe also a way to directly show my custom not supported page and not show the vaadin integrated warning page at all?

optimize TS generated Model output

At some time we should remove unused imports from Binder and FormValidator among the ts-ignore comment.

// @ts-ignore
import {Binder, ObjectModel, AnyModel, StringModel, NumberModel, ArrayModel, BooleanModel, PrimitiveModel, Required} from '@vaadin/flow-frontend/Binder'
// @ts-ignore
import {Email,Null,NotNull,NotEmpty,NotBlank,AssertTrue,AssertFalse,Negative,NegativeOrZero,Positive,PositiveOrZero,Size,Past,PastOrPresent,Future,FutureOrPresent,Digits,Min,Max,Pattern,DecimalMin,DecimalMax} from '@vaadin/flow-frontend/FormValidator'

CCDM: importing `generated-flow-imports` in `index.ts` does not create a valid `stats.json` for production

1.- in flow-tests/test-embedding/embedding-test-assets run mvn install
2.- in flow-tests/test-embedding/test-embedding-production enable client side bootstrapping

<properties>
    ...
    <vaadin.useDeprecatedV14Bootstrapping>false</vaadin.useDeprecatedV14Bootstrapping>
<properties>

3.- run mvn verify and every tests will fail
4.- create a frontend/index.ts file with just this content

import '../target/frontend/generated-flow-imports';

5.- run mvn verify and most tests pass but those that need view templates (there are exceptions in console when reading the template content from stats)
6.- check the content of target/classes//META-INF/VAADIN/config/stats.json and we can see that there are no modules for the required template

Otherwise by running mvn verify -Dvaadin.useDeprecatedV14Bootstrapping=true all tests pass, and the content of the stats file has the correct modules.

Note: that the import in the index.js file is needed since the application is exported as webcomponents and vaadin bootstrap is not needed but custom pages that includes the web component.

Other options for fixing webcomponents might be to deliver two bundles by webpack, one for the index.html that serves regular vaadin pages via index.ts, and another with the webcomponents with just the generated imports file

plugins added through webpack.config.js should have higher priority than the Flow-default

I want to use a different webpack loader for .css files (NOTE: Flow generates a webpack.generated.js that uses raw-loader for that).

This is what I have in webpack.config.js

module.exports = merge(flowDefaults, {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "lit-css-loader"
      }
    ]
  }
});

I want that to be sufficient to override the Flow defaults. With Flow 3.0.0.alpha7 it is not.

Investigation:

So the problem is that the merge appends new values to the other, try this:

module.exports = merge(flowDefaults, {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['lit-css-loader']
      }
    ]
  },
});
console.log(module.exports.module.rules);

then run webpack and you get

[ { test: /\.js$/, use: [ [BabelMultiTargetLoader] ] },
  { test: /\.ts$/,
    use: [ [BabelMultiTargetLoader], 'awesome-typescript-loader' ] },
  { test: /\.css$/i, use: [ 'raw-loader' ] },
  { test: /\.css$/i, use: [ 'lit-css-loader' ] } ]

CCDM: links do not work as in V14

In V14 router-link goes to the same view even when it does not handles that route.

Clicking on the link in the below snippet, V14 continues showing the same UI, otherwise V15 shows a router not found

@Route("foo")
public class MyView extends Div {
    public MyView() {
        Anchor anchor = new Anchor("bar", "click-me");
        anchor.getElement().setAttribute("router-link", true);
        add(anchor);
    }
} 

NOTE: It happens the same when calling getPage().setLocation('bar')
NOTE: in V14 reloading the page with the new location returns a not found like V15 does.

Make it easier to debug an empty @Endpoint class

If you e.g. forget to mark your endpoint method as public, then there will not be any generated .ts file for your @Endpoint class. This may lead you towards investigating whether the file is detected at all or whether the whole code generation is somehow not working.

If there would instead be an empty generated .ts file, then it would be easier to pinpoint the source of the problem. It would be even better if the generated file would contain a comment that points out that there aren't any public methods in the Java class.

Another alternative would be that the generator fails with a clear error in this kind of situation.

Add support for a RouterLink to client-side views

Vaadin 17.

RouterLink assumes you want to navigate to a java component. If I want to navigate to a client-side view, I need to create an Anchor and set the router-link attribute manually. It would be handy to have a new RouterLink(String href, String text) option.

Consider changing the response format for VaadinConnectException to use the RFC7807 standard

When calling Vaadin Service methods, Connect validates the parameters passed to the server-side (based on the JSR-380 spec. If validation fails the error details are propagated to the client side using Connect's custom protocol based on the VaadinConnectException class (see https://github.com/vaadin/vaadin-connect/blob/master/doc/vaadin-connect-exceptions.asciidoc).

There is an RFC that aims at standardizing this scenario: RFC 7807 - Problem Details for HTTP APIs.

Vaadin should consider changing the approach to passing validation error details over HTTP to the client-side so that it uses the RFC instead of the own custom implementation.

Keeping aside the generic 'following standards is better than not following' mantra, some immediate benefits from following this RFC in this case may be

  • Interop with tooling (Postman comes to mind, though I haven't verified whether they use something sensible based on the standard)
  • Interop with other frameworks, e.g. Spring MVC which to my understanding is using that standard
  • Developer familiarity

Accessing large data collections from the frontend (v1)

When using a Vaadin Grid component in a TypeScript view I want to have an easy way of loading the data lazily so that users do not have to wait until the entire database is loaded into their browsers when they open that page.

In scope (v1)

  • An easy-to-use offset/limit-based Java backend API to expose pageable collections
  • An easy-to-use offset/limit-based JS frontend API to consume pageable collections
  • The backend is stateless (i.e. nothing is left in the server memory after a page request)
  • Support for sorting (possibly by several columns) in the Java / JS API
  • Support for filtering (possibly by several columns) in the Java / JS API
  • Support for counting items (with filters applied) in the Java / JS API
  • Counting items in the collection can be done in the same round trip with fetching a page
  • Support for sub-properties for sorting and filtering (e.g. customer.address.country)
  • Reasonable defaults (no exceptions) if the backend collection is modified between two page requests

Out of scope (v1):

  • The API browser gives a ‘nice’ view of methods returning a pageable collection
  • An easy-to-use cursor-based Java backend API to expose pageable collections
  • An easy-to-use cursor-based JS frontend API to consume pageable collections
  • Guarantees of consistency if the backend collection is modified between the two calls when using offset/limit-based pagination

Servlet in Production mode shows error dialog

See ClientSideExceptionHandlingIT::productionModeExceptions .

In CCDM branch there is no anymore compatibility mode.
The test fails in NPM mode.
The view is opened using URI mapping view-production (which is handled by the ProductionModeViewTestServlet working in production mode).
In production mode the client side exception should not be shown (even though the client side is not built for production).

The test is disabled via @Ignore in CCDM branch. It should be enabled back once the ticket is fixed.

API for read-only caching for endpoint responses

When creating an endpoint, I want to add a @Cacheable annotation to the endpoint class / individual methods to have the generated Service Worker cache the responses from such endpoints, so that I can call such endpoints offline (and get back the last successful backend response from the cache).

DoD:

  • there is a @Cacheable annotation (can be added both to the endpoint class or individual endpoint methods)
  • after calling a cacheable endpoint method (online), the same method can be called again offline and return the same result

Implementation notes: There is a throw-away prototype for this feature in vlukashov/v18-offline-app@79d940c. It shows that cacheable endpoints need to work over HTTP GET requests rather than the default HTTP POST as POST requests cannot be cached by the Service Worker.

V15 Endpoint / entity generation should happen during build time in dev mode

In the actual version of V15, when in development mode, endpoints and related entities are generated only when the application starts up / during runtime. This behavior is un-intuitive and unexpected, since most tool chains, like for instance Lombok, create generated sources during build / compilation time, so that these can be used immediately for code development (e.g. for code completion or to prevent compilation errors).

Forcing the user to start the application when it's maybe still in a state of not being ready for a run (for instance during migration or early development phases) might lead to unnecessary frustration or irritation.

Request is, that small lightweight operations, that occur more often during the development of an application, but do not have such a huge impact on the performance should not need the application run (thus should not be part in the build-frontend goal).

To be more concrete with this issue the tequest is, that it should be possible to generate endpoints and entities during compilation time.

Potential ways of solving that could be:

  1. extend prepare-frontend to automatically do that, too
  2. create a new goal for the maven plugin (e.g. generate-frontend-sources), that could be triggered during maven's generate-source lifecycle phase
  3. make the maven plugin optionally configurable, which parts of the build-frontend actions should happen during the prepare-frontend step (not sure if that is really helpful or useful in the end, just some blurry idea)

I'd say the 2nd option makes the most sense.

See the internal Slack discussion for more context.

re-enable ignored test in `CompositedTest`

When moving generators from connect to flow, there is a test that fails only in TC due to some weird dependency conflict: CompositedTest#templateInsideComposite_compositeCanBeAdded

A workaround was send in the PR: vaadin/flow#6752 but it implies some hacks like copying and modifying a swagger class, thus we decided not to merge it.

NPE when navigating to server side view during first server side view initialization

Description of the bug

If you navigate to another Java view while Flow is being initialized, navigation fails, the application disappears and only a blank page is shown

Minimal reproducible example

App with three views:

  1. root view (TS)

  2. view 1 (Java)

  3. view 2 (Java)

  4. Open the "root" view

  5. Navigate to "view 1"

  6. While navigation is in progress, navigate to "view 2"

Test project: https://github.com/Artur-/routing-error

  1. Run mvn
  2. open http://localhost:8080/
  3. Rapidly click "View 1" and then "View 2"

Expected behavior

I should end up on "view 2"

Actual behavior

A blank screen is shown and a JS exception from https://github.com/vaadin/flow/blob/9a67dbc4393af97e57c36dcd7c112895e0687b9d/flow-client/src/main/resources/META-INF/resources/frontend/Flow.ts#L127 stating that onBeforeEnter cannot be set because this.container is undefined.

Versions:

- Vaadin / Flow version: 17
- Java version: 13
- OS version: Mac
- Browser version (if applicable): Chrome
- Application Server (if applicable): Spring Boot
- IDE (if applicable): VS Code

CCDM: v15 webpack build is slower than v14

See StartupPerformanceIT.devModeInitializerToWebpackUpIsBelow5500ms , the threshold should be set to 5500 but in v15 it needs to be 9500 to pass

Considerations:

  • The project for measuring performance produces a mostly empty entry point target/frontend/generated-flow-imports.js only importing the @vaadin/flow-frontend/dndConnector-es6.js, in consecuence webpack in does not visit, bundle and optimize complex dependencies
  • in v15 the entrypoint is frontend/index.ts which includes Router, Flow and Connect having to optimize a big amount of code. In a fast system it takes about 3000ms extra.
  • in addition, v15 webpack also visits BootstrapHandler.js and client-nnnnn.cache.js files in order to include them in the bundle.

Probably this increment of the build time is assumable, because browser does not need extra requests for getting the client-cache code.

Allow non-centralized route configuration for client side views

On the server side, you can create your view and define the routing info and related metadata for the view on the view itself as

@Route("some/route")
@PageTitle("my fine page");

There is no need to maintain a separate, global routing configuration.

There should be a similar way for client side routes so you could, instead of defining an explicit router configuration like

const routes = [
  {
	path: '',
	component: 'main-view', 
	action: async () => { await import ('./views/main/main-view'); },
	children: [
		{
			path: 'master-detail',
			component: 'master-detail-view', 
			action: async () => { await import ('./views/masterdetail/master-detail-view'); }
		},

you could do e.g.

@customElement('main-view')
export class MainEndpoint extends LitElement {
@Route("some/route")
@RouteLayout("main-view")
@PageTitle("my fine page");
@customElement('master-detail-view')
export class MasterDetailViewElement extends LitElement {

This would require being able to read annotations at build time to generate the same router configuration as above and make code splitting work. Also the runtime @customElement annotation needs to be read at build time

Potentially related to microsoft/TypeScript#30723

Non-existant pages return 200 instead of 404 in Vaadin 17

Description of the bug

If you create an app with only a "/" route, then all other URLs must return 404

Minimal reproducible example

vaadin init example
cd example
mvn
curl -I http://localhost:8080/robots.txt

Expected behavior

robots.txt does not exist so the request should return 404

Actual behavior

robots.txt returns 200 and search engines try to parse the Vaadin view as a robots.txt file

Versions:

- Vaadin / Flow version: 14

Allow customizing loading indicator for the first page load

When adding a splash screen to a V15+ app I want to keep the splash screen visible until the first view is completely loaded. In case if the first view is a server-side view I want to

  • either have an event telling me when the first view is done loading (so that I can hide my splash screen then)
  • or have an API to customize the first show of the loading indicator (so that I can hide my splash screen from the index.html template and replace it with the Flow loading indicator for the first view load).

Right now this can be done using internal API, which is unsupported can be changed without any prior warning:

index.ts:

// WARNING: accessing internal Flow APIs
// This may break on any Flow version upgrade without any prior notice
const original = (window as any).Vaadin.Flow.loading;
(window as any).Vaadin.Flow.loading = (action: boolean) => {
  // When this is called first time (action = true), do nothing.
  // That's when Flow starts loading and the splash screen is still visible.
  // There is no need to show the Flow loading indicator at that time.

  // When this is called second time (action = false), hide the splash screen
  // and restore the default behavior.
  // Next time Flow starts loading something the default loading indicator will
  // be visible.
  if (!action) {
    document.querySelector('#splash-screen')!.classList.add('loaded');
    (window as any).Vaadin.Flow.loading = original;
  }
};

source: https://vaadin.com/forum/thread/18285600/how-to-implement-initial-page-loading-animation-like-on-vaadin-com

Long polling Push bug in 3.0

ReconnectLongPollingIT test is failing in 3.0.
There is a bug which it reveals and it needs to be fixed.
The test is disabled in 3.0 for now.

The test works in the master branch, so there is no bug in the master.
But it fails in 3.0.
I'm not sure whether there is a bug specific for 3.0 or it's requires some special conditions which are not met in the master ( I don't expect serious difference between these branches).

There are two tests methods: both fails.

The test is for Long polling Push behavior on connection drop.

Here is a short description about one messageIsQueuedOnDisconnect method:

  • ClientServerCounterUI contains two buttons "incrementCounter" and "startTimer"
  • the "startTimer" starts a thread which updates the text in the element
  • the "incrementCounter" updates the text in another element once it's clicked.
  • test starts timer at the very beginning. So the value is updated repeatedly.

Test uses a proxy to access the server. The connection is done to the proxy (not the server directly).
After the server start the proxy is disconnected.

Then click on the "incrementCounter" is done.
This click would have incremented a value in the info element if connection had not been lost.
But since there is no a connection nothing happens.

But then connection is restored.
The counter which is updated from the server side increases its value after the connection restoration.

The expected result : the click message should reach the server from the client side (even thought it has been done during connection absence) and server should respond with message which updates the counter.
But it doesn't happen.

The same test for Websocket transport works file.

The same tests for the master branch works as expected.

Converter for client-side forms

As a developer, when creating client-side forms, and binding a text field to a non-string model data, Vaadin could convert the value for me.

DoD

  • A convertor interface similar to the Java version (flow/flow-data/src/main/java/com/vaadin/flow/data/converter/Converter.java).

  • Able to set a convertor to a model setConvertor(model, converter).

  • Able to get a convertor from a model getConvertor(model).

  • in the ts-forms-demo, remove hack returning LocalTime as string in the endpoint to return a proper object that has the proper convertor to work in client

CCDM: when invalidating session programatically, previous page is reloaded

  • open 'foo' router
  • navigate to 'invalidate' by clicking on the link

'invalidate' view should be displayed after the navigation happens, but 'foo' is shown instead.

@Route(value = "foo", layout = Layout.class)
public class MyView extends Div  {
    public MyView() {
        getElement().appendChild(ElementFactory
                .createRouterLink("invalidate", "invalidate"));
    }
}

@Route(value = "invalidate", layout = Layout.class)
public class MyInvalidateView extends Div implements BeforeEnterObserver {
    @Override
    public void beforeEnter(BeforeEnterEvent event) {
        VaadinSession.getCurrent().getSession().invalidate();
    }
}

Probably happening because window.reload is executed before history.pushState updating the route.

IndexHtmlRequestHandler#getIndexHtmlDocument: Error message improvement suggestions

I catch a Exception, When I download Vaadin 16 Business Starter and copy /target/index.ts(html) to fronted dir. Then run command :

mvn jetty:run-exploded -Dvaadin.productionMode

Open browser: 127.0.0.1:8080

HTTP ERROR 500
Problem accessing /. Reason:

    Server Error

The IDEA shows Error is

java.io.IOException: Failed to load content of 
'D:\broderick\develop\bklab\vaadin-16-business\frontendindex.html'. 
It is required to have 
'D:\broderick\develop\bklab\vaadin-16-business\frontendindex.html'
 file when using client side bootstrapping.
	at com.vaadin.flow.server.communication.IndexHtmlRequestHandler
.getIndexHtmlDocument(IndexHtmlRequestHandler.java:179)
	

There No file separator in 'frontendindex.html'. Should actually be '.\frontend\index.html'

Then I looking the current source code :

    private static Document getIndexHtmlDocument(VaadinRequest request)
            throws IOException {
        String index = FrontendUtils.getIndexHtmlContent(request.getService());
        if (index != null) {
            return Jsoup.parse(index);
        }
        String frontendDir = FrontendUtils.getProjectFrontendDir(
                request.getService().getDeploymentConfiguration());

>      String message = String
>              .format("Failed to load content of '%1$sindex.html'."
>                      + "It is required to have '%1$sindex.html' file when "
>                      + "using client side bootstrapping.", frontendDir);

        throw new IOException(message);
    }

So, can there Improve to like this :

        String message = String
                .format("Failed to load content of '%1$s%2$sindex.html'."
                        + "It is required to have '%1$s%2$sindex.html' file when "
                        + "using client side bootstrapping.", frontendDir, File.separator)
                .replaceAll("\\\\+", "\\\\")
                .replaceAll("/+", "/");

the output is

Failed to load content of 
'D:\broderick\develop\bklab\vaadin-16-business\frontend\index.html'.
It is required to have 
'D:\broderick\develop\bklab\vaadin-16-business\frontend\index.html' 
file when using client side bootstrapping.

Versions:

- Vaadin / Flow version: 3.2-SNAPSHOT 3.1.1
- Java version: java 14
- OS version: Windows 10
- Browser version (if applicable): Microsoft Edge Canary
- Application Server (if applicable):
- IDE (if applicable): IDEA

Referencing the build directory in index.ts could be problematic

I'm trying V15 and I see now there is a fronted/index.ts file to provide where you instantiate a Flow object passing the path ../target/frontend/generated-flow-imports. For the same reasons discussed in vaadin/flow#6749, referencing the target directory in versioned content can be problematic as it can be different in different build environments.

Forward to serverview with ccdm

Add tests validating that forward to serverview works with ccdm.

If it doesn't work, fix it.

Following TODOs are removed from code, but searching by the above and below code lines should lead to the code in JavaScriptBootstrapUI:

clientConnect

        if (getForwardToClientUrl() != null) {
            navigateToClient(getForwardToClientUrl());
            acknowledgeClient();

            // TODO: Handle forward to server view.

        } else if (isPostponed()) {

leaveNavigation

        navigateToPlaceholder(new Location(PathUtil.trimPath(route)));

        // TODO: Handle forward to server view which may happen in a
        // BeforeLeaveEvent or deny the forward or reroute to a server view in
        // BeforeLeaveEvent.

        // Inform the client whether the navigation should be postponed
        if (isPostponed()) {
            cancelClient();
        } else {
            acknowledgeClient();
        }

renderViewForRoute

                navigateToPlaceholder(location);

                // TODO: Handle forward to server view which may happen in a
                // BeforeLeaveEvent or deny the forward or reroute to a server view in
                // BeforeLeaveEvent.

                if (!isPostponed()) {
                    // Route does not exist, and current view does not prevent
                    // navigation thus an error page is shown
                    handleErrorNavigation(location);
                }

Consider also JavaScriptNavigationStateRenderer:

            if (continueNavigationAction != null
                    // We're trying to navigate to a client view.
                    && JavaScriptBootstrapUI.ClientViewPlaceholder.class
                            .isAssignableFrom(getNavigationState()
                                    .getNavigationTarget())) {

                // FIXME: This could be invoked from ClientViewPlaceholder or
                // another client placeholder view type.
                jsUI.navigateToClient(
                        event.getLocation().getPathWithQueryParameters());
            }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.