Error while using Redis or MongoDB (TypeError: scopesArray.map is not a function)
fonya2014 opened this issue ยท 11 comments
# Issue summary
Hello everyone!
I am working with the new app template and the API that was released in January 2023.
I am trying to use Redis or MongoDB as session storage. I use these packages:
@shopify/shopify-app-session-storage-redis'
@shopify/shopify-app-session-storage-mongodb'
according to these guides:
https://github.com/Shopify/shopify-app-js/tree/main/packages/shopify-app-session-storage-redis
https://github.com/Shopify/shopify-app-js/tree/main/packages/shopify-app-session-storage-mongodb
And I am getting the following error in the console:
node_modules\@shopify\shopify-api\lib\auth\scopes\index.js:15
.map(function (scope) { return scope.trim(); })
TypeError: scopesArray.map is not a function
If I try to use REST API, I am getting the 503 error on the frontend (browser console).
The session data document is created in MongoDB.
If I use SQLiteSessionStorage, everything works and loads fine.
Could you please help me to solve the issue?
@shopify/shopify-app-express
version: 1.0.2- Node version: 16.15.0
- Operating system: Windows 10
2023-01-11 12:29:31 | backend | [nodemon] starting `node index.js`
2023-01-11 12:30:57 | backend | [shopify-app/INFO] Running ensureInstalledOnShop
2023-01-11 12:30:57 | backend | [shopify-app/DEBUG] Checking if shop has installed the app | {shop: a-store-for-a-test-task.myshopify.com}
2023-01-11 12:30:57 | backend | [shopify-app/ERROR] Could not check if session was valid: TypeError: scopesArray.map is not a function | {shop: a-store-for-a-test-task.myshopify.com}
2023-01-11 12:30:57 | backend | [shopify-app/INFO] Found a session, but it is not valid. Redirecting to auth | {shop: a-store-for-a-test-task.myshopify.com}
2023-01-11 12:30:57 | backend | [shopify-app/DEBUG] Redirecting to auth at /api/auth, with callback /api/auth/callback | {shop: a-store-for-a-test-task.myshopify.com, isOnline: false}
2023-01-11 12:30:57 | backend | [shopify-api/INFO] Beginning OAuth | {shop: a-store-for-a-test-task.myshopify.com, isOnline: false, callbackPath: /api/auth/callback}
2023-01-11 12:30:57 | backend | [shopify-api/DEBUG] OAuth started, redirecting to https://a-store-for-a-test-task.myshopify.com/admin/oauth/authorize?client_id=88ed9b3b95307ea81c67173f944fd
19a&scope=write_themes&redirect_uri=https%3A%2F%2F0ead-188-163-108-143.ngrok.io%2Fapi%2Fauth%2Fcallback&state=439314155827792&grant_options%5B%5D= | {shop:
a-store-for-a-test-task.myshopify.com, isOnline: false}
2023-01-11 12:30:59 | backend | [shopify-app/INFO] Handling request to complete OAuth process
2023-01-11 12:30:59 | backend | [shopify-api/INFO] Completing OAuth | {shop: a-store-for-a-test-task.myshopify.com}
2023-01-11 12:30:59 | backend | [shopify-api/DEBUG] OAuth request is valid, requesting access token | {shop: a-store-for-a-test-task.myshopify.com}
2023-01-11 12:30:59 | backend | [shopify-app/DEBUG] Registering webhooks | {shop: a-store-for-a-test-task.myshopify.com}
2023-01-11 12:30:59 | backend | [shopify-api/INFO] Registering webhooks | {shop: a-store-for-a-test-task.myshopify.com}
2023-01-11 12:31:00 | backend | [shopify-api/DEBUG] Existing topics: [APP_UNINSTALLED] | {shop: a-store-for-a-test-task.myshopify.com}
2023-01-11 12:31:00 | backend | [shopify-api/DEBUG] Running webhook mutation | {topic: APP_UNINSTALLED, operation: create}
2023-01-11 12:31:01 | backend | [shopify-api/DEBUG] Running webhook mutation | {topic: APP_UNINSTALLED, operation: delete}
2023-01-11 12:31:01 | backend | [shopify-app/DEBUG] Completed OAuth callback | {shop: a-store-for-a-test-task.myshopify.com, isOnline: false}
2023-01-11 12:31:01 | backend | [shopify-app/DEBUG] Redirecting to host at https://a-store-for-a-test-task.myshopify.com/admin/apps/88ed9b3b95307ea81c67173f944fd19a | {shop:
a-store-for-a-test-task.myshopify.com}
2023-01-11 12:31:12 | backend | [shopify-app/INFO] Running ensureInstalledOnShop
2023-01-11 12:31:12 | backend | [shopify-app/DEBUG] Checking if shop has installed the app | {shop: a-store-for-a-test-task.myshopify.com}
2023-01-11 12:31:12 | backend | [shopify-app/INFO] App is installed and ready to load | {shop: a-store-for-a-test-task.myshopify.com}
2023-01-11 12:31:40 | backend | [shopify-app/INFO] Running validateAuthenticatedSession
2023-01-11 12:31:40 | backend | [shopify-api/DEBUG] App is embedded, looking for session id in JWT payload | {isOnline: false}
2023-01-11 12:31:40 | backend | [shopify-api/DEBUG] Found valid JWT payload | {shop: a-store-for-a-test-task.myshopify.com, isOnline: false}
2023-01-11 12:31:40 | backend | [shopify-app/DEBUG] Request session found and loaded | {shop: a-store-for-a-test-task.myshopify.com}
2023-01-11 12:31:40 | backend | C:\Users\Sofiia\VSCODEProjects\seo-schema-for-products\node_modules\@shopify\shopify-api\lib\auth\scopes\index.js:15
2023-01-11 12:31:40 | backend | .map(function (scope) { return scope.trim(); })
2023-01-11 12:31:40 | backend | ^
2023-01-11 12:31:40 | backend |
2023-01-11 12:31:40 | backend | TypeError: scopesArray.map is not a function
2023-01-11 12:31:40 | backend | at new AuthScopes (C:\Users\Sofiia\VSCODEProjects\seo-schema-for-products\node_modules\@shopify\shopify-api\lib\auth\scopes\index.js:15:14)
2023-01-11 12:31:40 | backend | at Session.isActive (C:\Users\Sofiia\VSCODEProjects\seo-schema-for-products\node_modules\@shopify\shopify-api\lib\session\session.js:80:77)
2023-01-11 12:31:40 | backend | at C:\Users\Sofiia\VSCODEProjects\seo-schema-for-products\web\node_modules\@shopify\shopify-app-express\build\cjs\middlewares\validate-authenticated-sess
ion.js:69:79
2023-01-11 12:31:40 | backend | at processTicksAndRejections (node:internal/process/task_queues:96:5)
2023-01-11 12:31:40 | backend | [nodemon] app crashed - waiting for file changes before starting...
My shopify.js file:
`
import { BillingInterval, LATEST_API_VERSION, BillingReplacementBehavior, LogSeverity } from "@shopify/shopify-api";
import { shopifyApp } from "@shopify/shopify-app-express";
let { restResources } = await import(
`@shopify/shopify-api/rest/admin/${LATEST_API_VERSION}`
);
import {RedisSessionStorage} from '@shopify/shopify-app-session-storage-redis';
import {MongoDBSessionStorage} from '@shopify/shopify-app-session-storage-mongodb';
import { SQLiteSessionStorage } from "@shopify/shopify-app-session-storage-sqlite";
const DB_PATH = `${process.cwd()}/database.sqlite`;
/// The transactions with Shopify will always be marked as test transactions, unless NODE_ENV is production.
// See the ensureBilling helper to learn more about billing in this template.
const billingConfig = {
"Basic Plan": {
// This is an example configuration that would do a one-time charge for $5 (only USD is currently supported)
amount: 4.99,
currencyCode: "USD",
interval: BillingInterval.Every30Days,
trialDays: 3,
replacementBehavior: BillingReplacementBehavior.ApplyImmediately,
},
};
const shopify = shopifyApp({
api: {
apiVersion: LATEST_API_VERSION,
restResources,
billing: billingConfig, // or replace with billingConfig above to enable example billing
logger: {
level: LogSeverity.Debug,
//httpRequests: true, // if the error seems to be related to requests
},
},
auth: {
path: "/api/auth",
callbackPath: "/api/auth/callback",
},
webhooks: {
path: "/api/webhooks",
},
// This should be replaced with your preferred storage strategy
//sessionStorage: new RedisSessionStorage("redis://login:pass@host.cloud.redislabs.com:19100"),
//sessionStorage: new SQLiteSessionStorage(DB_PATH),
sessionStorage: new MongoDBSessionStorage(
'mongodb+srv://login:pass@cluster.mongodb.net/?retryWrites=true&w=majority',
'DBName',
),
});
export default shopify;
`
## Expected behavior
The session and scope loads from DB.
## Actual behavior
The following error in the console:
node_modules\@shopify\shopify-api\lib\auth\scopes\index.js:15
.map(function (scope) { return scope.trim(); })
TypeError: scopesArray.map is not a function
## Steps to reproduce the problem
-
Use Redis or MongoDB according to
https://github.com/Shopify/shopify-app-js/tree/main/packages/shopify-app-session-storage-redis
https://github.com/Shopify/shopify-app-js/tree/main/packages/shopify-app-session-storage-mongodb -
Try to get access to the REST API using the app.
Here too
I am experiencing this issue, too. Just set up session storage with mongodb using the @shopify/shopify-app-session-storage-mongodb
package
another one, I want to storage in mongodb
I ended up replacing validateAuthenticatedSession()
with a custom middleware function to retrieve the session from the shopify_sessions
collection. I'm then setting the session in the following responses. Things seem to be working fine and I don't see this problem anymore.
export const customMiddleware = async function (req, res, next) {
const session = await db.collection('shopify_sessions').findOne({ /** whatever index **/ })
res.locals.shopify = { session: {} }
res.locals.shopify.session = session
next()
}
Hello everyone! I investigated the issue and here is what I discovered:
MongoDB and Redis return the following object:
AuthScopes {
compressedScopes: Set(1) { 'write_themes' },
expandedScopes: Set(2) { 'write_themes', 'read_themes' }
}
The error
TypeError: scopesArray.map is not a function
happens because .map doesn't work with objects.
I tried to replace the line:
scopesArray = scopes;
width this line:
scopesArray = [...scopes.expandedScopes]
In this part of the code (In the file node_modules@shopify\shopify-api\lib\auth\scopes\index.js):
function AuthScopes(scopes) {
var scopesArray = [];
if (typeof scopes === 'string') {
scopesArray = scopes.split(new RegExp("".concat(AuthScopes.SCOPE_DELIMITER, "\\s*")));
}
else if (scopes) {
//scopesArray = scopes;
scopesArray = [...scopes.expandedScopes]
}
And everything is working now
Hello everyone! I investigated the issue and here is what I discovered:
MongoDB and Redis return the following object:
AuthScopes { compressedScopes: Set(1) { 'write_themes' }, expandedScopes: Set(2) { 'write_themes', 'read_themes' } }
The error
TypeError: scopesArray.map is not a function
happens because .map doesn't work with objects.I tried to replace the line:
scopesArray = scopes;
width this line:scopesArray = [...scopes.expandedScopes]
In this part of the code (In the file node_modules@shopify\shopify-api\lib\auth\scopes\index.js):
function AuthScopes(scopes) { var scopesArray = []; if (typeof scopes === 'string') { scopesArray = scopes.split(new RegExp("".concat(AuthScopes.SCOPE_DELIMITER, "\\s*"))); } else if (scopes) { //scopesArray = scopes; scopesArray = [...scopes.expandedScopes] }
And everything is working now
Had exact same issue, saw the same output for scopesArray. Implemented the fix recommended in that reply and it all works. thanks @fonya2014
@paulomarg or @cquemin is this something that could be PR'd? Also, does using the expandedScopes
for the scopesArray cause problems or should we be combining the compressedScopes and expandedScopes, then use that array?
Try to remove shopify-api
dependency as answered here #1208 (comment)
@fonya2014 I'm having difficulty in reproducing this as you've described above.
- What version of MongoDB are you using?
- What versions of the following libraries?
@shopify-app-express
@shopify/shopify-app-session-storage-mongodb
@shopify/shopify-api
MongoDB and Redis return the following object:
AuthScopes { compressedScopes: Set(1) { 'write_themes' }, expandedScopes: Set(2) { 'write_themes', 'read_themes' } }
Where did you log this object?
I'm using the basic templates with version 1.0.2
of the mongodb package (which was the version around the time this issue was created), and I've modified the mongodb.js
file such that it logs what's returned by findOne()
in loadSession()
...
async loadSession(id) {
await this.ready;
const result = await this.collection.findOne({
id
});
console.log('MongoDBSessionStorage.loadSession', id, result); // added this debug line
return result ? new shopifyApi.Session(result) : undefined;
}
The output I get is
MongoDBSessionStorage.loadSession offline_testshop.myshopify.com {
_id: new ObjectId("644933001151c713ce729762"),
id: 'offline_testshop.myshopify.com',
shop: 'testshop.myshopify.com',
state: '424763202963782',
isOnline: false,
scope: 'write_products,write_orders',
accessToken: 'shpat_redacted_access_token'
}
scope
is showing as a string, not an AuthScopes
object, so I'm curious as to where you logged that return value?
Note that I've also tried this with @shopoify/shopify-app-express@1.0.2
and @shopify/shopify-app-session-storage-mongodb@1.0.1
Hello @mkevinosullivan !
Here are the versions of packages I probably used at that time:
@shopify-app-express 1.0.2
@shopify/shopify-app-session-storage-mongodb 1.0.1
@shopify/shopify-api 6.1.0
I don't remember for sure about the mongodb and shopify-api packages as it was 4 months earlier and I updated all apps to recent versions since then.
I added the console log to this file:
node_modules@shopify\shopify-api\lib\auth\scopes\index.js
function AuthScopes(scopes) {
var scopesArray = [];
console.log(scopes); // here is my console log
if (typeof scopes === 'string') {
scopesArray = scopes.split(new RegExp("".concat(AuthScopes.SCOPE_DELIMITER, "\\s*")));
}
else if (scopes) {
scopesArray = scopes;
}
And it showed me the following:
AuthScopes {
compressedScopes: Set(1) { 'write_themes' },
expandedScopes: Set(2) { 'write_themes', 'read_themes' }
}
As I can see, the changes done several days ago in this PR must address the issue:
Shopify/shopify-api-js#837
Released in @shopify/shopify-api@7.1.0
today.