Skip to content

Commit

Permalink
Merge pull request #12 from valory-xyz/feat/generic-client
Browse files Browse the repository at this point in the history
HTTP client for backend communication
  • Loading branch information
angrybayblade authored Feb 15, 2024
2 parents 7f232aa + 047b929 commit 6283003
Show file tree
Hide file tree
Showing 5 changed files with 305 additions and 31 deletions.
20 changes: 11 additions & 9 deletions backend/operate/ledger/profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@

"""Chain profiles."""

from operate.types import ChainType
from operate.types import ChainType, ContractAddresses

CONTRACTS = {
ChainType.GNOSIS: {
"service_manager": "0x04b0007b2aFb398015B76e5f22993a1fddF83644",
"service_registry": "0x9338b5153AE39BB89f50468E608eD9d764B755fD",
"service_registry_token_utility": "0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8",
"gnosis_safe_proxy_factory": "0x3C1fF68f5aa342D296d4DEe4Bb1cACCA912D95fE",
"gnosis_safe_same_address_multisig": "0x6e7f594f680f7aBad18b7a63de50F0FeE47dfD06",
"multisend": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D",
}
ChainType.GNOSIS: ContractAddresses(
{
"service_manager": "0x04b0007b2aFb398015B76e5f22993a1fddF83644",
"service_registry": "0x9338b5153AE39BB89f50468E608eD9d764B755fD",
"service_registry_token_utility": "0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8",
"gnosis_safe_proxy_factory": "0x3C1fF68f5aa342D296d4DEe4Bb1cACCA912D95fE",
"gnosis_safe_same_address_multisig": "0x6e7f594f680f7aBad18b7a63de50F0FeE47dfD06",
"multisend": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D",
}
)
}
8 changes: 1 addition & 7 deletions backend/operate/services/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,10 @@
from operate.ledger.profiles import CONTRACTS
from operate.services.protocol import OnChainManager
from operate.services.service import Service
from operate.types import (
ChainData,
ServicesType,
ServiceTemplate,
ServiceType,
)
from operate.types import ChainData, ServicesType, ServiceTemplate, ServiceType
from starlette.types import Receive, Scope, Send
from typing_extensions import TypedDict


OPERATE = ".operate"
CONFIG = "config.json"
SERVICES = "services"
Expand Down
19 changes: 4 additions & 15 deletions backend/operate/services/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@
Action,
ChainData,
ChainType,
DeployedNodes,
DeploymentConfig,
DeploymentType,
KeysType,
LedgerConfig,
LedgerType,
Expand Down Expand Up @@ -92,20 +94,6 @@ class DeleteServiceResponse(TypedDict):
"""Delete response."""


class DeployedNodes(TypedDict):
"""Deployed nodes type."""

agent: t.List[str]
tendermint: t.List[str]


class DeploymentType(TypedDict):
"""Deployment type."""

status: Status
nodes: DeployedNodes


class GetDeployment(TypedDict):
"""Create deployment payload."""

