Skip to content

Commit

Permalink
feat: drop organizations (#809)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan-tymoshenko authored Jul 19, 2024
1 parent e156f5a commit cc1c022
Show file tree
Hide file tree
Showing 5 changed files with 9 additions and 243 deletions.
42 changes: 0 additions & 42 deletions __mocks__/templates.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,47 +26,5 @@
"author": "Platformatic",
"homepage": "https://example.com",
"public": true
},
{
"orgId": "queue",
"orgName": "Queue",
"name": "Queue",
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam mollis fermentum velit et interdum. Nulla eget iaculis mauris. Fusce non eleifend elit. Proin metus turpis, vestibulum quis tempus ac, tincidunt vitae mi. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque tristique tristique finibus. Maecenas ut ultricies nisi. Quisque laoreet a enim at molestie. Integer vel mi quam. Aenean vulputate arcu egestas, feugiat risus vel, elementum justo. Maecenas tincidunt nisi augue, et mattis dui tincidunt ac.",
"author": "queue",
"homepage": "https://example.com"
},
{
"orgId": "authentication",
"orgName": "Authentication",
"name": "Authentication Service",
"description": "Sed at eleifend nulla. Morbi id elit ut mi finibus hendrerit nec vel lectus. Maecenas consequat lacus sit amet libero imperdiet egestas. Duis ut augue in risus sollicitudin scelerisque. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vestibulum dictum elit id nulla dictum volutpat. Aenean commodo lectus dolor, rutrum malesuada ante malesuada eu. Morbi pharetra, nibh id vehicula pellentesque, felis sem efficitur ligula, in posuere orci metus ac lacus. Proin sollicitudin justo augue, mollis interdum odio aliquet quis. Nullam aliquet dui quam, id pulvinar ante dignissim a. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nulla laoreet dapibus augue, id aliquet magna mattis sit amet. Phasellus non sagittis orci. Duis vel velit non nibh molestie faucibus. Quisque maximus lectus at euismod lacinia. Nunc nec sem neque.",
"author": "authentication",
"homepage": "https://example.com",
"public": true
},
{
"orgId": "universe",
"orgName": "Universe",
"name": "Universe",
"description": "Sed at eleifend nulla. Morbi id elit ut mi finibus hendrerit nec vel lectus. Maecenas consequat lacus sit amet libero imperdiet egestas. Duis ut augue in risus sollicitudin scelerisque. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vestibulum dictum elit id nulla dictum volutpat. Aenean commodo lectus dolor, rutrum malesuada ante malesuada eu. Morbi pharetra, nibh id vehicula pellentesque, felis sem efficitur ligula, in posuere orci metus ac lacus. Proin sollicitudin justo augue, mollis interdum odio aliquet quis. Nullam aliquet dui quam, id pulvinar ante dignissim a. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nulla laoreet dapibus augue, id aliquet magna mattis sit amet. Phasellus non sagittis orci. Duis vel velit non nibh molestie faucibus. Quisque maximus lectus at euismod lacinia. Nunc nec sem neque.",
"author": "Universe",
"homepage": "https://example.com"
},
{
"orgId": "inventory",
"orgName": "Inventory",
"name": "Inventory",
"description": "Morbi vel accumsan lorem, varius dictum enim. Donec dignissim enim vel congue tincidunt. In tristique dui sed arcu mattis, eget fringilla mi tempus. Aliquam sit amet lobortis leo. Nunc orci arcu, ullamcorper vel aliquet in, suscipit et erat. Aliquam in hendrerit massa. Aliquam blandit nunc nec urna luctus rhoncus. Morbi cursus elit arcu, id dapibus augue varius et. Donec viverra lorem quis euismod vulputate. Pellentesque vitae suscipit neque, vitae rutrum orci. Proin sollicitudin accumsan diam nec consectetur. Ut dictum ligula ligula, ut tincidunt nunc molestie ac. Praesent neque tortor, maximus id nisl in, eleifend aliquet felis. Praesent laoreet dui malesuada, gravida libero id, sollicitudin neque. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus",
"author": "inventory",
"homepage": "https://example.com",
"public": true
},
{
"orgId": "cart",
"orgName": "Cart",
"name": "Cart",
"description": "Morbi vel accumsan lorem, varius dictum enim. Donec dignissim enim vel congue tincidunt. In tristique dui sed arcu mattis, eget fringilla mi tempus. Aliquam sit amet lobortis leo. Nunc orci arcu, ullamcorper vel aliquet in, suscipit et erat. Aliquam in hendrerit massa. Aliquam blandit nunc nec urna luctus rhoncus. Morbi cursus elit arcu, id dapibus augue varius et. Donec viverra lorem quis euismod vulputate. Pellentesque vitae suscipit neque, vitae rutrum orci. Proin sollicitudin accumsan diam nec consectetur. Ut dictum ligula ligula, ut tincidunt nunc molestie ac. Praesent neque tortor, maximus id nisl in, eleifend aliquet felis. Praesent laoreet dui malesuada, gravida libero id, sollicitudin neque. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus",
"author": "cart",
"homepage": "https://example.com"
}
]
59 changes: 4 additions & 55 deletions src/main/client.mjs
Original file line number Diff line number Diff line change
@@ -1,62 +1,15 @@
import errors from './errors.mjs'
import { join } from 'path'
import { readFile } from 'node:fs/promises'
import { app, dialog } from 'electron'
import {
setUserLoggedIn,
setUserNoAPIKey,
setUserInvalidAPIKey
} from './user-status.mjs'
import { dialog } from 'electron'

