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

Improve GraphQL utilities #1570

Closed
Dhaiwat10 opened this issue Dec 20, 2023 · 3 comments
Closed

Improve GraphQL utilities #1570

Dhaiwat10 opened this issue Dec 20, 2023 · 3 comments
Labels
feat Issue is a feature

Comments

@Dhaiwat10
Copy link
Contributor

Dhaiwat10 commented Dec 20, 2023

We need to enhance our GraphQL API by implementing custom resolvers that manipulate the data returned from the queries to match the specific data types and structures required by the SDK and its consumers.

A prime example is the GraphQL type for UTXOs: InputCoin which needs to be manipulated to the corresponding SDK type: Resource.

We need to make this change to improve things not only for the internal use of the SDK but also for the SDK consumers.

Having said that, a deep analysis should be made not only in the SDK but also on the block explorer to identify all possible resolvers that can be created to reduce the data manipulation after queries, as the block explorer executes a lot of GraphQL queries.

Note

@Dhaiwat10 Dhaiwat10 added the feat Issue is a feature label Dec 20, 2023
@arboleya arboleya changed the title Investigate GQL utilities based off explorer Improve GraphQL utilities Dec 20, 2023
@nedsalk
Copy link
Contributor

nedsalk commented Dec 20, 2023

Do you mean that we write resolvers such that e.g. a call to provider.operations.getCoinsToSpend would give the same result as the current provider.getCoinsToSpend?

@Torres-ssf
Copy link
Contributor

Torres-ssf commented Dec 20, 2023

Do you mean that we write resolvers such that e.g. a call to provider.operations.getCoinsToSpend would give the same result as the current provider.getCoinsToSpend?

@nedsalk Not exactly. These resolvers should take care only of the data manipulation that happens before returning the data. For this specific case I believe would be this part

const coins = result.coinsToSpend
.flat()
.map((coin) => {
switch (coin.__typename) {
case 'MessageCoin':
return {
amount: bn(coin.amount),
assetId: coin.assetId,
daHeight: bn(coin.daHeight),
sender: Address.fromAddressOrString(coin.sender),
recipient: Address.fromAddressOrString(coin.recipient),
nonce: coin.nonce,
} as MessageCoin;
case 'Coin':
return {
id: coin.utxoId,
amount: bn(coin.amount),
assetId: coin.assetId,
owner: Address.fromAddressOrString(coin.owner),
maturity: bn(coin.maturity).toNumber(),
blockCreated: bn(coin.blockCreated),
txCreatedIdx: bn(coin.txCreatedIdx),
} as Coin;
default:
return null;
}
})
.filter((v) => !!v) as Array<Resource>;
return coins;

In this specific case, it could be a little different, I did not go deep into it.

@nedsalk
Copy link
Contributor

nedsalk commented Mar 18, 2024

The only way to go forward with this is to have an adapter between what is returned from the node and what is given to the users.

We can take the Tai64Timestamp scalar as an example. We infer it as a string and thus have to manually convert it to the DateTime helper class when we or our users want to work with date/time logic (e.g. here). Ideally, we'd want this conversion to happen automatically and this is where the adapter is relevant.

Problems with current tools

Further continuing with the DateTime example, we could change our config to infer Tai64Timestamp as DateTime:

{
// ...
  "config": {
	"scalars": {
	  //...
	  "Tai64Timestamp": "DateTime"
	},
}

However, this would only enforce the compile-time type, whereas the runtime type would still remain a string. Another major problem with this is that when we specify a type different to the one the node returns (string -> DateTime) we lose information on the type the node returns, which makes it harder for us to create a proper adapter.

Besides these type problems, there is no "catch all" way of defining the adapter and we'd have to manually create adapters for each operation of interest.

Solution

  1. Have all generated types match the actual returned type from the node (which our current code already does)
  2. For each operation_, define the return types we'd like to see
  3. Implement an adapter for each operation

The adapter function could look something like this:

type SdkOperations = Omit<Operations, "tai64Example"> & {
  tai64Example: Omit<Pick<Operations, "tai64Example">, "time"> & {time: DateTime}
}

function adapter(operations: Operations): SdkOperations {
  return {
    ...operations,
    tai64Example: {
      ...operations.tai64Example,
      time: tai64Adapter(operations.tai64Example.time)
    }
  }
}

The SdkOperations type would of course would be made generic and it'd be more sensible to define adapters with; the gist of the logic that the generic would do is in it, though.

@arboleya arboleya added p2 and removed p2 labels Jun 9, 2024
@arboleya arboleya added the temp:notion label Sep 8, 2024 — with Linear
@arboleya arboleya added the temp-linear label Sep 8, 2024 — with Linear
@danielbate danielbate closed this as not planned Won't fix, can't repro, duplicate, stale Nov 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feat Issue is a feature
Projects
None yet
Development

No branches or pull requests

5 participants