Skip to content

Commit

Permalink
Merge pull request #23 from strategyobject/feature/di
Browse files Browse the repository at this point in the history
Implement DI with Guice
  • Loading branch information
kalaninja authored Jul 4, 2022
2 parents 7d3b515 + 43fa079 commit 74ce9e8
Show file tree
Hide file tree
Showing 189 changed files with 1,541 additions and 985 deletions.
12 changes: 7 additions & 5 deletions api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ dependencies {
implementation project(':scale')
implementation project(':transport')

implementation 'com.google.inject:guice:5.1.0'

annotationProcessor project(':pallet:pallet-codegen')
annotationProcessor project(':rpc:rpc-codegen')

testImplementation project(':tests')

testAnnotationProcessor project(':pallet:pallet-codegen')

testImplementation 'ch.qos.logback:logback-classic:1.2.11'
testImplementation 'org.testcontainers:testcontainers:1.17.1'
testImplementation 'org.testcontainers:junit-jupiter:1.17.1'
testImplementation 'org.testcontainers:testcontainers:1.17.2'
testImplementation 'org.testcontainers:junit-jupiter:1.17.2'
testImplementation 'org.awaitility:awaitility:4.2.0'
}

testAnnotationProcessor project(':pallet:pallet-codegen')
}
45 changes: 21 additions & 24 deletions api/src/main/java/com/strategyobject/substrateclient/api/Api.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,27 @@
package com.strategyobject.substrateclient.api;

import com.strategyobject.substrateclient.pallet.GeneratedPalletResolver;
import com.strategyobject.substrateclient.pallet.PalletResolver;
import com.strategyobject.substrateclient.rpc.RpcGeneratedSectionFactory;
import com.strategyobject.substrateclient.rpc.api.section.State;
import com.google.inject.Module;
import com.strategyobject.substrateclient.pallet.PalletFactory;
import com.strategyobject.substrateclient.rpc.RpcSectionFactory;
import com.strategyobject.substrateclient.transport.ProviderInterface;
import lombok.NonNull;
import lombok.val;
import lombok.RequiredArgsConstructor;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

