Skip to content

Commit

Permalink
Merge pull request #147 from storyblok/bugfix/replace-ofetch-native-f…
Browse files Browse the repository at this point in the history
…etch

fix: replace ofetch native fetch
  • Loading branch information
alvarosabu authored Jan 14, 2025
2 parents 16c91fc + e2f670e commit bcbb136
Show file tree
Hide file tree
Showing 17 changed files with 372 additions and 82 deletions.
28 changes: 28 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@
"STUB": "true"
}
},
{
"type": "node",
"request": "launch",
"name": "Debug login by token",
"program": "${workspaceFolder}/dist/index.mjs",
"args": ["login", "--token", "1234567890"],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"env": {
"STUB": "true"
}
},
{
"type": "node",
"request": "launch",
Expand Down Expand Up @@ -68,6 +82,20 @@
"env": {
"STUB": "true"
}
},
{
"type": "node",
"request": "launch",
"name": "Debug Test",
"program": "${workspaceFolder}/dist/index.mjs",
"args": ["test"],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"env": {
"STUB": "true"
}
}
]
}
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
"chalk": "^5.3.0",
"commander": "^12.1.0",
"dotenv": "^16.4.5",
"ofetch": "^1.4.0",
"storyblok-js-client": "^6.9.2"
},
"devDependencies": {
Expand Down
22 changes: 0 additions & 22 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 15 additions & 11 deletions src/commands/login/actions.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import chalk from 'chalk'
import type { RegionCode } from '../../constants'
import { regionsDomain } from '../../constants'
import { FetchError, ofetch } from 'ofetch'
import { customFetch, FetchError } from '../../utils/fetch'
import { APIError, handleAPIError, maskToken } from '../../utils'
import { getStoryblokUrl } from '../../utils/api-routes'
import type { StoryblokLoginResponse, StoryblokLoginWithOtpResponse, StoryblokUser } from '../../types'

export const loginWithToken = async (token: string, region: RegionCode) => {
try {
return await ofetch(`https://${regionsDomain[region]}/v1/users/me`, {
const url = getStoryblokUrl(region)
return await customFetch<{
user: StoryblokUser
}>(`${url}/users/me`, {
headers: {
Authorization: token,
},
})
}
catch (error) {
if (error instanceof FetchError) {
const status = error.response?.status
const status = error.response.status

switch (status) {
case 401:
Expand All @@ -24,17 +28,16 @@ export const loginWithToken = async (token: string, region: RegionCode) => {
throw new APIError('network_error', 'login_with_token', error)
}
}
else {
throw new APIError('generic', 'login_with_token', error as Error)
}
throw new APIError('generic', 'login_with_token', error as FetchError)
}
}

export const loginWithEmailAndPassword = async (email: string, password: string, region: RegionCode) => {
try {
return await ofetch(`https://${regionsDomain[region]}/v1/users/login`, {
const url = getStoryblokUrl(region)
return await customFetch<StoryblokLoginResponse>(`${url}/users/login`, {
method: 'POST',
body: JSON.stringify({ email, password }),
body: { email, password },
})
}
catch (error) {
Expand All @@ -44,9 +47,10 @@ export const loginWithEmailAndPassword = async (email: string, password: string,

export const loginWithOtp = async (email: string, password: string, otp: string, region: RegionCode) => {
try {
return await ofetch(`https://${regionsDomain[region]}/v1/users/login`, {
const url = getStoryblokUrl(region)
return await customFetch<StoryblokLoginWithOtpResponse>(`${url}/users/login`, {
method: 'POST',
body: JSON.stringify({ email, password, otp_attempt: otp }),
body: { email, password, otp_attempt: otp },
})
}
catch (error) {
Expand Down
10 changes: 6 additions & 4 deletions src/commands/login/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,18 @@ export const loginCommand = program
})
const response = await loginWithEmailAndPassword(userEmail, userPassword, userRegion)

if (response.otp_required) {
if (response?.otp_required) {
const otp = await input({
message: 'Add the code from your Authenticator app, or the one we sent to your e-mail / phone:',
required: true,
})

const { access_token } = await loginWithOtp(userEmail, userPassword, otp, userRegion)
updateSession(userEmail, access_token, userRegion)
const otpResponse = await loginWithOtp(userEmail, userPassword, otp, userRegion)
if (otpResponse?.access_token) {
updateSession(userEmail, otpResponse?.access_token, userRegion)
}
}
else {
else if (response?.access_token) {
updateSession(userEmail, response.access_token, userRegion)
}
await persistCredentials(regionsDomain[userRegion])
Expand Down
29 changes: 13 additions & 16 deletions src/commands/pull-languages/actions.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,35 @@
import { join } from 'node:path'

import { handleAPIError, handleFileSystemError } from '../../utils'
import { ofetch } from 'ofetch'
import { regionsDomain } from '../../constants'
import type { FetchError } from '../../utils/fetch'
import { customFetch } from '../../utils/fetch'
import { resolvePath, saveToFile } from '../../utils/filesystem'
import type { PullLanguagesOptions } from './constants'
import type { RegionCode } from '../../constants'
import type { SpaceInternationalization } from '../../types'
import { getStoryblokUrl } from '../../utils/api-routes'

export interface SpaceInternationalizationOptions {
languages: SpaceLanguage[]
default_lang_name: string
}
export interface SpaceLanguage {
code: string
name: string
}

export const pullLanguages = async (space: string, token: string, region: string): Promise<SpaceInternationalizationOptions | undefined> => {
export const pullLanguages = async (space: string, token: string, region: RegionCode): Promise<SpaceInternationalization | undefined> => {
try {
const response = await ofetch(`https://${regionsDomain[region]}/v1/spaces/${space}`, {
const url = getStoryblokUrl(region)
const response = await customFetch<{
space: SpaceInternationalization
}>(`${url}/spaces/${space}`, {
headers: {
Authorization: token,
},
})

return {
default_lang_name: response.space.default_lang_name,
languages: response.space.languages,
}
}
catch (error) {
handleAPIError('pull_languages', error as Error)
handleAPIError('pull_languages', error as FetchError)
}
}

export const saveLanguagesToFile = async (space: string, internationalizationOptions: SpaceInternationalizationOptions, options: PullLanguagesOptions) => {
export const saveLanguagesToFile = async (space: string, internationalizationOptions: SpaceInternationalization, options: PullLanguagesOptions) => {
try {
const { filename = 'languages', suffix = space, path } = options
const data = JSON.stringify(internationalizationOptions, null, 2)
Expand Down
20 changes: 12 additions & 8 deletions src/commands/user/actions.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { FetchError, ofetch } from 'ofetch'
import { regionsDomain } from '../../constants'
import chalk from 'chalk'
import type { RegionCode } from '../../constants'
import { customFetch, FetchError } from '../../utils/fetch'
import { APIError, maskToken } from '../../utils'
import { getStoryblokUrl } from '../../utils/api-routes'
import type { StoryblokUser } from '../../types'

export const getUser = async (token: string, region: string) => {
export const getUser = async (token: string, region: RegionCode) => {
try {
return await ofetch(`https://${regionsDomain[region]}/v1/users/me`, {
const url = getStoryblokUrl(region)
const response = await customFetch<{
user: StoryblokUser
}>(`${url}/users/me`, {
headers: {
Authorization: token,
},
})
return response
}
catch (error) {
if (error instanceof FetchError) {
const status = error.response?.status
const status = error.response.status

switch (status) {
case 401:
Expand All @@ -23,8 +29,6 @@ export const getUser = async (token: string, region: string) => {
throw new APIError('network_error', 'get_user', error)
}
}
else {
throw new APIError('generic', 'get_user', error as Error)
}
throw new APIError('generic', 'get_user', error as FetchError)
}
}
3 changes: 2 additions & 1 deletion src/creds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { join } from 'node:path'
import { FileSystemError, handleFileSystemError, konsola } from './utils'
import chalk from 'chalk'
import { colorPalette, regionCodes } from './constants'
import type { RegionCode } from './constants'

export interface NetrcMachine {
login: string
password: string
region: string
region: RegionCode
}

export const getNetrcFilePath = () => {
Expand Down
20 changes: 17 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import './commands/logout'
import './commands/user'
import './commands/pull-languages'

import { loginWithToken } from './commands/login/actions'
import { customFetch } from './utils/fetch'
import { getStoryblokUrl } from './utils/api-routes'
import { session } from './session'

dotenv.config() // This will load variables from .env into process.env
const program = getProgram()
Expand All @@ -31,8 +33,20 @@ program.command('test').action(async () => {
konsola.title(`Test`, '#8556D3', 'Attempting a test...')
const verbose = program.opts().verbose
try {
// await loginWithEmailAndPassword('aw', 'passwrod', 'eu')
await loginWithToken('WYSYDHYASDHSYD', 'eu')
const { state, initializeSession } = session()
await initializeSession()
const url = getStoryblokUrl()

if (!state.password) {
throw new Error('No password found')
}

const response = await customFetch(`${url}/spaces/2950170505/components`, {
headers: {
Authorization: state.password,
},
})
console.log(response)
}
catch (error) {
handleError(error as Error, verbose)
Expand Down
6 changes: 3 additions & 3 deletions src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ interface SessionState {
isLoggedIn: boolean
login?: string
password?: string
region?: string
region?: RegionCode
envLogin?: boolean
}

Expand All @@ -24,7 +24,7 @@ function createSession() {
state.isLoggedIn = true
state.login = envCredentials.login
state.password = envCredentials.password
state.region = envCredentials.region
state.region = envCredentials.region as RegionCode
state.envLogin = true
return
}
Expand All @@ -36,7 +36,7 @@ function createSession() {
state.isLoggedIn = true
state.login = creds.login
state.password = creds.password
state.region = creds.region
state.region = creds.region as RegionCode
}
else {
// No credentials found; set state to logged out
Expand Down
10 changes: 10 additions & 0 deletions src/types/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Interface representing an HTTP response error
*/
export interface ResponseError extends Error {
response?: {
status: number
statusText: string
data?: any
}
}
Loading

0 comments on commit bcbb136

Please sign in to comment.