import buildMarketplaceClient from './marketplace-client/marketplace-client.mjs'

const marketplaceHost = import.meta.env.MAIN_VITE_MARKETPLACE_HOST || 'https://marketplace.platformatic.dev'

const getCurrentApiKey = async () => {
let platformaticHome
if (app) {
platformaticHome = app.getPath('home')
} else {
// unit tests
platformaticHome = process.env.HOME
}
const pltDirPath = join(platformaticHome, '.platformatic')
const configPath = join(pltDirPath, 'config.json')
let configFile
try {
configFile = await readFile(configPath, 'utf8')
} catch (err) {
// The file does not exist or is not readeable.
if (err.code === 'ENOENT') {
console.log('Config file not found')
}
return null
}
let config
try {
config = JSON.parse(configFile)
} catch (err) {
throw new errors.ConfigNotParsableError(configPath)
}
return config.userApiKey
}

// User API key is optional. If passed we used it to retrieve also the private stackables
async function getStackablesAPI (marketplaceHost, apiKey) {
async function getStackablesAPI (marketplaceHost) {
try {
const marketplaceClient = buildMarketplaceClient(marketplaceHost)
const { statusCode, body } = await marketplaceClient.getTemplates({
'x-platformatic-user-api-key': apiKey ?? undefined
})
if (statusCode === 401 || statusCode === 403) {
// the user presente an invalid api key
setUserInvalidAPIKey()
// If the user is not authorized, we return only the public stackables
return getStackablesAPI(marketplaceHost)
} else if (apiKey) {
// The API key is valid
setUserLoggedIn()
}
const { statusCode, body } = await marketplaceClient.getTemplates({})

if (statusCode !== 200) {
throw new errors.CannotGetStackablesError(body.message)
Expand Down Expand Up @@ -90,12 +43,8 @@ async function getPluginsAPI (marketplaceHost, search = '') {
}

export const getTemplates = async (names) => {
const apiKey = await getCurrentApiKey()
if (!apiKey) {
setUserNoAPIKey()
}
// TODO: pass the names to the API, we need to change the API to accept an array of names
const stackables = await getStackablesAPI(marketplaceHost, apiKey)
const stackables = await getStackablesAPI(marketplaceHost)
if (names) {
return stackables.filter((stackable) => names.includes(stackable?.name))
}
Expand Down
28 changes: 0 additions & 28 deletions src/main/user-status.mjs

This file was deleted.

68 changes: 2 additions & 66 deletions src/renderer/src/components/templates/SelectTemplate.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import NoResults from '~/components/ui/NoResults'
import useStackablesStore from '~/useStackablesStore'
import { getApiTemplates, registerUserStatusListener } from '~/api'
import { MAX_MUMBER_SELECT, MIN_MUMBER_SELECT, NO_RESULTS_VIEW, LIST_TEMPLATES_VIEW, MAX_HEIGHT_CHANGE_NUMBER_SELECT } from '~/ui-constants'
import Forms from '@platformatic/ui-components/src/components/forms'
import useWindowDimensions from '~/hooks/useWindowDimensions'

function SelectTemplate ({ onClick, serviceName }) {
Expand All @@ -21,7 +20,6 @@ function SelectTemplate ({ onClick, serviceName }) {
const [templates, setTemplates] = useState([])
const [innerLoading, setInnerLoading] = useState(true)
const [filteredTemplates, setFilteredTemplates] = useState([])
const [optionsOrganizationsTemplates, setOptionsOrganizationsTemplates] = useState([])
const [groupedTemplates, setGroupedTemplates] = useState([])
const [userStatus, setUserStatus] = useState(null)
const [currentPage, setCurrentPage] = useState(1)
Expand All @@ -30,7 +28,6 @@ function SelectTemplate ({ onClick, serviceName }) {
const [templateSelected, setTemplateSelected] = useState(null)
const scrollRef = useRef(null)
const [filterTemplatesByName, setFilterTemplatesByName] = useState('')
const [filterTemplatesByOrgName, setFilterTemplatesByOrgName] = useState('')
const [maxStackableDispay, setMaxStackableDispay] = useState(MAX_MUMBER_SELECT)
const { height: innerHeight } = useWindowDimensions()
const [templateContainerClassName, setTemplateContainerClassName] = useState(`${styles.templatesContainer} ${styles.templatesContainerMediumHeight}`)
Expand All @@ -42,7 +39,6 @@ function SelectTemplate ({ onClick, serviceName }) {
useEffect(() => {
async function getTemplates () {
const templates = await getApiTemplates()
setOptionsOrganizationsTemplates([...getOrganizationGrouped(templates)])
setTemplates(templates)
setFilteredTemplates([...templates])
setTemplateSelected(templates[0])
Expand Down Expand Up @@ -73,17 +69,14 @@ function SelectTemplate ({ onClick, serviceName }) {
}, [templates, serviceName, Object.keys(getService(serviceName).template).length])

useEffect(() => {
if (filterTemplatesByOrgName || filterTemplatesByName) {
if (filterTemplatesByName) {
let founds = [...templates]
if (filterTemplatesByOrgName) {
founds = templates.filter(template => template.orgName === filterTemplatesByOrgName)
}
founds = founds.filter(template => template.name.toLowerCase().includes(filterTemplatesByName.toLowerCase()))
setFilteredTemplates(founds)
} else {
setFilteredTemplates([...templates])
}
}, [filterTemplatesByOrgName, filterTemplatesByName])
}, [filterTemplatesByName])

useEffect(() => {
if (filteredTemplates.length > 0) {
Expand All @@ -109,36 +102,6 @@ function SelectTemplate ({ onClick, serviceName }) {
onClick()
}

function getOrganizationGrouped (templates) {
return templates.map(e => e?.orgName || 'No Name').reduce((acc, currentValue) => {
const found = acc.find(a => a.label === currentValue)
if (found) {
found.count += 1
} else {
acc.push({
label: currentValue,
count: 1
})
}
return acc
}, []).sort((a, b) => {
const labelA = a.label.toUpperCase()
const labelB = b.label.toUpperCase()
if (labelA < labelB) {
return -1
}
if (labelA > labelB) {
return 1
}
return 0
}).map(ele => ({
label: ele.label,
value: ele.label,
iconName: 'OrganizationIcon',
descriptionValue: `( ${ele.count} ${ele.count > 1 ? 'Templates' : 'Template'} )`
}))
}

function handleClearTemplates () {
setFilterTemplatesByName('')
}
Expand Down Expand Up @@ -166,18 +129,6 @@ function SelectTemplate ({ onClick, serviceName }) {
})
}

// Functions Related to Form.Select
function handleChangeOrganization (event) {
setFilterTemplatesByOrgName(event.target.value)
}

function handleSelectOrganization (event) {
setFilterTemplatesByOrgName(event.detail.value)
}

function handleClearOrganization () {
setFilterTemplatesByOrgName('')
}
// End Functions Related to Form.Select

function renderListTemplates () {
Expand Down Expand Up @@ -275,21 +226,6 @@ function SelectTemplate ({ onClick, serviceName }) {
{renderLoginStatus()}
</div>
<div className={`${commonStyles.tinyFlexRow} ${commonStyles.fullWidth}`}>
<Forms.SelectWithInput
defaultContainerClassName={styles.select}
backgroundColor={RICH_BLACK}
placeholder='Select Organization'
borderColor={WHITE}
options={optionsOrganizationsTemplates}
defaultOptionsClassName={styles.selectUl}
onChange={handleChangeOrganization}
onSelect={handleSelectOrganization}
onClear={handleClearOrganization}
optionsBorderedBottom={false}
mainColor={WHITE}
borderListColor={WHITE}
value={filterTemplatesByOrgName}
/>
<SearchBarV2
placeholder='Search for a Template'
onClear={handleClearTemplates}
Expand Down
55 changes: 3 additions & 52 deletions test/main/client.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { mkdirp } from 'mkdirp'
setUpEnvironment()
const { getTemplates, getPlugins } = await import('../../src/main/client.mjs')

test('should invoke marketplace for stackables, no user key', async () => {
test('should invoke marketplace for stackables', async () => {
const platformaticDir = await mkdtemp(join(tmpdir(), 'plat-app-test-home'))
onTestFinished(() => rm(platformaticDir, { recursive: true }))
process.env.HOME = platformaticDir
Expand All @@ -29,25 +29,21 @@ test('should invoke marketplace for stackables, no user key', async () => {
]
await startMarketplace({
getStackablesCallback: (request, reply) => {
const headers = request.headers
expect(headers['x-platformatic-user-api-key']).toBeUndefined()
reply.code(200).send(stacks)
}
})
const stackables = await getTemplates()
expect(stackables).toEqual(stacks)
})

test('should invoke marketplace for stackables, passing user key', async () => {
test('should invoke marketplace for stackables', async () => {
const platformaticDir = await mkdtemp(join(tmpdir(), 'plat-app-test-home'))
onTestFinished(() => rm(platformaticDir, { recursive: true }))
process.env.HOME = platformaticDir
await mkdirp(join(platformaticDir, '.platformatic'))
const configPath = join(platformaticDir, '.platformatic', 'config.json')
const userApiKey = '123456787654321'
const config = {
$schema: 'https://platformatic.dev/schemas/v1.12.1/login',
userApiKey
$schema: 'https://platformatic.dev/schemas/v1.12.1/login'
}
await writeFile(configPath, JSON.stringify(config), 'utf8')

Expand All @@ -67,58 +63,13 @@ test('should invoke marketplace for stackables, passing user key', async () => {
]
await startMarketplace({
getStackablesCallback: (request, reply) => {
const headers = request.headers
expect(headers['x-platformatic-user-api-key']).toEqual(userApiKey)
reply.code(200).send(stacks)
}
})
const stackables = await getTemplates()
expect(stackables).toEqual(stacks)
})

test('should invoke marketplace for stackables, passing user key but not authorized', async () => {
const platformaticDir = await mkdtemp(join(tmpdir(), 'plat-app-test-home'))
onTestFinished(() => rm(platformaticDir, { recursive: true }))
process.env.HOME = platformaticDir
await mkdirp(join(platformaticDir, '.platformatic'))
const configPath = join(platformaticDir, '.platformatic', 'config.json')
const userApiKey = '123456787654321'
const config = {
$schema: 'https://platformatic.dev/schemas/v1.12.1/login',
userApiKey
}
await writeFile(configPath, JSON.stringify(config), 'utf8')

const stacks = [
{
orgName: 'org1',
name: 'stackable1',
description: 'stackable1 description',
public: true
},
{
orgName: 'org2',
name: 'stackable2',
description: 'stackable2 description',
public: true
}
]
await startMarketplace({
getStackablesCallback: (request, reply) => {
const headers = request.headers
if (headers['x-platformatic-user-api-key']) {
reply.code(401).send({
message: 'Unauthorized'
})
} else {
reply.code(200).send(stacks)
}
}
})
const stackables = await getTemplates()
expect(stackables).toEqual(stacks)
})

test('should invoke marketplace for stackables', async () => {
const envVars1 = [{
name: 'TEST',
Expand Down

0 comments on commit cc1c022

Please sign in to comment.