katharsis-project / katharsis-framework

Katharsis adds powerful layer for RESTful endpoints providing implementenation of JSON:API standard
http://katharsis.io
Apache License 2.0
135 stars 65 forks source link

Simplify setup of Katharsis #184

Closed remmeier closed 7 years ago

remmeier commented 7 years ago

Katharsis is currently quite difficult to setup for newbie. Goal is to simplify the setup for JAX-RS with this ticket. Other containers may follow.

That can be dramatically simplified:

=> two dependencies, zero configuration, regular dependency management setup.

=> Potentially most of this logic can be moved to a KatharsisBoot so that Katharsis-rs, Katharsis-servlet, etc. can be simplified.

the necessary flexiblity is currently already available for this with the module api. => no breaking changes, just a new way to set everything up.

masterspambot commented 7 years ago

This looks really promising! Love the idea behind it!

On Sat, 29 Oct 2016, 18:36 Remo, notifications@github.com wrote:

Katharsis is currently quite difficult to setup for newbie. Goal is to simplify the setup for JAX-RS with this ticket. Other containers may follow.

  • KatharsisFeature feature has to be instantiated
  • some jax-rs properties have to be set
  • modules may need to be registered
  • must be integrated into the dependency injection environment
  • reflections instead of normal dependency injection is used to discover repositories
  • reflections is quite expensive
  • resource classes get discovered (would be implicitly available from discovered repositories)
  • QuerySpec/QueryParams configuratoin

