Skip to content

Commit 12d53d4

Browse files
authored
Merge pull request #35 from halprin/usps-no-remote-tracking
Add Support for Carriers that Don't Allow Tracking Over the Internet
2 parents 91adefb + ddfad40 commit 12d53d4

9 files changed

+99
-202
lines changed

package.json

-14
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,6 @@
3434
}
3535
],
3636
"preferences": [
37-
{
38-
"name": "uspsConsumerKey",
39-
"title": "USPS Consumer Key",
40-
"description": "The consumer key from your app in the USPS developer portal",
41-
"type": "password",
42-
"required": false
43-
},
44-
{
45-
"name": "uspsConsumerSecret",
46-
"title": "USPS Consumer Secret",
47-
"description": "The consumer secret from your app in the USPS developer portal",
48-
"type": "password",
49-
"required": false
50-
},
5137
{
5238
"name": "upsClientId",
5339
"title": "UPS Client ID",

src/carriers.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { Color } from "@raycast/api";
22
import { Package } from "./package";
3-
import updateUspsTracking from "./carriers/usps";
4-
import updateUpsTracking from "./carriers/ups";
5-
import updateFedexTracking from "./carriers/fedex";
3+
import { updateUspsTracking, ableToTrackUspsRemotely } from "./carriers/usps";
4+
import { updateUpsTracking, ableToTrackUpsRemotely } from "./carriers/ups";
5+
import { updateFedexTracking, ableToTrackFedexRemotely } from "./carriers/fedex";
6+
import { Delivery } from "./delivery";
67

78
interface Carrier {
89
id: string;
910
name: string;
1011
color: Color;
11-
updateTracking: (trackingNumber: string) => Promise<Package[]>;
12+
updateTracking: (delivery: Delivery) => Promise<Package[]>;
13+
ableToTrackRemotely: () => Promise<boolean>;
1214
}
1315

1416
const carriers: Carrier[] = [
@@ -17,18 +19,21 @@ const carriers: Carrier[] = [
1719
name: "USPS",
1820
color: Color.Blue,
1921
updateTracking: updateUspsTracking,
22+
ableToTrackRemotely: ableToTrackUspsRemotely,
2023
},
2124
{
2225
id: "ups",
2326
name: "UPS",
2427
color: Color.Orange,
2528
updateTracking: updateUpsTracking,
29+
ableToTrackRemotely: ableToTrackUpsRemotely,
2630
},
2731
{
2832
id: "fedex",
2933
name: "FedEx",
3034
color: Color.Purple,
3135
updateTracking: updateFedexTracking,
36+
ableToTrackRemotely: ableToTrackFedexRemotely,
3237
},
3338
];
3439

src/carriers/fedex.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
11
import { Package } from "../package";
22
import { Cache, getPreferenceValues } from "@raycast/api";
33
import fetch from "node-fetch";
4+
import { Delivery } from "../delivery";
45

56
const cache = new Cache();
67
const cacheKey = "fedexLogin";
78
const host = "apis.fedex.com";
89

9-
async function updateFedexTracking(trackingNumber: string): Promise<Package[]> {
10+
export async function ableToTrackFedexRemotely(): Promise<boolean> {
11+
const preferences = getPreferenceValues<Preferences.TrackDeliveries>();
12+
const apiKey = preferences.fedexApiKey;
13+
const secretKey = preferences.fedexSecretKey;
14+
15+
return Boolean(apiKey && secretKey);
16+
}
17+
18+
export async function updateFedexTracking(delivery: Delivery): Promise<Package[]> {
19+
const trackingNumber = delivery.trackingNumber;
20+
1021
console.log(`Updating tracking for ${trackingNumber}`);
1122

1223
const preferences = getPreferenceValues<Preferences.TrackDeliveries>();
@@ -180,5 +191,3 @@ function convertFedexDateToDate(fedexDate: string | undefined): Date | undefined
180191
// fedexDate is an ISO date formatted string
181192
return new Date(fedexDate);
182193
}
183-
184-
export default updateFedexTracking;

src/carriers/ups.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,23 @@ import { Package } from "../package";
22
import { getPreferenceValues, Cache } from "@raycast/api";
33
import fetch from "node-fetch";
44
import { randomUUID } from "node:crypto";
5+
import { Delivery } from "../delivery";
56

67
const cache = new Cache();
78
const cacheKey = "upsLogin";
89
const host = "onlinetools.ups.com";
910

10-
async function updateUpsTracking(trackingNumber: string): Promise<Package[]> {
11+
export async function ableToTrackUpsRemotely(): Promise<boolean> {
12+
const preferences = getPreferenceValues<Preferences.TrackDeliveries>();
13+
const clientId = preferences.upsClientId;
14+
const clientSecret = preferences.upsClientSecret;
15+
16+
return Boolean(clientId && clientSecret);
17+
}
18+
19+
export async function updateUpsTracking(delivery: Delivery): Promise<Package[]> {
20+
const trackingNumber = delivery.trackingNumber;
21+
1122
console.log(`Updating tracking for ${trackingNumber}`);
1223

1324
const preferences = getPreferenceValues<Preferences.TrackDeliveries>();
@@ -172,5 +183,3 @@ function convertUpsDateToDate(upsDate: string | undefined): Date | undefined {
172183

173184
return new Date(year, month, day);
174185
}
175-
176-
export default updateUpsTracking;

src/carriers/usps.ts

+14-174
Original file line numberDiff line numberDiff line change
@@ -1,183 +1,23 @@
11
import { Package } from "../package";
2-
import { Cache, getPreferenceValues } from "@raycast/api";
3-
import fetch from "node-fetch";
2+
import { Delivery } from "../delivery";
43

5-
const cache = new Cache();
6-
const cacheKey = "uspsLogin";
7-
const host = "api.usps.com";
8-
9-
async function updateUspsTracking(trackingNumber: string): Promise<Package[]> {
10-
console.log(`Updating tracking for ${trackingNumber}`);
11-
12-
const preferences = getPreferenceValues<Preferences.TrackDeliveries>();
13-
const consumerKey = preferences.uspsConsumerKey;
14-
const consumerSecret = preferences.uspsConsumerSecret;
15-
16-
if (!consumerKey || !consumerSecret) {
17-
console.log(`Unable to update tracking for ${trackingNumber} because consumerKey or consumerSecret is missing`);
18-
throw new Error(
19-
"USPS consumer key or consumer secret is missing. Ensure they are filled in this extension's settings.",
20-
);
21-
}
22-
23-
const loginResponse = await loginWithCachedData(consumerKey, consumerSecret);
24-
25-
console.log("Calling USPS tracking");
26-
const upsTrackingInfo = await track(trackingNumber, loginResponse.access_token);
27-
28-
const packages = convertUspsTrackingToPackages(upsTrackingInfo);
29-
30-
console.log(`Updated tracking for ${trackingNumber}`);
31-
32-
return packages;
33-
}
34-
35-
interface LoginResponseBody {
36-
access_token: string;
37-
token_type: string;
38-
issued_at: number;
39-
expires_in: number;
40-
status: string;
41-
scope: string;
42-
client_id: string;
4+
export async function ableToTrackUspsRemotely(): Promise<boolean> {
5+
// doesn't support remote tracking yet.
6+
return false;
437
}
448

45-
async function loginWithCachedData(consumerKey: string, consumerSecret: string): Promise<LoginResponseBody> {
46-
let loginResponse: LoginResponseBody;
47-
48-
if (!cache.has(cacheKey)) {
49-
console.log("Logging into USPS");
50-
loginResponse = await login(consumerKey, consumerSecret);
51-
52-
cache.set(cacheKey, JSON.stringify(loginResponse));
53-
} else {
54-
loginResponse = JSON.parse(cache.get(cacheKey) ?? "{}");
55-
56-
if (loginResponse.issued_at + loginResponse.expires_in * 1000 < new Date().getTime() + 30 * 1000) {
57-
// we are less than 30 seconds form the access token expiring
58-
console.log("Access key expired; logging into USPS");
59-
loginResponse = await login(consumerKey, consumerSecret);
60-
61-
cache.set(cacheKey, JSON.stringify(loginResponse));
62-
}
63-
}
9+
export async function updateUspsTracking(delivery: Delivery): Promise<Package[]> {
10+
const trackingNumber = delivery.trackingNumber;
6411

65-
return loginResponse;
66-
}
67-
68-
async function login(consumerKey: string, consumerSecret: string): Promise<LoginResponseBody> {
69-
const response = await fetch(`https://${host}/oauth2/v3/token`, {
70-
method: "POST",
71-
headers: {
72-
"Content-Type": "application/x-www-form-urlencoded",
73-
},
74-
body: new URLSearchParams({
75-
grant_type: "client_credentials",
76-
client_id: consumerKey,
77-
client_secret: consumerSecret,
78-
}),
79-
});
80-
81-
if (!response.ok) {
82-
console.log("Failed to login to USPS", response.status, response.statusText, await response.text());
83-
throw new Error(
84-
`Failed to login to USPS with status ${response.statusText}. Ensure consumer key and consumer secret are correct.`,
85-
);
86-
}
87-
88-
const loginResponse = (await response.json()) as LoginResponseBody;
89-
if (!loginResponse) {
90-
console.log("Failed to parse USPS login response");
91-
throw new Error("Failed to parse USPS login response. Please file a bug report.");
92-
}
93-
94-
return loginResponse;
95-
}
96-
97-
interface UspsTrackingInfo {
98-
trackResponse: {
99-
shipment: [
100-
{
101-
inquiryNumber: string;
102-
package: [
103-
{
104-
trackingNumber: string;
105-
deliveryDate: [
106-
{
107-
type: string;
108-
date: string;
109-
},
110-
];
111-
activity: [object];
112-
currentStatus: {
113-
description: string;
114-
code: string;
115-
};
116-
},
117-
];
118-
},
119-
];
120-
};
121-
}
12+
console.log(`Updating tracking for ${trackingNumber}`);
12213

123-
async function track(trackingNumber: string, accessToken: string): Promise<UspsTrackingInfo> {
124-
// const response = await fetch(
125-
// `https://${host}/tracking/v3/tracking/${trackingNumber}?expand=SUMMARY`,
126-
// {
127-
// method: "GET",
128-
// headers: {
129-
// Authorization: "Bearer " + accessToken,
130-
// },
131-
// },
132-
// );
133-
//
134-
// if (!response.ok) {
135-
// console.log("Failed to get USPS tracking", response.status, response.statusText, await response.text());
136-
// throw new Error(`Failed to get USPS tracking with status ${response.statusText}.`);
137-
// }
138-
//
139-
// const trackingResponse = (await response.json()) as UspsTrackingInfo;
140-
// if (!trackingResponse) {
141-
// console.log("Failed to parse USPS login response");
142-
// throw new Error("Failed to parse USPS track response. Please file a bug report.");
143-
// }
14+
console.log(`Updated tracking for ${trackingNumber}`);
14415

145-
const trackingResponse: UspsTrackingInfo = {
146-
trackResponse: {
147-
shipment: [
148-
{
149-
inquiryNumber: "",
150-
package: [
151-
{
152-
activity: [{}],
153-
currentStatus: { code: "", description: "" },
154-
deliveryDate: [{ date: "", type: "" }],
155-
trackingNumber: "",
156-
},
157-
],
158-
},
159-
],
16+
return [
17+
{
18+
delivered: new Date() === delivery.manualDeliveryDate,
19+
deliveryDate: delivery.manualDeliveryDate,
20+
activity: [],
16021
},
161-
};
162-
163-
return trackingResponse;
22+
];
16423
}
165-
166-
function convertUspsTrackingToPackages(upsTrackingInfo: UspsTrackingInfo): Package[] {
167-
return [];
168-
// return upsTrackingInfo.trackResponse.shipment
169-
// .flatMap((shipment) => shipment.package)
170-
// .map((aPackage) => {
171-
// const deliveryDate = aPackage.deliveryDate.find((deliveryDate) => deliveryDate.type === "DEL")?.date;
172-
// const rescheduledDeliveryDate = aPackage.deliveryDate.find((deliveryDate) => deliveryDate.type === "RDD")?.date;
173-
// const scheduledDeliveryDate = aPackage.deliveryDate.find((deliveryDate) => deliveryDate.type === "SDD")?.date;
174-
//
175-
// return {
176-
// delivered: aPackage.currentStatus.code === "011",
177-
// deliveryDate: convertUpsDateToDate(deliveryDate || rescheduledDeliveryDate || scheduledDeliveryDate),
178-
// activity: [],
179-
// };
180-
// });
181-
}
182-
183-
export default updateUspsTracking;

src/delivery.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ export interface Delivery {
33
name: string;
44
trackingNumber: string;
55
carrier: string;
6+
manualDeliveryDate?: Date;
67
debug?: boolean;
78
}

src/track-deliveries.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ async function refreshTracking(
169169
}
170170

171171
try {
172-
const refreshedPackages = await carrier.updateTracking(delivery.trackingNumber);
172+
const refreshedPackages = await carrier.updateTracking(delivery);
173173

174174
setPackages((packagesMap) => {
175175
packagesMap[delivery.id] = {

0 commit comments

Comments
 (0)