Skip to content

Commit

Permalink
feat: Artsy Open (#156)
Browse files Browse the repository at this point in the history
  • Loading branch information
olerichter00 committed May 26, 2021
1 parent 25cd2b1 commit 34e5aa1
Show file tree
Hide file tree
Showing 11 changed files with 543 additions and 4 deletions.
4 changes: 4 additions & 0 deletions README.md
Expand Up @@ -11,6 +11,10 @@ Artsy CLI is published on npm, so installing is really easy:
$ npm install --global @artsy/cli
```

## [Docs]

- [Artsy Open Docs](docs/open.md)

## Releasing

The release process happens automatically on every PR merge thanks to [auto](https://github.com/intuit/auto). To ensure
Expand Down
55 changes: 55 additions & 0 deletions docs/open.md
@@ -0,0 +1,55 @@
# Artsy CLI Open

Open Artsy links with iOS, Android or the browser.

## Usage

**With an alias:**

```
artsy open artwork
```

opens https://www.artsy.net/artwork/banksy-love-rat-signed-16 on iOS

**With a path:**

```
artsy open /artwork/andy-warhol-watercolor-paint-kit-with-brushes-11
```

opens https://www.artsy.net/artwork/andy-warhol-watercolor-paint-kit-with-brushes-11 on iOS

**With a full URL:**

```
artsy open https://www.staging.artsy.net/artwork/andy-warhol-watercolor-paint-kit-with-brushes-11
```

opens https://www.staging.artsy.net/artwork/andy-warhol-watercolor-paint-kit-with-brushes-11 on iOS

**With flags:**

```
artsy open artwork -a -l
```

opens localhost:3000/artwork/banksy-love-rat-signed-16 on Android

**With custom variables:**

```
artsy open artist artistID:andy-warhol
```

opens https://www.artsy.net/artist/andy-warhol on Android

## Config

All aliases and defaults can be found [here](src/lib/open/data/config.json).

The config can be overriden by providing `~/.config/artsy-open.json`. Just copy the config from GitHub and modify it:

```
curl https://www.raw.githubusercontent.com/artsy/cli/master/src/lib/open/data/config.json > ~/.config/artsy-open.json
```
7 changes: 6 additions & 1 deletion package.json
Expand Up @@ -20,7 +20,9 @@
"apollo-client": "^2.6.10",
"apollo-link-http": "^1.5.17",
"cli-ux": "^5.3.1",
"colon-template": "^1.0.3",
"cross-fetch": "^3.0.6",
"dashify": "^2.0.0",
"dotenv": "^8.0.0",
"graphql": "^15.5.0",
"graphql-tag": "^2.11.0",
Expand All @@ -29,7 +31,8 @@
"node-fetch": "^2.6.0",
"querystring": "^0.2.0",
"rss-parser": "^3.11.0",
"tslib": "^2"
"tslib": "^2",
"uri-scheme": "^1.0.76"
},
"devDependencies": {
"@artsy/auto-config": "^1.0.1",
Expand All @@ -42,6 +45,7 @@
"@oclif/test": "^1",
"@oclif/tslint": "^3",
"@types/chai": "^4",
"@types/dashify": "^1.0.0",
"@types/mocha": "^8",
"@types/node": "^14",
"@types/node-fetch": "^2.5.0",
Expand All @@ -52,6 +56,7 @@
"nock": "^13.0.10",
"nyc": "^13",
"prettier": "^1.18.2",
"sinon": "^10.0.0",
"ts-node": "^9",
"tslint": "^5",
"tslint-config-prettier": "^1.18.0",
Expand Down
81 changes: 81 additions & 0 deletions src/commands/open.ts
@@ -0,0 +1,81 @@
import { flags } from "@oclif/command"
import Command from "../base"

import openPage from "../lib/open"
import config from "../lib/open/config"

export default class Open extends Command {
static description =
"Open Artsy links with the iOS/Android emulator or the browser"

static examples = [
`$ artsy open artwork
opens "https://www.artsy.net/artwork/banksy-love-rat-signed-16" on iOS`,
`$ artsy open artwork/andy-warhol-watercolor-paint-kit-with-brushes-11
opens "https://www.artsy.net/artwork/andy-warhol-watercolor-paint-kit-with-brushes-11" on iOS`,
`artsy open artist artistID:andy-warhol
Open "https://www.artsy.net/artist/andy-warhol" on iOS`,
`$ artsy open home -a
opens "https://www.artsy.net/" on Android`,
`$ artsy open about -l
opens "localhost/about" on iOS`,
`\nALL PAGES`,
Object.keys(config.pages).join(", "),
]

static flags = {
...Command.flags,
ios: flags.boolean({ char: "i" }),
android: flags.boolean({ char: "a" }),
web: flags.boolean({ char: "w" }),
production: flags.boolean({ char: "p" }),
staging: flags.boolean({ char: "s" }),
local: flags.boolean({ char: "l" }),
}

static args = [
{ name: "page", required: false, default: "/" },
{ name: "customVariables", required: false },
]

parsePlatform(): string | undefined {
const { flags } = this.parse(Open)

if (flags.ios) return "ios"
if (flags.android) return "android"
if (flags.web) return "web"
}

parseEnvironment(): string | undefined {
const { flags } = this.parse(Open)

if (flags.staging) return "staging"
if (flags.production) return "production"
if (flags.local) return "local"
}

parseCustomVariables(): object {
const {
args: { customVariables },
} = this.parse(Open)

if (!customVariables) return {}

return customVariables.split(",").reduce((acc: any, element: string) => {
const [key, value] = element.split(":")
acc[key] = value
return acc
}, {})
}

async run() {
const { args } = this.parse(Open)

openPage({
page: args.page as string,
platform: this.parsePlatform(),
environment: this.parseEnvironment(),
customVariables: this.parseCustomVariables(),
})
}
}
9 changes: 9 additions & 0 deletions src/config.ts
Expand Up @@ -5,6 +5,15 @@ export const Config = {
path: (): string => {
return `${os.homedir()}/.config/artsy`
},
readOpenConfig: (): any => {
try {
return JSON.parse(
fs.readFileSync(`${os.homedir()}/.config/artsy-open.json`)
)
} catch {
return {}
}
},
readToken: (): string => {
return fs.readFileSync(Config.path(), { encoding: "utf-8" })
},
Expand Down
13 changes: 13 additions & 0 deletions src/lib/open/config.ts
@@ -0,0 +1,13 @@
import { Config } from "../../config"
import { defaults, environments, pages, variables } from "./data/config.json"

const homeConfig = Config.readOpenConfig()

const config = {
environments: { ...environments, ...homeConfig.environments },
pages: { ...pages, ...homeConfig.pages },
variables: { ...variables, ...homeConfig.variables },
defaults: { ...defaults, ...homeConfig.defaults },
}

export default config
103 changes: 103 additions & 0 deletions src/lib/open/data/config.json
@@ -0,0 +1,103 @@
{
"defaults": {
"environment": "production",
"platform": "ios"
},
"environments": {
"local": "localhost:3000",
"production": "https://www.artsy.net",
"staging": "https://www.staging.artsy.net"
},
"pages": {
"about": "/about",
"admin": "/admin",
"admin2": "/admin2",
"artist": "/artist/:artistID",
"artist-articles": "/artist/:artistID/articles",
"artist-series": "/artist-series/:artistSeriesID",
"artwork": "/artwork/:artworkID",
"artwork-attribution-class-faq": "/artwork-classifications",
"artwork-medium": "/artwork/:artworkID/medium",
"auction": "/auction/:auctionID",
"auction-bid-artwork": "/auction/:saleID/bid/:artworkID",
"auction-faq": "/auction-faq",
"auction-info": "/auction/:saleID/info",
"auction-registration": "/auction-registration/:saleID",
"auction-result": "/artist/:artistID/auction-result/:auctionResultInternalID",
"auction2": "/auction/:saleID",
"auctions": "/auctions",
"buy-now-feature-faq": "/buy-now-feature-faq",
"categories": "/categories",
"checkout": "/orders/:orderID",
"city-bmwlist": "/city-bmw-list/:citySlug",
"city-fair-list": "/city-fair/:citySlug",
"city-saved-list": "/city-save/:citySlug",
"city-section-list": "/city/:citySlug/:section",
"collection": "/collection/:collectionID",
"conditions-of-sale": "/conditions-of-sale",
"consignments-submission-form": "/consign/submission",
"conversation": "/user/conversations/:conversationID",
"fair": "/fair/:fairID/exhibitors",
"fair-all-followed-artists": "/fair/:fairID/followedArtists",
"fair-articles": "/fair/:fairID/articles",
"fair-bmwart-activation": "/fair/:fairID/bmw-sponsored-content",
"fair-more-info": "/fair/:fairID/info",
"favorites": "/favorites",
"feature": "/feature/:featureSlug",
"full-artist-series-list": "/artist/:artistID/artist-series",
"full-featured-artist-list": "/collection/:collectionID/artists",
"gene": "/gene/:geneID",
"home": "/",
"identity-verification-faq": "/identity-verification-faq",
"inbox": "/inbox",
"inquiry": "/inquiry/:artworkID",
"local-discovery": "/local-discovery",
"make-offer-modal": "/make-offer/:artworkID",
"my-account": "/my-account",
"my-account-edit-email": "/my-account/edit-email",
"my-account-edit-name": "/my-account/edit-name",
"my-account-edit-password": "/my-account/edit-password",
"my-account-edit-phone": "/my-account/edit-phone",
"my-collection": "/my-collection",
"my-collection-artwork": "/my-collection/artwork/:artworkSlug",
"my-collection-artwork-full-details": "/my-collection/artwork-details/:artworkSlug",
"my-collection-artwork-images": "/my-collection/artwork-images/:artworkSlug",
"my-profile": "/my-profile",
"my-profile-payment": "/my-profile/payment",
"my-profile-payment-new-credit-card": "/my-profile/payment/new-card",
"my-profile-push-notifications": "/my-profile/push-notifications",
"order": "/orders/:orderID",
"partner-locations": "/partner-locations/:partnerID",
"privacy": "/privacy",
"privacy-request": "/privacy-request",
"sales": "/sales",
"sales-not-root-tab-view": "/collections/my-collection/marketing-landing",
"search": "/search",
"show": "/show/:showID",
"show-more-info": "/show/:showID/info",
"terms": "/terms",
"viewing-room": "/viewing-room/:viewing_room_id",
"viewing-room-artwork": "/viewing-room/:viewing_room_id/:artworkID",
"viewing-room-artworks": "/viewing-room/:viewing_room_id/artworks",
"viewing-rooms": "/viewing-rooms",
"works-for-you": "/works-for-you"
},
"variables": {
"artistID": "banksy",
"artistSeriesID": "andy-warhol-watercolor-paint-kit-with-brushes",
"artworkID": "banksy-love-rat-signed-16",
"auctionID": "american-friends-of-museums-in-israel-benefit-auction-2021",
"auctionResultInternalID": "",
"citySlug": "berlin",
"collectionID": "abstract-expressionism-works-on-paper",
"conversationID": "",
"fairID": "one-x-artsy",
"featureSlug": "museum-of-contemporary-art-san-diego-benefit-auction-2020",
"geneID": "design",
"orderID": "",
"partnerID": "menconi-plus-schoelkopf",
"saleID": "american-friends-of-museums-in-israel-benefit-auction-2021",
"showID": "first-floor-gallery-harare-amanda-mushate-nguve-inemuridzi",
"viewing_room_id": "artcn-panacea-of-liu-zhenchen"
}
}
62 changes: 62 additions & 0 deletions src/lib/open/index.ts
@@ -0,0 +1,62 @@
const { Android, Ios } = require("uri-scheme")
const dashify = require("dashify")
const template = require("colon-template")
const { cli } = require("cli-ux")

import config from "./config"
interface OpenPageProps {
page: string
platform: string | undefined
environment: string | undefined
customVariables: object
}

const openUri = (uri: string, platform: string) => {
switch (platform) {
case "android":
Android.openAsync({ uri })
break
case "ios":
Ios.openAsync({ uri })
break
case "web":
cli.open(uri)
}
}

const removeTrailingSlash = (path: string): string => {
return path.replace(/^\//, "")
}

const getURI = (
input: string,
variables: object,
environment: string
): string => {
if (input.includes("://")) return input

const { pages, environments } = config

const templatePath = removeTrailingSlash(pages[dashify(input)] || input)

const path = template(templatePath, variables)

return `${environments[environment]}/${path}`
}

const openPage = ({
page,
platform,
environment,
customVariables,
}: OpenPageProps) => {
const variables = { ...config.variables, ...customVariables }
const env = environment || config.defaults.environment
const uri = getURI(page, variables, env)

console.log(`Open "${uri}" on ${platform || config.defaults.platform}`)

openUri(uri, platform || config.defaults.platform)
}

export default openPage

0 comments on commit 34e5aa1

Please sign in to comment.