That can be dramatically simplified:

  • user adds dependency to katharsis-rs and e.g. katharsis-cdi
  • katharsis-cdi registers a "ServiceDiscovery" service. Others like spring would to the same.
  • KatharsisFeature gets a default constructor (creates new ObjectMapper, QuerySpec setup, and looks up any ServiceDiscovery implementation.
  • KatharsisFeature gets all modules from ServiceDiscovery
  • KatharsisFeature gets all repositories from ServiceDiscovery
  • KatharsisFeature finds all resources based on the discovered repositories
  • KatharsisFeature gets all exception mappers from ServiceDiscovery

=> two dependencies, zero configuration, regular dependency management setup.

=> Potentially most of this logic can be moved to a KatharsisBoot so that Katharsis-rs, Katharsis-servlet, etc. can be simplified.

β€” You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/katharsis-project/katharsis-framework/issues/184, or mute the thread https://github.com/notifications/unsubscribe-auth/AAjeAZro3ilRQgl_GnGT1utOJHmcD_Cgks5q43YHgaJpZM4KkKAS .

remmeier commented 7 years ago

first version is available for jax-rs/cdi. KatharsisFeature detects katharsis-cdi and then loads everything with that.

as a postive side effect Katharsis starts much quicker. Even if its fallback to Reflections. Before multiple Reflections instances were created and that was very expensive.

corrspt commented 7 years ago

This might not be the place to ask, but I think it applies. What about generic steps regarding integrating Katharsis with other frameworks? (Like the Spark Framework or Play Framework), we basically get a request and can send a response... I've been doing a very hacky set of operations (but it works). I'm using Katharsis 2.1.1 (but I've got a branch with the same things using Katharsis 2.7)

I've seen the integrations with Servlet, JAX-RS but they all seem to use very specific things from those technologies.

I have to use a lot of Katharsis Internals (which of course break when I upgrade), this is my Controller in PlayFramework currently (for Katharsis 2.1.1, I can put the one with Katharsis 2.7 if needed):

Any help would be much appreciated, thanks :)

public static ResourceRegistry resourceRegistry;

    @Inject
    private Injector injector;

    private ResourceRegistry getResourceRegistry() {
        if (resourceRegistry == null) {
            ResourceInformationBuilder resourceInformationBuilder = new ResourceInformationBuilder(new ResourceFieldNameTransformer());
            ResourceRegistryBuilder registryBuilder = new ResourceRegistryBuilder(new ServiceLocator(injector), resourceInformationBuilder);
            resourceRegistry = registryBuilder.build("com.weldnote.api", ConfigFactory.load().getString("api.host"));
        }
        return resourceRegistry;
    }

    @com.avaje.ebean.annotation.Transactional
    @Security.Authenticated(SecuredRequest.class)
    public Result getJsonApiResult(String resource) throws Exception {

        String path = resource;
        Request req = request();
        String requestType = req.method();

        logger.trace("{}Β - {} ", requestType, path);

        PathBuilder pathBuilder = new PathBuilder(getResourceRegistry());
        TypeParser typeParser = new TypeParser();

        JsonApiModuleBuilder jsonApiModuleBuilder = new JsonApiModuleBuilder();
        ObjectMapper om = new ObjectMapper();
        om.registerModule(jsonApiModuleBuilder.build(getResourceRegistry()));

        ControllerRegistryBuilder controllerBuilder = new ControllerRegistryBuilder(getResourceRegistry(), typeParser, om);
        ControllerRegistry controllerRegistry = controllerBuilder.build();

        ExceptionMapperRegistryBuilder b = new ExceptionMapperRegistryBuilder();
        RequestDispatcher sut = new RequestDispatcher(controllerRegistry, b.build("com.weldnote.api.mappers"));

        JsonPath jsonPath = pathBuilder.buildPath(path);

        RequestBody body = null;
        if ("POST".equalsIgnoreCase(requestType)
                || "PUT".equalsIgnoreCase(requestType)
                || "PATCH".equalsIgnoreCase(requestType)
                ){
            play.mvc.Http.RequestBody playBody =  req.body();
            String raw =  new String(playBody.asRaw().asBytes().toArray());
            body = om.readValue(raw, RequestBody.class);
        }

        Map<String,Set<String>> queryParams = new HashMap<>();
        QueryParamsBuilder paramsBuilder = new QueryParamsBuilder();
        JSONAPIParameterParser paramParser = new JSONAPIParameterParser();

        Map<String,Set<String>> filters = paramParser.parseFilters(req.queryString());
        if (!filters.isEmpty()){
            queryParams.putAll(filters);
        }

        Map<String,Set<String>> sorting = paramParser.parseSorting(req.queryString());
        if (!sorting.isEmpty()){
            queryParams.putAll(sorting);
        }

        Map<String,Set<String>> included = paramParser.parseIncluded(req.queryString());
        if (!included.isEmpty()){
            queryParams.putAll(included);
        }

        Map<String,Set<String>> paging = paramParser.parsePaging(req.queryString());
        if (!paging.isEmpty()) {
            queryParams.putAll(paging);
        }

        BaseResponse<?> response = sut.dispatchRequest(jsonPath, requestType, paramsBuilder.buildQueryParams(queryParams), null, body);
        if (response != null){
            String json = om.writeValueAsString(response);
            return status(response.getHttpStatus(), json);
        } else {
            if ("DELETE".equalsIgnoreCase(requestType)){
                return status(204);
            }
        }
        return status(200);

    }
remmeier commented 7 years ago

I think the question is perfectly right here. There is now a KatharsisBoot in katharsis-core that allows to slim down the effort needed to setup a new integration. I'm sure more can be done in this direction. But it is a start. I updated now JAX-RS and I will continue with Servlet/Spring. Then we can see and may further slim down the integrations. I assume the parameter/request/response handling is one candiate.

In your case: consider updating and contributing your integration to katharsis, then it will stay in sync. A lot of things happened since 2.1

remmeier commented 7 years ago

As a next step here I also cleanup katharsis-client. Reflections and package search paths are no longer necessary. Stubs are created dynamically when they are requested by the consumer.

corrspt commented 7 years ago

@remmeier Thanks for replying, really appreciate it πŸ‘

I'm going to take a look at that KatharsisBoot that you mention and see If I can make something out of it. My main issue with the things I've been using is that they feel like a lot of hacks :).

remmeier commented 7 years ago

also updated Spring with a version 3 of the ticket. Closing this ticket.

@corrspt will free to open up a ticket dedicated to the playframework.

corrspt commented 7 years ago

@remmeier Thanks for the updates, I've seen a PR where you add some additional things to KatharsisBoot which I'm waiting on a release to use.

At the moment I can get data and update data, I'm still missing parameters (like sort, filter). Hope to get to it soon πŸ‘

chb0github commented 7 years ago

There was a recent 2.8.1 release. Does this contain what you need?

On Fri, Nov 18, 2016, 11:34 AM Pedro Rio notifications@github.com wrote:

@remmeier https://github.com/remmeier Thanks for the updates, I've seen a PR where you add some additional things to KatharsisBoot which I'm waiting on a release to use.

At the moment I can get data and update data, I'm still missing parameters (like sort, filter). Hope to get to it soon πŸ‘

β€” You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/katharsis-project/katharsis-framework/issues/184#issuecomment-261621275, or mute the thread https://github.com/notifications/unsubscribe-auth/ABaI0CV4Smi53a-4dzX3_3O6bJJePUHOks5q_f2tgaJpZM4KkKAS .

corrspt commented 7 years ago

I don't think it contains this. It's not a big deal, but since if I want to use them I have to wait for a release, I said that :)

I still haven't tried error mapping to see how that is working, hopefully soon.