diff --git a/.cursor/rules/tee-types_update_plan.mdc b/.cursor/rules/tee-types_update_plan.mdc deleted file mode 100644 index 741eea6..0000000 --- a/.cursor/rules/tee-types_update_plan.mdc +++ /dev/null @@ -1,48 +0,0 @@ ---- -description: -globs: -alwaysApply: false ---- -# tee-types: LinkedIn Data Structures Extension - -## Overview -This plan details the required changes for the `github.com/masa-finance/tee-types` repository. These changes are a prerequisite for integrating the new LinkedIn profile fetching functionality into the `tee-worker`. The goal is to extend the existing data structures to support both profile search and full profile fetching jobs. - -## ⚠️ CRITICAL REQUIREMENTS -- **BACKWARD COMPATIBILITY**: The changes must not break existing `tee-worker` functionality that relies on `searchbyquery`. -- **CONSISTENCY**: The new data structures should align with the output of the `linkedin-scraper` SDK (`v1.0.0`). -- **CLARITY**: Use clear and descriptive naming for new structs and fields. - -## Implementation Steps - -### Phase 1: Argument Structure Update - -#### Step 1.1: Extend and Rename Job Arguments -**Objective**: Create a unified argument struct that supports both search and profile fetching. -**Files**: `args/linkedin.go` -**Action**: -- Rename the existing `LinkedInSearchArguments` struct to `LinkedInArguments`. This provides a more generic name for future extensions. -- Add a new field `PublicIdentifier string `json:"public_identifier,omitempty"` to the renamed `LinkedInArguments` struct. This will be used to specify the target profile for fetching. -**Verification**: The new `LinkedInArguments` struct contains fields for both search (`Query`, `MaxResults`, etc.) and profile fetching (`PublicIdentifier`). -**Commit**: `feat(args): extend and rename linkedin arguments for profile fetching` - -### Phase 2: Result Structure Extension - -#### Step 2.1: Define Comprehensive Profile Result -**Objective**: Create a new struct to hold the rich data from a full profile fetch. -**Files**: `types/linkedin.go` -**Action**: -- Create a new struct `LinkedInFullProfileResult`. -- This struct should include fields for all the data provided by the scraper's `GetProfile` method, such as: - - `PublicIdentifier`, `URN`, `FullName`, `Headline`, `Location`, `Summary` - - Slices for `[]Experience`, `[]Education`, `[]Skill` - - `ProfilePictureURL` -- Define helper structs for `Experience`, `Education`, and `Skill` with relevant fields (e.g., `Title`, `CompanyName` for experience; `SchoolName`, `DegreeName` for education). -**Verification**: The `LinkedInFullProfileResult` and its nested structs are defined and compile correctly. The structure matches the expected output from the `linkedin-scraper`. -**Commit**: `feat(types): add LinkedInFullProfileResult for detailed profiles` - -## Success Criteria -- ✅ `args/linkedin.go` contains the updated `LinkedInArguments` struct. -- ✅ `types/linkedin.go` contains the new `LinkedInFullProfileResult` and its associated substructures. -- ✅ The changes are non-breaking for code that uses the old `LinkedInSearchArguments` (after a name update). -- ✅ The new structures are ready to be consumed by the `tee-worker`. diff --git a/.gitignore b/.gitignore index f5f7bd6..cffb9a7 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ go.work # LLM-related files .aider* GEMINI.md + +/examples/*json \ No newline at end of file diff --git a/args/args.go b/args/args.go index ab7abe4..f090037 100644 --- a/args/args.go +++ b/args/args.go @@ -3,13 +3,17 @@ package args import ( "encoding/json" "fmt" - "strings" + + "github.com/masa-finance/tee-types/args/linkedin" + teetypes "github.com/masa-finance/tee-types/types" ) +type LinkedInProfileArguments = linkedin.ProfileArguments + // QueryTypeArgument provides a minimal structure to extract the QueryType (json "type") // This is used across different job types to determine the specific capability being requested type QueryTypeArgument struct { - QueryType string `json:"type"` + QueryType teetypes.Capability `json:"type"` } // UnmarshalJSON implements custom JSON unmarshaling with normalization @@ -20,6 +24,6 @@ func (q *QueryTypeArgument) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return fmt.Errorf("failed to unmarshal QueryType arguments: %w", err) } - q.QueryType = strings.ToLower(aux.QueryType) + q.QueryType = aux.QueryType return nil } diff --git a/args/linkedin.go b/args/linkedin.go deleted file mode 100644 index dc3ba93..0000000 --- a/args/linkedin.go +++ /dev/null @@ -1,97 +0,0 @@ -package args - -import ( - "encoding/json" - "fmt" - "strings" - - "github.com/masa-finance/tee-types/pkg/util" - teetypes "github.com/masa-finance/tee-types/types" -) - -// LinkedInArguments defines args for LinkedIn operations -type LinkedInArguments struct { - QueryType string `json:"type"` // "searchbyquery", "getprofile" - Query string `json:"query"` // Keywords for search or username for profile - PublicIdentifier string `json:"public_identifier,omitempty"` - NetworkFilters []string `json:"network_filters,omitempty"` // ["F", "S", "O"] - First, Second, Other (default: all) - MaxResults int `json:"max_results"` // Maximum number of results to return - Start int `json:"start"` // Pagination start offset -} - -// UnmarshalJSON implements custom JSON unmarshaling with validation -func (l *LinkedInArguments) UnmarshalJSON(data []byte) error { - // Prevent infinite recursion (you call json.Unmarshal which then calls `UnmarshalJSON`, which then calls `json.Unmarshal`...) - type Alias LinkedInArguments - aux := &struct { - *Alias - }{ - Alias: (*Alias)(l), - } - - if err := json.Unmarshal(data, aux); err != nil { - return fmt.Errorf("failed to unmarshal LinkedIn arguments: %w", err) - } - - // Normalize QueryType to lowercase - l.QueryType = strings.ToLower(l.QueryType) - - return l.Validate() -} - -// Validate validates the LinkedIn arguments (general validation) -func (l *LinkedInArguments) Validate() error { - // Note: QueryType is not required for all capabilities, similar to Twitter pattern - // Query is also not required for all capabilities - - if l.MaxResults < 0 { - return fmt.Errorf("max_results must be non-negative, got: %d", l.MaxResults) - } - - if l.Start < 0 { - return fmt.Errorf("start must be non-negative, got: %d", l.Start) - } - - return nil -} - -// ValidateForJobType validates LinkedIn arguments for a specific job type -func (l *LinkedInArguments) ValidateForJobType(jobType teetypes.JobType) error { - if err := l.Validate(); err != nil { - return err - } - - // Validate QueryType against job-specific capabilities - return jobType.ValidateCapability(teetypes.Capability(l.QueryType)) -} - -// GetCapability returns the QueryType as a typed Capability -func (l *LinkedInArguments) GetCapability() teetypes.Capability { - return teetypes.Capability(l.QueryType) -} - -// IsSearchOperation returns true if this is a search operation -func (l *LinkedInArguments) IsSearchOperation() bool { - capability := l.GetCapability() - return capability == teetypes.CapSearchByQuery -} - -// IsProfileOperation returns true if this is a profile operation -func (l *LinkedInArguments) IsProfileOperation() bool { - capability := l.GetCapability() - return capability == teetypes.CapGetProfile -} - -// HasNetworkFilters returns true if network filters are specified -func (l *LinkedInArguments) HasNetworkFilters() bool { - return len(l.NetworkFilters) > 0 -} - -// GetEffectiveMaxResults returns the effective maximum results, defaulting to a reasonable limit -func (l *LinkedInArguments) GetEffectiveMaxResults() int { - return util.Max(l.MaxResults, 10) -} - -// LinkedInSearchArguments is an alias for LinkedInArguments for backward compatibility. -// Deprecated: use LinkedInArguments instead. -type LinkedInSearchArguments = LinkedInArguments diff --git a/args/linkedin/linkedin.go b/args/linkedin/linkedin.go new file mode 100644 index 0000000..92ed3e7 --- /dev/null +++ b/args/linkedin/linkedin.go @@ -0,0 +1,7 @@ +package linkedin + +import ( + "github.com/masa-finance/tee-types/args/linkedin/profile" +) + +type ProfileArguments = profile.Arguments diff --git a/args/linkedin/profile/profile.go b/args/linkedin/profile/profile.go new file mode 100644 index 0000000..399caa0 --- /dev/null +++ b/args/linkedin/profile/profile.go @@ -0,0 +1,132 @@ +package profile + +import ( + "encoding/json" + "errors" + "fmt" + + teetypes "github.com/masa-finance/tee-types/types" + "github.com/masa-finance/tee-types/types/linkedin/experiences" + "github.com/masa-finance/tee-types/types/linkedin/functions" + "github.com/masa-finance/tee-types/types/linkedin/industries" + "github.com/masa-finance/tee-types/types/linkedin/profile" + "github.com/masa-finance/tee-types/types/linkedin/seniorities" +) + +var ( + ErrScraperModeNotSupported = errors.New("scraper mode not supported") + ErrMaxItemsTooLarge = errors.New("max items must be less than or equal to 100") + ErrExperienceNotSupported = errors.New("years of experience not supported") + ErrSeniorityNotSupported = errors.New("seniority level not supported") + ErrFunctionNotSupported = errors.New("function not supported") + ErrIndustryNotSupported = errors.New("industry not supported") +) + +const ( + DefaultMaxItems = 10 + DefaultScraperMode = profile.ScraperModeShort + MaxItems = 1000 // 2500 on the actor, but we will run over 1MB memory limit on responses +) + +// Arguments defines args for LinkedIn profile operations +type Arguments struct { + QueryType teetypes.Capability `json:"type"` + ScraperMode profile.ScraperMode `json:"profileScraperMode"` + Query string `json:"searchQuery"` + MaxItems uint `json:"maxItems"` + Locations []string `json:"locations,omitempty"` + CurrentCompanies []string `json:"currentCompanies,omitempty"` + PastCompanies []string `json:"pastCompanies,omitempty"` + CurrentJobTitles []string `json:"currentJobTitles,omitempty"` + PastJobTitles []string `json:"pastJobTitles,omitempty"` + Schools []string `json:"schools,omitempty"` + YearsOfExperience []experiences.Id `json:"yearsOfExperienceIds,omitempty"` + YearsAtCurrentCompany []experiences.Id `json:"yearsAtCurrentCompanyIds,omitempty"` + SeniorityLevels []seniorities.Id `json:"seniorityLevelIds,omitempty"` + Functions []functions.Id `json:"functionIds,omitempty"` + Industries []industries.Id `json:"industryIds,omitempty"` + FirstNames []string `json:"firstNames,omitempty"` + LastNames []string `json:"lastNames,omitempty"` + RecentlyChangedJobs bool `json:"recentlyChangedJobs,omitempty"` + StartPage uint `json:"startPage,omitempty"` +} + +func (a *Arguments) UnmarshalJSON(data []byte) error { + type Alias Arguments + aux := &struct { + *Alias + }{ + Alias: (*Alias)(a), + } + + if err := json.Unmarshal(data, aux); err != nil { + return fmt.Errorf("failed to unmarshal LinkedIn profile arguments: %w", err) + } + + a.setDefaultValues() + + return a.Validate() +} + +func (a *Arguments) setDefaultValues() { + if a.MaxItems == 0 { + a.MaxItems = DefaultMaxItems + } + if a.ScraperMode == "" { + a.ScraperMode = DefaultScraperMode + } +} + +func (a *Arguments) Validate() error { + var errs []error + + if a.MaxItems > MaxItems { + errs = append(errs, ErrMaxItemsTooLarge) + } + if !profile.AllScraperModes.Contains(a.ScraperMode) { + errs = append(errs, ErrScraperModeNotSupported) + } + for _, yoe := range a.YearsOfExperience { + if !experiences.All.Contains(yoe) { + errs = append(errs, fmt.Errorf("%w: %v", ErrExperienceNotSupported, yoe)) + } + } + for _, yac := range a.YearsAtCurrentCompany { + if !experiences.All.Contains(yac) { + errs = append(errs, fmt.Errorf("%w: %v", ErrExperienceNotSupported, yac)) + } + } + for _, sl := range a.SeniorityLevels { + if !seniorities.All.Contains(sl) { + errs = append(errs, fmt.Errorf("%w: %v", ErrSeniorityNotSupported, sl)) + } + } + for _, f := range a.Functions { + if !functions.All.Contains(f) { + errs = append(errs, fmt.Errorf("%w: %v", ErrFunctionNotSupported, f)) + } + } + for _, i := range a.Industries { + if !industries.All.Contains(i) { + errs = append(errs, fmt.Errorf("%w: %v", ErrIndustryNotSupported, i)) + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + + return nil +} + +func (a *Arguments) GetCapability() teetypes.Capability { + return a.QueryType +} + +func (a *Arguments) ValidateForJobType(jobType teetypes.JobType) error { + if err := a.Validate(); err != nil { + return err + } + + return jobType.ValidateCapability(a.QueryType) +} diff --git a/args/linkedin/profile/profile_suite_test.go b/args/linkedin/profile/profile_suite_test.go new file mode 100644 index 0000000..713e96d --- /dev/null +++ b/args/linkedin/profile/profile_suite_test.go @@ -0,0 +1,13 @@ +package profile_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestArgs(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Args Suite") +} diff --git a/args/linkedin/profile/profile_test.go b/args/linkedin/profile/profile_test.go new file mode 100644 index 0000000..6c9e5de --- /dev/null +++ b/args/linkedin/profile/profile_test.go @@ -0,0 +1,222 @@ +package profile_test + +import ( + "encoding/json" + "errors" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/masa-finance/tee-types/args" + "github.com/masa-finance/tee-types/args/linkedin/profile" + "github.com/masa-finance/tee-types/types" + "github.com/masa-finance/tee-types/types/linkedin/experiences" + "github.com/masa-finance/tee-types/types/linkedin/functions" + "github.com/masa-finance/tee-types/types/linkedin/industries" + profiletypes "github.com/masa-finance/tee-types/types/linkedin/profile" + "github.com/masa-finance/tee-types/types/linkedin/seniorities" +) + +var _ = Describe("LinkedIn Profile Arguments", func() { + Describe("Marshalling and unmarshalling", func() { + It("should set default values", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByProfile, + Query: "software engineer", + } + jsonData, err := json.Marshal(args) + Expect(err).ToNot(HaveOccurred()) + err = json.Unmarshal([]byte(jsonData), &args) + Expect(err).ToNot(HaveOccurred()) + Expect(args.MaxItems).To(Equal(uint(10))) + Expect(args.ScraperMode).To(Equal(profiletypes.ScraperModeShort)) + }) + + It("should override default values", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByProfile, + Query: "software engineer", + MaxItems: 50, + ScraperMode: profiletypes.ScraperModeFull, + } + jsonData, err := json.Marshal(args) + Expect(err).ToNot(HaveOccurred()) + err = json.Unmarshal([]byte(jsonData), &args) + Expect(err).ToNot(HaveOccurred()) + Expect(args.MaxItems).To(Equal(uint(50))) + Expect(args.ScraperMode).To(Equal(profiletypes.ScraperModeFull)) + }) + }) + + Describe("Validation", func() { + It("should succeed with valid arguments", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByProfile, + Query: "software engineer", + ScraperMode: profiletypes.ScraperModeShort, + MaxItems: 10, + YearsOfExperience: []experiences.Id{experiences.ThreeToFiveYears}, + SeniorityLevels: []seniorities.Id{seniorities.Senior}, + Functions: []functions.Id{functions.Engineering}, + Industries: []industries.Id{industries.SoftwareDevelopment}, + } + err := args.Validate() + Expect(err).ToNot(HaveOccurred()) + }) + + It("should fail with max items too large", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByProfile, + Query: "software engineer", + ScraperMode: profiletypes.ScraperModeShort, + MaxItems: 1500, + } + err := args.Validate() + Expect(err).To(HaveOccurred()) + Expect(errors.Is(err, profile.ErrMaxItemsTooLarge)).To(BeTrue()) + }) + + It("should fail with invalid scraper mode", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByProfile, + Query: "software engineer", + ScraperMode: "InvalidMode", + MaxItems: 10, + } + err := args.Validate() + Expect(err).To(HaveOccurred()) + Expect(errors.Is(err, profile.ErrScraperModeNotSupported)).To(BeTrue()) + }) + + It("should fail with invalid years of experience", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByProfile, + Query: "software engineer", + ScraperMode: profiletypes.ScraperModeShort, + MaxItems: 10, + YearsOfExperience: []experiences.Id{"invalid"}, + } + err := args.Validate() + Expect(err).To(HaveOccurred()) + Expect(errors.Is(err, profile.ErrExperienceNotSupported)).To(BeTrue()) + + }) + + It("should fail with invalid years at current company", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByProfile, + Query: "software engineer", + ScraperMode: profiletypes.ScraperModeShort, + MaxItems: 10, + YearsAtCurrentCompany: []experiences.Id{"invalid"}, + } + err := args.Validate() + Expect(err).To(HaveOccurred()) + Expect(errors.Is(err, profile.ErrExperienceNotSupported)).To(BeTrue()) + + }) + + It("should fail with invalid seniority level", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByProfile, + Query: "software engineer", + ScraperMode: profiletypes.ScraperModeShort, + MaxItems: 10, + SeniorityLevels: []seniorities.Id{"invalid"}, + } + err := args.Validate() + Expect(err).To(HaveOccurred()) + Expect(errors.Is(err, profile.ErrSeniorityNotSupported)).To(BeTrue()) + }) + + It("should fail with invalid function", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByProfile, + Query: "software engineer", + ScraperMode: profiletypes.ScraperModeShort, + MaxItems: 10, + Functions: []functions.Id{"invalid"}, + } + err := args.Validate() + Expect(err).To(HaveOccurred()) + Expect(errors.Is(err, profile.ErrFunctionNotSupported)).To(BeTrue()) + + }) + + It("should fail with invalid industry", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByProfile, + Query: "software engineer", + ScraperMode: profiletypes.ScraperModeShort, + MaxItems: 10, + Industries: []industries.Id{"invalid"}, + } + err := args.Validate() + Expect(err).To(HaveOccurred()) + Expect(errors.Is(err, profile.ErrIndustryNotSupported)).To(BeTrue()) + + }) + + It("should handle multiple validation errors", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByProfile, + Query: "software engineer", + ScraperMode: "InvalidMode", + MaxItems: 1500, + YearsOfExperience: []experiences.Id{"invalid"}, + SeniorityLevels: []seniorities.Id{"invalid"}, + } + err := args.Validate() + Expect(err).To(HaveOccurred()) + // Should contain multiple error messages + Expect(errors.Is(err, profile.ErrMaxItemsTooLarge)).To(BeTrue()) + Expect(errors.Is(err, profile.ErrScraperModeNotSupported)).To(BeTrue()) + Expect(errors.Is(err, profile.ErrExperienceNotSupported)).To(BeTrue()) + Expect(errors.Is(err, profile.ErrSeniorityNotSupported)).To(BeTrue()) + }) + }) + + Describe("GetCapability", func() { + It("should return the query type", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByProfile, + } + Expect(args.GetCapability()).To(Equal(types.CapSearchByProfile)) + }) + }) + + Describe("ValidateForJobType", func() { + It("should succeed with valid job type and capability", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByProfile, + Query: "software engineer", + ScraperMode: profiletypes.ScraperModeShort, + MaxItems: 10, + } + err := args.ValidateForJobType(types.LinkedInJob) + Expect(err).ToNot(HaveOccurred()) + }) + + It("should fail with invalid job type", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByQuery, + Query: "software engineer", + ScraperMode: profiletypes.ScraperModeShort, + MaxItems: 10, + } + err := args.ValidateForJobType(types.LinkedInJob) + Expect(err).To(HaveOccurred()) + }) + + It("should fail if base validation fails", func() { + args := args.LinkedInProfileArguments{ + QueryType: types.CapSearchByProfile, + Query: "software engineer", + ScraperMode: "InvalidMode", + MaxItems: 10, + } + err := args.ValidateForJobType(types.LinkedInJob) + Expect(err).To(HaveOccurred()) + }) + }) +}) diff --git a/args/twitter.go b/args/twitter.go index abee6d4..6c08f65 100644 --- a/args/twitter.go +++ b/args/twitter.go @@ -4,7 +4,6 @@ import ( "encoding/json" "errors" "fmt" - "strings" teetypes "github.com/masa-finance/tee-types/types" ) @@ -22,13 +21,13 @@ const ( // TwitterSearchArguments defines args for Twitter searches type TwitterSearchArguments struct { - QueryType string `json:"type"` // Optional, type of search - Query string `json:"query"` // Username or search query - Count int `json:"count"` - StartTime string `json:"start_time"` // Optional ISO timestamp - EndTime string `json:"end_time"` // Optional ISO timestamp - MaxResults int `json:"max_results"` // Optional, max number of results - NextCursor string `json:"next_cursor"` + QueryType teetypes.Capability `json:"type"` // Optional, type of search + Query string `json:"query"` // Username or search query + Count int `json:"count"` + StartTime string `json:"start_time"` // Optional ISO timestamp + EndTime string `json:"end_time"` // Optional ISO timestamp + MaxResults int `json:"max_results"` // Optional, max number of results + NextCursor string `json:"next_cursor"` } // UnmarshalJSON implements custom JSON unmarshaling with validation @@ -45,9 +44,6 @@ func (t *TwitterSearchArguments) UnmarshalJSON(data []byte) error { return fmt.Errorf("failed to unmarshal Twitter arguments: %w", err) } - // Normalize QueryType to lowercase - t.QueryType = strings.ToLower(t.QueryType) - return t.Validate() } diff --git a/args/unmarshaller.go b/args/unmarshaller.go index 1d3c26d..9057885 100644 --- a/args/unmarshaller.go +++ b/args/unmarshaller.go @@ -3,7 +3,6 @@ package args import ( "encoding/json" "fmt" - "strings" "github.com/masa-finance/tee-types/types" ) @@ -55,16 +54,15 @@ func unmarshalTikTokArguments(args map[string]any) (JobArguments, error) { if err := unmarshalToStruct(args, minimal); err != nil { return nil, fmt.Errorf("failed to unmarshal TikTok arguments: %w", err) } - capability := types.Capability(strings.ToLower(minimal.QueryType)) - if capability == types.CapEmpty { + if minimal.QueryType == types.CapEmpty { defaultCap, exists := types.JobDefaultCapabilityMap[types.TiktokJob] if !exists { return nil, fmt.Errorf("no default capability configured for job type: %s", types.TiktokJob) } - capability = defaultCap + minimal.QueryType = defaultCap } - switch capability { + switch minimal.QueryType { case types.CapSearchByQuery: searchArgs := &TikTokSearchByQueryArguments{} if err := unmarshalToStruct(args, searchArgs); err != nil { @@ -93,7 +91,7 @@ func unmarshalTikTokArguments(args map[string]any) (JobArguments, error) { } return transcriptionArgs, nil default: - return nil, fmt.Errorf("unknown tiktok type: %s", capability) + return nil, fmt.Errorf("unknown tiktok type: %s", minimal.QueryType) } } @@ -106,7 +104,7 @@ func unmarshalTwitterArguments(jobType types.JobType, args map[string]any) (*Twi // If no QueryType is specified, use the default capability for this job type if twitterArgs.QueryType == "" { if defaultCap, exists := types.JobDefaultCapabilityMap[jobType]; exists { - twitterArgs.QueryType = string(defaultCap) + twitterArgs.QueryType = defaultCap } } @@ -118,25 +116,31 @@ func unmarshalTwitterArguments(jobType types.JobType, args map[string]any) (*Twi return twitterArgs, nil } -func unmarshalLinkedInArguments(jobType types.JobType, args map[string]any) (*LinkedInArguments, error) { - linkedInArgs := &LinkedInArguments{} - if err := unmarshalToStruct(args, linkedInArgs); err != nil { - return nil, fmt.Errorf("failed to unmarshal LinkedIn job arguments: %w", err) +func unmarshalLinkedInArguments(jobType types.JobType, args map[string]any) (JobArguments, error) { + minimal := &QueryTypeArgument{} + if err := unmarshalToStruct(args, minimal); err != nil { + return nil, fmt.Errorf("failed to unmarshal LinkedIn arguments: %w", err) } - // If no QueryType is specified, use the default capability for this job type - if linkedInArgs.QueryType == "" { + if minimal.QueryType == types.CapEmpty { if defaultCap, exists := types.JobDefaultCapabilityMap[jobType]; exists { - linkedInArgs.QueryType = string(defaultCap) + minimal.QueryType = defaultCap } } - // Perform job-type-specific validation for LinkedIn - if err := linkedInArgs.ValidateForJobType(jobType); err != nil { - return nil, fmt.Errorf("linkedin job validation failed: %w", err) + switch minimal.QueryType { + case types.CapSearchByProfile: + linkedInArgs := &LinkedInProfileArguments{} + if err := unmarshalToStruct(args, linkedInArgs); err != nil { + return nil, fmt.Errorf("failed to unmarshal LinkedIn job arguments: %w", err) + } + if err := linkedInArgs.ValidateForJobType(jobType); err != nil { + return nil, fmt.Errorf("linkedin job validation failed: %w", err) + } + return linkedInArgs, nil + default: + return nil, fmt.Errorf("unknown linkedin type: %s", minimal.QueryType) } - - return linkedInArgs, nil } func unmarshalRedditArguments(jobType types.JobType, args map[string]any) (*RedditArguments, error) { diff --git a/args/unmarshaller_test.go b/args/unmarshaller_test.go index 4231cbd..03baaca 100644 --- a/args/unmarshaller_test.go +++ b/args/unmarshaller_test.go @@ -51,7 +51,7 @@ var _ = Describe("Unmarshaller", func() { Expect(err).ToNot(HaveOccurred()) twitterArgs, ok := jobArgs.(*args.TwitterSearchArguments) Expect(ok).To(BeTrue()) - Expect(twitterArgs.QueryType).To(Equal("searchbyquery")) + Expect(twitterArgs.QueryType).To(Equal(types.CapSearchByQuery)) Expect(twitterArgs.Query).To(Equal("golang")) Expect(twitterArgs.Count).To(Equal(10)) }) diff --git a/types/jobs.go b/types/jobs.go index 753a970..628cb46 100644 --- a/types/jobs.go +++ b/types/jobs.go @@ -111,7 +111,6 @@ const ( var ( AlwaysAvailableTelemetryCaps = []Capability{CapTelemetry, CapEmpty} AlwaysAvailableTiktokCaps = []Capability{CapTranscription, CapEmpty} - AlwaysAvailableLinkedInCaps = []Capability{CapSearchByQuery, CapGetProfile, CapEmpty} // AlwaysAvailableCapabilities defines the job capabilities that are always available regardless of configuration AlwaysAvailableCapabilities = WorkerCapabilities{ @@ -142,6 +141,9 @@ var ( // WebCaps are all the Web capabilities (only available with Apify) WebCaps = []Capability{CapScraper, CapEmpty} + + // LinkedInCaps are all the LinkedIn capabilities (only available with Apify) + LinkedInCaps = []Capability{CapSearchByProfile} ) // JobCapabilityMap defines which capabilities are valid for each job type @@ -163,6 +165,9 @@ var JobCapabilityMap = map[JobType][]Capability{ // Web job capabilities WebJob: WebCaps, + // LinkedIn job capabilities + LinkedInJob: LinkedInCaps, + // TikTok job capabilities TiktokJob: combineCapabilities( AlwaysAvailableTiktokCaps, @@ -186,4 +191,5 @@ var JobDefaultCapabilityMap = map[JobType]Capability{ TiktokJob: CapTranscription, RedditJob: CapScrapeUrls, TelemetryJob: CapTelemetry, + LinkedInJob: CapSearchByProfile, } diff --git a/types/linkedin.go b/types/linkedin.go deleted file mode 100644 index b5c050a..0000000 --- a/types/linkedin.go +++ /dev/null @@ -1,52 +0,0 @@ -// Package types provides shared types between tee-worker and tee-indexer -package types - -// LinkedInProfileResult defines the structure of a LinkedIn profile search result -type LinkedInProfileResult struct { - PublicIdentifier string `json:"public_identifier"` // Username/slug in profile URL - URN string `json:"urn"` // LinkedIn's unique resource name - FullName string `json:"full_name"` // Person's full name - Headline string `json:"headline"` // Professional headline/title - Location string `json:"location"` // Geographic location - ProfileURL string `json:"profile_url"` // Full LinkedIn profile URL - Degree string `json:"degree,omitempty"` // Connection degree (1st, 2nd, etc.) -} - -// Experience defines the structure for a single entry in a user's work experience -type Experience struct { - Title string `json:"title"` - CompanyName string `json:"company_name"` - Location string `json:"location,omitempty"` - StartDate string `json:"start_date,omitempty"` - EndDate string `json:"end_date,omitempty"` - Description string `json:"description,omitempty"` -} - -// Education defines the structure for a single entry in a user's education history -type Education struct { - SchoolName string `json:"school_name"` - DegreeName string `json:"degree_name,omitempty"` - FieldOfStudy string `json:"field_of_study,omitempty"` - StartDate string `json:"start_date,omitempty"` - EndDate string `json:"end_date,omitempty"` - Description string `json:"description,omitempty"` -} - -// Skill defines the structure for a single skill entry -type Skill struct { - Name string `json:"name"` -} - -// LinkedInFullProfileResult defines the structure for a detailed LinkedIn profile -type LinkedInFullProfileResult struct { - PublicIdentifier string `json:"public_identifier"` - URN string `json:"urn"` - FullName string `json:"full_name"` - Headline string `json:"headline"` - Location string `json:"location"` - Summary string `json:"summary,omitempty"` - ProfilePictureURL string `json:"profile_picture_url,omitempty"` - Experiences []Experience `json:"experiences,omitempty"` - Education []Education `json:"education,omitempty"` - Skills []Skill `json:"skills,omitempty"` -} diff --git a/types/linkedin/experiences/experiences.go b/types/linkedin/experiences/experiences.go new file mode 100644 index 0000000..e714b58 --- /dev/null +++ b/types/linkedin/experiences/experiences.go @@ -0,0 +1,41 @@ +package experiences + +import "github.com/masa-finance/tee-types/pkg/util" + +// id represents a LinkedIn experience level identifier +type Id string + +// Experience level constants +const ( + LessThanAYear Id = "1" + OneToTwoYears Id = "2" + ThreeToFiveYears Id = "3" + SixToTenYears Id = "4" + MoreThanTenYears Id = "5" +) + +var All = util.NewSet( + LessThanAYear, + OneToTwoYears, + ThreeToFiveYears, + SixToTenYears, + MoreThanTenYears, +) + +type ExperiencesConfig struct { + All util.Set[Id] + LessThanAYear Id + OneToTwoYears Id + ThreeToFiveYears Id + SixToTenYears Id + MoreThanTenYears Id +} + +var Experiences = ExperiencesConfig{ + All: *All, + LessThanAYear: LessThanAYear, + OneToTwoYears: OneToTwoYears, + ThreeToFiveYears: ThreeToFiveYears, + SixToTenYears: SixToTenYears, + MoreThanTenYears: MoreThanTenYears, +} diff --git a/types/linkedin/functions/functions.go b/types/linkedin/functions/functions.go new file mode 100644 index 0000000..cd08f33 --- /dev/null +++ b/types/linkedin/functions/functions.go @@ -0,0 +1,121 @@ +package functions + +import "github.com/masa-finance/tee-types/pkg/util" + +// id represents a LinkedIn function identifier +type Id string + +// Function constants +const ( + Accounting Id = "1" + Administrative Id = "2" + ArtsAndDesign Id = "3" + BusinessDevelopment Id = "4" + CommunityAndSocialServices Id = "5" + Consulting Id = "6" + Education Id = "7" + Engineering Id = "8" + Entrepreneurship Id = "9" + Finance Id = "10" + HealthcareServices Id = "11" + HumanResources Id = "12" + InformationTechnology Id = "13" + Legal Id = "14" + Marketing Id = "15" + MediaAndCommunication Id = "16" + MilitaryAndProtectiveServices Id = "17" + Operations Id = "18" + ProductManagement Id = "19" + ProgramAndProjectManagement Id = "20" + Purchasing Id = "21" + QualityAssurance Id = "22" + RealEstate Id = "23" + Research Id = "24" + Sales Id = "25" +) + +var All = util.NewSet( + Accounting, + Administrative, + ArtsAndDesign, + BusinessDevelopment, + CommunityAndSocialServices, + Consulting, + Education, + Engineering, + Entrepreneurship, + Finance, + HealthcareServices, + HumanResources, + InformationTechnology, + Legal, + Marketing, + MediaAndCommunication, + MilitaryAndProtectiveServices, + Operations, + ProductManagement, + ProgramAndProjectManagement, + Purchasing, + QualityAssurance, + RealEstate, + Research, + Sales, +) + +type FunctionsConfig struct { + All util.Set[Id] + Accounting Id + Administrative Id + ArtsAndDesign Id + BusinessDevelopment Id + CommunityAndSocialServices Id + Consulting Id + Education Id + Engineering Id + Entrepreneurship Id + Finance Id + HealthcareServices Id + HumanResources Id + InformationTechnology Id + Legal Id + Marketing Id + MediaAndCommunication Id + MilitaryAndProtectiveServices Id + Operations Id + ProductManagement Id + ProgramAndProjectManagement Id + Purchasing Id + QualityAssurance Id + RealEstate Id + Research Id + Sales Id +} + +var Functions = FunctionsConfig{ + All: *All, + Accounting: Accounting, + Administrative: Administrative, + ArtsAndDesign: ArtsAndDesign, + BusinessDevelopment: BusinessDevelopment, + CommunityAndSocialServices: CommunityAndSocialServices, + Consulting: Consulting, + Education: Education, + Engineering: Engineering, + Entrepreneurship: Entrepreneurship, + Finance: Finance, + HealthcareServices: HealthcareServices, + HumanResources: HumanResources, + InformationTechnology: InformationTechnology, + Legal: Legal, + Marketing: Marketing, + MediaAndCommunication: MediaAndCommunication, + MilitaryAndProtectiveServices: MilitaryAndProtectiveServices, + Operations: Operations, + ProductManagement: ProductManagement, + ProgramAndProjectManagement: ProgramAndProjectManagement, + Purchasing: Purchasing, + QualityAssurance: QualityAssurance, + RealEstate: RealEstate, + Research: Research, + Sales: Sales, +} diff --git a/types/linkedin/industries/industries.go b/types/linkedin/industries/industries.go new file mode 100644 index 0000000..772ad9d --- /dev/null +++ b/types/linkedin/industries/industries.go @@ -0,0 +1,717 @@ +package industries + +import "github.com/masa-finance/tee-types/pkg/util" + +// Id represents a LinkedIn industry identifier +type Id string + +// Industry constants +const ( + // Technology & Software + SoftwareDevelopment Id = "4" + ComputerHardwareManufacturing Id = "3" + ComputerNetworkingProducts Id = "5" + ItServicesAndItConsulting Id = "96" + ComputerAndNetworkSecurity Id = "118" + Telecommunications Id = "8" + WirelessServices Id = "119" + TechnologyInformationAndInternet Id = "6" + DataInfrastructureAndAnalytics Id = "2458" + InformationServices Id = "84" + InternetPublishing Id = "3132" + SocialNetworkingPlatforms Id = "3127" + ComputerGames Id = "109" + MobileGamingApps Id = "3131" + BlockchainServices Id = "3134" + BusinessIntelligencePlatforms Id = "3128" + + // Financial Services + FinancialServices Id = "43" + Banking Id = "41" + Insurance Id = "42" + InvestmentBanking Id = "45" + CapitalMarkets Id = "129" + VentureCapitalAndPrivateEquityPrincipals Id = "106" + SecuritiesAndCommodityExchanges Id = "1713" + FundsAndTrusts Id = "1742" + + // Healthcare & Medical + Hospitals Id = "2081" + MedicalPractices Id = "13" + MedicalEquipmentManufacturing Id = "17" + PublicHealth Id = "2358" + VeterinaryServices Id = "16" + BiotechnologyResearch Id = "12" + + // Manufacturing + Manufacturing Id = "25" + ComputersAndElectronicsManufacturing Id = "24" + SemiconductorManufacturing Id = "7" + MachineryManufacturing Id = "55" + IndustrialMachineryManufacturing Id = "135" + FoodAndBeverageManufacturing Id = "23" + TextileManufacturing Id = "60" + MotorVehicleManufacturing Id = "53" + MotorVehiclePartsManufacturing Id = "1042" + AviationAndAerospaceComponentManufacturing Id = "52" + DefenseAndSpaceManufacturing Id = "1" + PlasticsManufacturing Id = "117" + RubberProductsManufacturing Id = "763" + PaperAndForestProductManufacturing Id = "61" + WoodProductManufacturing Id = "784" + FurnitureAndHomeFurnishingsManufacturing Id = "26" + SportingGoodsManufacturing Id = "20" + PrintingServices Id = "83" + + // Retail & Consumer Goods + Retail Id = "27" + RetailGroceries Id = "22" + OnlineAndMailOrderRetail Id = "1445" + RetailApparelAndFashion Id = "19" + RetailAppliancesElectricalAndElectronicEquipment Id = "1319" + RetailBooksAndPrintedNews Id = "1409" + RetailBuildingMaterialsAndGardenEquipment Id = "1324" + RetailFurnitureAndHomeFurnishings Id = "1309" + RetailHealthAndPersonalCareProducts Id = "1359" + RetailLuxuryGoodsAndJewelry Id = "143" + RetailMotorVehicles Id = "1292" + RetailOfficeEquipment Id = "138" + RetailOfficeSuppliesAndGifts Id = "1424" + + // Professional Services + ProfessionalServices Id = "1810" + Accounting Id = "47" + LegalServices Id = "10" + LawPractice Id = "9" + BusinessConsultingAndServices Id = "11" + StrategicManagementServices Id = "102" + HumanResourcesServices Id = "137" + MarketingServices Id = "1862" + AdvertisingServices Id = "80" + PublicRelationsAndCommunicationsServices Id = "98" + MarketResearch Id = "97" + ArchitectureAndPlanning Id = "50" + DesignServices Id = "99" + GraphicDesign Id = "140" + InteriorDesign Id = "3126" + EngineeringServices Id = "3242" + EnvironmentalServices Id = "86" + ResearchServices Id = "70" + ThinkTanks Id = "130" + Photography Id = "136" + TranslationAndLocalization Id = "108" + WritingAndEditing Id = "103" + + // Education + Education Id = "1999" + HigherEducation Id = "68" + ProfessionalTrainingAndCoaching Id = "105" + SportsAndRecreationInstruction Id = "2027" + + // Transportation & Logistics + TransportationLogisticsSupplyChainAndStorage Id = "116" + AirlinesAndAviation Id = "94" + FreightAndPackageTransportation Id = "87" + MaritimeTransportation Id = "95" + RailTransportation Id = "1481" + TruckTransportation Id = "92" + WarehousingAndStorage Id = "93" + PostalServices Id = "1573" + + // Energy & Utilities + Utilities Id = "59" + ElectricPowerGeneration Id = "383" + RenewableEnergyPowerGeneration Id = "3240" + OilAndGas Id = "57" + Mining Id = "56" + OilGasAndMining Id = "332" + + // Media & Entertainment + TechnologyInformationAndMedia Id = "1594" + BroadcastMediaProductionAndDistribution Id = "36" + RadioAndTelevisionBroadcasting Id = "1633" + MoviesVideosAndSound Id = "35" + MediaProduction Id = "126" + SoundRecording Id = "1623" + BookAndPeriodicalPublishing Id = "82" + NewspaperPublishing Id = "81" + PeriodicalPublishing Id = "1600" + EntertainmentProviders Id = "28" + ArtistsAndWriters Id = "38" + Musicians Id = "115" + + // Construction & Real Estate + Construction Id = "48" + CivilEngineering Id = "51" + RealEstate Id = "44" + RealEstateAgentsAndBrokers Id = "1770" + + // Hospitality & Services + Hospitality Id = "31" + HotelsAndMotels Id = "2194" + Restaurants Id = "32" + FoodAndBeverageServices Id = "34" + TravelArrangements Id = "30" + EventsServices Id = "110" + WellnessAndFitnessServices Id = "124" + ConsumerServices Id = "91" + + // Government & Non-Profit + ArmedForces Id = "71" + GovernmentRelationsServices Id = "148" + NonProfitOrganizations Id = "100" + CivicAndSocialOrganizations Id = "90" + PoliticalOrganizations Id = "107" + ProfessionalOrganizations Id = "1911" + Fundraising Id = "101" + + // Wholesale & Distribution + Wholesale Id = "133" + WholesaleImportAndExport Id = "134" + WholesaleComputerEquipment Id = "1157" + WholesaleFoodAndBeverage Id = "1231" + WholesaleBuildingMaterials Id = "49" + WholesaleMachinery Id = "1187" + WholesaleMotorVehiclesAndParts Id = "1128" + + // Other Services + StaffingAndRecruiting Id = "104" + ExecutiveSearchServices Id = "1923" + OfficeAdministration Id = "1916" + SecurityAndInvestigations Id = "121" + EquipmentRentalServices Id = "1779" + Libraries Id = "85" +) + +var All = util.NewSet( + // Technology & Software + SoftwareDevelopment, + ComputerHardwareManufacturing, + ComputerNetworkingProducts, + ItServicesAndItConsulting, + ComputerAndNetworkSecurity, + Telecommunications, + WirelessServices, + TechnologyInformationAndInternet, + DataInfrastructureAndAnalytics, + InformationServices, + InternetPublishing, + SocialNetworkingPlatforms, + ComputerGames, + MobileGamingApps, + BlockchainServices, + BusinessIntelligencePlatforms, + + // Financial Services + FinancialServices, + Banking, + Insurance, + InvestmentBanking, + CapitalMarkets, + VentureCapitalAndPrivateEquityPrincipals, + SecuritiesAndCommodityExchanges, + FundsAndTrusts, + + // Healthcare & Medical + Hospitals, + MedicalPractices, + MedicalEquipmentManufacturing, + PublicHealth, + VeterinaryServices, + BiotechnologyResearch, + + // Manufacturing + Manufacturing, + ComputersAndElectronicsManufacturing, + SemiconductorManufacturing, + MachineryManufacturing, + IndustrialMachineryManufacturing, + FoodAndBeverageManufacturing, + TextileManufacturing, + MotorVehicleManufacturing, + MotorVehiclePartsManufacturing, + AviationAndAerospaceComponentManufacturing, + DefenseAndSpaceManufacturing, + PlasticsManufacturing, + RubberProductsManufacturing, + PaperAndForestProductManufacturing, + WoodProductManufacturing, + FurnitureAndHomeFurnishingsManufacturing, + SportingGoodsManufacturing, + PrintingServices, + + // Retail & Consumer Goods + Retail, + RetailGroceries, + OnlineAndMailOrderRetail, + RetailApparelAndFashion, + RetailAppliancesElectricalAndElectronicEquipment, + RetailBooksAndPrintedNews, + RetailBuildingMaterialsAndGardenEquipment, + RetailFurnitureAndHomeFurnishings, + RetailHealthAndPersonalCareProducts, + RetailLuxuryGoodsAndJewelry, + RetailMotorVehicles, + RetailOfficeEquipment, + RetailOfficeSuppliesAndGifts, + + // Professional Services + ProfessionalServices, + Accounting, + LegalServices, + LawPractice, + BusinessConsultingAndServices, + StrategicManagementServices, + HumanResourcesServices, + MarketingServices, + AdvertisingServices, + PublicRelationsAndCommunicationsServices, + MarketResearch, + ArchitectureAndPlanning, + DesignServices, + GraphicDesign, + InteriorDesign, + EngineeringServices, + EnvironmentalServices, + ResearchServices, + ThinkTanks, + Photography, + TranslationAndLocalization, + WritingAndEditing, + + // Education + Education, + HigherEducation, + ProfessionalTrainingAndCoaching, + SportsAndRecreationInstruction, + + // Transportation & Logistics + TransportationLogisticsSupplyChainAndStorage, + AirlinesAndAviation, + FreightAndPackageTransportation, + MaritimeTransportation, + RailTransportation, + TruckTransportation, + WarehousingAndStorage, + PostalServices, + + // Energy & Utilities + Utilities, + ElectricPowerGeneration, + RenewableEnergyPowerGeneration, + OilAndGas, + Mining, + OilGasAndMining, + + // Media & Entertainment + TechnologyInformationAndMedia, + BroadcastMediaProductionAndDistribution, + RadioAndTelevisionBroadcasting, + MoviesVideosAndSound, + MediaProduction, + SoundRecording, + BookAndPeriodicalPublishing, + NewspaperPublishing, + PeriodicalPublishing, + EntertainmentProviders, + ArtistsAndWriters, + Musicians, + + // Construction & Real Estate + Construction, + CivilEngineering, + RealEstate, + RealEstateAgentsAndBrokers, + + // Hospitality & Services + Hospitality, + HotelsAndMotels, + Restaurants, + FoodAndBeverageServices, + TravelArrangements, + EventsServices, + WellnessAndFitnessServices, + ConsumerServices, + + // Government & Non-Profit + ArmedForces, + GovernmentRelationsServices, + NonProfitOrganizations, + CivicAndSocialOrganizations, + PoliticalOrganizations, + ProfessionalOrganizations, + Fundraising, + + // Wholesale & Distribution + Wholesale, + WholesaleImportAndExport, + WholesaleComputerEquipment, + WholesaleFoodAndBeverage, + WholesaleBuildingMaterials, + WholesaleMachinery, + WholesaleMotorVehiclesAndParts, + + // Other Services + StaffingAndRecruiting, + ExecutiveSearchServices, + OfficeAdministration, + SecurityAndInvestigations, + EquipmentRentalServices, + Libraries, +) + +type IndustriesConfig struct { + All util.Set[Id] + // Technology & Software + SoftwareDevelopment Id + ComputerHardwareManufacturing Id + ComputerNetworkingProducts Id + ItServicesAndItConsulting Id + ComputerAndNetworkSecurity Id + Telecommunications Id + WirelessServices Id + TechnologyInformationAndInternet Id + DataInfrastructureAndAnalytics Id + InformationServices Id + InternetPublishing Id + SocialNetworkingPlatforms Id + ComputerGames Id + MobileGamingApps Id + BlockchainServices Id + BusinessIntelligencePlatforms Id + + // Financial Services + FinancialServices Id + Banking Id + Insurance Id + InvestmentBanking Id + CapitalMarkets Id + VentureCapitalAndPrivateEquityPrincipals Id + SecuritiesAndCommodityExchanges Id + FundsAndTrusts Id + + // Healthcare & Medical + Hospitals Id + MedicalPractices Id + MedicalEquipmentManufacturing Id + PublicHealth Id + VeterinaryServices Id + BiotechnologyResearch Id + + // Manufacturing + Manufacturing Id + ComputersAndElectronicsManufacturing Id + SemiconductorManufacturing Id + MachineryManufacturing Id + IndustrialMachineryManufacturing Id + FoodAndBeverageManufacturing Id + TextileManufacturing Id + MotorVehicleManufacturing Id + MotorVehiclePartsManufacturing Id + AviationAndAerospaceComponentManufacturing Id + DefenseAndSpaceManufacturing Id + PlasticsManufacturing Id + RubberProductsManufacturing Id + PaperAndForestProductManufacturing Id + WoodProductManufacturing Id + FurnitureAndHomeFurnishingsManufacturing Id + SportingGoodsManufacturing Id + PrintingServices Id + + // Retail & Consumer Goods + Retail Id + RetailGroceries Id + OnlineAndMailOrderRetail Id + RetailApparelAndFashion Id + RetailAppliancesElectricalAndElectronicEquipment Id + RetailBooksAndPrintedNews Id + RetailBuildingMaterialsAndGardenEquipment Id + RetailFurnitureAndHomeFurnishings Id + RetailHealthAndPersonalCareProducts Id + RetailLuxuryGoodsAndJewelry Id + RetailMotorVehicles Id + RetailOfficeEquipment Id + RetailOfficeSuppliesAndGifts Id + + // Professional Services + ProfessionalServices Id + Accounting Id + LegalServices Id + LawPractice Id + BusinessConsultingAndServices Id + StrategicManagementServices Id + HumanResourcesServices Id + MarketingServices Id + AdvertisingServices Id + PublicRelationsAndCommunicationsServices Id + MarketResearch Id + ArchitectureAndPlanning Id + DesignServices Id + GraphicDesign Id + InteriorDesign Id + EngineeringServices Id + EnvironmentalServices Id + ResearchServices Id + ThinkTanks Id + Photography Id + TranslationAndLocalization Id + WritingAndEditing Id + + // Education + Education Id + HigherEducation Id + ProfessionalTrainingAndCoaching Id + SportsAndRecreationInstruction Id + + // Transportation & Logistics + TransportationLogisticsSupplyChainAndStorage Id + AirlinesAndAviation Id + FreightAndPackageTransportation Id + MaritimeTransportation Id + RailTransportation Id + TruckTransportation Id + WarehousingAndStorage Id + PostalServices Id + + // Energy & Utilities + Utilities Id + ElectricPowerGeneration Id + RenewableEnergyPowerGeneration Id + OilAndGas Id + Mining Id + OilGasAndMining Id + + // Media & Entertainment + TechnologyInformationAndMedia Id + BroadcastMediaProductionAndDistribution Id + RadioAndTelevisionBroadcasting Id + MoviesVideosAndSound Id + MediaProduction Id + SoundRecording Id + BookAndPeriodicalPublishing Id + NewspaperPublishing Id + PeriodicalPublishing Id + EntertainmentProviders Id + ArtistsAndWriters Id + Musicians Id + + // Construction & Real Estate + Construction Id + CivilEngineering Id + RealEstate Id + RealEstateAgentsAndBrokers Id + + // Hospitality & Services + Hospitality Id + HotelsAndMotels Id + Restaurants Id + FoodAndBeverageServices Id + TravelArrangements Id + EventsServices Id + WellnessAndFitnessServices Id + ConsumerServices Id + + // Government & Non-Profit + ArmedForces Id + GovernmentRelationsServices Id + NonProfitOrganizations Id + CivicAndSocialOrganizations Id + PoliticalOrganizations Id + ProfessionalOrganizations Id + Fundraising Id + + // Wholesale & Distribution + Wholesale Id + WholesaleImportAndExport Id + WholesaleComputerEquipment Id + WholesaleFoodAndBeverage Id + WholesaleBuildingMaterials Id + WholesaleMachinery Id + WholesaleMotorVehiclesAndParts Id + + // Other Services + StaffingAndRecruiting Id + ExecutiveSearchServices Id + OfficeAdministration Id + SecurityAndInvestigations Id + EquipmentRentalServices Id + Libraries Id +} + +var Industries = IndustriesConfig{ + All: *All, + // Technology & Software + SoftwareDevelopment: SoftwareDevelopment, + ComputerHardwareManufacturing: ComputerHardwareManufacturing, + ComputerNetworkingProducts: ComputerNetworkingProducts, + ItServicesAndItConsulting: ItServicesAndItConsulting, + ComputerAndNetworkSecurity: ComputerAndNetworkSecurity, + Telecommunications: Telecommunications, + WirelessServices: WirelessServices, + TechnologyInformationAndInternet: TechnologyInformationAndInternet, + DataInfrastructureAndAnalytics: DataInfrastructureAndAnalytics, + InformationServices: InformationServices, + InternetPublishing: InternetPublishing, + SocialNetworkingPlatforms: SocialNetworkingPlatforms, + ComputerGames: ComputerGames, + MobileGamingApps: MobileGamingApps, + BlockchainServices: BlockchainServices, + BusinessIntelligencePlatforms: BusinessIntelligencePlatforms, + + // Financial Services + FinancialServices: FinancialServices, + Banking: Banking, + Insurance: Insurance, + InvestmentBanking: InvestmentBanking, + CapitalMarkets: CapitalMarkets, + VentureCapitalAndPrivateEquityPrincipals: VentureCapitalAndPrivateEquityPrincipals, + SecuritiesAndCommodityExchanges: SecuritiesAndCommodityExchanges, + FundsAndTrusts: FundsAndTrusts, + + // Healthcare & Medical + Hospitals: Hospitals, + MedicalPractices: MedicalPractices, + MedicalEquipmentManufacturing: MedicalEquipmentManufacturing, + PublicHealth: PublicHealth, + VeterinaryServices: VeterinaryServices, + BiotechnologyResearch: BiotechnologyResearch, + + // Manufacturing + Manufacturing: Manufacturing, + ComputersAndElectronicsManufacturing: ComputersAndElectronicsManufacturing, + SemiconductorManufacturing: SemiconductorManufacturing, + MachineryManufacturing: MachineryManufacturing, + IndustrialMachineryManufacturing: IndustrialMachineryManufacturing, + FoodAndBeverageManufacturing: FoodAndBeverageManufacturing, + TextileManufacturing: TextileManufacturing, + MotorVehicleManufacturing: MotorVehicleManufacturing, + MotorVehiclePartsManufacturing: MotorVehiclePartsManufacturing, + AviationAndAerospaceComponentManufacturing: AviationAndAerospaceComponentManufacturing, + DefenseAndSpaceManufacturing: DefenseAndSpaceManufacturing, + PlasticsManufacturing: PlasticsManufacturing, + RubberProductsManufacturing: RubberProductsManufacturing, + PaperAndForestProductManufacturing: PaperAndForestProductManufacturing, + WoodProductManufacturing: WoodProductManufacturing, + FurnitureAndHomeFurnishingsManufacturing: FurnitureAndHomeFurnishingsManufacturing, + SportingGoodsManufacturing: SportingGoodsManufacturing, + PrintingServices: PrintingServices, + + // Retail & Consumer Goods + Retail: Retail, + RetailGroceries: RetailGroceries, + OnlineAndMailOrderRetail: OnlineAndMailOrderRetail, + RetailApparelAndFashion: RetailApparelAndFashion, + RetailAppliancesElectricalAndElectronicEquipment: RetailAppliancesElectricalAndElectronicEquipment, + RetailBooksAndPrintedNews: RetailBooksAndPrintedNews, + RetailBuildingMaterialsAndGardenEquipment: RetailBuildingMaterialsAndGardenEquipment, + RetailFurnitureAndHomeFurnishings: RetailFurnitureAndHomeFurnishings, + RetailHealthAndPersonalCareProducts: RetailHealthAndPersonalCareProducts, + RetailLuxuryGoodsAndJewelry: RetailLuxuryGoodsAndJewelry, + RetailMotorVehicles: RetailMotorVehicles, + RetailOfficeEquipment: RetailOfficeEquipment, + RetailOfficeSuppliesAndGifts: RetailOfficeSuppliesAndGifts, + + // Professional Services + ProfessionalServices: ProfessionalServices, + Accounting: Accounting, + LegalServices: LegalServices, + LawPractice: LawPractice, + BusinessConsultingAndServices: BusinessConsultingAndServices, + StrategicManagementServices: StrategicManagementServices, + HumanResourcesServices: HumanResourcesServices, + MarketingServices: MarketingServices, + AdvertisingServices: AdvertisingServices, + PublicRelationsAndCommunicationsServices: PublicRelationsAndCommunicationsServices, + MarketResearch: MarketResearch, + ArchitectureAndPlanning: ArchitectureAndPlanning, + DesignServices: DesignServices, + GraphicDesign: GraphicDesign, + InteriorDesign: InteriorDesign, + EngineeringServices: EngineeringServices, + EnvironmentalServices: EnvironmentalServices, + ResearchServices: ResearchServices, + ThinkTanks: ThinkTanks, + Photography: Photography, + TranslationAndLocalization: TranslationAndLocalization, + WritingAndEditing: WritingAndEditing, + + // Education + Education: Education, + HigherEducation: HigherEducation, + ProfessionalTrainingAndCoaching: ProfessionalTrainingAndCoaching, + SportsAndRecreationInstruction: SportsAndRecreationInstruction, + + // Transportation & Logistics + TransportationLogisticsSupplyChainAndStorage: TransportationLogisticsSupplyChainAndStorage, + AirlinesAndAviation: AirlinesAndAviation, + FreightAndPackageTransportation: FreightAndPackageTransportation, + MaritimeTransportation: MaritimeTransportation, + RailTransportation: RailTransportation, + TruckTransportation: TruckTransportation, + WarehousingAndStorage: WarehousingAndStorage, + PostalServices: PostalServices, + + // Energy & Utilities + Utilities: Utilities, + ElectricPowerGeneration: ElectricPowerGeneration, + RenewableEnergyPowerGeneration: RenewableEnergyPowerGeneration, + OilAndGas: OilAndGas, + Mining: Mining, + OilGasAndMining: OilGasAndMining, + + // Media & Entertainment + TechnologyInformationAndMedia: TechnologyInformationAndMedia, + BroadcastMediaProductionAndDistribution: BroadcastMediaProductionAndDistribution, + RadioAndTelevisionBroadcasting: RadioAndTelevisionBroadcasting, + MoviesVideosAndSound: MoviesVideosAndSound, + MediaProduction: MediaProduction, + SoundRecording: SoundRecording, + BookAndPeriodicalPublishing: BookAndPeriodicalPublishing, + NewspaperPublishing: NewspaperPublishing, + PeriodicalPublishing: PeriodicalPublishing, + EntertainmentProviders: EntertainmentProviders, + ArtistsAndWriters: ArtistsAndWriters, + Musicians: Musicians, + + // Construction & Real Estate + Construction: Construction, + CivilEngineering: CivilEngineering, + RealEstate: RealEstate, + RealEstateAgentsAndBrokers: RealEstateAgentsAndBrokers, + + // Hospitality & Services + Hospitality: Hospitality, + HotelsAndMotels: HotelsAndMotels, + Restaurants: Restaurants, + FoodAndBeverageServices: FoodAndBeverageServices, + TravelArrangements: TravelArrangements, + EventsServices: EventsServices, + WellnessAndFitnessServices: WellnessAndFitnessServices, + ConsumerServices: ConsumerServices, + + // Government & Non-Profit + ArmedForces: ArmedForces, + GovernmentRelationsServices: GovernmentRelationsServices, + NonProfitOrganizations: NonProfitOrganizations, + CivicAndSocialOrganizations: CivicAndSocialOrganizations, + PoliticalOrganizations: PoliticalOrganizations, + ProfessionalOrganizations: ProfessionalOrganizations, + Fundraising: Fundraising, + + // Wholesale & Distribution + Wholesale: Wholesale, + WholesaleImportAndExport: WholesaleImportAndExport, + WholesaleComputerEquipment: WholesaleComputerEquipment, + WholesaleFoodAndBeverage: WholesaleFoodAndBeverage, + WholesaleBuildingMaterials: WholesaleBuildingMaterials, + WholesaleMachinery: WholesaleMachinery, + WholesaleMotorVehiclesAndParts: WholesaleMotorVehiclesAndParts, + + // Other Services + StaffingAndRecruiting: StaffingAndRecruiting, + ExecutiveSearchServices: ExecutiveSearchServices, + OfficeAdministration: OfficeAdministration, + SecurityAndInvestigations: SecurityAndInvestigations, + EquipmentRentalServices: EquipmentRentalServices, + Libraries: Libraries, +} diff --git a/types/linkedin/linkedin.go b/types/linkedin/linkedin.go new file mode 100644 index 0000000..80e0ff8 --- /dev/null +++ b/types/linkedin/linkedin.go @@ -0,0 +1,25 @@ +package linkedin + +import ( + "github.com/masa-finance/tee-types/types/linkedin/experiences" + "github.com/masa-finance/tee-types/types/linkedin/functions" + "github.com/masa-finance/tee-types/types/linkedin/industries" + "github.com/masa-finance/tee-types/types/linkedin/profile" + "github.com/masa-finance/tee-types/types/linkedin/seniorities" +) + +type LinkedInConfig struct { + Experiences *experiences.ExperiencesConfig + Seniorities *seniorities.SenioritiesConfig + Functions *functions.FunctionsConfig + Industries *industries.IndustriesConfig +} + +var LinkedIn = LinkedInConfig{ + Experiences: &experiences.Experiences, + Seniorities: &seniorities.Seniorities, + Functions: &functions.Functions, + Industries: &industries.Industries, +} + +type Profile = *profile.Profile diff --git a/types/linkedin/linkedin_suite_test.go b/types/linkedin/linkedin_suite_test.go new file mode 100644 index 0000000..8f46e3d --- /dev/null +++ b/types/linkedin/linkedin_suite_test.go @@ -0,0 +1,13 @@ +package linkedin_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestTypes(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Types Suite") +} diff --git a/types/linkedin/linkedin_test.go b/types/linkedin/linkedin_test.go new file mode 100644 index 0000000..3ce35ae --- /dev/null +++ b/types/linkedin/linkedin_test.go @@ -0,0 +1,135 @@ +package linkedin_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/masa-finance/tee-types/types" + "github.com/masa-finance/tee-types/types/linkedin/experiences" + "github.com/masa-finance/tee-types/types/linkedin/functions" + "github.com/masa-finance/tee-types/types/linkedin/industries" + "github.com/masa-finance/tee-types/types/linkedin/seniorities" +) + +var _ = Describe("LinkedIn Types", func() { + Describe("LinkedIn Package", func() { + It("should have all required fields", func() { + linkedin := types.LinkedIn + + Expect(linkedin.Seniorities).ToNot(BeNil()) + Expect(linkedin.Experiences).ToNot(BeNil()) + Expect(linkedin.Functions).ToNot(BeNil()) + Expect(linkedin.Industries).ToNot(BeNil()) + }) + }) + + Describe("Seniorities", func() { + It("should have all seniority levels", func() { + s := types.LinkedIn.Seniorities + + Expect(s.InTraining).To(Equal(seniorities.InTraining)) + Expect(s.EntryLevel).To(Equal(seniorities.EntryLevel)) + Expect(s.Senior).To(Equal(seniorities.Senior)) + Expect(s.Strategic).To(Equal(seniorities.Strategic)) + Expect(s.EntryLevelManager).To(Equal(seniorities.EntryLevelManager)) + Expect(s.ExperiencedManager).To(Equal(seniorities.ExperiencedManager)) + Expect(s.Director).To(Equal(seniorities.Director)) + Expect(s.VicePresident).To(Equal(seniorities.VicePresident)) + Expect(s.CXO).To(Equal(seniorities.CXO)) + Expect(s.Partner).To(Equal(seniorities.Partner)) + }) + + It("should have All set containing all seniorities", func() { + all := types.LinkedIn.Seniorities.All + + Expect(all.Contains(seniorities.InTraining)).To(BeTrue()) + Expect(all.Contains(seniorities.EntryLevel)).To(BeTrue()) + Expect(all.Contains(seniorities.Senior)).To(BeTrue()) + Expect(all.Contains(seniorities.Strategic)).To(BeTrue()) + Expect(all.Contains(seniorities.EntryLevelManager)).To(BeTrue()) + Expect(all.Contains(seniorities.ExperiencedManager)).To(BeTrue()) + Expect(all.Contains(seniorities.Director)).To(BeTrue()) + Expect(all.Contains(seniorities.VicePresident)).To(BeTrue()) + Expect(all.Contains(seniorities.CXO)).To(BeTrue()) + Expect(all.Contains(seniorities.Partner)).To(BeTrue()) + + Expect(all.Length()).To(Equal(10)) + }) + }) + + Describe("Experiences", func() { + It("should have all experience levels", func() { + e := types.LinkedIn.Experiences + + Expect(e.LessThanAYear).To(Equal(experiences.LessThanAYear)) + Expect(e.OneToTwoYears).To(Equal(experiences.OneToTwoYears)) + Expect(e.ThreeToFiveYears).To(Equal(experiences.ThreeToFiveYears)) + Expect(e.SixToTenYears).To(Equal(experiences.SixToTenYears)) + Expect(e.MoreThanTenYears).To(Equal(experiences.MoreThanTenYears)) + }) + + It("should have All set containing all experiences", func() { + all := types.LinkedIn.Experiences.All + + Expect(all.Contains(experiences.LessThanAYear)).To(BeTrue()) + Expect(all.Contains(experiences.OneToTwoYears)).To(BeTrue()) + Expect(all.Contains(experiences.ThreeToFiveYears)).To(BeTrue()) + Expect(all.Contains(experiences.SixToTenYears)).To(BeTrue()) + Expect(all.Contains(experiences.MoreThanTenYears)).To(BeTrue()) + + Expect(all.Length()).To(Equal(5)) + }) + }) + + Describe("Functions", func() { + It("should have all function types", func() { + f := types.LinkedIn.Functions + + Expect(f.Accounting).To(Equal(functions.Accounting)) + Expect(f.Engineering).To(Equal(functions.Engineering)) + Expect(f.Marketing).To(Equal(functions.Marketing)) + Expect(f.Sales).To(Equal(functions.Sales)) + Expect(f.HumanResources).To(Equal(functions.HumanResources)) + }) + + It("should have All set containing all functions", func() { + all := types.LinkedIn.Functions.All + + Expect(all.Contains(functions.Accounting)).To(BeTrue()) + Expect(all.Contains(functions.Engineering)).To(BeTrue()) + Expect(all.Contains(functions.Marketing)).To(BeTrue()) + Expect(all.Contains(functions.Sales)).To(BeTrue()) + Expect(all.Contains(functions.HumanResources)).To(BeTrue()) + Expect(all.Contains(functions.InformationTechnology)).To(BeTrue()) + Expect(all.Contains(functions.Finance)).To(BeTrue()) + + Expect(all.Length()).To(Equal(25)) + }) + }) + + Describe("Industries", func() { + It("should have all industry types", func() { + i := types.LinkedIn.Industries + + Expect(i.SoftwareDevelopment).To(Equal(industries.SoftwareDevelopment)) + Expect(i.FinancialServices).To(Equal(industries.FinancialServices)) + Expect(i.Manufacturing).To(Equal(industries.Manufacturing)) + Expect(i.Retail).To(Equal(industries.Retail)) + Expect(i.Education).To(Equal(industries.Education)) + }) + + It("should have All set containing all industries", func() { + all := types.LinkedIn.Industries.All + + Expect(all.Contains(industries.SoftwareDevelopment)).To(BeTrue()) + Expect(all.Contains(industries.FinancialServices)).To(BeTrue()) + Expect(all.Contains(industries.Manufacturing)).To(BeTrue()) + Expect(all.Contains(industries.Retail)).To(BeTrue()) + Expect(all.Contains(industries.Education)).To(BeTrue()) + Expect(all.Contains(industries.Hospitals)).To(BeTrue()) + Expect(all.Contains(industries.ProfessionalServices)).To(BeTrue()) + + Expect(all.Length()).To(BeNumerically(">=", 100)) // Should have many industries + }) + }) +}) diff --git a/types/linkedin/profile/profile.go b/types/linkedin/profile/profile.go new file mode 100644 index 0000000..e74439e --- /dev/null +++ b/types/linkedin/profile/profile.go @@ -0,0 +1,261 @@ +package profile + +import ( + "time" + + "github.com/masa-finance/tee-types/pkg/util" +) + +type ScraperMode string + +const ( + ScraperModeShort ScraperMode = "Short" + ScraperModeFull ScraperMode = "Full" + ScraperModeFullEmail ScraperMode = "Full + email search" +) + +var AllScraperModes = util.NewSet(ScraperModeShort, ScraperModeFull, ScraperModeFullEmail) + +// Profile represents a complete profile response +type Profile struct { + ID string `json:"id"` + PublicIdentifier string `json:"publicIdentifier,omitempty"` + URL string `json:"linkedinUrl"` + FirstName string `json:"firstName"` + LastName string `json:"lastName"` + Headline *string `json:"headline,omitempty"` + About *string `json:"about,omitempty"` + Summary *string `json:"summary,omitempty"` + OpenToWork bool `json:"openToWork,omitempty"` + OpenProfile bool `json:"openProfile,omitempty"` + Hiring bool `json:"hiring,omitempty"` + Photo *string `json:"photo,omitempty"` + PictureUrl *string `json:"pictureUrl,omitempty"` + Premium bool `json:"premium,omitempty"` + Influencer bool `json:"influencer,omitempty"` + Location Location `json:"location,omitempty"` + Verified bool `json:"verified,omitempty"` + RegisteredAt time.Time `json:"registeredAt,omitempty"` + TopSkills *string `json:"topSkills,omitempty"` + ConnectionsCount int `json:"connectionsCount,omitempty"` + FollowerCount int `json:"followerCount,omitempty"` + ComposeOptionType *string `json:"composeOptionType,omitempty"` + + // Full mode + CurrentPosition []CurrentPosition `json:"currentPosition,omitempty"` + + // Short mode + CurrentPositions []ShortCurrentPosition `json:"currentPositions,omitempty"` + + Experience []Experience `json:"experience,omitempty"` + Education []Education `json:"education,omitempty"` + Certifications []Certification `json:"certifications,omitempty"` + Projects []Project `json:"projects,omitempty"` + Volunteering []Volunteering `json:"volunteering,omitempty"` + ReceivedRecommendations []Recommendation `json:"receivedRecommendations,omitempty"` + Skills []Skill `json:"skills,omitempty"` + Courses []Course `json:"courses,omitempty"` + Publications []Publication `json:"publications,omitempty"` + Patents []Patent `json:"patents,omitempty"` + HonorsAndAwards []HonorAndAward `json:"honorsAndAwards,omitempty"` + Languages []Language `json:"languages,omitempty"` + Featured any `json:"featured,omitempty"` + MoreProfiles []MoreProfile `json:"moreProfiles,omitempty"` + + // Email mode + Emails []string `json:"emails,omitempty"` + CompanyWebsites []CompanyWebsite `json:"companyWebsites,omitempty"` +} + +// Location represents the location information +type Location struct { + Text string `json:"linkedinText"` + CountryCode string `json:"countryCode,omitempty"` + Parsed ParsedLocation `json:"parsed,omitempty"` +} + +// ParsedLocation represents the parsed location details +type ParsedLocation struct { + Text string `json:"text,omitempty"` + CountryCode string `json:"countryCode,omitempty"` + RegionCode *string `json:"regionCode,omitempty"` + Country string `json:"country,omitempty"` + CountryFull string `json:"countryFull,omitempty"` + State string `json:"state,omitempty"` + City string `json:"city,omitempty"` +} + +// CurrentPosition represents current position information +type CurrentPosition struct { + CompanyID *string `json:"companyId,omitempty"` + CompanyLinkedinUrl *string `json:"companyLinkedinUrl,omitempty"` + CompanyName string `json:"companyName"` + DateRange *DatePeriod `json:"dateRange,omitempty"` +} + +// Experience represents work experience +type Experience struct { + Position string `json:"position"` + Location *string `json:"location,omitempty"` + EmploymentType *string `json:"employmentType,omitempty"` + WorkplaceType *string `json:"workplaceType,omitempty"` + CompanyName string `json:"companyName"` + CompanyURL *string `json:"companyUrl,omitempty"` + CompanyID *string `json:"companyId,omitempty"` + CompanyUniversalName *string `json:"companyUniversalName,omitempty"` + Duration string `json:"duration"` + Description *string `json:"description,omitempty"` + Skills []string `json:"skills,omitempty"` + StartDate DateRange `json:"startDate"` + EndDate DateRange `json:"endDate"` +} + +// DateRange represents a date range with month, year, and text +type DateRange struct { + Month *string `json:"month,omitempty"` + Year *int `json:"year,omitempty"` + Text string `json:"text"` +} + +// Education represents educational background +type Education struct { + SchoolName string `json:"schoolName,omitempty"` + SchoolURL string `json:"schoolUrl,omitempty"` + Degree string `json:"degree,omitempty"` + FieldOfStudy *string `json:"fieldOfStudy,omitempty"` + Skills []string `json:"skills,omitempty"` + StartDate DateRange `json:"startDate,omitempty"` + EndDate DateRange `json:"endDate,omitempty"` + Period string `json:"period,omitempty"` +} + +// Certification represents a certification +type Certification struct { + Title string `json:"title,omitempty"` + IssuedAt string `json:"issuedAt,omitempty"` + IssuedBy string `json:"issuedBy,omitempty"` + IssuedByLink string `json:"issuedByLink,omitempty"` +} + +// Project represents a project +type Project struct { + Title string `json:"title,omitempty"` + Description string `json:"description,omitempty"` + Duration string `json:"duration,omitempty"` + StartDate DateRange `json:"startDate,omitempty"` + EndDate DateRange `json:"endDate,omitempty"` +} + +// Volunteering represents volunteer experience +type Volunteering struct { + Role string `json:"role,omitempty"` + Duration string `json:"duration,omitempty"` + StartDate *DateRange `json:"startDate,omitempty"` + EndDate *DateRange `json:"endDate,omitempty"` + OrganizationName string `json:"organizationName,omitempty"` + OrganizationURL *string `json:"organizationUrl,omitempty"` + Cause string `json:"cause,omitempty"` +} + +// Skill represents a skill with optional positions and endorsements +type Skill struct { + Name string `json:"name,omitempty"` + Positions []string `json:"positions,omitempty"` + Endorsements string `json:"endorsements,omitempty"` +} + +// Course represents a course +type Course struct { + Title string `json:"title,omitempty"` + AssociatedWith string `json:"associatedWith,omitempty"` + AssociatedWithLink string `json:"associatedWithLink,omitempty"` +} + +// Publication represents a publication +type Publication struct { + Title string `json:"title,omitempty"` + PublishedAt string `json:"publishedAt,omitempty"` + Link string `json:"link,omitempty"` +} + +// HonorAndAward represents an honor or award +type HonorAndAward struct { + Title string `json:"title,omitempty"` + IssuedBy string `json:"issuedBy,omitempty"` + IssuedAt string `json:"issuedAt,omitempty"` + Description string `json:"description,omitempty"` + AssociatedWith string `json:"associatedWith,omitempty"` + AssociatedWithLink string `json:"associatedWithLink,omitempty"` +} + +// Language represents a language with proficiency level +type Language struct { + Name string `json:"name,omitempty"` + Proficiency string `json:"proficiency,omitempty"` +} + +// MoreProfile represents a related profile +type MoreProfile struct { + ID string `json:"id,omitempty"` + FirstName string `json:"firstName,omitempty"` + LastName string `json:"lastName,omitempty"` + Position *string `json:"position,omitempty"` + PublicIdentifier string `json:"publicIdentifier,omitempty"` + URL string `json:"linkedinUrl,omitempty"` +} + +// ShortCurrentPosition represents the short profile current positions array +type ShortCurrentPosition struct { + TenureAtPosition *Tenure `json:"tenureAtPosition,omitempty"` + CompanyName string `json:"companyName,omitempty"` + Title *string `json:"title,omitempty"` + Current *bool `json:"current,omitempty"` + TenureAtCompany *Tenure `json:"tenureAtCompany,omitempty"` + StartedOn *StartedOn `json:"startedOn,omitempty"` + CompanyID *string `json:"companyId,omitempty"` + CompanyLinkedinUrl *string `json:"companyLinkedinUrl,omitempty"` +} + +type Tenure struct { + NumYears *int `json:"numYears,omitempty"` + NumMonths *int `json:"numMonths,omitempty"` +} + +type StartedOn struct { + Month int `json:"month,omitempty"` + Year int `json:"year,omitempty"` +} + +// DatePeriod represents a date period with optional start and end parts +type DatePeriod struct { + Start *DateParts `json:"start,omitempty"` + End *DateParts `json:"end,omitempty"` +} + +type DateParts struct { + Month *int `json:"month,omitempty"` + Year *int `json:"year,omitempty"` + Day *int `json:"day,omitempty"` +} + +// CompanyWebsite represents company website with validation hint +type CompanyWebsite struct { + URL string `json:"url,omitempty"` + Domain string `json:"domain,omitempty"` + ValidEmailServer *bool `json:"validEmailServer,omitempty"` +} + +// Recommendation captures received recommendations +type Recommendation struct { + GivenBy *string `json:"givenBy,omitempty"` + GivenByLink *string `json:"givenByLink,omitempty"` + GivenAt *string `json:"givenAt,omitempty"` + Description string `json:"description,omitempty"` +} + +// Patent represents a patent entry +type Patent struct { + Title string `json:"title,omitempty"` + Number *string `json:"number,omitempty"` + IssuedAt string `json:"issuedAt,omitempty"` +} diff --git a/types/linkedin/seniorities/seniorities.go b/types/linkedin/seniorities/seniorities.go new file mode 100644 index 0000000..98e978c --- /dev/null +++ b/types/linkedin/seniorities/seniorities.go @@ -0,0 +1,61 @@ +package seniorities + +import "github.com/masa-finance/tee-types/pkg/util" + +// id represents a LinkedIn seniority level identifier +type Id string + +// Seniority level constants +const ( + InTraining Id = "100" + EntryLevel Id = "110" + Senior Id = "120" + Strategic Id = "130" + EntryLevelManager Id = "200" + ExperiencedManager Id = "210" + Director Id = "220" + VicePresident Id = "300" + CXO Id = "310" + Partner Id = "320" +) + +var All = util.NewSet( + InTraining, + EntryLevel, + Senior, + Strategic, + EntryLevelManager, + ExperiencedManager, + Director, + VicePresident, + CXO, + Partner, +) + +type SenioritiesConfig struct { + All util.Set[Id] + InTraining Id + EntryLevel Id + Senior Id + Strategic Id + EntryLevelManager Id + ExperiencedManager Id + Director Id + VicePresident Id + CXO Id + Partner Id +} + +var Seniorities = SenioritiesConfig{ + All: *All, + InTraining: InTraining, + EntryLevel: EntryLevel, + Senior: Senior, + Strategic: Strategic, + EntryLevelManager: EntryLevelManager, + ExperiencedManager: ExperiencedManager, + Director: Director, + VicePresident: VicePresident, + CXO: CXO, + Partner: Partner, +} diff --git a/types/types.go b/types/types.go new file mode 100644 index 0000000..64b43d0 --- /dev/null +++ b/types/types.go @@ -0,0 +1,7 @@ +package types + +import ( + linkedin "github.com/masa-finance/tee-types/types/linkedin" +) + +var LinkedIn = linkedin.LinkedIn