/**
* Provides the ability to query a node and interact with the Polkadot or Substrate chains.
* It allows interacting with blockchain in various ways: using RPC's queries directly or
* accessing Pallets and its APIs, such as storages, transactions, etc.
*/
@RequiredArgsConstructor
public class Api implements AutoCloseable {
private final @NonNull ProviderInterface providerInterface;
private final @NonNull PalletResolver palletResolver;
private final @NonNull RpcSectionFactory rpcSectionFactory;
private final @NonNull PalletFactory palletFactory;
private final Map<Class<?>, Object> resolvedCache = new ConcurrentHashMap<>();

private Api(@NonNull ProviderInterface providerInterface) {
this.providerInterface = providerInterface;

val state = RpcGeneratedSectionFactory.create(State.class, providerInterface);
this.palletResolver = GeneratedPalletResolver.with(state);
}

/**
* Resolves the instance of a rpc by its definition.
Expand All @@ -35,9 +30,8 @@ private Api(@NonNull ProviderInterface providerInterface) {
* @param <T> the type of the rpc
* @return appropriate instance of the rpc
*/
public <T> T rpc(Class<T> clazz) {
return clazz.cast(resolvedCache
.computeIfAbsent(clazz, x -> RpcGeneratedSectionFactory.create(x, providerInterface)));
public <T> T rpc(@NonNull Class<T> clazz) {
return clazz.cast(resolvedCache.computeIfAbsent(clazz, rpcSectionFactory::create));
}

/**
Expand All @@ -48,18 +42,21 @@ public <T> T rpc(Class<T> clazz) {
* @return appropriate instance of the pallet
*/
public <T> T pallet(@NonNull Class<T> clazz) {
return clazz.cast(resolvedCache
.computeIfAbsent(clazz, palletResolver::resolve));
}

public static Api with(ProviderInterface providerInterface) {
return new Api(providerInterface);
return clazz.cast(resolvedCache.computeIfAbsent(clazz, palletFactory::create));
}

@Override
public void close() throws Exception {
if (providerInterface instanceof AutoCloseable) {
((AutoCloseable) providerInterface).close();
if (rpcSectionFactory instanceof AutoCloseable) {
((AutoCloseable) rpcSectionFactory).close();
}
}

public static ApiBuilder<DefaultModule> with(@NonNull Supplier<ProviderInterface> providerInterface) {
return with(new DefaultModule(providerInterface.get()));
}

public static <M extends Module> ApiBuilder<M> with(@NonNull M module) {
return new ApiBuilder<>(module);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.strategyobject.substrateclient.api;

import com.google.inject.Guice;
import com.google.inject.Module;
import com.strategyobject.substrateclient.transport.ProviderInterface;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.val;

import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Function;

@RequiredArgsConstructor
public class ApiBuilder<M extends Module> {
private final @NonNull M module;

public ApiBuilder<M> configure(@NonNull Consumer<M> configuration) {
configuration.accept(module);
return this;
}

public <N extends Module> ApiBuilder<N> reconfigure(@NonNull Function<M, N> configuration) {
return new ApiBuilder<>(configuration.apply(this.module));
}

public CompletableFuture<Api> build() {
val injector = Guice.createInjector(new RequireModule(), module);
val provider = injector.getInstance(ProviderInterface.class);
val result = provider.isConnected() ?
CompletableFuture.<Void>completedFuture(null) :
provider.connect();

return result.thenApply(ignored -> injector.getInstance(Api.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.strategyobject.substrateclient.api;

import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.strategyobject.substrateclient.common.types.AutoRegistry;
import com.strategyobject.substrateclient.pallet.GeneratedPalletFactory;
import com.strategyobject.substrateclient.pallet.PalletFactory;
import com.strategyobject.substrateclient.rpc.GeneratedRpcSectionFactory;
import com.strategyobject.substrateclient.rpc.RpcSectionFactory;
import com.strategyobject.substrateclient.rpc.api.section.State;
import com.strategyobject.substrateclient.rpc.registries.RpcDecoderRegistry;
import com.strategyobject.substrateclient.rpc.registries.RpcEncoderRegistry;
import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry;
import com.strategyobject.substrateclient.scale.registries.ScaleWriterRegistry;
import com.strategyobject.substrateclient.transport.ProviderInterface;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.val;

import java.util.function.Consumer;

@RequiredArgsConstructor
public class DefaultModule extends AbstractModule {
private final @NonNull ProviderInterface providerInterface;

private Consumer<ScaleReaderRegistry> configureScaleReaderRegistry = this::autoRegister;
private Consumer<ScaleWriterRegistry> configureScaleWriterRegistry = this::autoRegister;
private Consumer<RpcDecoderRegistry> configureRpcDecoderRegistry = this::autoRegister;
private Consumer<RpcEncoderRegistry> configureRpcEncoderRegistry = this::autoRegister;

private void autoRegister(AutoRegistry registry) {
registry.registerAnnotatedFrom("com.strategyobject.substrateclient");
}

public DefaultModule configureScaleReaderRegistry(Consumer<ScaleReaderRegistry> configure) {
configureScaleReaderRegistry = configureScaleReaderRegistry.andThen(configure);
return this;
}

public DefaultModule configureScaleWriterRegistry(Consumer<ScaleWriterRegistry> configure) {
configureScaleWriterRegistry = configureScaleWriterRegistry.andThen(configure);
return this;
}

public DefaultModule configureRpcDecoderRegistry(Consumer<RpcDecoderRegistry> configure) {
configureRpcDecoderRegistry = configureRpcDecoderRegistry.andThen(configure);
return this;
}

public DefaultModule configureRpcEncoderRegistry(Consumer<RpcEncoderRegistry> configure) {
configureRpcEncoderRegistry = configureRpcEncoderRegistry.andThen(configure);
return this;
}

@Override
protected void configure() {
try {
bind(ProviderInterface.class).toInstance(providerInterface);
bind(RpcSectionFactory.class)
.toConstructor(
GeneratedRpcSectionFactory.class.getConstructor(
ProviderInterface.class,
RpcEncoderRegistry.class,
ScaleWriterRegistry.class,
RpcDecoderRegistry.class,
ScaleReaderRegistry.class))
.asEagerSingleton();
bind(PalletFactory.class)
.toConstructor(
GeneratedPalletFactory.class.getConstructor(
ScaleReaderRegistry.class,
ScaleWriterRegistry.class,
State.class
))
.asEagerSingleton();
bind(Api.class)
.toConstructor(
Api.class.getConstructor(
RpcSectionFactory.class,
PalletFactory.class))
.asEagerSingleton();
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}

@Provides
@Singleton
public ScaleReaderRegistry provideScaleReaderRegistry() {
val registry = new ScaleReaderRegistry();
configureScaleReaderRegistry.accept(registry);
return registry;
}

@Provides
@Singleton
public ScaleWriterRegistry provideScaleWriterRegistry() {
val registry = new ScaleWriterRegistry();
configureScaleWriterRegistry.accept(registry);
return registry;
}

@Provides
@Singleton
public RpcDecoderRegistry provideRpcDecoderRegistry(ScaleReaderRegistry scaleReaderRegistry) {
val registry = new RpcDecoderRegistry(scaleReaderRegistry);
configureRpcDecoderRegistry.accept(registry);
return registry;
}

@Provides
@Singleton
public RpcEncoderRegistry provideRpcEncoderRegistry(ScaleWriterRegistry scaleWriterRegistry) {
val registry = new RpcEncoderRegistry(scaleWriterRegistry);
configureRpcEncoderRegistry.accept(registry);
return registry;
}

@Provides
@Singleton
public State provideState(RpcSectionFactory rpcSectionFactory) {
return rpcSectionFactory.create(State.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.strategyobject.substrateclient.api;

import com.google.inject.AbstractModule;
import com.strategyobject.substrateclient.pallet.PalletFactory;
import com.strategyobject.substrateclient.rpc.RpcSectionFactory;
import com.strategyobject.substrateclient.rpc.registries.RpcDecoderRegistry;
import com.strategyobject.substrateclient.rpc.registries.RpcEncoderRegistry;
import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry;
import com.strategyobject.substrateclient.scale.registries.ScaleWriterRegistry;
import com.strategyobject.substrateclient.transport.ProviderInterface;

public class RequireModule extends AbstractModule {
@Override
protected void configure() {
requireBinding(ProviderInterface.class);
requireBinding(ScaleReaderRegistry.class);
requireBinding(ScaleWriterRegistry.class);
requireBinding(RpcDecoderRegistry.class);
requireBinding(RpcEncoderRegistry.class);
requireBinding(RpcSectionFactory.class);
requireBinding(PalletFactory.class);
requireBinding(Api.class);
}
}
Loading

0 comments on commit 74ce9e8

Please sign in to comment.