@learnpack/learnpack 5.0.343 → 5.0.346

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,14 @@
1
1
  import SessionCommand from "../utils/SessionCommand";
2
+ type ExistingAssetInfo = {
3
+ lang: string;
4
+ slug: string;
5
+ exists: boolean;
6
+ academyId?: number;
7
+ };
2
8
  export declare const handleAssetCreation: (sessionPayload: {
3
9
  token: string;
4
10
  rigobotToken: string;
5
- }, learnJson: any, selectedLang: string, learnpackDeployUrl: string, b64IndexReadme: string, academyId: number | undefined, all_translations?: string[]) => Promise<any>;
11
+ }, learnJson: any, selectedLang: string, learnpackDeployUrl: string, b64IndexReadme: string, academyId: number | undefined, preflightInfo?: ExistingAssetInfo, all_translations?: string[]) => Promise<any>;
6
12
  declare class BuildCommand extends SessionCommand {
7
13
  static description: string;
8
14
  static flags: {
@@ -45,7 +45,33 @@ const getLocalizedValue = (translations, lang, fallbackLangs = ["en", "us"]) =>
45
45
  const first = firstKey ? translations[firstKey] : "";
46
46
  return typeof first === "string" ? first : "";
47
47
  };
48
- const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, learnpackDeployUrl, b64IndexReadme, academyId, all_translations = []) => {
48
+ const getExistingAssetsInfo = async (token, learnJson) => {
49
+ const availableLangs = getAvailableLangs(learnJson);
50
+ const results = [];
51
+ for (const lang of availableLangs) {
52
+ const assetTitle = getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, lang);
53
+ if (!assetTitle)
54
+ continue;
55
+ let slug = (0, creatorUtilities_1.slugify)(assetTitle).slice(0, 47);
56
+ slug = `${slug}-${lang}`;
57
+ // eslint-disable-next-line no-await-in-loop
58
+ const { exists, academyId } = await api_1.default.doesAssetExists(token, slug);
59
+ results.push({ lang, slug, exists, academyId });
60
+ }
61
+ return results;
62
+ };
63
+ const determinePublishAcademyMode = (existingAssets) => {
64
+ const academyIds = existingAssets
65
+ .filter((a) => a.exists && a.academyId !== undefined)
66
+ .map((a) => a.academyId);
67
+ const unique = [...new Set(academyIds)];
68
+ if (unique.length === 0)
69
+ return { type: "select" };
70
+ if (unique.length === 1)
71
+ return { type: "locked", academyId: unique[0] };
72
+ return { type: "conflict", academies: unique };
73
+ };
74
+ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, learnpackDeployUrl, b64IndexReadme, academyId, preflightInfo, all_translations = []) => {
49
75
  const category = "uncategorized";
50
76
  try {
51
77
  const user = await api_1.default.validateToken(sessionPayload.token);
@@ -56,15 +82,8 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
56
82
  }
57
83
  let slug = (0, creatorUtilities_1.slugify)(assetTitle).slice(0, 47);
58
84
  slug = `${slug}-${selectedLang}`;
59
- const { exists, academyId: existingAcademyId } = await api_1.default.doesAssetExists(sessionPayload.token, slug);
60
- // Compare academy IDs if asset exists and academyId is provided
61
- if (exists &&
62
- existingAcademyId !== undefined &&
63
- academyId !== undefined &&
64
- existingAcademyId !== academyId) {
65
- console_1.default.warning(`Asset exists in academy ${existingAcademyId}, but attempting to publish to academy ${academyId}. ` +
66
- `The asset will be updated in its current academy (${existingAcademyId}).`);
67
- }
85
+ // Use pre-flight info when available to avoid an extra GET request
86
+ const { exists, academyId: existingAcademyId } = preflightInfo !== null && preflightInfo !== void 0 ? preflightInfo : (await api_1.default.doesAssetExists(sessionPayload.token, slug));
68
87
  // const technologies: unknown[] = Array.isArray(learnJson?.technologies) ?
69
88
  // learnJson.technologies :
70
89
  // []
@@ -105,14 +124,19 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
105
124
  return asset;
106
125
  }
107
126
  console_1.default.info("Asset exists, updating it");