Expand Down Expand Up @@ -486,8 +474,9 @@ def new(
service.store()
return service

def update(self, data: ServiceType) -> ServiceType:
def __update(self, data: ServiceType) -> ServiceType:
"""Update service."""
# TODO: Finish implementation
self.hash = data["hash"]
self.keys = data["keys"]
self.variables = data.get("variables", self.variables)
Expand Down
14 changes: 14 additions & 0 deletions backend/operate/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,17 @@ class Status(enum.IntEnum):
STOPPING = 4
STOPPED = 5
DELETED = 6


class DeployedNodes(TypedDict):
"""Deployed nodes type."""

agent: t.List[str]
tendermint: t.List[str]


class DeploymentType(TypedDict):
"""Deployment type."""

status: Status
nodes: DeployedNodes
275 changes: 275 additions & 0 deletions client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
type ServiceHash = string;

enum Action {
STATUS = 0,
BUILD = 1,
DEPLOY = 2,
STOP = 3,
}

enum ChainType {
ETHEREUM = 0,
GOERLI = 1,
GNOSIS = 2,
SOLANA = 3,
}

enum LedgerType {
ETHEREUM = 0,
SOLANA = 1,
}

type LedgerConfig = {
rpc: string;
type: LedgerType;
chain: ChainType;
};

type Key = {
address: string;
private_key: string;
ledger: ChainType;
};

type KeysType = Key[];

type ChainData = {
instances?: string[];
token?: number;
multisig?: string;
};

type ServiceType = {
name: string;
hash: string;
keys: KeysType;
readme?: string;
ledger?: LedgerConfig;
chain_data?: ChainData;
};

type ServicesType = ServiceType[];

type ServiceTemplate = {
rpc: string;
name: string;
hash: string;
image: string;
description: string;
};

enum Status {
CREATED = 0,
BUILT = 1,
DEPLOYING = 2,
DEPLOYED = 3,
STOPPING = 4,
STOPPED = 5,
DELETED = 6,
}

type DeployedNodes = {
agent: string[];
tendermint: string[];
};

type DeploymentType = {
status: Status;
nodes: DeployedNodes;
};

type EmptyPayload = {};

type EmptyResponse = {};

type HttpResponse = {
error?: string;
data?: string;
};

type ClientResponse<ResponseType> = {
error?: string;
data?: ResponseType;
};

class HttpClient<
GetResponse,
PostRequest,
PostResponse,
PutRequest,
PutResponse,
DeleteRequest,
DeleteResponse,
> {
endpoint: string;

constructor(endpoint: string) {
this.endpoint = endpoint;
}

async request({
data,
method,
}: {
data: string;
method: string;
}): Promise<HttpResponse> {
try {
let result = await fetch(this.endpoint, {
method: method,
body: data,
headers: {
"Content-Type": "application/json",
},
});
let response = await result.json();
if (response.error) {
return {
error: response.error,
data: undefined,
};
}
return {
error: undefined,
data: response,
};
} catch (err) {
return {
error: String(err),
data: undefined,
};
}
}

async get(): Promise<ClientResponse<GetResponse>> {
try {
let result = await fetch(this.endpoint);
let response = await result.json();
return {
error: undefined,
data: response,
};
} catch {
return {
error: "Error connecting to studio server",
data: undefined,
};
}
}

async post({
data,
}: {
data: PostRequest;
}): Promise<ClientResponse<PostResponse>> {
let response = await this.request({
data: JSON.stringify(data),
method: "POST",
});
return response as ClientResponse<PostResponse>;
}

async put({
data,
}: {
data: PutRequest;
}): Promise<ClientResponse<PutResponse>> {
let response = await this.request({
data: JSON.stringify(data),
method: "PUT",
});
return response as ClientResponse<PutResponse>;
}

async delete({
data,
}: {
data: DeleteRequest;
}): Promise<ClientResponse<DeleteResponse>> {
let response = await this.request({
data: JSON.stringify(data),
method: "DELETE",
});
return response as ClientResponse<DeleteResponse>;
}
}

type StopDeployment = {
delete: boolean /* Delete deployment*/;
};

class DeploymentEndpoint extends HttpClient<
DeploymentType,
EmptyPayload,
DeploymentType,
EmptyPayload,
DeploymentType,
StopDeployment,
DeploymentType
> {}

class ServiceEndpoint extends HttpClient<
ServiceType,
EmptyPayload,
EmptyResponse,
ServiceType,
ServiceType,
EmptyPayload,
EmptyResponse
> {
actions = {
0: "status",
1: "build",
2: "deploy",
3: "stop",
};

constructor(endpoint: string) {
super(endpoint);
}

deployment(action: Action) {
return new DeploymentEndpoint(`${this.endpoint}/${this.actions[action]}`);
}
}

type UpdateServicePayload = {
old: ServiceHash;
new: ServiceTemplate;
};

type DeleteServicesPayload = {
hashes: Array<ServiceHash>;
};

type DeleteServicesResponse = {
hashes: Array<ServiceHash>;
};

class ServicesEndpoint extends HttpClient<
ServicesType,
ServiceTemplate,
ServiceType,
UpdateServicePayload,
ServiceType,
DeleteServicesPayload,
DeleteServicesResponse
> {
constructor(endpoint: string) {
super(endpoint);
}

service(hash: string): ServiceEndpoint {
return new ServiceEndpoint(`${this.endpoint}/${hash}`);
}
}

function getClient(url: string) {
const services = new ServicesEndpoint(`${url}/services`);
return {
url: url,
services: services,
};
}

export { getClient, Action };

0 comments on commit 6283003

Please sign in to comment.