Skip to content

Commit

Permalink
Merge pull request #145 from near/main
Browse files Browse the repository at this point in the history
DPLT-1072 Prod Release
  • Loading branch information
morgsmccauley committed Jul 26, 2023
2 parents 8bb81dc + dbe4c75 commit dd69196
Show file tree
Hide file tree
Showing 39 changed files with 1,280 additions and 449 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @near/queryapi-core
53 changes: 33 additions & 20 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,51 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## What is this repo?

Frontend for Near QueryAPI that allows users to create, manage, and explore indexers stored on-chain. You can visit the app [here](https://near.org/dataplatform.near/widget/QueryApi.App)


BOS widgets are stored in the `widgets/` folder while the main NextJS application lives in the root.

## Getting Started

First, run the development server:
First, download the bos-loader cli by following this guide [here](https://docs.near.org/bos/dev/bos-loader).

From the root of QueryAPI Frontend repo, run the following command

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
yarn serve:widgets
```
> Near.org or any other BOS gateway queries the blockchain state to pull the latest widgets code and renders it. If we would like to test our BOS widgets, we need to override the path at which the gateway (near.org) queries for the widget code. We do this using the Bos-loader tool (the underlying CLI tool used in the `yarn serve:widgets` command) which allows us to serve out widgets locally (http://127.0.0.1:3030 by default). At this point, we have served our widgets locally but have not yet told the BOS gateway (near.org) where to load our local widgets from.

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
**Then, Head to `near.org/flags` and enter `http://127.0.0.1:3030`**

You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
> In order to tell our BOS gateway (near.org), where to load the local widgets from, we head to `near.org/flags` and enter the local path we got from running the previous command. If you have not changed any configurations then the default should be `http://127.0.0.1:3030`
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
**Finally**, run the following to serve the local NextJS frontend
```bash
yarn dev
```

The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
**Now, head to the path where the widgets are served on the BOS.**

## Learn More
- Prod Environment: `https://near.org/dataplatform.near/widget/QueryApi.App`
- Dev Environment: `https://near.org/dev-queryapi.dataplatform.near/widget/QueryApi.dev-App`

---
### Notes
> **Make sure to change your widgets code (while testing only) to point to where your local nextJS app is being served.**
```QueryApi.App.jsx
---const EXTERNAL_APP_URL = "https://queryapi.io";
+++const EXTERNAL_APP_URL = "http://localhost:3000";
```

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
> **You may need to change the accountId argument to the bos-loader CLI command in `package.json` to load from `dataplatform.near` or `dev-queryapi.dataplatform.near`. This depends on what environment you are testing for.**
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
`bos-loader dev-queryapi.dataplatform.near --path widgets/src`
`bos-loader dataplatform.near --path widgets/src`

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
6 changes: 3 additions & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
"private": true,
"scripts": {
"dev": "next dev",
"social::dev": "cd near.social && yarn run dev",
"serve:widgets": "bos-loader dev-queryapi.dataplatform.near --path widgets/src",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@graphiql/plugin-explorer": "^0.1.20",
"@graphiql/plugin-code-exporter": "^0.1.2",
"@graphiql/plugin-explorer": "0.3.0",
"@graphiql/plugin-code-exporter": "0.3.0",
"@monaco-editor/react": "^4.1.3",
"@near-lake/primitives": "0.1.0",
"@next/font": "13.1.6",
Expand Down
52 changes: 17 additions & 35 deletions frontend/src/components/Editor/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const Editor = ({
debugMode,
isCreateNewIndexer,
indexerNameField,
setAccountId,
} = useContext(IndexerDetailsContext);

const DEBUG_LIST_STORAGE_KEY = `QueryAPI:debugList:${indexerDetails.accountId}#${indexerDetails.indexerName}`
Expand Down Expand Up @@ -90,28 +91,6 @@ const Editor = ({
localStorage.setItem(DEBUG_LIST_STORAGE_KEY, heights);
}, [heights]);

// useEffect(() => {
// if (selectedOption == "latestBlockHeight") {
// setBlockHeightError(null);
// return;
// }
//
// if (height - blockHeight > BLOCKHEIGHT_LIMIT) {
// setBlockHeightError(
// `Warning: Please enter a valid start block height. At the moment we only support historical indexing of the last ${BLOCKHEIGHT_LIMIT} blocks or ${BLOCKHEIGHT_LIMIT / 3600
// } hrs. Choose a start block height between ${height - BLOCKHEIGHT_LIMIT
// } - ${height}.`
// );
// } else if (blockHeight > height) {
// setBlockHeightError(
// `Warning: Start Block Hieght can not be in the future. Please choose a value between ${height - BLOCKHEIGHT_LIMIT
// } - ${height}.`
// );
// } else {
// setBlockHeightError(null);
// }
// }, [blockHeight, height, selectedOption]);

const checkSQLSchemaFormatting = () => {
try {
let formatted_sql = formatSQL(schema);
Expand All @@ -127,10 +106,21 @@ const Editor = ({
}
};


const forkIndexer = async(indexerName) => {
let code = indexingCode;
setAccountId(currentUserAccountId)
let prevAccountId = indexerDetails.accountId.replaceAll(".", "_");
let newAccountId = currentUserAccountId.replaceAll(".", "_");
let prevIndexerName = indexerDetails.indexerName.replaceAll("-", "_").trim().toLowerCase();
let newIndexerName = indexerName.replaceAll("-", "_").trim().toLowerCase();
code = code.replaceAll(prevAccountId, newAccountId);
code = code.replaceAll(prevIndexerName, newIndexerName);
setIndexingCode(formatIndexingCode(code))
}

const registerFunction = async (indexerName, indexerConfig) => {
let formatted_schema = checkSQLSchemaFormatting();
let isForking = indexerDetails.accountId !== currentUserAccountId;

let innerCode = indexingCode.match(/getBlock\s*\([^)]*\)\s*{([\s\S]*)}/)[1];
indexerName = indexerName.replaceAll(" ", "_");
if (formatted_schema == undefined) {
Expand All @@ -140,14 +130,6 @@ const Editor = ({
);
return;
}

if (isForking) {
let prevAccountName = indexerDetails.accountId.replace(".", "_");
let newAccountName = currentUserAccountId.replace(".", "_");

innerCode = innerCode.replaceAll(prevAccountName, newAccountName);
}

setError(() => undefined);

request("register-function", {
Expand All @@ -170,8 +152,8 @@ const Editor = ({
const handleReload = async () => {
if (isCreateNewIndexer) {
setShowResetCodeModel(false);
setIndexingCode(defaultCode);
setSchema(defaultSchema);
setIndexingCode((formatIndexingCode(indexerDetails.code)));
setSchema(formatSQL(indexerDetails.schema))
return;
}

Expand Down Expand Up @@ -331,7 +313,7 @@ const Editor = ({
blockHeightError={blockHeightError}
/>
<ForkIndexerModal
registerFunction={registerFunction}
forkIndexer={forkIndexer}
/>

<div
Expand Down
23 changes: 17 additions & 6 deletions frontend/src/components/Editor/EditorButtons.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const EditorButtons = ({
<Breadcrumb.Item className="flex align-center " href="#">
{accountId}
</Breadcrumb.Item>
{!isCreateNewIndexer && (
{indexerName && (
<Breadcrumb.Item href="#" active style={{ display: "flex" }}>
{indexerName}
</Breadcrumb.Item>
Expand Down Expand Up @@ -169,21 +169,33 @@ const EditorButtons = ({
<Justify style={{ paddingRight: "2px" }} size={24} />
</Button>
</OverlayTrigger>
{currentUserAccountId && (
{(!isUserIndexer && !isCreateNewIndexer) ? (
<OverlayTrigger
placement="bottom"
overlay={<Tooltip>{getActionButtonText()}</Tooltip>}
overlay={<Tooltip>Fork Indexer</Tooltip>}
>
<Button
variant="primary"
className="px-3"
onClick={() => setShowForkIndexerModal(true)}
>
Fork Indexer
</Button>
</OverlayTrigger>
) : (
<OverlayTrigger
placement="bottom"
overlay={<Tooltip>Publish</Tooltip>}
>
<Button
variant="primary"
className="px-3"
onClick={() => setShowPublishModal(true)}
>
{getActionButtonText()}
Publish
</Button>
</OverlayTrigger>
)}

</ButtonGroup>
</Col>
</Row>
Expand All @@ -208,6 +220,5 @@ const EditorButtons = ({
</>
);
};


export default EditorButtons;
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ const IndexerConfigOptions = ({ updateConfig }) => {
<InputGroup.Text> Indexer Name </InputGroup.Text>
<Form.Control
type="text"
placeholder="Indexer Name"
placeholder="indexer_name"
aria-label="IndexerName"
value={indexerNameField}
disabled={!isCreateNewIndexer && showPublishModal}
onChange={(e) => setIndexerNameField(e.target.value)}
onChange={(e) => setIndexerNameField(e.target.value.toLowerCase().trim())}
/>
</InputGroup>
<InputGroup size="sm" className="pt-3">
Expand Down
69 changes: 38 additions & 31 deletions frontend/src/components/Modals/ForkIndexerModal.jsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,41 @@
import React, { useContext, useState } from "react";
import { Button, Modal, Alert } from "react-bootstrap";
import { Button, Modal, Alert, InputGroup, Form } from "react-bootstrap";
import IndexerConfigOptions from "../Form/IndexerConfigOptionsInputGroup";
import { IndexerDetailsContext } from '../../contexts/IndexerDetailsContext';
import { IndexerDetailsContext } from "../../contexts/IndexerDetailsContext";
import { validateContractId } from "../../utils/validators";

export const ForkIndexerModal = ({
registerFunction,
}) => {
export const ForkIndexerModal = ({ registerFunction, forkIndexer }) => {
const {
indexerDetails,
showForkIndexerModal,
setShowForkIndexerModal,
setIsCreateNewIndexer,
setIndexerName,
setIndexerConfig,
isCreateNewIndexer,
} = useContext(IndexerDetailsContext);
const [indexerConfig, setIndexerConfig] = useState({ filter: "social.near", startBlockHeight: null })
const [indexerName, setIndexerName] = useState("")
const [error, setError] = useState(null)
const [indexerName, setIndexerNameField] = useState("");
const [error, setError] = useState(null);

const updateConfig = (indexerName, filter, startBlockHeight, option) => {
let finalStartBlockHeight = option === "latestBlockHeight" ? null : startBlockHeight;
setIndexerConfig({ filter, startBlockHeight: finalStartBlockHeight })
setIndexerName(indexerName)
}
const register = async () => {
const fork = async () => {
if (!indexerName) {
setError("Please provide an Indexer Name")
return
setError("Please provide an Indexer Name");
return;
}

if (indexerName === indexerDetails.indexerName) {
setError("Please provide a different Indexer Name than the orginal Indexer")
return
setError(
"Please provide a different Indexer Name than the orginal Indexer"
);
return;
}


if (!validateContractId(indexerConfig.filter)) {
setError("Please provide a valid contract name")
return
}
setError(null)
registerFunction(indexerName, indexerConfig)
setShowForkIndexerModal(false)
}
setError(null);
setIndexerName(indexerName);
setIsCreateNewIndexer(true);
forkIndexer(indexerName);
setShowForkIndexerModal(false);
};

return (
<Modal
Expand All @@ -52,19 +47,31 @@ export const ForkIndexerModal = ({
<Modal.Title> Enter Indexer Details</Modal.Title>
</Modal.Header>
<Modal.Body>
<IndexerConfigOptions updateConfig={updateConfig} />
<InputGroup size="sm">
<InputGroup.Text> Indexer Name </InputGroup.Text>
<Form.Control
type="text"
placeholder="indexer_name"
aria-label="IndexerName"
value={indexerName}
onChange={(e) => setIndexerNameField(e.target.value.trim().toLowerCase())}
/>
</InputGroup>
{error && (
<Alert className="px-3 mt-3" variant="danger">
{error}
</Alert>
)}
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={() => setShowPublishModal(false)}>
<Button
variant="secondary"
onClick={() => setShowForkIndexerModal(false)}
>
Cancel
</Button>
<Button variant="primary" onClick={() => register()}>
Fork Your Own Indexer
<Button variant="primary" onClick={() => fork()}>
Fork Indexer
</Button>
</Modal.Footer>
</Modal>
Expand Down
Loading

0 comments on commit dd69196

Please sign in to comment.