108
- const asset = await api_1.default.updateAsset(sessionPayload.token, slug, {
127
+ const updatePayload = {
109
128
  graded: true,
110
129
  learnpack_deploy_url: learnpackDeployUrl,
111
130
  title: assetTitle,
112
131
  category: category,
113
132
  description: assetDescription,
114
133
  all_translations,
115
- });
134
+ };
135
+ // Only set academy when the asset has none yet and the user selected one
136
+ if (existingAcademyId === undefined && academyId !== undefined) {
137
+ updatePayload.academy_id = academyId;
138
+ }
139
+ const asset = await api_1.default.updateAsset(sessionPayload.token, slug, updatePayload);
116
140
  try {
117
141
  await api_1.default.updateRigoPackage(sessionPayload.rigobotToken.trim(), learnJson.slug, {
118
142
  asset_id: asset.id,
@@ -130,7 +154,7 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
130
154
  }
131
155
  };
132
156
  exports.handleAssetCreation = handleAssetCreation;
133
- const createMultiLangAssetFromDisk = async (sessionPayload, learnJson, deployUrl) => {
157
+ const createMultiLangAssetFromDisk = async (sessionPayload, learnJson, deployUrl, selectedAcademyId, existingAssetsInfo = []) => {
134
158
  const availableLangs = getAvailableLangs(learnJson);
135
159
  if (availableLangs.length === 0) {
136
160
  console_1.default.error("No languages found in learn.json.title. Add at least one language (e.g. title.en).");
@@ -150,9 +174,10 @@ const createMultiLangAssetFromDisk = async (sessionPayload, learnJson, deployUrl
150
174
  indexReadmeString = "";
151
175
  }
152
176
  const b64IndexReadme = Buffer.from(indexReadmeString).toString("base64");
177
+ const preflightInfo = existingAssetsInfo.find((a) => a.lang === lang);
153
178
  try {
154
179
  // eslint-disable-next-line no-await-in-loop
155
- const asset = await (0, exports.handleAssetCreation)(sessionPayload, learnJson, lang, deployUrl, b64IndexReadme, undefined, all_translations);
180
+ const asset = await (0, exports.handleAssetCreation)(sessionPayload, learnJson, lang, deployUrl, b64IndexReadme, selectedAcademyId, preflightInfo, all_translations);
156
181
  if (!asset) {
157
182
  console_1.default.debug("Could not create/update asset for lang", lang);
158
183
  continue;
@@ -230,7 +255,7 @@ class BuildCommand extends SessionCommand_1.default {
230
255
  await this.initSession(flags);
231
256
  }
232
257
  async run() {
233
- var _a, _b, _c;
258
+ var _a, _b, _c, _d;
234
259
  const buildDir = path.join(process.cwd(), "build");
235
260
  const { flags } = this.parse(BuildCommand);
236
261
  const strict = flags.strict;
@@ -269,17 +294,35 @@ class BuildCommand extends SessionCommand_1.default {
269
294
  console_1.default.debug("Building exercises");
270
295
  (_c = this.configManager) === null || _c === void 0 ? void 0 : _c.buildIndex();
271
296
  }
297
+ const learnJsonPath = path.join(process.cwd(), "learn.json");
298
+ if (!fs.existsSync(learnJsonPath)) {
299
+ this.error("learn.json not found");
300
+ }
301
+ const learnJson = JSON.parse(fs.readFileSync(learnJsonPath, "utf-8"));
272
302
  const academies = await api_1.default.listUserAcademies(sessionPayload.token);
273
303
  if (academies.length === 0) {
274
304
  console_1.default.error("It seems you cannot publish tutorials. Make sure you creator subscription is up to date here: https://4geeks.com/profile/subscriptions. If you believe there is an issue you can always contact support@4geeks.com");
275
305
  process.exit(1);
276
306
  }
277
- const { academy, category } = await selectAcademy(academies, sessionPayload.token);
278
- const learnJsonPath = path.join(process.cwd(), "learn.json");
279
- if (!fs.existsSync(learnJsonPath)) {
280
- this.error("learn.json not found");
307
+ console_1.default.info("Checking existing assets...");
308
+ const existingAssetsInfo = await getExistingAssetsInfo(sessionPayload.token, learnJson);
309
+ const academyMode = determinePublishAcademyMode(existingAssetsInfo);
310
+ let selectedAcademyId;
311
+ if (academyMode.type === "conflict") {
312
+ console_1.default.warning(`Some of your assets are associated with different academies ` +
313
+ `(${academyMode.academies.join(", ")}). ` +
314
+ `Academy assignment will be skipped to avoid conflicts.`);
315
+ }
316
+ else if (academyMode.type === "locked") {
317
+ const lockedAcademy = academies.find((a) => a.id === academyMode.academyId);
318
+ console_1.default.info(`This package is associated with academy: ${(_d = lockedAcademy === null || lockedAcademy === void 0 ? void 0 : lockedAcademy.name) !== null && _d !== void 0 ? _d : academyMode.academyId}. Academy cannot be changed.`);
319
+ selectedAcademyId = academyMode.academyId;
320
+ }
321
+ else {
322
+ // mode === "select": all existing assets have no academy, user picks one
323
+ const { academy } = await selectAcademy(academies, sessionPayload.token);
324
+ selectedAcademyId = academy === null || academy === void 0 ? void 0 : academy.id;
281
325
  }
282
- const learnJson = JSON.parse(fs.readFileSync(learnJsonPath, "utf-8"));
283
326
  const zipFilePath = path.join(process.cwd(), `${learnJson.slug}.zip`);
284
327
  // Ensure build directory exists
285
328
  if (!fs.existsSync(buildDir)) {
@@ -405,7 +448,7 @@ class BuildCommand extends SessionCommand_1.default {
405
448
  console.log(res.data);
406
449
  fs.unlinkSync(zipFilePath);
407
450
  this.removeDirectory(buildDir);
408
- await createMultiLangAssetFromDisk({ token: sessionPayload.token, rigobotToken: rigoToken }, learnJson, res.data.url);
451
+ await createMultiLangAssetFromDisk({ token: sessionPayload.token, rigobotToken: rigoToken }, learnJson, res.data.url, selectedAcademyId, existingAssetsInfo);
409
452
  }
410
453
  catch (error) {
411
454
  if (axios_1.default.isAxiosError(error)) {