Skip to content

Commit

Permalink
Merge pull request #792 from near/develop
Browse files Browse the repository at this point in the history
Daily release
  • Loading branch information
gabehamilton authored Apr 29, 2024
2 parents 3c7cd99 + c8ac37a commit 2cbb780
Show file tree
Hide file tree
Showing 15 changed files with 439 additions and 173 deletions.
23 changes: 9 additions & 14 deletions src/ActivityFeeds/DetermineActivityFeed.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
const GRAPHQL_ENDPOINT = props.GRAPHQL_ENDPOINT ?? "https://near-queryapi.api.pagoda.co";
const { fetchGraphQL, GRAPHQL_ENDPOINT } = VM.require("${REPL_ACCOUNT}/widget/Entities.QueryApi.Client");

if (!fetchGraphQL || !GRAPHQL_ENDPOINT) return <></>;

const LIMIT = 10;

let lastPostSocialApi = Social.index("post", "main", {
limit: 1,
Expand All @@ -14,18 +18,6 @@ State.init({
shouldFallback: false,
});

function fetchGraphQL(operationsDoc, operationName, variables) {
return asyncFetch(`${GRAPHQL_ENDPOINT}/v1/graphql`, {
method: "POST",
headers: { "x-hasura-role": "dataplatform_near" },
body: JSON.stringify({
query: operationsDoc,
variables: variables,
operationName: operationName,
}),
});
}

const lastPostQuery = `
query IndexerQuery {
dataplatform_near_social_feed_posts( limit: 1, order_by: { block_height: desc }) {
Expand Down Expand Up @@ -69,13 +61,15 @@ return (
props={{
showFlagAccountFeature: true,
accounts: props.filteredAccountIds,
limit: LIMIT,
}}
/>
) : (
<Widget
src={`${REPL_ACCOUNT}/widget/v1.Posts`}
props={{
showFlagAccountFeature: true,
limit: LIMIT,
}}
/>
)}
Expand All @@ -84,8 +78,9 @@ return (
<Widget
src={`${REPL_ACCOUNT}/widget/ActivityFeeds.PostsFeedControls`}
props={{
GRAPHQL_ENDPOINT,
GRAPHQL_ENDPOINT: props.GRAPHQL_ENDPOINT || GRAPHQL_ENDPOINT,
showFlagAccountFeature: true,
limit: LIMIT,
...props,
}}
/>
Expand Down
25 changes: 8 additions & 17 deletions src/ActivityFeeds/PostsFeedControls.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
const GRAPHQL_ENDPOINT = props.GRAPHQL_ENDPOINT || "https://near-queryapi.api.pagoda.co";
const LIMIT = 10;
const { fetchGraphQL, GRAPHQL_ENDPOINT } = VM.require("${REPL_ACCOUNT}/widget/Entities.QueryApi.Client");

if (!fetchGraphQL || !GRAPHQL_ENDPOINT) return <></>;

const limit = props.limit ?? 10;
const feeds = props.feeds ?? ["all", "following"];
const feedLabels = { all: "All", following: "Following", mutual: "Mutual Activity" };
const showCompose = props.showCompose ?? true;
Expand Down Expand Up @@ -86,17 +89,6 @@ const shouldFilter = (item, socialDBObjectType) => {
}) || matchesModeration(selfModeration, socialDBObjectType, item)
);
};
function fetchGraphQL(operationsDoc, operationName, variables) {
return asyncFetch(`${GRAPHQL_ENDPOINT}/v1/graphql`, {
method: "POST",
headers: { "x-hasura-role": "dataplatform_near" },
body: JSON.stringify({
query: operationsDoc,
variables: variables,
operationName: operationName,
}),
});
}

const createQuery = (type, isUpdate) => {
let querySortOption = "";
Expand Down Expand Up @@ -214,10 +206,9 @@ const loadMorePosts = (isUpdate) => {
setIsLoading(true);
}
const offset = isUpdate ? 0 : postsData.posts.length;
const limit = isUpdate ? 100 : LIMIT;
fetchGraphQL(createQuery(selectedTab, isUpdate), queryName, {
offset: offset,
limit: LIMIT,
offset,
limit,
}).then((result) => {
if (result.status === 200 && result.body) {
if (result.body.errors) {
Expand Down Expand Up @@ -550,7 +541,7 @@ return (
content: item.content,
comments: item.comments,
likes: item.accounts_liked,
GRAPHQL_ENDPOINT,
GRAPHQL_ENDPOINT: props.GRAPHQL_ENDPOINT ?? GRAPHQL_ENDPOINT,
verifications: item.verifications,
showFlagAccountFeature: false,
}}
Expand Down
119 changes: 94 additions & 25 deletions src/Entities/QueryApi/DataPlatformIndexerStatuses.jsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,73 @@
const GRAPHQL_ENDPOINT = props.GRAPHQL_ENDPOINT || "https://near-queryapi.api.pagoda.co";

const indexerAccount = props.indexerAccount || "dataplatform.near";
const indexerAccountLink = indexerAccount.replace(".", "_");
const sanitizedAccountId = indexerAccount.replace(/[^a-zA-Z0-9]/g, "_").replace(/^([0-9])/, "_$1");
const indexerFilter = props.indexerFilter || null;
const fullFilter = indexerFilter ? indexerAccount + "/" + indexerFilter : indexerAccount;

const [statuses, setIndexerStatuses] = useState([]);
const [statuses, setIndexerStatuses] = useState({});
const [errors, setErrors] = useState("");
const [timer, setTimer] = useState(null);

const [latestBlock, setLatestBlock] = useState(0);
const [indexerList, setIndexerList] = useState(null);
const registryContract = "queryapi.dataplatform.near";
const registry = Near.view(registryContract, "list_indexer_functions", {
account_id: indexerAccount,
});

if (!registry) {
return <div>Loading indexer list from contract...</div>;
} else {
try {
const keys = Object.keys(registry["Account"]);
const sanitizedIndexerNames = keys.map((k) => k.replace(/[^a-zA-Z0-9]/g, "_").replace(/^([0-9])/, "_$1"));

setIndexerList(sanitizedIndexerNames);
} catch (e) {
setErrors(e);
}
}

const defaultIndexerList = [
"access_keys_v1",
"accounts",
"components",
"entities",
"feed",
"moderation",
"notifications",
"social_feed",
"verifications",
];

if (!indexerList) {
return (
<div>
<p>Indexer list failed to load from contract</p>
<button onClick={() => setIndexerList(defaultIndexerList)}>Use default indexer list</button>
</div>
);
}

function fetchGraphQL(operationsDoc, operationName, variables) {
return asyncFetch(`${GRAPHQL_ENDPOINT}/v1/graphql`, {
method: "POST",
headers: { "x-hasura-role": "append" },
headers: { "x-hasura-role": sanitizedAccountId },
body: JSON.stringify({
query: operationsDoc,
variables: variables,
operationName: operationName,
}),
});
}
const query = `query MyQuery {
indexer_state(where: {function_name: {_like: "${fullFilter}%"}}) {
status
current_block_height
function_name
const query = (indexer) => `query MyQuery {
${sanitizedAccountId}_${indexer}_sys_metadata {
attribute
value
}
}`;

function handleResults(result) {
function handleResults(indexer, result) {
try {
if (result.status === 200 && result.body) {
if (result.body.errors) {
Expand All @@ -36,9 +76,12 @@ function handleResults(result) {
}
let data = result.body.data;
if (data) {
const statuses = data.indexer_state;
const statuses = data[`${sanitizedAccountId}_${indexer}_sys_metadata`];
if (statuses.length > 0) {
setIndexerStatuses(statuses);
const status = statuses.find((s) => s.attribute === "STATUS")?.value;
const blockHeight = statuses.find((s) => s.attribute === "LAST_PROCESSED_BLOCK_HEIGHT")?.value;
const newStatus = { [indexer]: { status, blockHeight } };
setIndexerStatuses((prev) => ({ ...prev, ...newStatus }));
}
}
}
Expand All @@ -47,10 +90,27 @@ function handleResults(result) {
}
}

fetchGraphQL(query).then((result) => handleResults(result));
const update = () => {
asyncFetch("https://rpc.mainnet.near.org", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: "dontcare",
method: "status",
params: {},
}),
}).then((res) => setLatestBlock(res.body.result.sync_info.latest_block_height));
if (indexerList) {
indexerList.forEach((indexer) => fetchGraphQL(query(indexer)).then((result) => handleResults(indexer, result)));
}
};

update();
if (!timer) {
setTimer(setInterval(() => fetchGraphQL(query).then((result) => handleResults(result)), 3000));
setTimer(setInterval(update, 3000));
}

const StatusTable = styled.table`
Expand All @@ -70,6 +130,7 @@ const TableHeader = styled.th`
`;

const TableElement = styled.td`
font-weight: ${(td) => (td.bold ? "800" : "400")};
word-wrap: break-word;
padding: 1em;
`;
Expand All @@ -87,21 +148,29 @@ return (
</tr>
</thead>
<tbody>
{statuses.map((status) => (
<tr key={status.function_name}>
<TableElement>{status.function_name?.split("/")[1]}</TableElement>
<TableElement>
<span style={{ color: status.status === "RUNNING" ? "green" : "red" }}>{status.status}</span>
</TableElement>
<TableElement>{status.current_block_height}</TableElement>
</tr>
))}
<tr>
<TableElement bold>Latest Block</TableElement>
<TableElement>MAINNET</TableElement>
<TableElement bold>{latestBlock}</TableElement>
</tr>
{Object.entries(statuses)
.sort()
.map(([indexer, status]) => (
<tr key={indexer}>
<TableElement>{indexer}</TableElement>
<TableElement>
<span style={{ color: status.status === "RUNNING" ? "green" : "red" }}>{status.status}</span>
</TableElement>
<TableElement>{status.blockHeight}</TableElement>
</tr>
))}
</tbody>
</StatusTable>
<div>{errors}</div>
<hr />
<div>{errors ? JSON.stringify(errors) : ""}</div>
<Link
target="_blank"
href={`https://cloud.hasura.io/public/graphiql?endpoint=https://near-queryapi.api.pagoda.co/v1/graphql&header=x-hasura-role%3A${indexerAccountLink}`}
href={`https://cloud.hasura.io/public/graphiql?endpoint=https://near-queryapi.api.pagoda.co/v1/graphql&header=x-hasura-role%3A${sanitizedAccountId}`}
>
Explore Data
</Link>
Expand Down
15 changes: 15 additions & 0 deletions src/Entities/QueryApi/Ipfs.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const ipfsUpload = (f) =>
asyncFetch("https://ipfs.near.social/add", {
method: "POST",
headers: {
Accept: "application/json",
},
body: f,
}).then((res) => res.body.cid);

const ipfsUrl = (cid) => {
const c = typeof cid === "object" ? cid.cid : cid;
return `https://ipfs.near.social/ipfs/${c}`;
};

return { ipfsUpload, ipfsUrl };
16 changes: 14 additions & 2 deletions src/Entities/Template/EntityCreate.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,19 @@ const inputsValidator = (formValues) =>
return !required || typeof formValues[key] === "string";
});

const actionType = data ? (data.accountId === context.accountId ? "Edit" : "Fork") : "Create";
const flattenAttributes = (data) => {
if (!data.attributes) {
return data;
}
const flattened = { ...data };
Object.keys(data.attributes).forEach((key) => {
flattened[key] = data.attributes[key];
});
delete flattened.attributes;
return flattened;
};
const flattenedData = data ? flattenAttributes(data) : data;
const actionType = data ? (flattenedData.accountId === context.accountId ? "Edit" : "Fork") : "Create";

const initialValues = (schema, data) => {
const initial = data ?? {};
Expand Down Expand Up @@ -62,7 +74,7 @@ return (
submitLabel: data ? "Save" : "Launch",
onCancel: onCancel,
cancelLabel: cancelLabel,
externalState: initialValues(schema, data),
externalState: initialValues(schema, flattenedData),
namespace,
entityType,
}}
Expand Down
53 changes: 44 additions & 9 deletions src/Entities/Template/EntityDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ const Properties = styled.div`
`;
const Text = styled.p`
margin: 0;
font-size: 14px;
line-height: 20px;
color: ${(p) => (p.bold ? "#11181C" : "#687076")};
font-weight: ${(p) => (p.bold ? "600" : "400")};
Expand All @@ -132,14 +131,50 @@ const entityProperties = (obj) => {
const attributes = obj.attributes;
return (
<Properties>
{Object.keys(attributes).map((k) => (
<>
<Text bold key={k}>
{capitalize(k)}:
</Text>
<PropValue>{attributes[k]}</PropValue>
</>
))}
{attributes &&
Object.keys(attributes).map((key) => {
const schemaField = schema[key];
const value = attributes[key];
switch (schemaField.type) {
case "image":
return (
<>
<Text bold key={key}>
{capitalize(key)}:
</Text>
<PropValue>
<img className="logo" src={imageUrl} alt={key} />
</PropValue>
</>
);
case "file":
return (
<>
<Text bold key={key}>
{capitalize(key)}:
</Text>
<PropValue>
{value && (
<>
<p>{value?.name}</p>
<p>{value.type}</p>
<p>{value.size} bytes</p>
</>
)}
</PropValue>
</>
);
default:
return (
<>
<Text bold key={key}>
{capitalize(key)}:
</Text>
<PropValue>{value}</PropValue>
</>
);
}
})}
</Properties>
);
};
Expand Down
Loading

0 comments on commit 2cbb780

Please sign in to comment.